Android

个人Android学习总结


获取Activity实例

<p>我们刚才在看ActivityThread的源代码的时候,发现有这么一个方法:</p> <pre><code> public final Activity getActivity(IBinder token) { return mActivities.get(token).activity; }</code></pre> <p>你没有看错,这里也有一个方法直接获取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层是很好理解的,只需要记住,不同进程之间都是拿对方的代理对象进行干活的),听不懂是吧,下面看看代码吧(AMS中):</p> <pre><code class="language-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&amp;lt;IActivityManager&amp;gt; gDefault = new Singleton&amp;lt;IActivityManager&amp;gt;() { protected IActivityManager create() { //通过ServiceManager获取到了真正的ActivityManagerService服务 IBinder b = ServiceManager.getService(&amp;quot;activity&amp;quot;); if (false) { Log.v(&amp;quot;ActivityManager&amp;quot;, &amp;quot;default service binder = &amp;quot; + b); } //这里看到没,调用asInterface把AMS传进去转换了一下了 IActivityManager am = asInterface(b); if (false) { Log.v(&amp;quot;ActivityManager&amp;quot;, &amp;quot;default service = &amp;quot; + am); } //因此这里返回来的是ActivityManagerProxy对象了,如果是不同进程的话,就是Binder机制中 return am; } }; }</code></pre> <p>我们只需要替换为我们自己的代理对象进行干活,然后在activityResumed方法中进行拦截就好了,好了,说干就干,hook AMS</p> <pre><code class="language-java">public static void hookActivityManagerService() throws Throwable { Class&amp;lt;?&amp;gt; activityManagerNativeClass=Class.forName(&amp;quot;android.app.ActivityManagerNative&amp;quot;); //4.0以后,ActivityManagerNative有gDefault单例来进行保存,这个代码中一看就知道了 Field gDefaultField=activityManagerNativeClass.getDeclaredField(&amp;quot;gDefault&amp;quot;); gDefaultField.setAccessible(true); Object gDefault=gDefaultField.get(null); Class&amp;lt;?&amp;gt; singleton=Class.forName(&amp;quot;android.util.Singleton&amp;quot;); //mInstance其实就是真正的一个对象 Field mInstance=singleton.getDeclaredField(&amp;quot;mInstance&amp;quot;); mInstance.setAccessible(true); //真正的对象,就是干活的对象啦,其实就是ActivityManagerProxy而已啦 Object originalIActivityManager=mInstance.get(gDefault); Log.d(&amp;quot;[app]&amp;quot;,&amp;quot;originalIActivityManager=&amp;quot;+originalIActivityManager); //通过动态代理生成一个接口的对象 Class&amp;lt;?&amp;gt; iActivityManagerInterface=Class.forName(&amp;quot;android.app.IActivityManager&amp;quot;); Object object= Proxy.newProxyInstance(iActivityManagerInterface.getClassLoader(), new Class[]{iActivityManagerInterface},new IActivityManagerServiceHandler(originalIActivityManager)); //这里偷梁换柱,替换为我们自己的对象进行干活就好了 mInstance.set(gDefault,object); Log.d(&amp;quot;[app]&amp;quot;,&amp;quot;Hook AMS成功&amp;quot;); }</code></pre> <p>IActivityManagerServiceHandler实现类动态代理接口InvocationHandler,在里面拦截了activityResumed方法而已,拦截之后拿到Token,然后调用反射方法就可以获取Activity的实例啦,下面是具体的代码,比较简单,</p> <pre><code class="language-java">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(&amp;quot;activityResumed&amp;quot;)){ //这里拿到想要的IBinder啦,就是token IBinder iBinder= (IBinder) args[0]; Log.d(&amp;quot;[app]&amp;quot;,&amp;quot;执行activityResumed方法了,参数toke为&amp;quot;+iBinder); Class&amp;lt;?&amp;gt; clazz=Class.forName(&amp;quot;android.app.ActivityThread&amp;quot;); Method method1=clazz.getDeclaredMethod(&amp;quot;currentActivityThread&amp;quot;); Object object=method1.invoke(null); Method getActivity=clazz.getDeclaredMethod(&amp;quot;getActivity&amp;quot;,IBinder.class); Activity mActivity= (Activity) getActivity.invoke(object,iBinder); Log.d(&amp;quot;[app]&amp;quot;,&amp;quot;Hook AMS以后:当前的Activity为:&amp;quot;+mActivity); } return method.invoke(base,args); } }</code></pre> <p>然后在application中进行注入就好了,好了,下面打印看看吧</p> <p>问题: 有办法干预Activity的启动或者其他过程吗,常规下是没办法的,因为这是框架层的调度,再调用Activity生命周期方法的时候,其实已经是很晚时机了,已经没办法再进一步操作了,也就是说在应用层上是无法干预这些行为的,当然了,你可以在框架层调度的时候hook</p> <p>参考:<a href="http://blog.csdn.net/shifuhetudi/article/details/52078445">http://blog.csdn.net/shifuhetudi/article/details/52078445</a></p> <p>问题: 有办法在启动Activity的时候弹出一个土司吗,或者做其他的事情,就是在执行LaunchActivity方法的时候弹土司或者其他事情? 常规下是没办法的,有人会说我在onCreate中弹出来,呵呵,这就违背了我的条件,是在LaunchActivity的时候,就是在AMS给ActivityThread发消息启动Activiy的时候做一些事情,我们都知道这个过程是由Handler发送消息来实现的,可是通过Handler处理消息的代码来看,其实是有顺序的,下面是Handler处理消息的代码:</p> <pre><code>/** * 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); } }</code></pre> <p>看到了吗,handler处理消息的时候,首先去检查是否实现了callback接口,如果有实现的的话,那么会直接执行接口方法,然后才是handleMessage方法,最后才是执行重写的handleMessage方法,我们一般大部分时候都是重写了handleMessage方法,而ActivityThread主线程用的正是重写的方法,这种方法的优先级是最低的,我们完全可以实现接口来替换掉系统Handler的处理过程,下面请看代码:</p> <pre><code class="language-java">public static void hookHandler(Context context) throws Exception { Class&amp;lt;?&amp;gt; activityThreadClass = Class.forName(&amp;quot;android.app.ActivityThread&amp;quot;); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod(&amp;quot;currentActivityThread&amp;quot;); currentActivityThreadMethod.setAccessible(true); //获取主线程对象 Object activityThread = currentActivityThreadMethod.invoke(null); //获取mH字段 Field mH = activityThreadClass.getDeclaredField(&amp;quot;mH&amp;quot;); mH.setAccessible(true); //获取Handler Handler handler = (Handler) mH.get(activityThread); //获取原始的mCallBack字段 Field mCallBack = Handler.class.getDeclaredField(&amp;quot;mCallback&amp;quot;); mCallBack.setAccessible(true); //这里设置了我们自己实现了接口的CallBack对象 mCallBack.set(handler, new CustomHandler(context, handler)); }</code></pre> <p>当然还有CustomHandler类,这个类实现了Callback接口,一样可以拦截方法</p> <pre><code class="language-java">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(), &amp;quot;hello,I am going launch&amp;quot;, Toast.LENGTH_SHORT).show(); } origin.handleMessage(msg); return false; } }</code></pre> <p>写完之后在application中注入就好了,OK,你可以去测试一下,看看是不是每次启动Activity的时候都会弹出hello,I am going launch这个字符串。</p> <p>当然除了这些比较特殊的问题之外,一些时候,特别是插件化的时候,经常遇到这种问题,这些问题往往在应用层是无法拦截的,因为到达应用层的时候,调用链已经调用完毕,没机会去插手了,这种时候就可以考虑这些比较特殊的方法了,当然啦,开发中,哪种最快解决问题用哪种吧。</p>

页面列表

ITEM_HTML