关于Android主线程(ActivityThread)源代码分析以及一些特殊问题的非常规方法
2016-08-01 22:01
721 查看
在写这篇博客之前,先抛出一个问题,安卓应用程序的入口是什么呢?我想不少人可能回答说:application的onCreate方法,其实并不是的,即使是application,也有一个方法比onCreate先执行,这个方法就是attachBaseContext(Context context)方法:一般情况下,可以在这个方法中进行多dex的分包注入,比如下面的代码:
当然了我这是举例说明这个方法的作用,这个方法是application初始化之后会立即被执行的方法,其次才是onCreate方法,当然application并不是安卓程序的入口,安卓应用程序作为一个控制类程序,跟Java程序类似,都是有一个入口的,而这个入口就是ActivityThread,ActiviyThread也有一个main方法,这个main方法是安卓应用程序真正的入口,下面我将详细分析ActivityThread以及一些问题的非常规方法,文章比较长,请大家有空慢慢看,相信看完你会对安卓主线程有一个全新的认识。
首先要明白,ActivityThread有什么作用呢?ActivityThread的作用很多,但最主要的作用是根据AMS(ActivityManagerService的要求,通过IApplicationTHread的接口)负责调度和执行activities、broadcasts和其它操作。在Android系统中,四大组件默认都是运行在主线程上的,接下来的代码分析你会看到这些组件的管理。
首先是ActivityThread入口的主要代码如下:
以上代码短短几句,却可以看出ActivityThread的重要作用, 与AMS交互并且管理Activity和Service,那么问一个实际问题,一个app中,如何知道用户去过哪些页面呢?当然方法有多种,但通过上面的代码,或许聪明的你想到了去找到mActivities这个字段就好了,是的,因为这个字段存储了所有的Activity对象,拿到这个字段的值就可以知道有哪些Activity了,而那些Activity都是用户停留过的,这也是一个解决方法。
ActivityClientRecord是ActivtityThread的一个内部类,这个ActivityClientRecord是传入AMS的一个标志来的,里面携带了很多信息,上面的代码可以看出,其中有一个Activity对象,里面的Activity就是真正的Activity实例了,通过或者它,可以知道用户去哪些页面,当然可能比较繁琐,但这种方法是可行的。
下面重点分析ApplicationThread,首先它不是一个线程,而是一个Binder对象,
看到这里,我想熟悉Binder机制的人都知道是怎么回事了吧,没错,ApplicationThread经过包装之后变成代理对象ApplicationThreadProxy,也许你会问你怎么知道是ApplicationThreadProxy呢,其实这个嘛,debug一下就知道的啦,下面是图片
看到红色框框的吗,IApplictionThread在AMS就是ApplicationThreadProxy对象啦,呵呵
然后传递到AMS,而AMS有了这个对象,就可以调用里面的方法了,schedulePauseActivity这个方法就是一个Activity被暂停而执行的方法了,可见ActivityThread与AMS的交互是一次IPC调用,当然这里要搞清楚些,AMS调用ActivityThread是通过ApplicationThreadProxy对象,而ActivityThread调用AMS的方法却是ActivityManagerProxy,这也是一个IPC过程调用,在结尾会有更详细的代码,好了,现在你应该明白了AMS调用ActivityThread方法是通过ApplicationThreadProxy对象了,里面的方法一般都是schedule开头,比如scheduleDestroyActivity,scheduleReceiver等等,一个问题,Activity被暂停之后执行的第一个方法是schedulePauseActivity,之后通过消息分发机制调用handlePauseActivity->performPauseActivity->callActivityOnPause->Activity.onPause()方法,也就是说Activity的里面的生命周期方法其实是比较晚调用了,由AMS调度,ActivityThread执行,再到Activity本身。
接下里分析Android中非常重要的消息分发机制在ActivityThread的调用过程,消息分发机制在整个Android系统中占据了非常重要的地位,Android系统也是依靠消息分发机制来实现系统的运转的,消息分发机制也是Android面试中的必问知识点,在ActivityThread中是H这个继承了Handler类,下面是一些变量:
可以看到,里面的常量表示的是具体操作的what值,当然了,其他操作也有对应的,不一一列出了,然后重写了handMessage方法,下面是代码:
当然了,其他都是这样处理的,不一一列出。看到没,LAUNCH_ACTIVITY这个是处理启动Activity的,接着调用了handleLaunchActivity方法,handleLaunchActivity调用了创建Activity的方法performLaunchActivity,
看到没,Activity其实就一个普普通通的Java对象,利用反射创建,然后由ClassLoader加载进去,之后由框架层的调用,从而具有了生命周期,成为了一个组件,从而也可以知道在插件化中,仅仅加载Activity是不行的,还必须交给框架层去调用才具有生命力,不然没意义,当然了,不仅是Activity,其实,Service,BroadCase,等都是这样由反射创建,然后加载由框架层调用的,无一例外,
看到没,创建Activity之后,会调用attach方法绑定,然后判断是否设置主题,如果有的话就设置主题,然后再调用mInstrumentation的callActivityOnCreate,这里其实就是调用了Activity的onCreate方法,Activity的创建也是由Instrumentation创建的,有关的Instrumentation的说明,请看我的上一篇文章:
http://blog.csdn.net/shifuhetudi/article/details/52078445
至此,onCreate被调用了,
上面可以看到执行完onCreate方法之后,执行onStart方法,然后执行onPostOnCreate方法
回到上面的调用handleLaunchActivity方法,在执行完Activity的onCreate,onStart生命周期方法之后,就来到
handleResumeActivity方法了,
看到没,在执行onResume的时候,其实那时候是不可见的,直到执行完r.activity.mVisibleFromClient这个条件为真的时候才真正可见,从代码我们也可以看出,其中Activity就一个View添加到窗口而已,因而Activity本质上只是一个窗口而已,至此,Activity才真正对用户可见,可以看到,上面的调用栈其实都是Handler消息分发机制实现而已,一步一步来进行,通过IPC跟AMS通信,有没有觉得Android系统博大精深呢,本人以为正是由于Binder机制的存在,Android调用远程进程如同调用本地进程一样,来回自由,不得不佩服谷歌那帮神一般存在的程序员,不知道你有没有这种想法呢?当然了,其他Handler分发消息的调用都是一样的,各位读者可以自己去分析其他消息的调用,其实,所有Activity的生命周期,包括其他组件的调用都由H 来负责分发调用。
上面的4个方法,其实作用很有用,在一些特殊场合的情况下用得到,后面的问题中会有这些方法的调用。
下面分析广播在ActivityThread的调用,当然了广播也一样,首先通过IPC调用到AMS,然后由AMS调度返回到ActivityThread处理,下面是代码:
从上面可以看到,广播本质跟Activity一样,都是反射创建,唯一不同的是,Activity需要经过框架层的调用并具有了生命周期方法,而广播没有生命方法的概念,只是回调了一个onReceive方法,所以相对Activity,广播比较容易理解,这里先留下一个问题,为什么广播能够跨进程调用呢,而EventBus,OTTO等事件总线就不能,其实根本原因在于广播是由AMS系统进程统一管理,我们本地进程只是负责执行而已,而EventBus,OTTO那些事件总线的处理都是建立在同个进程之间的,因此他们不能实现跨进程发送消息,而广播却可,当然了AMS处理广播也比较复杂,今天暂时不讨论。
下面分析Service在ActivityThread的处理,代码如下:
可以看到Service的处理跟前面的广播没啥大区别,都是反射创建,然后回调方法,当然还剩下最后一个ContentProcider,其实这玩意也是反射创建,当然了这个ContentProcider流程比较复杂,
可以看到四大组件都是反射创建,并执行生命周期方法,而调度是由AMS负责,ActivityThread主线程负责执行,所以说AMS是调度者,主线程是执行者,就像一个公司里面,AMS是董事会,而ActivityThread是CEO,负责执行具体的事情同时报告给AMS,AMS备份,以有案可查。
OK,以上便是ActivityThread的主要负责的事情,当然了,那些停止,销毁Activity跟启动Activity的流程是一样的,跟AMS都是一个IPC过程调用,下面给出ActivityThread的工作流程(AMS调用ActivityThread方法):
这个是AMS调用ActivityThread的方法,当然了ActivityThread调用AMS都差不多,只是对象换成了ActivityManagerProxy对象而已了。
最后是一些问题在特殊情况下的非常规方法,有兴趣的可以看看:
1.任何时候,任何地方,任何逻辑,如何获取全局Application对象
对于这个问题,也许很多人会说,在自定义的application中定义一个方法,然后在onCreate中赋值,这样就可以获取了,没错,绝大部分的人是这样做,而且好像也从来都没有出错过,是的,这种方法是目前大部分的人都会用,然而,这种方法其实是由限制的,当然了在普通的开发中还是没问题的,然而如果不能接触程序逻辑本身的话(在逆向等比较特殊的情况下),那么上面的方法就无效了,那么是不是就没办法了呢,不是的,上面的代码中曾经说过有个方法:
可以看到是可以获取的,可是ActivityThread是一个隐藏类,那怎么办呢,反射吧,下面是代码:
结果如下:
可以看到成功获取了,问题解决!
2.如何获取Activity实例
可能有人会说不是可以getApplication对象吗,那个方法获取的是上下文对象,并不是真正的activity实例,那么如何获取呢,下面是其中一个方法:
首先在基类定义一个Activity变量,然后在onCreate中赋值给具体的子类,这样子类获取的Activity实例。
这种方法是最简单,也是一般情况下用到的。
第二种方法:利用application的生命周期监听方法ActivityLifecycleCallbacks,下面是具体的代码:
OK,也成功获取了当前ActivityThread实例了。
第三种方法:
我们刚才在看ActivityThread的源代码的时候,发现有这么一个方法:
你没有看错,这里也有一个方法直接获取Activity实例的,可是傻眼了,参数必须是IBinder类型,就是activity身份的唯一标志,那么这个IBinder该如何获取呢?额,我想想哈,在Activity启动的时候,我们曾经说过在执行完onResume的时候,会报告给AMS,方法为activityResumed,而这个方法的参数刚好就是IBinder类型的,这个参数就代表了当前Activity的toke,那么只要有了这个token,不就可以调用上面的代码来获取Activity了吗,可是activityResumed这个方法是AMS执行的啊,AMS可是运行在系统进程里面的哦,怎么办呢?那就来点暴力点的吧,我们直接Hook AMS,上面说过ActivityThread调用AMS的方法的时候,也是用了Binder机制,具体点说是用了ActivitymanagerProxy这个代理对象进行调用的,而这个类是ActivityManagerNative的子类ActivityManagerService在本地进程的一个代理对象而已(个人认为Binder机制在Java层是很好理解的,只需要记住,不同进程之间都是拿对方的代理对象进行干活的),听不懂是吧,下面看看代码吧:
我们只需要替换为我们自己的代理对象进行干活,然后在activityResumed方法中进行拦截就好了,好了,说干就干,
IActivityManagerServiceHandler实现类动态代理接口InvocationHandler,在里面拦截了activityResumed方法而已,拦截之后拿到Token,然后调用反射方法就可以获取Activity的实例啦,下面是具体的代码,比较简单,
然后在application中进行注入就好了,好了,下面打印看看吧,
可以看到成功了吧,呵呵,欺骗系统的感觉如何啊,顺便说句,360插件化的核心思想就是瞒天过海,欺骗系统,通过把系统完的团团转,从而实现插件系统的天衣无缝,当然需要处理一些兼容性问题,OK,上面的问题也解决了。
3.有办法干预Activity的启动或者其他过程吗,常规下是没办法的,因为这是框架层的调度,再调用Activity生命周期方法的时候,其实已经是很晚时机了,已经没办法再进一步操作了,也就是说在应用层上是无法干预这些行为的,当然了,你可以在框架层调度的时候,半路杀出个程咬金来就好了,具体的见我的上一篇文章:
http://blog.csdn.net/shifuhetudi/article/details/52078445
4.有办法在启动Activity的时候弹出一个土司吗,或者做其他的事情,就是在执行LaunchActivity方法的时候弹土司或者其他事情?
额,常规下是没办法的,有人会说我在onCreate中弹出来,呵呵,这就违背了我的条件,是在LaunchActivity的时候,就是在AMS给ActivityThread发消息启动Activiy的时候做一些事情,我们都知道这个过程是由Handler发送消息来实现的,可是通过Handler处理消息的代码来看,其实是有顺序的,下面是Handler处理消息的代码:
看到了吗,handler处理消息的时候,首先去检查是否实现了callback接口,如果有实现的的话,那么会直接执行接口方法,然后才是handleMessage方法,最后才是执行重写的handleMessage方法,我们一般大部分时候都是重写了handleMessage方法,而ActivityThread主线程用的正是重写的方法,这种方法的优先级是最低的,我们完全可以实现接口来替换掉系统Handler的处理过程,下面请看代码:
当然还有CustomHandler类,这个类实现了Callback接口,一样可以拦截方法
写完之后在application中注入就好了,OK,你可以去测试一下,看看是不是每次启动Activity的时候都会弹出hello,I am going launch这个字符串。
当然除了这些比较特殊的问题之外,一些时候,特别是插件化的时候,经常遇到这种问题,这些问题往往在应用层是无法拦截的,因为到达应用层的时候,调用链已经调用完毕,没机会去插手了,这种时候就可以考虑这些比较特殊的方法了,当然啦,开发中,哪种最快解决问题用哪种吧。
今天就写到这里吧,小弟水平有限,不足之处敬请指出,感谢大家阅读。
@Override protected void attachBaseContext(Context base) { MultiDex.install(base); super.attachBaseContext(base); try { HandlerInject.hookHandler(base); } catch (Exception e) { e.printStackTrace(); } }
当然了我这是举例说明这个方法的作用,这个方法是application初始化之后会立即被执行的方法,其次才是onCreate方法,当然application并不是安卓程序的入口,安卓应用程序作为一个控制类程序,跟Java程序类似,都是有一个入口的,而这个入口就是ActivityThread,ActiviyThread也有一个main方法,这个main方法是安卓应用程序真正的入口,下面我将详细分析ActivityThread以及一些问题的非常规方法,文章比较长,请大家有空慢慢看,相信看完你会对安卓主线程有一个全新的认识。
首先要明白,ActivityThread有什么作用呢?ActivityThread的作用很多,但最主要的作用是根据AMS(ActivityManagerService的要求,通过IApplicationTHread的接口)负责调度和执行activities、broadcasts和其它操作。在Android系统中,四大组件默认都是运行在主线程上的,接下来的代码分析你会看到这些组件的管理。
首先是ActivityThread入口的主要代码如下:
//初始化Looper Looper.prepareMainLooper(); //创建ActivityThread对象,并绑定到AMS ActivityThread thread = new ActivityThread(); //一般的应用程序都不是系统应用,因此设置为false,在这里面会绑定到AMS thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); //开启循环 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");
上面的代码都不难,但正是这些代码,让我们明白了2个问题:1.我们之所以可以在Activity用Handler handler=new Handler()直接创建出来就默认绑定到主线程了,是因为上面的代码为我们做了绑定主线程的Looper的事情,2.主线程的Looper是不能在程序中调用退出的,最后一句代码看到没,如果调用的话,就会抛出异常,退出主线程的循环是框架层在调用退出应用程序的时候才调用的,这个下面会讲到。
//IBinder对象,AMS持有此对象的代理对象,从而通知ActivityThread管理其他事情 final ApplicationThread mAppThread = new ApplicationThread(); final Looper mLooper = Looper.myLooper(); final H mH = new H(); //存储了所有的Activity,以IBinder作为key,IBinder是Activity在框架层的唯一表示 final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>(); //存储了所有的Service final ArrayMap<IBinder, Service> mServices = new ArrayMap<>(); //ActivityThread对象,拿到这个对象,可以反射调用这个类的需要的方法 private static ActivityThread sCurrentActivityThread;
以上代码短短几句,却可以看出ActivityThread的重要作用, 与AMS交互并且管理Activity和Service,那么问一个实际问题,一个app中,如何知道用户去过哪些页面呢?当然方法有多种,但通过上面的代码,或许聪明的你想到了去找到mActivities这个字段就好了,是的,因为这个字段存储了所有的Activity对象,拿到这个字段的值就可以知道有哪些Activity了,而那些Activity都是用户停留过的,这也是一个解决方法。
//存储的Activity的表示对象ActivityClientRecord static final class ActivityClientRecord { //唯一表示 IBinder token; int ident; Intent intent; String referrer; IVoiceInteractor voiceInteractor; Bundle state; PersistableBundle persistentState; //这里存储了真正的Activity对象 Activity activity; Window window; Activity parent; String embeddedID; Activity.NonConfigurationInstances lastNonConfigurationInstances; boolean paused; boolean stopped; boolean hideForNow; Configuration newConfig; Configuration createdConfig; Configuration overrideConfig; // Used for consolidating configs before sending on to Activity. private Configuration tmpConfig = new Configuration(); ActivityClientRecord nextIdle; ProfilerInfo profilerInfo; ActivityInfo activityInfo; CompatibilityInfo compatInfo; LoadedApk packageInfo; List<ResultInfo> pendingResults; List<ReferrerIntent> pendingIntents; boolean startsNotResumed; boolean isForward; int pendingConfigChanges; boolean onlyLocalRequest; View mPendingRemoveWindow; WindowManager mPendingRemoveWindowManager; }
ActivityClientRecord是ActivtityThread的一个内部类,这个ActivityClientRecord是传入AMS的一个标志来的,里面携带了很多信息,上面的代码可以看出,其中有一个Activity对象,里面的Activity就是真正的Activity实例了,通过或者它,可以知道用户去哪些页面,当然可能比较繁琐,但这种方法是可行的。
下面重点分析ApplicationThread,首先它不是一个线程,而是一个Binder对象,
private class ApplicationThread extends ApplicationThreadNative { //省略一些代码 //准备暂停Activity public final void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, int configChanges, boolean dontReport) { sendMessage( finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY, token, (userLeaving ? 1 : 0) | (dontReport ? 2 : 0), configChanges); } public final void scheduleStopActivity(IBinder token, boolean showWindow, int configChanges) { sendMessage( showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE, token, 0, configChanges); } //省略一些代码 public abstract class ApplicationThreadNative extends Binder implements IApplicationThread /** * Cast a Binder object into an application thread interface, generating * a proxy if needed. */ static public IApplicationThread asInterface(IBinder obj) { if (obj == null) { return null; } IApplicationThread in = (IApplicationThread)obj.queryLocalInterface(descriptor); if (in != null) { return in; } //不同进程,生成一个代理对象具体干活 return new ApplicationThreadProxy(obj); }
看到这里,我想熟悉Binder机制的人都知道是怎么回事了吧,没错,ApplicationThread经过包装之后变成代理对象ApplicationThreadProxy,也许你会问你怎么知道是ApplicationThreadProxy呢,其实这个嘛,debug一下就知道的啦,下面是图片
看到红色框框的吗,IApplictionThread在AMS就是ApplicationThreadProxy对象啦,呵呵
然后传递到AMS,而AMS有了这个对象,就可以调用里面的方法了,schedulePauseActivity这个方法就是一个Activity被暂停而执行的方法了,可见ActivityThread与AMS的交互是一次IPC调用,当然这里要搞清楚些,AMS调用ActivityThread是通过ApplicationThreadProxy对象,而ActivityThread调用AMS的方法却是ActivityManagerProxy,这也是一个IPC过程调用,在结尾会有更详细的代码,好了,现在你应该明白了AMS调用ActivityThread方法是通过ApplicationThreadProxy对象了,里面的方法一般都是schedule开头,比如scheduleDestroyActivity,scheduleReceiver等等,一个问题,Activity被暂停之后执行的第一个方法是schedulePauseActivity,之后通过消息分发机制调用handlePauseActivity->performPauseActivity->callActivityOnPause->Activity.onPause()方法,也就是说Activity的里面的生命周期方法其实是比较晚调用了,由AMS调度,ActivityThread执行,再到Activity本身。
接下里分析Android中非常重要的消息分发机制在ActivityThread的调用过程,消息分发机制在整个Android系统中占据了非常重要的地位,Android系统也是依靠消息分发机制来实现系统的运转的,消息分发机制也是Android面试中的必问知识点,在ActivityThread中是H这个继承了Handler类,下面是一些变量:
private class H extends Handler { //启动Activity public static final int LAUNCH_ACTIVITY = 100; //暂停Activity public static final int PAUSE_ACTIVITY = 101; public static final int PAUSE_ACTIVITY_FINISHING= 102; public static final int STOP_ACTIVITY_SHOW = 103; public static final int STOP_ACTIVITY_HIDE = 104; public static final int SHOW_WINDOW = 105; .......... }
可以看到,里面的常量表示的是具体操作的what值,当然了,其他操作也有对应的,不一一列出了,然后重写了handMessage方法,下面是代码:
public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); }
当然了,其他都是这样处理的,不一一列出。看到没,LAUNCH_ACTIVITY这个是处理启动Activity的,接着调用了handleLaunchActivity方法,handleLaunchActivity调用了创建Activity的方法performLaunchActivity,
Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } }
看到没,Activity其实就一个普普通通的Java对象,利用反射创建,然后由ClassLoader加载进去,之后由框架层的调用,从而具有了生命周期,成为了一个组件,从而也可以知道在插件化中,仅仅加载Activity是不行的,还必须交给框架层去调用才具有生命力,不然没意义,当然了,不仅是Activity,其实,Service,BroadCase,等都是这样由反射创建,然后加载由框架层调用的,无一例外,
activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor); if (customIntent != null) { activity.mIntent = customIntent; } r.lastNonConfigurationInstances = null; activity.mStartedActivity = false; int theme = r.activityInfo.getThemeResource(); if (theme != 0) { //设置主题 activity.setTheme(theme); } activity.mCalled = false; if (r.isPersistable()) { //调用onCreate方法 mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnCreate(activity, r.state); }
看到没,创建Activity之后,会调用attach方法绑定,然后判断是否设置主题,如果有的话就设置主题,然后再调用mInstrumentation的callActivityOnCreate,这里其实就是调用了Activity的onCreate方法,Activity的创建也是由Instrumentation创建的,有关的Instrumentation的说明,请看我的上一篇文章:
http://blog.csdn.net/shifuhetudi/article/details/52078445
至此,onCreate被调用了,
if (!r.activity.mFinished) { //执行onStart方法 activity.performStart(); r.stopped = false; } if (!r.activity.mFinished) { if (r.isPersistable()) { if (r.state != null || r.persistentState != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState); } } else if (r.state != null) { mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); } } if (!r.activity.mFinished) { activity.mCalled = false; if (r.isPersistable()) { //OnPostCreate方法也是一个周期方法,只是一般开发用不到 mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState); } else { mInstrumentation.callActivityOnPostCreate(activity, r.state); //这里把Activity存储起来,可以知道拿到mActivities就可以知道有哪些Activity了,也就是我上面提到的问题,如何统计用户去过多少页面呢,这个方法是可以实现的。 mActivities.put(r.token, r);
上面可以看到执行完onCreate方法之后,执行onStart方法,然后执行onPostOnCreate方法
回到上面的调用handleLaunchActivity方法,在执行完Activity的onCreate,onStart生命周期方法之后,就来到
handleResumeActivity方法了,
if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); //一开始执行onResume的时候其实页面是不可见的 decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; //这里非常重要,把顶级devorView添加进去 wm.addView(decor, l); } ....... if (r.activity.mVisibleFromClient) { //执行到这里,Activity才是真正对用户可见 r.activity.makeVisible(); } // Tell the activity manager we have resumed. if (reallyResume) { try { //这里通知AMS,Activity已经Resume了 ActivityManagerNative.getDefault().activityResumed(token); } catch (RemoteException ex) { } }
看到没,在执行onResume的时候,其实那时候是不可见的,直到执行完r.activity.mVisibleFromClient这个条件为真的时候才真正可见,从代码我们也可以看出,其中Activity就一个View添加到窗口而已,因而Activity本质上只是一个窗口而已,至此,Activity才真正对用户可见,可以看到,上面的调用栈其实都是Handler消息分发机制实现而已,一步一步来进行,通过IPC跟AMS通信,有没有觉得Android系统博大精深呢,本人以为正是由于Binder机制的存在,Android调用远程进程如同调用本地进程一样,来回自由,不得不佩服谷歌那帮神一般存在的程序员,不知道你有没有这种想法呢?当然了,其他Handler分发消息的调用都是一样的,各位读者可以自己去分析其他消息的调用,其实,所有Activity的生命周期,包括其他组件的调用都由H 来负责分发调用。
//获取ActivityThread对象 public static ActivityThread currentActivityThread() { return sCurrentActivityThread; } //获取当前包名 public static String currentPackageName() { ActivityThread am = currentActivityThread(); return (am != null && am.mBoundApplication != null) ? am.mBoundApplication.appInfo.packageName : null; } //获取当前进程名 public static String currentProcessName() { ActivityThread am = currentActivityThread(); return (am != null && am.mBoundApplication != null) ? am.mBoundApplication.processName : null; } //获取当前Application对象 public static Application currentApplication() { ActivityThread am = currentActivityThread(); return am != null ? am.mInitialApplication : null; } //获取LoadApk,LoadApk是一个Apk在内存中的表示,这个跟插件化有关 public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, int flags) { return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId()); }
上面的4个方法,其实作用很有用,在一些特殊场合的情况下用得到,后面的问题中会有这些方法的调用。
下面分析广播在ActivityThread的调用,当然了广播也一样,首先通过IPC调用到AMS,然后由AMS调度返回到ActivityThread处理,下面是代码:
private void handleReceiver(ReceiverData data) { LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); IActivityManager mgr = ActivityManagerNative.getDefault(); BroadcastReceiver receiver; try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); data.intent.setExtrasClassLoader(cl); data.intent.prepareToEnterProcess(); data.setExtrasClassLoader(cl); //看到没,广播也是由反射创建的,跟Activity一样 receiver = (BroadcastReceiver)cl.loadClass(component).newInstance(); } ContextImpl context = (ContextImpl)app.getBaseContext(); sCurrentBroadcastIntent.set(data.intent); receiver.setPendingResult(data); //大家都知道的广播回调方法 receiver.onReceive(context.getReceiverRestrictedContext(), data.intent); }
从上面可以看到,广播本质跟Activity一样,都是反射创建,唯一不同的是,Activity需要经过框架层的调用并具有了生命周期方法,而广播没有生命方法的概念,只是回调了一个onReceive方法,所以相对Activity,广播比较容易理解,这里先留下一个问题,为什么广播能够跨进程调用呢,而EventBus,OTTO等事件总线就不能,其实根本原因在于广播是由AMS系统进程统一管理,我们本地进程只是负责执行而已,而EventBus,OTTO那些事件总线的处理都是建立在同个进程之间的,因此他们不能实现跨进程发送消息,而广播却可,当然了AMS处理广播也比较复杂,今天暂时不讨论。
下面分析Service在ActivityThread的处理,代码如下:
private void handleCreateService(CreateServiceData data) { LoadedApk packageInfo = getPackageInfoNoCheck( data.info.applicationInfo, data.compatInfo); Service service = null; try { java.lang.ClassLoader cl = packageInfo.getClassLoader(); //看到没,Service跟BroadCast,Activity一样反射创建 service = (Service) cl.loadClass(data.info.name).newInstance(); } } try { if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name); ContextImpl context = ContextImpl.createAppContext(this, packageInfo); context.setOuterContext(service); Application app = packageInfo.makeApplication(false, mInstrumentation); service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault()); //执行onCreate方法 service.onCreate(); //把Service存储起来 mServices.put(data.token, service); } }
可以看到Service的处理跟前面的广播没啥大区别,都是反射创建,然后回调方法,当然还剩下最后一个ContentProcider,其实这玩意也是反射创建,当然了这个ContentProcider流程比较复杂,
private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) { ContentProvider localProvider = null; IContentProvider provider; if (holder == null || holder.provider == null) { if (DEBUG_PROVIDER || noisy) { Slog.d(TAG, "Loading provider " + info.authority + ": " + info.name); } Context c = null; ApplicationInfo ai = info.applicationInfo; if (context.getPackageName().equals(ai.packageName)) { c = context; } else if (mInitialApplication != null && mInitialApplication.getPackageName().equals(ai.packageName)) { c = mInitialApplication; } else { try { c = context.createPackageContext(ai.packageName, Context.CONTEXT_INCLUDE_CODE); } catch (PackageManager.NameNotFoundException e) { // Ignore } } if (c == null) { Slog.w(TAG, "Unable to get context for package " + ai.packageName + " while loading content provider " + info.name); return null; } try { final java.lang.ClassLoader cl = c.getClassLoader(); //这里是重点反射创建 localProvider = (ContentProvider)cl. loadClass(info.name).newInstance(); provider = localProvider.getIContentProvider(); //执行attachInfo方法 localProvider.attachInfo(c, info); }
可以看到四大组件都是反射创建,并执行生命周期方法,而调度是由AMS负责,ActivityThread主线程负责执行,所以说AMS是调度者,主线程是执行者,就像一个公司里面,AMS是董事会,而ActivityThread是CEO,负责执行具体的事情同时报告给AMS,AMS备份,以有案可查。
OK,以上便是ActivityThread的主要负责的事情,当然了,那些停止,销毁Activity跟启动Activity的流程是一样的,跟AMS都是一个IPC过程调用,下面给出ActivityThread的工作流程(AMS调用ActivityThread方法):
这个是AMS调用ActivityThread的方法,当然了ActivityThread调用AMS都差不多,只是对象换成了ActivityManagerProxy对象而已了。
最后是一些问题在特殊情况下的非常规方法,有兴趣的可以看看:
1.任何时候,任何地方,任何逻辑,如何获取全局Application对象
对于这个问题,也许很多人会说,在自定义的application中定义一个方法,然后在onCreate中赋值,这样就可以获取了,没错,绝大部分的人是这样做,而且好像也从来都没有出错过,是的,这种方法是目前大部分的人都会用,然而,这种方法其实是由限制的,当然了在普通的开发中还是没问题的,然而如果不能接触程序逻辑本身的话(在逆向等比较特殊的情况下),那么上面的方法就无效了,那么是不是就没办法了呢,不是的,上面的代码中曾经说过有个方法:
public static Application currentApplication() { ActivityThread am = currentActivityThread(); return am != null ? am.mInitialApplication : null; }
可以看到是可以获取的,可是ActivityThread是一个隐藏类,那怎么办呢,反射吧,下面是代码:
private void getGlobalApplication()throws Exception{ Class<?> clazz=Class.forName("android.app.ActivityThread"); Method method=clazz.getDeclaredMethod("currentApplication"); Application application= (Application) method.invoke(null); Log.d("[app]","通过反射主线程获取的pplication为:"+application); }
结果如下:
可以看到成功获取了,问题解决!
2.如何获取Activity实例
可能有人会说不是可以getApplication对象吗,那个方法获取的是上下文对象,并不是真正的activity实例,那么如何获取呢,下面是其中一个方法:
首先在基类定义一个Activity变量,然后在onCreate中赋值给具体的子类,这样子类获取的Activity实例。
这种方法是最简单,也是一般情况下用到的。
第二种方法:利用application的生命周期监听方法ActivityLifecycleCallbacks,下面是具体的代码:
OK,也成功获取了当前ActivityThread实例了。
第三种方法:
我们刚才在看ActivityThread的源代码的时候,发现有这么一个方法:
public final Activity getActivity(IBinder token) { return mActivities.get(token).activity; }
你没有看错,这里也有一个方法直接获取Activity实例的,可是傻眼了,参数必须是IBinder类型,就是activity身份的唯一标志,那么这个IBinder该如何获取呢?额,我想想哈,在Activity启动的时候,我们曾经说过在执行完onResume的时候,会报告给AMS,方法为activityResumed,而这个方法的参数刚好就是IBinder类型的,这个参数就代表了当前Activity的toke,那么只要有了这个token,不就可以调用上面的代码来获取Activity了吗,可是activityResumed这个方法是AMS执行的啊,AMS可是运行在系统进程里面的哦,怎么办呢?那就来点暴力点的吧,我们直接Hook AMS,上面说过ActivityThread调用AMS的方法的时候,也是用了Binder机制,具体点说是用了ActivitymanagerProxy这个代理对象进行调用的,而这个类是ActivityManagerNative的子类ActivityManagerService在本地进程的一个代理对象而已(个人认为Binder机制在Java层是很好理解的,只需要记住,不同进程之间都是拿对方的代理对象进行干活的),听不懂是吧,下面看看代码吧:
public abstract class ActivityManagerNative extends Binder implements IActivityManager { /** * Cast a Binder object into an activity manager interface, generating * a proxy if needed. */ static public IActivityManager asInterface(IBinder obj) { if (obj == null) { return null; } IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor); if (in != null) { //如果是本进程的话,之间返回来 return in; } //如果是不同进程的话,就生成一个ActivityManagerProxy代理对象 return new ActivityManagerProxy(obj); } private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() { protected IActivityManager create() { //通过ServiceManager获取到了真正的ActivityManagerService服务 IBinder b = ServiceManager.getService("activity"); if (false) { Log.v("ActivityManager", "default service binder = " + b); } //这里看到没,调用asInterface把AMS传进去转换了一下了 IActivityManager am = asInterface(b); if (false) { Log.v("ActivityManager", "default service = " + am); } //因此这里返回来的是ActivityManagerProxy对象了,如果是不同进程的话,就是Binder机制中 return am; } }; }
我们只需要替换为我们自己的代理对象进行干活,然后在activityResumed方法中进行拦截就好了,好了,说干就干,
public static void hookActivityManagerService() throws Throwable { Class<?> activityManagerNativeClass=Class.forName("android.app.ActivityManagerNative"); //4.0以后,ActivityManagerNative有gDefault单例来进行保存,这个代码中一看就知道了 Field gDefaultField=activityManagerNativeClass.getDeclaredField("gDefault"); gDefaultField.setAccessible(true); Object gDefault=gDefaultField.get(null); Class<?> singleton=Class.forName("android.util.Singleton"); //mInstance其实就是真正的一个对象 Field mInstance=singleton.getDeclaredField("mInstance"); mInstance.setAccessible(true); //真正的对象,就是干活的对象啦,其实就是ActivityManagerProxy而已啦 Object originalIActivityManager=mInstance.get(gDefault); Log.d("[app]","originalIActivityManager="+originalIActivityManager); //通过动态代理生成一个接口的对象 Class<?> iActivityManagerInterface=Class.forName("android.app.IActivityManager"); Object object= Proxy.newProxyInstance(iActivityManagerInterface.getClassLoader(), new Class[]{iActivityManagerInterface},new IActivityManagerServiceHandler(originalIActivityManager)); //这里偷梁换柱,替换为我们自己的对象进行干活就好了 mInstance.set(gDefault,object); Log.d("[app]","Hook AMS成功"); }
IActivityManagerServiceHandler实现类动态代理接口InvocationHandler,在里面拦截了activityResumed方法而已,拦截之后拿到Token,然后调用反射方法就可以获取Activity的实例啦,下面是具体的代码,比较简单,
public class IActivityManagerServiceHandler implements InvocationHandler { private Object base; public IActivityManagerServiceHandler(Object base) { this.base = base; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判断是不是activityResumed,如果是的话,那么拦截参数,然后反射获取实例就好 if (method.getName().equals("activityResumed")){ //这里拿到想要的IBinder啦,就是token IBinder iBinder= (IBinder) args[0]; Log.d("[app]","执行activityResumed方法了,参数toke为"+iBinder); Class<?> clazz=Class.forName("android.app.ActivityThread"); Method method1=clazz.getDeclaredMethod("currentActivityThread"); Object object=method1.invoke(null); Method getActivity=clazz.getDeclaredMethod("getActivity",IBinder.class); Activity mActivity= (Activity) getActivity.invoke(object,iBinder); Log.d("[app]","Hook AMS以后:当前的Activity为:"+mActivity); } return method.invoke(base,args); } }
然后在application中进行注入就好了,好了,下面打印看看吧,
可以看到成功了吧,呵呵,欺骗系统的感觉如何啊,顺便说句,360插件化的核心思想就是瞒天过海,欺骗系统,通过把系统完的团团转,从而实现插件系统的天衣无缝,当然需要处理一些兼容性问题,OK,上面的问题也解决了。
3.有办法干预Activity的启动或者其他过程吗,常规下是没办法的,因为这是框架层的调度,再调用Activity生命周期方法的时候,其实已经是很晚时机了,已经没办法再进一步操作了,也就是说在应用层上是无法干预这些行为的,当然了,你可以在框架层调度的时候,半路杀出个程咬金来就好了,具体的见我的上一篇文章:
http://blog.csdn.net/shifuhetudi/article/details/52078445
4.有办法在启动Activity的时候弹出一个土司吗,或者做其他的事情,就是在执行LaunchActivity方法的时候弹土司或者其他事情?
额,常规下是没办法的,有人会说我在onCreate中弹出来,呵呵,这就违背了我的条件,是在LaunchActivity的时候,就是在AMS给ActivityThread发消息启动Activiy的时候做一些事情,我们都知道这个过程是由Handler发送消息来实现的,可是通过Handler处理消息的代码来看,其实是有顺序的,下面是Handler处理消息的代码:
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
看到了吗,handler处理消息的时候,首先去检查是否实现了callback接口,如果有实现的的话,那么会直接执行接口方法,然后才是handleMessage方法,最后才是执行重写的handleMessage方法,我们一般大部分时候都是重写了handleMessage方法,而ActivityThread主线程用的正是重写的方法,这种方法的优先级是最低的,我们完全可以实现接口来替换掉系统Handler的处理过程,下面请看代码:
public static void hookHandler(Context context) throws Exception { Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); //获取主线程对象 Object activityThread = currentActivityThreadMethod.invoke(null); //获取mH字段 Field mH = activityThreadClass.getDeclaredField("mH"); mH.setAccessible(true); //获取Handler Handler handler = (Handler) mH.get(activityThread); //获取原始的mCallBack字段 Field mCallBack = Handler.class.getDeclaredField("mCallback"); mCallBack.setAccessible(true); //这里设置了我们自己实现了接口的CallBack对象 mCallBack.set(handler, new CustomHandler(context, handler)); }
当然还有CustomHandler类,这个类实现了Callback接口,一样可以拦截方法
public class CustomHandler implements Callback { //这个100一般情况下最好也反射获取,当然了你也可以直接写死,跟系统的保持一致就好了 public static final int LAUNCH_ACTIVITY = 100; private Handler origin; private Context context; public CustomHandler(Context origin, Handler context) { this.context = origin; this.origin = context; } @Override public boolean handleMessage(Message msg) { if (msg.what == LAUNCH_ACTIVITY) { //这样每次启动的时候便会弹出土司来 Toast.makeText( context.getApplicationContext(), "hello,I am going launch", Toast.LENGTH_SHORT).show(); } origin.handleMessage(msg); return false; } }
写完之后在application中注入就好了,OK,你可以去测试一下,看看是不是每次启动Activity的时候都会弹出hello,I am going launch这个字符串。
当然除了这些比较特殊的问题之外,一些时候,特别是插件化的时候,经常遇到这种问题,这些问题往往在应用层是无法拦截的,因为到达应用层的时候,调用链已经调用完毕,没机会去插手了,这种时候就可以考虑这些比较特殊的方法了,当然啦,开发中,哪种最快解决问题用哪种吧。
今天就写到这里吧,小弟水平有限,不足之处敬请指出,感谢大家阅读。
相关文章推荐
- 关于在tomcat中点击startup.bat cmd一闪而过的原因以及分析这个问题的方法
- 关于无线数据传输中产生的流量的一些计算方法以及问题!
- 关于使用SuperMap以及开发中一些问题的解决方法。
- Android ValueAnimator时长错乱或者不起作用的解决方法以及问题分析
- 关于Android stdio使用的一些小问题的解决方法
- 关于android-support-v7-appcompat.jar的使用的一些问题和解决方法
- 关于Android下的JNI编程、SO库以及NDK的一些问题
- 关于Android Fragment生命周期以及其他方法的调用执行顺序问题
- android反编译apk方法以及一些相关问题解决方案
- Android ValueAnimator时长错乱或者不起作用的解决方法以及问题分析
- 关于Android发送短信字数问题的分析及解决方法
- Mac中用Ant实现Android的批量打包碰到的一些问题以及解决方法
- 关于Resin在Eclipse或者Myeclipse上利用Debug模式部署的方法以及遇到的一些问题
- 关于内网版网站建设的一些问题以及分析
- 关于android视频播放显示区域不正常的问题,一些处理方法
- Android 软键盘控制方法、以及开发中遇到的一些问题。
- 关于图片资源在android2.1/2.2/2.3下无法显示的问题以及解决方法
- 关于android初学时一些问题的解决方法资料(持续更新)
- 关于R项目存在问题的一些解决方法
- 商务随需应变与用例分析方法--网友关于工作流类型应用的建模方法问题的回复