要想在后台里在最上层的activity中做点什么事,首先要做的就是获取当前正在显示的activity引用,但遗憾的是系统中没有直接提供这样子的一个接口来让别人直接获取。下文简述如何获取当前应用下栈顶的activity引用。
在Android应用(进程)里,只会存在一个UI线程,按google的设计来看,这件事就是交由android.app.ActivityThread实现的。应用中的众多activity都是由ActivityThread来调度的,下面是ActivityThread的类简介。1
2
3
4
5
6
7
8/**
* This manages the execution of the main thread in an
* application process, scheduling and executing activities,
* broadcasts, and other operations on it as the activity
* manager requests.
*
* {@hide}
*/
其中,成员变量1
final HashMap<IBinder, ActivityClientRecord> mActivities = new HashMap<IBinder, ActivityClientRecord>();
就是用来放置activity引用的一个map,其key为activity的token。
参考activity的启动过程可知,在启动的最后,调用的是ActivityThread::performLaunchActivity方法,并产生一个新的activity实例,并将其token与生成的实例以健值对的方式放置入了mActivities。我们的目标就是查找到一个路径,以反射的方法获取到activity的实例。
明显,这是一个隐藏类,那在一个应用内它究竟存在于哪里呢?经过一翻查看,它是android.app.LoadedApk的一个成员变量,LoadedApk包含了当前加载的APK的一些状态信息,包括所装APK的位置信息,类加载器等,其中,也包括ActivityThread的实例。以下是LoadedApk的类简介。1
2
3
4/**
* Local state maintained about a currently loaded .apk.
* @hide
*/
继续在同包中查找,容易发现,在android.app.Application下就包含了LoadedApk的一个成员变量。由于Application在一个应用用来保存全局的状态的,且是单例的,但系统没有一个方便的办法到处去获取这个实例,但这问题不能解决,获取的话就只能在activity的getApplication()方法里获取了,获取完了自己找个地方保存就好。
最后,整条需要找到activity实例的路径都已经找到了,但怎么去把它们拿出来呢,嗯,反射。示例如下。
1 | public Activity getActivity() { |
需要注意的是,在4.4以下的ActivityThread中,mActivities是HashMap类型的,但在4.4及以上的系统中,mActivities是ArrayMap类型的,个中区别可谷。所以以上代码成真正使用时,需要判别清楚当前系统的版本,如果使用4.4以上的SDK把ArrayMap编译了,那么程序运行在低版本的时候是会报错的。
按以上办法就能获取到当前应用中存活的activity实例引用了,但因为存在的map结构是hash的,不能直接toArray()根据其顺序获取到最顶的一个实例。虽然系统不能提供最顶activity的引用,但可以获取到最顶activity的信息,方法如下。
1 | ActivityManager activityManager = (ActivityManager) MainActivity.this.getSystemService(Context.ACTIVITY_SERVICE); |
通过获取历史任务中第一个任务中的最topActivity的ComponentName信息,就能获取到其activity对应的类名,而在上面,我们已经可以获取到最top的activity实例了,接下来只要做个对比即可。
因为需要获取历史任务,故需要添加GET_TASKS的权限。
另外,使用此方法去获取毕竟不能与实时相比,所以需要考虑在获取到引用到我们使用前的这段时间内activity对应引用会不会发生变化,如已经销毁等,避免无必要的崩溃。
以上过程就是获取最top的activity的方法,在匹配activity上可能存在一定的性能问题,还存在优化的空间。
==========================================================================
更新于2.015.7.3
如果activity没有更新更新,则HashMap或ArrayMap对象是不会改变的,故能使用它的hashCode的是否变化来减少操作
另外,4.0以上有Application.ActivityLifecycleCallbacks 这个新的回调接口,这个接口可以在application中进行注册,在4.0以上的Activity类中的生命周期方法中,会直接回调application中的对应回调方法,并把对应的activity对象返回。