一、明确android的近期任務是什麼:
我們的手機下方一般有三個鍵,一個是傳回鍵,中間的是home鍵,另一個是RecentList鍵,也就是最近浏覽記錄的記錄鍵,這個的實作在4.0及以上版本使用,android 5.0(api 21)之後,為了系統的安全性,不再允許被第三方開發人員使用,也就是api中不再被使用。但是,為了向前的相容性,還是允許使用獲得近期浏覽記錄的api,隻是隻能獲得部分不敏感資料。
它的樣子:

就是這樣的一個清單,具體實作的原理,這裡簡單講解一下:
我們的桌面簡單講就是一個launch,桌面上的每一個圖示代表一個application,每次啟動一個app,都會往系統的一個叫做RecentTask的棧中傳入一個task(任務),當我們退出app的時候,不論home退出還是按傳回鍵退出,它在離開目前的顯示界面,也就是不在onResume狀态,就是不在前台的時候,系統都會截圖離開時候的狀态,并記錄下目前的狀态資訊,包括具體的Activity,以便于從背景直接調到前台來使用。這個Task棧儲存的是Activity的活動狀态,但是不全是一個app的,而是不同app的。
(之是以要将我們要清楚這個是什麼,在于我們要認識到android系統常用的幾種狀态:process、task和app。因為我在開發的初期階段,認為這是一個running application清單,一直在調用系統中運作的process,是以走偏了很久。)
具體詳細介紹請查閱官方文檔
二、AMS與ActivityManager的通信原理:
android系統的是以服務、程序等管理都是通過SysytenService來實作的,而管理是通過ActivityManager.java。關于它的含義,上篇部落格中已經做了介紹,ActivityManager隻是一個傳遞資訊的接口,它的目的是傳遞需要的東西給ActivityManagerService,後者才是真正實作的方法。
關于AMS通信的原理,我這裡畫了一個圖:
ActivityManagerService與ActivityManager之間的通信是通過Binder機制來完成的,具體如何實作的呢:
ActivityManagerNative中實作的代碼是運作在Android應用程式的程序空間中的,可直接使用的對象,Intent會由應用程式通過這個類将方法對應的Binder指令發送出去,而它本身繼承了Binder類,并實作了ActivityManager接口,源碼如圖:
是以它可以獲得ActivityManager關于記憶體、任務等内部資訊,而ActivityManagerService作為ActivityManagerNative的子類,自然也就可以獲得這些資訊。
例如:
ActivityManager中的方法getAppTasks()方法:
我們會發現這些方法都會先調用ActivityManagerNative的getDefault()方法來獲得ActivityManager的代理接口對象。那麼getDefault()方法又是什麼呢?
我們打開這個方法會發現,如圖:
我們會發現,它主要是調用SystemService對象,并進行它的方法調用,比如它的getService(“activity”)的調用。
而關于ServiceManager類,它是系統最最基本的一個管理類,所有的服務都是通過getService方法得到的,這裡的AMS和ActivityManager的通信,就是通過得到相關的Binder來實作的。
在得到了Binder之後,就可以通過ActivityManagerProxy類來進行與AMS通信,ActivityManagerProxy繼承了ActivityManager,可以看做是ActivityManager的一個代理。由此就可以通過transact傳遞資料給ActivityManagerService(AMS)來進行具體的處理了,處理完之後再打包成相應的Binder傳回給ActivityManager。
三、RecentList的擷取和删除功能的實作。
1.RecentList清單的擷取:
使用的方法是ActivityManager的getRecentTasks()方法,它有兩個參數,一個是最大擷取的數量值,另一個是flag标志位,具體實作代碼:
public static void reloadButtons(Activity activity, List<HashMap<String, Object>> appInfos,
int appNumber) {
int MAX_RECENT_TASKS = appNumber; // allow for some discards
int repeatCount = appNumber;// 保證上面兩個值相等,設定存放的程式個數
/* 每次加載必須清空list中的内容 */
appInfos.removeAll(appInfos);
// 得到包管理器和activity管理器
final Context context = activity.getApplication();
final PackageManager pm = context.getPackageManager();
final ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
// 從ActivityManager中取出使用者最近launch過的 MAX_RECENT_TASKS + 1 個,以從早到晚的時間排序,
// 注意這個 0x0002,它的值在launcher中是用ActivityManager.RECENT_IGNORE_UNAVAILABLE
// 但是這是一個隐藏域,是以我把它的值直接拷貝到這裡
final List<ActivityManager.RecentTaskInfo> recentTasks = am
.getRecentTasks(MAX_RECENT_TASKS + 1, 0x0002);
//.getRecentTasks(MAX_RECENT_TASKS + 1, 8);
// 這個activity的資訊是我們的launcher
ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(
Intent.CATEGORY_HOME).resolveActivityInfo(pm, 0);
int numTasks = recentTasks.size();
for (int i = 1; i < numTasks && (i < MAX_RECENT_TASKS); i++) {
HashMap<String, Object> singleAppInfo = new HashMap<String, Object>();// 當個啟動過的應用程式的資訊
final ActivityManager.RecentTaskInfo info = recentTasks.get(i);
Intent intent = new Intent(info.baseIntent);
if (info.origActivity != null) {
intent.setComponent(info.origActivity);
}
/**
* 如果找到是launcher,直接continue,後面的appInfos.add操作就不會發生了
*/
if (homeInfo != null) {
if (homeInfo.packageName.equals(intent.getComponent()
.getPackageName())
&& homeInfo.name.equals(intent.getComponent()
.getClassName())) {
MAX_RECENT_TASKS = MAX_RECENT_TASKS + 1;
continue;
}
}
// 設定intent的啟動方式為 建立新task()【并不一定會建立】
intent.setFlags((intent.getFlags() & ~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
| Intent.FLAG_ACTIVITY_NEW_TASK);
// 擷取指定應用程式activity的資訊(按我的了解是:某一個應用程式的最後一個在前台出現過的activity。)
final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
if (resolveInfo != null) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
final String title = activityInfo.loadLabel(pm).toString();
Drawable icon = activityInfo.loadIcon(pm);
//&& info.id != -1
if (title != null && title.length() > 0 && icon != null ) {
singleAppInfo.put("title", title);
singleAppInfo.put("icon", icon);
singleAppInfo.put("tag", intent);
singleAppInfo.put("packageName", activityInfo.packageName);
singleAppInfo.put("id", info.persistentId);
appInfos.add(singleAppInfo);
}
}
}
MAX_RECENT_TASKS = repeatCount;
}
(代碼原文部落格: http://blog.csdn.net/benyoulai5/article/details/48447079)
2.删除具體某個應用的記錄的方法:removeTask(),這裡傳入的參數是int型的id,這個id在RecentTaskInfo中指的是persistentId。
關于removeTask方法,這個方法隻能在有系統權限下才能使用,官方API中是沒有的。
如圖:
(這是源碼中的解釋。在此之前,我嘗試了很多種方法去解決删除task棧中的元素方法,但是發現沒有removeTask方法,而通過停止運作process或者強制結束運作應用的方法都無法删除RecentList中的資料,因為它隻是一個記錄棧,而且屬于系統級别的Task棧,必須獲得系統的這個資料棧才能将它删除掉。)
另一種獲得removeTask的方法是反射,我嘗試了一下網上的方法,并不行,因為反射我也不會,是以不确定是個人問題還是方法的問題。
我這裡實作的方式是導入系統的架包,直接擷取的方法,如圖:
(關于引入系統架包與本地SDK沖突的解決方式,比較簡單的方式是更改項目下的編譯時的擷取api的加載順序,以後我會專門寫一個部落格詳細講解。)