Android動(dòng)態(tài)變更圖標(biāo)和應(yīng)用名

需求背景

大家可能會(huì)有注意到睡汹,每逢重大節(jié)日肴甸,很多應(yīng)用圖標(biāo)會(huì)自動(dòng)調(diào)整,類(lèi)似于春節(jié)版帮孔、國(guó)慶版等等雷滋。
這個(gè)功能最簡(jiǎn)單的實(shí)現(xiàn)方式可能就是發(fā)布一個(gè)新的版本了不撑,直接替換相關(guān)資源文兢,然后應(yīng)用升級(jí)體驗(yàn)匆骗。 但是這種方式工作量較大官紫,很不方便利诺。并且像今日頭條餐济、支付寶這類(lèi)軟件积暖,我們好像也沒(méi)有注意到有應(yīng)用升級(jí)就實(shí)現(xiàn)了圖標(biāo)替換噪径,很神奇吧满败,今天我們就實(shí)現(xiàn)這個(gè)功能驮瞧。

實(shí)現(xiàn)過(guò)程

以開(kāi)源項(xiàng)目睡眠助手為例腊敲,實(shí)現(xiàn)應(yīng)用切換圖標(biāo)功能击喂。 首先我們找到清單文件AndroidManifest.xml,可以看到啟動(dòng)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>

然后我們?cè)谠揳ctivity定義之后碰辅,添加新的定義文件懂昂,定義一個(gè)activity-alias。 需注意没宾,該activity-alias一定要在啟動(dòng)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>

此時(shí)安裝應(yīng)用,我們會(huì)發(fā)現(xiàn)循衰,桌面上會(huì)出現(xiàn)兩個(gè)應(yīng)用:睡眠助理铲敛、睡眠豬豬,點(diǎn)擊兩個(gè)圖標(biāo)均可實(shí)現(xiàn)打開(kāi)應(yīng)用会钝,使用功能伐蒋。那么很顯然activity-alias實(shí)現(xiàn)了新的應(yīng)用入口。我們要實(shí)現(xiàn)應(yīng)用圖標(biāo)變更迁酸,那么可以先把a(bǔ)ctivity-alias狀態(tài)關(guān)閉咽弦,需要開(kāi)啟時(shí)再進(jìn)行開(kāi)啟,通過(guò)android:enabled="false"進(jìn)行設(shè)置:

<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>

現(xiàn)在啟動(dòng)應(yīng)用會(huì)看到圖標(biāo)又恢復(fù)成一個(gè)了胁出,接下來(lái)實(shí)現(xiàn)控制圖標(biāo)的變更型型。
動(dòng)態(tài)控制應(yīng)用圖標(biāo)可以使用PackageManager實(shí)現(xiàn),可以借助于推送全蝶、時(shí)間判斷闹蒜、用戶(hù)點(diǎn)擊等方式觸發(fā)寺枉,我們演示功能就采用用戶(hù)點(diǎn)擊的方式。在設(shè)置界面添加操作按鈕绷落,實(shí)現(xiàn)點(diǎn)擊進(jìn)行變更:

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);
}

此時(shí)我們就通過(guò)按鈕實(shí)現(xiàn)圖標(biāo)的切換功能了姥闪。

發(fā)現(xiàn)問(wèn)題

  • 問(wèn)題一

由小伙伴反饋,一旦切換圖標(biāo)后砌烁,應(yīng)用安裝會(huì)出現(xiàn)問(wèn)題:

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

仔細(xì)看提示筐喳,發(fā)現(xiàn)并不是應(yīng)用安裝出現(xiàn)問(wèn)題。之所以報(bào)這個(gè)錯(cuò)誤函喉,是因?yàn)樵撔』锇橹苯訌腁ndroid Studio運(yùn)行應(yīng)用避归。由于默認(rèn)的啟動(dòng)Activity已經(jīng)被設(shè)置為COMPONENT_ENABLED_STATE_DISABLED(不可用),所以i同無(wú)法找到默認(rèn)的Activity管呵,無(wú)法啟動(dòng)應(yīng)用梳毙,報(bào)錯(cuò)了。若是用戶(hù)使用安裝包或從應(yīng)用商店安裝則不存在該問(wèn)題捐下。

  • 問(wèn)題二

有小伙伴反饋?zhàn)兏鼞?yīng)用圖標(biāo)后账锹,應(yīng)用會(huì)再3秒后關(guān)閉。 我們看一下變更圖標(biāo)的方法: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.

翻譯為:

設(shè)置包四大組件(activity, receiver, service, provider)的啟用設(shè)置坷襟。此設(shè)置將覆蓋組件在其清單文件(AndroidManifest.xml)中設(shè)置的任何啟用狀態(tài)奸柬。

其中有一個(gè)flags參數(shù),可選為: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)的標(biāo)志參數(shù)排抬,用于指示您不希望終止包含該組件的應(yīng)用程序懂从。設(shè)置此選項(xiàng)時(shí)要小心,因?yàn)楦慕M件狀態(tài)會(huì)使包含應(yīng)用程序的行為不可預(yù)測(cè)蹲蒲。

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)的標(biāo)志參數(shù)届搁,用于指示給定用戶(hù)的包限制狀態(tài)將在更新組件狀態(tài)后序列化到磁盤(pán)缘薛。請(qǐng)注意,這是同步磁盤(pán)訪問(wèn)卡睦,因此使用此標(biāo)志的調(diào)用應(yīng)該在后臺(tái)線(xiàn)程上運(yùn)行宴胧。

