Android动态变更图标和应用名

需求背景

大家可能会有注意到,每逢重大节日,很多应用图标会自动调整,类似于春节版、国庆版等等。
这个功能最简单的实现方式可能就是发布一个新的版本了,直接替换相关资源,然后应用升级体验。 但是这种方式工作量较大,很不方便。并且像今日头条、支付宝这类软件,我们好像也没有注意到有应用升级就实现了图标替换,很神奇吧,今天我们就实现这个功能。

实现过程

以开源项目睡眠助手为例,实现应用切换图标功能。 首先我们找到清单文件AndroidManifest.xml,可以看到启动Activity配置如下:

<activity
    android:exported="true"
    android:name=".activity.GuideActivity"
    android:theme="@style/AppTheme.Launcher">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>

然后我们在该activity定义之后,添加新的定义文件,定义一个activity-alias。 需注意,该activity-alias一定要在启动activity之后定义才可。

<activity
    android:exported="true"
    android:name=".activity.GuideActivity"
    android:theme="@style/AppTheme.Launcher">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity-alias
    android:exported="true"
    android:icon="@mipmap/icon"
    android:label="睡眠猪猪"
    android:name=".activity.NewGuideActivity"
    android:targetActivity=".activity.GuideActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>

此时安装应用,我们会发现,桌面上会出现两个应用:睡眠助理、睡眠猪猪,点击两个图标均可实现打开应用,使用功能。那么很显然activity-alias实现了新的应用入口。我们要实现应用图标变更,那么可以先把activity-alias状态关闭,需要开启时再进行开启,通过android:enabled="false"进行设置:

<activity
    android:exported="true"
    android:name=".activity.GuideActivity"
    android:theme="@style/AppTheme.Launcher">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity-alias
    android:enabled="false"
    android:exported="true"
    android:icon="@mipmap/icon"
    android:label="睡眠猪猪"
    android:name=".activity.NewGuideActivity"
    android:targetActivity=".activity.GuideActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity-alias>

现在启动应用会看到图标又恢复成一个了,接下来实现控制图标的变更。
动态控制应用图标可以使用PackageManager实现,可以借助于推送、时间判断、用户点击等方式触发,我们演示功能就采用用户点击的方式。在设置界面添加操作按钮,实现点击进行变更:

