使用反射技术在Android平台上实现本进程内Activity切换时的拦截

为统计应用内Activity切换次数与频度,通过反射访问ActivityThread的mInstrumentation字段,创建新Instramentation对象,重写onPause和onResume方法,在不修改每个Activity的情况下实现切换拦截。在Application初始化时启用,切换时Logcat会显示拦截信息。

有需求需要统计本应用内各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.”等信息。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值