Android:hook应用之拦截系统的startActivity方法

安卓开发中我们可以使用hook技术拦截系统的方法做自己想要的操作,大多是通过反射实现.
应用场景之一:在用户跳转到目标页面时,我们经常需要判断用户是否登录,如果已登录,正常跳转.否则先跳转到登录页面,登录之后再跳转到目标页面.

1,先写工具类的方法

我们目标是:替换ActivityThread中的H类中的callback

public void hookHandlerCallback(Context context) {
        this.context = context;
        try {
            //通过反射获取到ActivityThread对象threadObj
            Class<?> ActivityThreadClass = Class.forName("android.app.ActivityThread");
            Field sCurrentActivityThread = ActivityThreadClass.getDeclaredField("sCurrentActivityThread");
            sCurrentActivityThread.setAccessible(true);
            Object threadObj = sCurrentActivityThread.get(null);
            //通过ActivityThread对象threadObj把系统mCallback替换为自己的callback
            Field handlerField = ActivityThreadClass.getDeclaredField("mH");
            handlerField.setAccessible(true);
            Handler mH = (Handler) handlerField.get(threadObj);
            Field callbackField = Handler.class.getDeclaredField("mCallback");
            callbackField.setAccessible(true);
            HandlerCallBack handlerCallBack = new HandlerCallBack(mH);
            callbackField.set(mH, handlerCallBack);

        } catch (Exception e) {
            e.printStackTrace();
        }

其中HandlerCallBack类实现如下:

class HandlerCallBack implements Handler.Callback {
        private Handler mH;

        public HandlerCallBack(Handler mH) {
            this.mH = mH;
        }

        @Override
        public boolean handleMessage(Message msg) {
            handleLaunchActivity(msg);
            mH.handleMessage(msg);
            return true;
        }

        private void handleLaunchActivity(Message msg) {
            Object obj = msg.obj;
            try {
                if (Build.VERSION.SDK_INT >= 26) { //如果是8.0以上
                    if (msg.what == 159) {
                        Field mActivityCallbacksField = obj.getClass().getDeclaredField("mActivityCallbacks");
                        mActivityCallbacksField.setAccessible(true);
                        List mActivityCallbacks = (List) mActivityCallbacksField.get(msg.obj);
                        for (int i = 0; i < mActivityCallbacks.size(); i++) {
                            if (mActivityCallbacks.get(i).getClass().getName()
                                    .equals("android.app.servertransaction.LaunchActivityItem")) {
                                Log.d(TAG, "捕捉到启动activity消息");
                                Object launchActivityItem = mActivityCallbacks.get(i);

                                Field mIntentField = launchActivityItem.getClass().getDeclaredField("mIntent");
                                mIntentField.setAccessible(true);
                                Intent realIntent = (Intent) mIntentField.get(launchActivityItem);
                                Intent oldIntent = realIntent.getParcelableExtra("oldIntent");
                                if (oldIntent != null) {
                                    SharedPreferences sp = context.getSharedPreferences("lzy", Context.MODE_PRIVATE);
                                    if (sp.getBoolean("login", false)) {
                                        realIntent.setComponent(oldIntent.getComponent());
                                    } else {
                                        Log.d(TAG, "handleLaunchActivity: 跳转到登录页面");
                                        ComponentName componentName = new ComponentName(context, LoginActivity.class);
                                        realIntent.putExtra("extraIntent", oldIntent.getComponent().getClassName());
                                        realIntent.setComponent(componentName);
                                    }
                                }
                            }
                        }
                    }
                } else {
                    if (msg.what == 100) {
                        Log.d(TAG, "捕捉到启动activity消息");
                        Field intentField = obj.getClass().getDeclaredField("intent");
                        intentField.setAccessible(true);
                        Intent realIntent = (Intent) intentField.get(obj);
                        Intent oldIntent = realIntent.getParcelableExtra("oldIntent");
                        if (oldIntent != null) {
                            SharedPreferences sp = context.getSharedPreferences("lzy", Context.MODE_PRIVATE);
                            if (sp.getBoolean("login", false)) {
                                realIntent.setComponent(oldIntent.getComponent());
                            } else {
                                Log.d(TAG, "handleLaunchActivity: 跳转到登录页面");
                                ComponentName componentName = new ComponentName(context, LoginActivity.class);
                                realIntent.putExtra("extraIntent", oldIntent.getComponent().getClassName());
                                realIntent.setComponent(componentName);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

这样我们启动activity时,系统会调用我们的callback方法,我们在里边做一些判断或其他一些操作,来控制activity的跳转.

2,在application中调用
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        HookUtil hookUtil = new HookUtil();
        hookUtil.hookStartActivity(this);
        hookUtil.hookHandlerCallback(this);
    }
}
3,在MainActivity中写跳转方法
startActivity(new Intent(this,SecondActivity.class));
4,在LoginActivity中模拟登陆
public void login(View view) {
        SharedPreferences sp = getSharedPreferences("lzy", Context.MODE_PRIVATE);
        SharedPreferences.Editor edit = sp.edit();
        edit.putBoolean("login",true);
        edit.apply();
        if (className != null){
            ComponentName componentName = new ComponentName(this, className);
            Intent intent = new Intent();
            intent.setComponent(componentName);
            startActivity(intent);
            finish();
        }
    }

完整源码地址


版权声明:本文为qq_36335563原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。