Let your App share 2 billion!

Preface

Ok? What the hell is divided into 2 billion, see the picture below:


During the Chinese New Year, the icons of many apps have become 2 billion points and 1 billion points. Fortunately, my App does not have the function of updating icons. Wouldn’t it save 2 billion?

This penny, oh, no, this function, we should all know that it is definitely not updated through App update. During the festival, in order to update an icon and let users upgrade the App, it is estimated that they will be killed. The common name for this function is: dynamically replacing the App icon.

 

activity-alias

In fact, there are many ways to implement replacement icons, such as modifying or intercepting the system Launcher, but this method requires system permissions and is not suitable for ordinary developers. activity-alias means an alias for activity, which can be used to create a shortcut for activity. In this way, we will show you how to replace the icon. For activity-alias, you can go to the introduction on the official website . The most important thing here is to step on the pit.

 

Implementation steps

We prepare two different icons in advance as shown in the figure below:

Add activity-alias

Add the activity-alias tag to the AndroidManifest.xml Application tag to set the above two icons respectively, the code is as follows:

<activity-alias
    android:icon="@mipmap/icon1"
    android:name=".icon1"
    android:targetActivity=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>

<activity-alias
    android:icon="@mipmap/icon2"
    android:name=".icon2"
    android:targetActivity=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>

The results of running the App are as follows:

We see that three icons are displayed on the desktop at the same time. Clicking on each icon displays the MainActivity page. If you are interested in the process of clicking on the icon to start the App, you can move to my previous article  App start process analysis

At the same time, it should be noted here that no matter which icon we click to start, we can see that the icon in the taskbar is always the first to start. We only need to display the default icon by default, so we set the activity-alias attribute android:enabled is set to false, which disables two other icon entries.

 

Definition modification method

First, we add three button positions to the layout: switch icon 1, switch icon 2, and switch default

Define three corresponding ComponentName codes for the three icons as follows:

private lateinit var componDefault: ComponentName
private lateinit var componIcon1: ComponentName
private lateinit var componIcon2: ComponentName
componDefault = ComponentName(this, "$packageName.MainActivity")
componIcon1 = ComponentName(this, "$packageName.icon1")
componIcon2 = ComponentName(this, "$packageName.icon2")

The icon1 here corresponds to the name attribute in alias.

Update method we use the setComponentEnabledSetting method of packageManager, the code is as follows:

/**
 * 更新别名显示
 * @param componentName componentName
 * @param enable 是否启用
 */
private fun updateAlias(enable: Boolean, componentName: ComponentName) {
    val newState = if (enable) {
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED
    } else {
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED
    }
    packageManager.setComponentEnabledSetting(
        componentName,
        newState,
        PackageManager.DONT_KILL_APP
    )
}

Assuming that we want to enable icon 1 now, what we need to do is to disable the others first and then enable icon 1, so click the switch icon 1 to listen to the event method as follows:

//切换图标1
findViewById<Button>(R.id.button1).setOnClickListener {
    updateAlias(false,componDefault)
    updateAlias(false,componIcon2)
    updateAlias(true,componIcon1)
}

 

operation result

The results of the operation are as follows:

We can see that the icon has changed, but the application has automatically exited. This kind of experience does not feel good to people, it feels like it has crashed, so how can we solve it?

The third parameter in the setComponentEnabledSetting method can be seen from the source code, and there are two values ​​that can be set

/**
 * Flag parameter for
 * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
 * that you don't want to kill the app containing the component.  Be careful when you set this
 * since changing component states can make the containing application's behavior unpredictable.
 */
public static final int DONT_KILL_APP = 0x00000001;

/**
 * Flag parameter for
 * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
 * that the given user's package restrictions state will be serialised to disk after the
 * component state has been updated. Note that this is synchronous disk access, so calls using
 * this flag should be run on a background thread.
 */
public static final int SYNCHRONOUS = 0x00000002;

The value we currently set is DONT_KILL_APP that does not kill the App. It can be seen that the actual effect is that the application automatically exits after about 1.5s. Now we change the value to SYNCHRONOUS and look at the effect.

/**
 * 更新别名显示
 * @param componentName componentName
 * @param enable 是否启用
 */
private fun updateAlias(enable: Boolean, componentName: ComponentName) {
    val newState = if (enable) {
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED
    } else {
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED
    }
    packageManager.setComponentEnabledSetting(
        componentName,
        newState,
        PackageManager.SYNCHRONOUS
    )
}

The result of running again is as follows:

We can see that when the value of flag is set to PackageManager.SYNCHRONOUS, the effect is to exit the application immediately, and the program is killed no matter which way. Obviously, whether the program is killed or not, we cannot handle it, and it may behave differently on different mobile phone systems.

 

How to trigger in the actual project

We use the button click event simulation here, so how do we trigger it in our online project?

There are usually two ways:

  • Method 1: The client App judges based on the timestamp to determine whether the current system time is within a certain holiday to switch icons. The problem with this method is that if the phone system time is inaccurate or deliberately adjusted, the App will automatically switch the corresponding icons. But those who adjust the time want to celebrate the holiday earlier, so we have to cooperate too, right~
  • The second method is that the client judges whether the icon needs to be changed by receiving the server message, and the specific method is divided into request interface or push.

The above two methods, no matter which method, can not avoid the fact that the program will be killed, but we cannot say that the switch is called immediately after receiving the interface, so the test tells us that your App has crashed! ! We will also be at a loss.

So, how do we optimize this? The suggestion is to find the right time to change. For example, when the application is switched to the background, when the application is in the background, it is judged whether it needs to be switched. If it needs to be switched, then switch again. I will not demonstrate here. Now, if you don’t know how to monitor the app in the background, you can refer to my previous article  on Lifecycle in the Android Jetpack series . However, even if you switch in the background, there will still be problems due to coincidences. Then how do other software such as **bao deal with it? After many rounds and consultations, I got a convincing answer: when switching If you are not killed, you need system permissions.

 

Some things you need to know about the pit

Application upgrade

Do you think this is OK? That's too simple. Following the example above, we will switch the icon to icon 1. We will remove the configuration information of icon 1 during the next version upgrade. Let's see what happens. :

What?, the icon is missing? The icon is missing, that is to say: the new version of Activity-alias must contain all the Activity-alias of the previous version , otherwise, the entry may not be found after the application is installed. At the same time, we should also pay attention to the changes in the test upgrade process. The suggestion here is that once the Alias ​​tag is added, it can only be added but not deleted, and the value of the enable attribute should not be changed arbitrarily, otherwise there will be unexpected things.

 

Startup during handover

When the above icon is switched, we also mentioned that in the model tested this time (OPPO ACE 10.0), it will switch after about 1.5s. This time will be different on different models. Now I am a very handy Quick test expert, click the switch icon 2, and then immediately return to the desktop, click the old icon before the icon is updated, the test result diagram:

We can see that when the start icon is clicked between the execution of the switching icon method and before the switching is completed, it will prompt "application data reading failed...", and some models may directly prompt "application does not exist." ..."

Yes, what can I do, I don't know~, if you know, please let me know.

Write at the end

It is not recommended for small apps to do this function. After all, without the support of manufacturers, it is difficult to do the same as system apps.

There are also people who say that dynamic replacement is still hard-coded. Can you get *** from the server? Obviously not. As for whether the hot repair method of dynamically adding aliases is feasible in actual projects, it depends on your bosses. Practice, if you have a good way, please leave a message to tell me~

 

 

Guess you like

Origin blog.csdn.net/huangliniqng/article/details/114037467