recent
一、明确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的加载顺序,以后我会专门写一个博客详细讲解。)
相关阅读
dnf安图恩攻坚前置任务完成攻略,dnf使命四个任务怎么做?想必不少友友都不太清楚,下面跟随小编冉冉一起去看看吧!dnf安图恩攻坚前置
A5创业网(公众号:iadmin5)8月7日报道,今天Android P正式版发布,代号为Android Pie,版本号Android 9。Android Pie的主要更新内容有:1、手
“双十一”前夕,随着零点校园生活服务平台逐步完善,新上线的以校园跑腿为主的任务大厅将带给同学们全新的体验。时间不够
在平时玩电脑的时候不知道因为什么原因电脑屏幕上的任务栏在顶部或者在左右两侧,就是不在原来的底部了,也不知道触碰到了哪里或者按
毫无疑问,自熊掌号上线以来,百度算法我们几乎认为每天都在调整,从“号”变“ID”虽然,战略层面的因素更多,但相关