PackageManager pm = getPackageManager();
if(PackageManager.COMPONENT_ENABLED_STATE_DISABLED != pm.getComponentEnabledSetting(new ComponentName(this, "com.devdroid.sleepassistant.activity.GuideActivity"))) {
    pm.setComponentEnabledSetting(new ComponentName(this, "com.devdroid.sleepassistant.activity.GuideActivity"),
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    pm.setComponentEnabledSetting(new ComponentName(this, "com.devdroid.sleepassistant.activity.NewGuideActivity"),
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
} else {
    pm.setComponentEnabledSetting(new ComponentName(this, "com.devdroid.sleepassistant.activity.NewGuideActivity"),
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    pm.setComponentEnabledSetting(new ComponentName(this, "com.devdroid.sleepassistant.activity.GuideActivity"),
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}

此时我们就通过按钮实现图标的切换功能了。

发现问题

  • 问题一

由小伙伴反馈,一旦切换图标后,应用安装会出现问题:

Error while executing: am start -n "com.devdroid.sleepassistant/com.devdroid.sleepassistant.activity.GuideActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.devdroid.sleepassistant/.activity.GuideActivity }
Error type 3
Error: Activity class {com.devdroid.sleepassistant/com.devdroid.sleepassistant.activity.GuideActivity} does not exist.
Error while Launching activity

仔细看提示,发现并不是应用安装出现问题。之所以报这个错误,是因为该小伙伴直接从Android Studio运行应用。由于默认的启动Activity已经被设置为COMPONENT_ENABLED_STATE_DISABLED(不可用),所以i同无法找到默认的Activity,无法启动应用,报错了。若是用户使用安装包或从应用商店安装则不存在该问题。

  • 问题二

有小伙伴反馈变更应用图标后,应用会再3秒后关闭。 我们看一下变更图标的方法:setComponentEnabledSetting():

Set the enabled setting for a package component (activity, receiver, service, provider). This setting will override any enabled state which may have been set by the component in its manifest.

翻译为:

设置包四大组件(activity, receiver, service, provider)的启用设置。此设置将覆盖组件在其清单文件(AndroidManifest.xml)中设置的任何启用状态。

其中有一个flags参数,可选为:DONT_KILL_APP,SYNCHRONOUS。其中: DONT_KILL_APP

Flag parameter for setComponentEnabledSetting(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.

翻译:

setComponentEnabledSetting(ComponentName,int,int)的标志参数,用于指示您不希望终止包含该组件的应用程序。设置此选项时要小心,因为更改组件状态会使包含应用程序的行为不可预测。

SYNCHRONOUS

Flag parameter for setComponentEnabledSetting(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.

翻译:

setComponentEnabledSetting(ComponentName,int,int)的标志参数,用于指示给定用户的包限制状态将在更新组件状态后序列化到磁盘。请注意,这是同步磁盘访问,因此使用此标志的调用应该在后台线程上运行。

测试发现:使用DONT_KILL_APP时,应用在3秒内退出;使用SYNCHRONOUS应用立即退出。
但是DONT_KILL_APP和实际不符啊,为什么呢?
网上查阅资料,大多回答是一头雾水,有人反馈是Android系统的一个系统级bug。自己到谷歌社区查找主题,通过官方人员沟通,了解到:当使用DONT_KILL_APP时,Application不会主动结束进程,但是由于作为启动页的GuideActivity被设置为COMPONENT_ENABLED_STATE_DISABLED(不可用),这时候APP会将GuideActivity创建的任务栈清空,由于APP所有Activity都是由GuideActivity任务栈创建的,所以就看到类似于退出应用的效果。 好了,原因确定了,那么就看如何解决了。

此时我们只需要使用新的任务栈启动SettingsActivity,然后在SettingsActivity内清空启动栈,应用就不会退出了。

Intent intent = new Intent(mAppCompatActivity, SettingsActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
mAppCompatActivity.startActivity(intent);

注意: 实际使用时,发现setComponentEnabledSetting生效速度较慢,大概有3s左右。在3s内启动应用,会仍然调用原来的启动页面,导致3s退出应用时,将新建的任务栈清空,应用退出。

原因了解了,通过代码验证,确实可以借助上面的方式实现图标变更。
但是该方案还存在一个弊端:当GuideActivity设置不可用时,应用内其他页面需要跳转到GuideActivity时是不能实现的,同时也无法跳转到activity-alias定义的NewGuideActivity中,这个暂时没有找到解决方案。

通过谷歌官方人员沟通,了解到官方不建议通过使用activity-alias方式实现这种功能,他们提供了一种新的方案。

最终方案

谷歌认为,图标变更功能应该使用独立的LAUNCHER Activity实现,而不应借助activity-alias。
最建议方案如下:
首先创建类文件NewGuideActivity,实现如下代码:

class NewGuideActivity extends GuideActivity{

}

清单文件添加声明:

<activity
    android:enabled="false"
    android:exported="true"
    android:icon="@mipmap/icon"
    android:label="睡眠猪猪"
    android:name=".activity.NewGuideActivity"
    android:targetActivity=".activity.GuideActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

这时候,NewGuideActivity就是一个真实的LAUNCHER了。由于NewGuideActivity直接继承GuideActivity,本身没有任何实质代码,所以功能也是完全一致的。对于NewGuideActivity、GuideActivity的设置和activity-alias方式类似。这个能够满足变更的需求。

以上相关代码请参考开源项目:睡眠助手

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容