获取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&lt;IActivityManager&gt; gDefault = new Singleton&lt;IActivityManager&gt;() {
protected IActivityManager create() {
//通过ServiceManager获取到了真正的ActivityManagerService服务
IBinder b = ServiceManager.getService(&quot;activity&quot;);
if (false) {
Log.v(&quot;ActivityManager&quot;, &quot;default service binder = &quot; + b);
}
//这里看到没,调用asInterface把AMS传进去转换了一下了
IActivityManager am = asInterface(b);
if (false) {
Log.v(&quot;ActivityManager&quot;, &quot;default service = &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&lt;?&gt; activityManagerNativeClass=Class.forName(&quot;android.app.ActivityManagerNative&quot;);
//4.0以后,ActivityManagerNative有gDefault单例来进行保存,这个代码中一看就知道了
Field gDefaultField=activityManagerNativeClass.getDeclaredField(&quot;gDefault&quot;);
gDefaultField.setAccessible(true);
Object gDefault=gDefaultField.get(null);
Class&lt;?&gt; singleton=Class.forName(&quot;android.util.Singleton&quot;);
//mInstance其实就是真正的一个对象
Field mInstance=singleton.getDeclaredField(&quot;mInstance&quot;);
mInstance.setAccessible(true);
//真正的对象,就是干活的对象啦,其实就是ActivityManagerProxy而已啦
Object originalIActivityManager=mInstance.get(gDefault);
Log.d(&quot;[app]&quot;,&quot;originalIActivityManager=&quot;+originalIActivityManager);
//通过动态代理生成一个接口的对象
Class&lt;?&gt; iActivityManagerInterface=Class.forName(&quot;android.app.IActivityManager&quot;);
Object object= Proxy.newProxyInstance(iActivityManagerInterface.getClassLoader(),
new Class[]{iActivityManagerInterface},new IActivityManagerServiceHandler(originalIActivityManager));
//这里偷梁换柱,替换为我们自己的对象进行干活就好了
mInstance.set(gDefault,object);
Log.d(&quot;[app]&quot;,&quot;Hook AMS成功&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(&quot;activityResumed&quot;)){
//这里拿到想要的IBinder啦,就是token
IBinder iBinder= (IBinder) args[0];
Log.d(&quot;[app]&quot;,&quot;执行activityResumed方法了,参数toke为&quot;+iBinder);
Class&lt;?&gt; clazz=Class.forName(&quot;android.app.ActivityThread&quot;);
Method method1=clazz.getDeclaredMethod(&quot;currentActivityThread&quot;);
Object object=method1.invoke(null);
Method getActivity=clazz.getDeclaredMethod(&quot;getActivity&quot;,IBinder.class);
Activity mActivity= (Activity) getActivity.invoke(object,iBinder);
Log.d(&quot;[app]&quot;,&quot;Hook AMS以后:当前的Activity为:&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&lt;?&gt; activityThreadClass =
Class.forName(&quot;android.app.ActivityThread&quot;);
Method currentActivityThreadMethod =
activityThreadClass.getDeclaredMethod(&quot;currentActivityThread&quot;);
currentActivityThreadMethod.setAccessible(true);
//获取主线程对象
Object activityThread = currentActivityThreadMethod.invoke(null);
//获取mH字段
Field mH = activityThreadClass.getDeclaredField(&quot;mH&quot;);
mH.setAccessible(true);
//获取Handler
Handler handler = (Handler) mH.get(activityThread);
//获取原始的mCallBack字段
Field mCallBack = Handler.class.getDeclaredField(&quot;mCallback&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(),
&quot;hello,I am going launch&quot;, Toast.LENGTH_SHORT).show();
}
origin.handleMessage(msg);
return false;
}
}</code></pre>
<p>写完之后在application中注入就好了,OK,你可以去测试一下,看看是不是每次启动Activity的时候都会弹出hello,I am going launch这个字符串。</p>
<p>当然除了这些比较特殊的问题之外,一些时候,特别是插件化的时候,经常遇到这种问题,这些问题往往在应用层是无法拦截的,因为到达应用层的时候,调用链已经调用完毕,没机会去插手了,这种时候就可以考虑这些比较特殊的方法了,当然啦,开发中,哪种最快解决问题用哪种吧。</p>