有需求需要统计本应用内各Activity切换的次数与频度等,由于之前架构设计并未让所有Activity共同继承于一个基础类,同时TabActivity、FragmentActivity等的使用也没法让它们使用同一个父类,因此在本着不想改动每个Activity的目的下,我们摸索出了一个使用反射的方式来实现本应用内部Activity切换时的拦截,具体说来就是拦截本应用中每一个Activity的onPause和onResume事件。
基本思路:
访问ActivityThread的静态方法currentActivityThread拿到当前ActivityThread的实例,再通过反射拿到其private的mInstrumentation对象,并设置一个新的对象给此Field。该新的对象继承自Instramentation,重写了callActivityonPause和callActivityonResume方法,并在方法体内调用了之前旧的Instrumentation对象的同名方法。
ActivityThreadHooker.java
package com.testme.testinstr;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.app.Instrumentation;
import android.util.Log;
public class ActivityThreadHooker {
private static final String TAG = "ATHOOK";
private static ActivityThreadHooker instance = null;
private HookInstrumentation mHookInstr = null;
private Instrumentation mOldInstr = null;
private static boolean mHooked = false;
public static boolean getHooked()
{
return mHooked;
}
public ActivityThreadHooker() throws Throwable
{
synchronized (ActivityThreadHooker.class)
{
if (instance != null)
throw new Exception("Only one ActivityThreadHooker instance can
be created.");
instance = this;
}
mHookInstr = new HookInstrumentation();
try
{
initHook();
} catch (NoSuchFieldException ensf)
{
ensf.printStackTrace();
return;
} catch (IllegalArgumentException eilar)
{
eilar.printStackTrace();
return;
} catch (IllegalAccessException eilac)
{
eilac.printStackTrace();
return;
} catch (ClassNotFoundException ecnf)
{
ecnf.printStackTrace();
return;
} catch (NoSuchMethodException ensm)
{
ensm.printStackTrace();
return;
}
mHooked = true;
}
private void initHook() throws NoSuchFieldException, IllegalArgumentException,
IllegalAccessException, ClassNotFoundException, NoSuchMethodException
{
Class<?> atClass = Class.forName("android.app.ActivityThread");
Method cat = atClass.getMethod("currentActivityThread");
Object at = null;
try
{
at = cat.invoke(atClass, new Object[0]);
}
catch (InvocationTargetException eit)
{
return;
}
if (at == null)
return;
Field instrField = atClass.getDeclaredField("mInstrumentation");
instrField.setAccessible(true);
mOldInstr = (Instrumentation) instrField.get(at);
boolean res = mHookInstr.initOldInstr(mOldInstr);
if (!res)
{
Log.e(TAG, "Error process Old Instrumentation.");
return;
}
instrField.set(at, mHookInstr);
Log.d(TAG, "Hook success!");
}
}
HookInstrumentation.java
package com.testme.testinstr;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.app.Activity;
import android.app.Instrumentation;
import android.util.Log;
public class HookInstrumentation extends Instrumentation {
private static final String TAG = "ATHOOK";
private Instrumentation mOldInstr = null;
private Method methodResume = null;
private Method methodPause = null;
private void obtainMethods() throws NoSuchMethodException
{
if ( mOldInstr == null )
return;
Class<?> inClass = mOldInstr.getClass();
methodResume = inClass.getMethod("callActivityOnResume", Activity.class);
methodPause = inClass.getMethod("callActivityOnPause", Activity.class);
}
@Override
public void callActivityOnPause(Activity activity) {
Log.d(TAG, "OnPause Intercept here.");
if ( methodPause != null )
{
try {
methodPause.invoke(mOldInstr, activity);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return;
} catch (IllegalAccessException e) {
e.printStackTrace();
return;
} catch (InvocationTargetException e) {
e.printStackTrace();
return;
}
}
else
super.callActivityOnPause(activity);
}
@Override
public void callActivityOnResume(Activity activity) {
if ( methodResume != null )
{
try {
methodResume.invoke(mOldInstr, activity);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return;
} catch (IllegalAccessException e) {
e.printStackTrace();
return;
} catch (InvocationTargetException e) {
e.printStackTrace();
return;
}
Log.d(TAG, "OnResume Intercept here.");
}
else
super.callActivityOnResume(activity);
}
public boolean initOldInstr(Instrumentation oldInstr) {
mOldInstr = oldInstr;
try {
obtainMethods();
return true;
} catch (NoSuchMethodException e) {
e.printStackTrace();
return false;
}
}
}
使用时只需要在Application初始化时,new一个ActivityThreadHooker即可。切换Activity时,会在logcat中打印出“OnPause Intercept here.”以及“OnResume Intercept here.”等信息。
为统计应用内Activity切换次数与频度,通过反射访问ActivityThread的mInstrumentation字段,创建新Instramentation对象,重写onPause和onResume方法,在不修改每个Activity的情况下实现切换拦截。在Application初始化时启用,切换时Logcat会显示拦截信息。

490

被折叠的 条评论
为什么被折叠?



