正常情況下,如果應用已經啟動,並將應用切到后台,在通知欄中調起頁面時,該應用的Task首先會被調起,然后會將我們的Activity顯示在這個Task的頂端。手機百度的通知欄里面有一個快速搜索欄,無論什么情況下,點擊之后都會直接彈出搜索頁面,透明背景后顯示的是桌面。怎么來實現這個功能呢?這就要提到我們的主角TaskAffinity了。
什么是affinity?
affinity是指Activity的歸屬,Activity與Task的吸附關系,也就是該Activity屬於哪個Task。一般情況下在同一個應用中,啟動的Activity都在同一個Task中,它們在該Task中度過自己的生命。每個Activity都有taskAffinity屬性,這個屬性指出了它希望進入的Task。如果一個Activity沒有顯式的指明taskAffinity,那么它的這個屬性就等於Application指明的taskAffinity,如果Application也沒有指明,那么該taskAffinity的值就等於應用的包名。我們可以通過在元素中增加taskAffinity屬性來為某一個Activity指定單獨的affinity。這個屬性的值是一個字符串,可以指定為任意字符串,但是必須至少包含一個”.”,否則會報錯。
affinity在什么場合應用呢?
1.根據affinity重新為Activity選擇宿主task(與allowTaskReparenting屬性配合使用)
allowTaskReparenting用來標記Activity能否從啟動的Task移動到taskAffinity指定的Task,當把Activity的allowTaskReparenting屬性設置成true時,Activity就擁有了一個轉移所在Task的能力。具體點來說,就是一個Activity現在是處於某個Task當中的,但是它與另外一個Task具有相同的affinity值,那么當另外這個任務切換到前台的時候,該Activity就可以轉移到現在的這個任務當中。allowTaskReparenting默認是繼承至application中的allowTaskReparenting=false,如果為true,則表示可以更換;false表示不可以。
舉一個形象點的例子,比如有一個天氣預報程序,它有一個用於顯示天氣信息的Activity,allowTaskReparenting屬性設置成true,這個Activity和天氣預報程序的所有其它Activity具體相同的affinity值。這個時候,你自己的應用程序通過Intent去啟動了這個用於顯示天氣信息的Activity,那么此時這個Activity應該是和你的應用程序是在同一個任務當中的。但是當把天氣預報程序切換到前台的時候,這個Activity會被轉移到天氣預報程序的任務當中,並顯示出來。如果將你自己的應用切換到前台,發現你自己應用Task里的那個Activity消失了。
2.啟動一個Activity過程中Intent使用了FLAG_ACTIVITY_NEW_TASK標記,根據affinity查找或創建一個新的具有對應affinity的task。
當調用startActivity()方法來啟動一個Activity時,默認是將它放入到當前的任務當中。但是,如果在Intent中加入了FLAG_ACTIVITY_NEW_TASK flag的話,情況就會變的復雜起來。首先,系統會去檢查這個Activity的affinity是否與當前Task的affinity相同。如果相同的話就會把它放入到當前Task當中,如果不同則會先去檢查是否已經有一個名字與該Activity的affinity相同的Task,如果有,這個Task將被調到前台,同時這個Activity將顯示在這個Task的頂端;如果沒有的話,系統將會嘗試為這個Activity創建一個新的Task。需要注意的是,如果一個Activity在manifest文件中聲明的啟動模式是”singleTask”,那么他被啟動的時候,行為模式會和前面提到的指定FLAG_ACTIVITY_NEW_TASK一樣。
那么,有了上面的知識,我們應該可以實現開頭提到的功能了。
功能的實現
首先,在mainifest中配置我們的Activity,
<activity
android:name="com.test.TestActivity"
android:configChanges="orientation|keyboard|keyboardHidden"
android:exported="true"
android:taskAffinity="com.test.TestActivity"
android:screenOrientation="portrait"/>
然后增加通知欄的邏輯
NotificationManager mNotifManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification();
notification.icon = R.drawable.icon;
notification.flags = Notification.FLAG_ONGOING_EVENT;
notification.flags = Notification.FLAG_AUTO_CANCEL;
notification.flags = Notification.FLAG_NO_CLEAR;
RemoteViews mContentView = new RemoteViews(mContext.getPackageName(),
R.layout.notification_test);
notification.contentView = mContentView;
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClass(mContext, TestActivity.class);
PendingIntent pendingIntent =PendingIntent.getActivity(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
notification.contentView.setOnClickPendingIntent(R.id.rl_notification,
pendingIntent);
mNotifManager.notify(NOTIFYID, notification);
現在我們可以實現開頭提到的那種效果了。但是,我發現最近任務中會有兩個我們應用的圖標,看起來像是啟動了兩個我們的應用,非常奇怪,而實際上是因為我們的應用啟動了兩個Task。我們可以通過在manifest此Activity的屬性中增加Android:excludeFromRecents=”true”。這屬性用於設置由該Activity所啟動的任務是否應該被排除在最近使用的應用程序列表之外。也就是說,當這個Activity是一個新任務的根節點時,這個屬性決定了這個任務是否會顯示在用戶最近使用的應用程序列表中。如果設置為true,則這個任務會被排除在列表之外,為false,則表示會包含在最近使用的應用列表中。默認值是false。
場景驗證
前提:將頁面背景設置為半透明。
1、未使用affinity
如果應用沒有啟動,點擊通知欄,當前Activity被調起,透明背景后顯示為桌面。
如果應用已經啟動,點擊通知欄,應用的Task調到前台,當前Activy顯示在該Task的頂端。
2、使用affinity
無論應用是否已經啟動,點擊通知欄,當前Activity都會被調起,透明背景后顯示為桌面。
兩種情況下,看最近任務欄,都只顯示一個應用圖標。
這樣就實現了我們想要的效果。