美文网首页插件化android开发技巧组建化
Android9.0和10.0插件化原理实现

Android9.0和10.0插件化原理实现

作者: digtal_ | 来源:发表于2020-04-08 15:31 被阅读0次

    Activity的插件化解决的一个根本性问题就是插件中的Activity并没有在宿主的AndroidManifest.xml中进行注册,也就是说我们需要启动一个未注册的Activity,因此需要对Activity的启动过程有个了解,Android各个版本源码启动流程略有不同,但大致流程一样,这里给出9.0和10.0实现的二种方式。

    1.继承Instrumentation的方式,这种方式比较简单在9.0和10.0都能用
    • 1.重写一个类继承Instrumentation
    public class InstrumentationProxy extends Instrumentation {
        private Instrumentation mInstrumentation;
    
        public InstrumentationProxy(Instrumentation instrumentation) {
            mInstrumentation = instrumentation;
        }
    
        public ActivityResult execStartActivity(
                Context who, IBinder contextThread, IBinder token, Activity target,
                Intent intent, int requestCode, Bundle options) {
            List<ResolveInfo> resolveInfo = who.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_ALL);
            LogUtil.logE("execStartActivityexecStartActivity");
            //判断启动的插件Activity是否在AndroidManifest.xml中注册过
            if (resolveInfo.size() == 0) {
                //保存目标插件
                intent.putExtra(Const.INTENT_DATA, intent.getComponent().getClassName());
                //设置为占坑Activity
                intent.setClassName(who, "com.example.client.hook.ProxyActivity");
            }
            try {
                Method execStartActivity = getClass().getSuperclass().getDeclaredMethod("execStartActivity",
                        Context.class, IBinder.class, IBinder.class, Activity.class,
                        Intent.class, int.class, Bundle.class);
                return (ActivityResult) execStartActivity.invoke(mInstrumentation, who, contextThread, token, target, intent, requestCode, options);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        @Override
        public Activity newActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            String data = intent.getStringExtra(Const.INTENT_DATA);
            if (!TextUtils.isEmpty(data)) {
                return super.newActivity(cl, data, intent);
            }
            return super.newActivity(cl, className, intent);
        }
    }
    
    • 2.在Application中onCreate方法中去替换我们自己的Instrumentation
     private void hook1() {
            try {
                Class<?> clazz = Class.forName("android.app.ActivityThread");
                //获取ActivityThread
                Field sCurrentActivityThreadFiled = clazz.getDeclaredField("sCurrentActivityThread");
                sCurrentActivityThreadFiled.setAccessible(true);
                Object sCurrentActivityThread = sCurrentActivityThreadFiled.get(null);
                //获取instrumentation
                Field instrumentationFiled = clazz.getDeclaredField("mInstrumentation");
                instrumentationFiled.setAccessible(true);
                Instrumentation instrumentation = (Instrumentation) instrumentationFiled.get(sCurrentActivityThread);
                //设置自己的instrumentation
                InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
                instrumentationFiled.set(sCurrentActivityThread,instrumentationProxy);      
            } catch (Exception e) {       
            }
        }
    
      <application
            android:name=".hook.App"
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <!--代理的Activity-->
            <activity android:name=".ProxyActivity">
    
            </activity>
        </application>
    
    2.使用动态代理的方式,拦截启动activity的方法替换intent

    大体的时序图


    a.jpg
     private void hook() {
    
            try {
                //1.获取IActivityManagerSingleton
                Object IActivityManagerSingleton = getSingletonByVersion();
    
                //2.获取mInstance
                Class<?> singletonclazz = Class.forName("android.util.Singleton");
                Field mInstanceField = singletonclazz.getDeclaredField("mInstance");
                mInstanceField.setAccessible(true);
    
                if (Build.VERSION.SDK_INT == 29) {
                    //Q上需要动态执行create方法
                    Method getMethod = singletonclazz.getMethod("get");
                    getMethod.setAccessible(true);
                    getMethod.invoke(IActivityManagerSingleton);
    
                }
    
    
                Object mInstance = mInstanceField.get(IActivityManagerSingleton);
                Logutils.LogE(mInstance+"");
                //3.动态代理设置自己的mInstance
                Object proxyInstance = Proxy.newProxyInstance(getClassLoader(),
                        mInstance.getClass().getInterfaces(),
                        new MyInvocationHandler(mInstance));
    
                //4.设置代理的proxyInstance
    
                mInstanceField.set(IActivityManagerSingleton, proxyInstance);
    
                //5.获取ActivityThread实例
                Class<?> ActivityThreadclass = Class.forName("android.app.ActivityThread");
    
                Field sCurrentActivityThreadFiled = ActivityThreadclass.getDeclaredField(
                        "sCurrentActivityThread");
                sCurrentActivityThreadFiled.setAccessible(true);
                Object sCurrentActivityThread = sCurrentActivityThreadFiled.get(null);
    
                //6.获取mH实例
                Field mHFiled = ActivityThreadclass.getDeclaredField("mH");
                mHFiled.setAccessible(true);
                Object mH = mHFiled.get(sCurrentActivityThread);
    
                Field mCallbackFiled = Handler.class.getDeclaredField("mCallback");
                mCallbackFiled.setAccessible(true);
                //7.设置进入我们自己的Callback
    
                mCallbackFiled.set(mH, new MyHandlerCallback((Handler) mH));
    
            } catch (Exception e) {
                e.printStackTrace();
                Log.e("TAG------", e.toString());
            }
        }
    

    需要注意的是10.0在获取mInstance的时候必须动态执行下create方法,否则拿不到mInstance 的实例.

        private Object getSingletonByVersion() {
            try {
                if (Build.VERSION.SDK_INT == 28) {
                    Class<?> clazz = Class.forName("android.app.ActivityManager");
                    Field field = clazz.getDeclaredField("IActivityManagerSingleton");
                    field.setAccessible(true);
                    return field.get(null);
                } else if (Build.VERSION.SDK_INT == 29) {
                    Class<?> clazz = Class.forName("android.app.ActivityTaskManager");
                    Field field = clazz.getDeclaredField("IActivityTaskManagerSingleton");
                    field.setAccessible(true);
                    return field.get(null);
                }
            } catch (Exception e) {
                e.printStackTrace();
                Logutils.LogE(e.toString());
            }
            return null;
        }
    

    10.0和9.0获取SingleTon的方法也略有不同,10.0把几个类修改了下,但大致流程相同

      private class MyInvocationHandler implements InvocationHandler {
    
            private Object mIActivityManager;
    
            public MyInvocationHandler(Object IActivityManager) {
                mIActivityManager = IActivityManager;
            }
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("startActivity")) {
                    for (Object arg : args) {
                        if (arg instanceof Intent) {
                            Intent intent = (Intent) arg;
                            //把插件的Activity类名传入
                            intent.putExtra(INTENT_DATA, intent.getComponent().getClassName());
                            //设置代理的activity
                            intent.setClass(getApplicationContext(), ProxyActivity.class);
                        }
    
                    }
                }
                return method.invoke(mIActivityManager, args);
            }
        }
    

    使用动态让代理拦截startActivity方法替换intent里面的class绕过AndroidManifest的检测,并把我们设置的class当作参数传递。

     private class MyHandlerCallback implements Handler.Callback {
            private Handler mHandler;
    
            public MyHandlerCallback(Handler handler) {
                mHandler = handler;
            }
    
            @Override
            public boolean handleMessage(@NonNull Message msg) {
                if (msg.what == 159) {
                    Object obj = msg.obj;
                    try {
                        //获取ClientTransaction中的mActivityCallbacks集合
                        Class<?> clazz = Class.forName("android.app.servertransaction" +
                                ".ClientTransaction");
                        Field mActivityCallbacksFiled = clazz.getDeclaredField("mActivityCallbacks");
                        mActivityCallbacksFiled.setAccessible(true);
                        List list = (List) mActivityCallbacksFiled.get(obj);
                        if (list != null && list.size() > 0) {
                            //得到集合中的LaunchActivityItem
                            Object o = list.get(0);
                            //获取LaunchActivityItem中的mIntent
                            Class<?> LaunchActivityItemClazz = Class.forName("android.app" +
                                    ".servertransaction.LaunchActivityItem");
                            Field mIntentFiled = LaunchActivityItemClazz.getDeclaredField("mIntent");
                            mIntentFiled.setAccessible(true);
                            Intent intent = (Intent) mIntentFiled.get(o);
                            //得到我们设置的class 替换进去
                            if (intent.getStringExtra(INTENT_DATA) != null) {
                                String className = intent.getStringExtra(INTENT_DATA);
                                intent.setClassName(getApplicationContext(), className);
                            }
    
                        }
    
                    } catch (Exception e) {
                        e.printStackTrace();
                        Logutils.LogE(e.toString());
                    }
                }
    
                mHandler.handleMessage(msg);
                return true;
            }
        }
    
    

    在ActivityThread回调的handlemessage方法中在创建activity时来替换我们原来设置的Activity,从intent中获取我们设置的参数, 有了上面的代码后在Activity中可以正常运行但是在AppcompatActivity中还是报错需要再次Hook

    private void hook1() {
            try {
                Class<?> ActivityThreadclass = Class.forName("android.app.ActivityThread");
    
                Field sCurrentActivityThreadFiled = ActivityThreadclass.getDeclaredField(
                        "sCurrentActivityThread");
                sCurrentActivityThreadFiled.setAccessible(true);
                Object sCurrentActivityThread = sCurrentActivityThreadFiled.get(null);
    
                Field sPackageManagerFiled = ActivityThreadclass.getDeclaredField(
                        "sPackageManager");
                sPackageManagerFiled.setAccessible(true);
                Object sPackageManager = sPackageManagerFiled.get(null);
    
                Object proxyInstance = Proxy.newProxyInstance(getClassLoader(),
                        sPackageManager.getClass().getInterfaces(),
                        new PackageManagerHandler(sPackageManager));
    
                sPackageManagerFiled.set(sCurrentActivityThread, proxyInstance);
    
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private class PackageManagerHandler implements InvocationHandler {
            private Object IPackageManager;
    
            public PackageManagerHandler(Object IPackageManager) {
                this.IPackageManager = IPackageManager;
            }
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("getActivityInfo".equals(method.getName())) {
                    args[0] = new ComponentName(getPackageName(), ProxyActivity.class.getName());
                }
                return method.invoke(IPackageManager, args);
            }
        }
    

    在getActivityInfo时候找不到我们的Activity,依然使用动态代理的方式替换我们代理的Activity。
    看下最后效果


    aa.gif

    相关文章

      网友评论

        本文标题:Android9.0和10.0插件化原理实现

        本文链接:https://www.haomeiwen.com/subject/unptmhtx.html