測(cè)試發(fā)現(xiàn):使用DONT_KILL_APP時(shí),應(yīng)用在3秒內(nèi)退出表锻;使用SYNCHRONOUS應(yīng)用立即退出恕齐。
但是DONT_KILL_APP和實(shí)際不符啊,為什么呢瞬逊?
網(wǎng)上查閱資料显歧,大多回答是一頭霧水仪或,有人反饋是Android系統(tǒng)的一個(gè)系統(tǒng)級(jí)bug。自己到谷歌社區(qū)查找主題士骤,通過(guò)官方人員溝通范删,了解到:當(dāng)使用DONT_KILL_APP時(shí),Application不會(huì)主動(dòng)結(jié)束進(jìn)程拷肌,但是由于作為啟動(dòng)頁(yè)的GuideActivity被設(shè)置為COMPONENT_ENABLED_STATE_DISABLED(不可用)到旦,這時(shí)候APP會(huì)將GuideActivity創(chuàng)建的任務(wù)棧清空,由于APP所有Activity都是由GuideActivity任務(wù)棧創(chuàng)建的巨缘,所以就看到類(lèi)似于退出應(yīng)用的效果添忘。 好了,原因確定了带猴,那么就看如何解決了昔汉。

此時(shí)我們只需要使用新的任務(wù)棧啟動(dòng)SettingsActivity懈万,然后在SettingsActivity內(nèi)清空啟動(dòng)棧拴清,應(yīng)用就不會(huì)退出了。

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

注意: 實(shí)際使用時(shí)会通,發(fā)現(xiàn)setComponentEnabledSetting生效速度較慢口予,大概有3s左右。在3s內(nèi)啟動(dòng)應(yīng)用涕侈,會(huì)仍然調(diào)用原來(lái)的啟動(dòng)頁(yè)面沪停,導(dǎo)致3s退出應(yīng)用時(shí),將新建的任務(wù)棧清空裳涛,應(yīng)用退出木张。

原因了解了,通過(guò)代碼驗(yàn)證端三,確實(shí)可以借助上面的方式實(shí)現(xiàn)圖標(biāo)變更舷礼。
但是該方案還存在一個(gè)弊端:當(dāng)GuideActivity設(shè)置不可用時(shí),應(yīng)用內(nèi)其他頁(yè)面需要跳轉(zhuǎn)到GuideActivity時(shí)是不能實(shí)現(xiàn)的郊闯,同時(shí)也無(wú)法跳轉(zhuǎn)到activity-alias定義的NewGuideActivity中妻献,這個(gè)暫時(shí)沒(méi)有找到解決方案。

通過(guò)谷歌官方人員溝通团赁,了解到官方不建議通過(guò)使用activity-alias方式實(shí)現(xiàn)這種功能育拨,他們提供了一種新的方案。

最終方案

谷歌認(rèn)為欢摄,圖標(biāo)變更功能應(yīng)該使用獨(dú)立的LAUNCHER Activity實(shí)現(xiàn)熬丧,而不應(yīng)借助activity-alias。
最建議方案如下:
首先創(chuàng)建類(lèi)文件NewGuideActivity怀挠,實(shí)現(xiàn)如下代碼:

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>

這時(shí)候析蝴,NewGuideActivity就是一個(gè)真實(shí)的LAUNCHER了矗钟。由于NewGuideActivity直接繼承GuideActivity,本身沒(méi)有任何實(shí)質(zhì)代碼嫌变,所以功能也是完全一致的吨艇。對(duì)于NewGuideActivity、GuideActivity的設(shè)置和activity-alias方式類(lèi)似腾啥。這個(gè)能夠滿(mǎn)足變更的需求东涡。

以上相關(guān)代碼請(qǐng)參考開(kāi)源項(xiàng)目:睡眠助手

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市倘待,隨后出現(xiàn)的幾起案子疮跑,更是在濱河造成了極大的恐慌,老刑警劉巖凸舵,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祖娘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡啊奄,警方通過(guò)查閱死者的電腦和手機(jī)渐苏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)菇夸,“玉大人琼富,你說(shuō)我怎么就攤上這事∽拢” “怎么了鞠眉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)择诈。 經(jīng)常有香客問(wèn)我械蹋,道長(zhǎng),這世上最難降的妖魔是什么羞芍? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任哗戈,我火速辦了婚禮,結(jié)果婚禮上涩金,老公的妹妹穿的比我還像新娘谱醇。我一直安慰自己,他們只是感情好步做,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布副渴。 她就那樣靜靜地躺著,像睡著了一般全度。 火紅的嫁衣襯著肌膚如雪煮剧。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音勉盅,去河邊找鬼佑颇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛草娜,可吹牛的內(nèi)容都是我干的挑胸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼宰闰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼茬贵!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起移袍,我...
    開(kāi)封第一講書(shū)人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤解藻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后葡盗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體螟左,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年觅够,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了胶背。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蔚约,死狀恐怖奄妨,靈堂內(nèi)的尸體忽然破棺而出涂籽,到底是詐尸還是另有隱情苹祟,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布评雌,位于F島的核電站树枫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏景东。R本人自食惡果不足惜砂轻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望斤吐。 院中可真熱鬧搔涝,春花似錦、人聲如沸和措。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)派阱。三九已至诬留,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背文兑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工盒刚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绿贞。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓因块,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親籍铁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贮聂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容