美文网首页
插件化学习记录

插件化学习记录

作者: nullpt | 来源:发表于2019-10-18 15:54 被阅读0次

    1.插件化知识相关

    插件化简介(此处省略若干字,,,)

    这篇文章主要是自己学习的记录,不实用~~~

    • 反射、动态代理
    • Android中的几个相关的ClassLoader
    • Android中四大组件的相关原理
    • ManagerServer
    • 资源加载、资源打包
    • 等等

    2.插件化工作大体流程

    • 加载apk
    • ClassLoader解析
    • 代理反射替换(maybe outdate)
    • 使用插件

    3.Activity的插件化

    接下来跳过加载apk,ClassLoader解析过程,通过Activity插件化的样例讲解如何通过代理和反射替换并使用插件工作;

    首先需要说明一点的是,启动一个完全没有在AndroidManifest注册的Activity是不可能的。因为在启动的过程中,存在一个校验的过程,而这个校验则是由PMS来完成的,这个我们无法干预。因此,Activity的插件化方案大多使用占坑的思想。不同的是如何在检验之前替换,在生成对象的时候还原。

    由于系统源代码过于繁杂,这里通过 极极极极极极简化版 的样例,简介通过hook实现插件化的思想

    代码:
    • 模拟Android系统加载Activity:
    package com.baiguoqing.test;
    
    import com.baiguoqing.test.activity.ActivityMain;
    import com.baiguoqing.test.activity.ActivitySub;
    import com.baiguoqing.test.activity.ActivityTest;
    import com.baiguoqing.test.app.MyApp;
    
    public class AndroidManifest {
    
        /**
         * 模拟配置 application
         */
        public static String DEFAULT_LAUNCHER_APPLICATION = MyApp.MyAppClass;
    
        /**
         * 模拟配置 launcher activity
         */
        public static String DEFAULT_LAUNCHER_ACTIVITY = ActivityMain.ActivityMainClass;
    
        /**
         * 模拟配饰 registered activity
         */
        public static String[] REGISTERED_ACTIVITY = new String[]{
                DEFAULT_LAUNCHER_ACTIVITY,
                ActivityTest.ActivityTestClass,
                /*实现组件化占位Activity:ActivitySub*/
                ActivitySub.ActivitySubClass
        };
    }
    
    package com.baiguoqing.android;
    
    public interface IBinder {
    
        /**
         * 创建 application
         */
        Application newApplication(String appName);
    
        /**
         * 检查 activity
         */
        boolean checkActivity(String className);
    
        /**
         * 创建 activity
         */
        Activity newActivity(String className);
    
    }
    
    package com.baiguoqing.android;
    
    public interface IActivityManager {
    
        /**
         * 开启 application
         */
        void startApplication(IBinder binder);
    
        /**
         * 开启 launcher activity
         */
        void startLauncherActivity(IBinder binder);
    
        /**
         * 开启指定activity
         */
        void startActivity(IBinder binder, String className);
    
        /**
         * 获取操作binder
         */
        IBinder getDefault();
    
        /**
         * 获取全局application
         */
        Application getApplication();
    
    }
    
    package com.baiguoqing.android;
    
    import com.baiguoqing.test.AndroidManifest;
    
    public class Binder implements IBinder {
    
        @Override
        public Application newApplication(String appName) {
            try {
                Class<?> clazz = Class.forName(appName);
                return (Application) clazz.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        @Override
        public boolean checkActivity(String className) {
            for (String s : AndroidManifest.REGISTERED_ACTIVITY) {
                if (s.equals(className)) {
                    /*do something*/
                    return true;
                }
            }
            return false;
        }
    
        @Override
        public Activity newActivity(String className) {
            try {
                Class<?> clazz = Class.forName(className);
                return (Activity) clazz.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    
    package com.baiguoqing.android;
    
    import com.baiguoqing.test.AndroidManifest;
    
    public class ActivityManager implements IActivityManager {
    
        /**
         * 全局 application
         */
        private Application mApplication;
    
        @Override
        public void startApplication(IBinder binder) {
            mApplication = binder.newApplication(AndroidManifest.DEFAULT_LAUNCHER_APPLICATION);
            mApplication.onCreate();
        }
    
        @Override
        public void startLauncherActivity(IBinder binder) {
            startActivity(binder, AndroidManifest.DEFAULT_LAUNCHER_ACTIVITY);
        }
    
        @Override
        public void startActivity(IBinder binder, String className) {
            if (binder.checkActivity(className)) {
                Activity activity = binder.newActivity(className);
                activity.onCreate();
                return;
            }
            throw new RuntimeException("startActivity fail, " + className + " not register");
        }
    
        @Override
        public Application getApplication() {
            return mApplication;
        }
    
        @Override
        public IBinder getDefault() {
            return new Binder();
        }
    }
    
    package com.baiguoqing.android;
    
    public class Singleton {
    
        private static IActivityManager mManagerInstance = new ActivityManager();
    
        public static IActivityManager getInstance() {
            if (mManagerInstance == null) {
                synchronized (Singleton.class) {
                    if (mManagerInstance == null) {
                        return new ActivityManager();
                    }
                }
            }
            return mManagerInstance;
        }
    
    }
    
    package com.baiguoqing.android;
    
    public class Application {
    
        public Application() {
        }
    
        public void onCreate() {
            /*maybe do something*/
        }
    
    }
    
    package com.baiguoqing.android;
    
    public class Activity {
    
        private IActivityManager mManager = Singleton.getInstance();
    
        public Activity() {
        }
    
        public void onCreate() {
            /*maybe do something*/
        }
    
        public void startActivity(String className) {
            mManager.startActivity(mManager.getDefault(), className);
        }
    
        public Application getApplication() {
            return mManager.getApplication();
        }
    }
    
    package com.baiguoqing.android;
    
    public class Main {
        /**
         * 模拟 Thread
         */
        public static void main(String[] args) {
            new Thread(() -> {
                IActivityManager manager = Singleton.getInstance();
                manager.startApplication(manager.getDefault());
                manager.startLauncherActivity(manager.getDefault());
            }).start();
        }
    }
    
    • 通过hook和proxy实现插件

    package com.baiguoqing.test.activity;
    
    import com.baiguoqing.android.Activity;
    
    /**
     * 测试开启Activity
     */
    public class ActivityTest extends Activity {
    
        public static final String ActivityTestClass = "com.baiguoqing.test.activity.ActivityTest";
    
        private static final String ActivityTestTag = "ActivityTest created";
    
        @Override
        public void onCreate() {
            super.onCreate();
            System.out.println(ActivityTestTag);
        }
    }
    
    package com.baiguoqing.test.activity;
    
    import com.baiguoqing.android.Activity;
    
    /**
     * 插件Activity
     */
    public class ActivityTarget extends Activity {
    
        public static final String ActivityTargetClass = "com.baiguoqing.test.activity.ActivityTarget";
    
        private static final String ActivityTargetTag = "ActivityTarget created";
    
        @Override
        public void onCreate() {
            super.onCreate();
            System.out.println(ActivityTargetTag);
        }
    }
    
    package com.baiguoqing.test.activity;
    
    import com.baiguoqing.android.Activity;
    
    /**
     * 占坑Activity
     */
    public class ActivitySub extends Activity {
    
        public static final String ActivitySubClass = "com.baiguoqing.test.activity.ActivitySub";
    
        private static final String ActivitySubTag = "ActivitySub created";
    
        @Override
        public void onCreate() {
            super.onCreate();
            System.out.println(ActivitySubTag);
        }
    }
    
    package com.baiguoqing.test.activity;
    
    import com.baiguoqing.android.Activity;
    
    /**
     * 主启动Activity
     */
    public class ActivityMain extends Activity {
    
        public static final String ActivityMainClass = "com.baiguoqing.test.activity.ActivityMain";
    
        private static final String ActivityMainTag = "ActivityMain created";
    
        @Override
        public void onCreate() {
            super.onCreate();
            System.out.println(ActivityMainTag);
    
            //开启其他activity
            startActivity(ActivityTest.ActivityTestClass);
    
            //开启组件activity
            startActivity(ActivityTarget.ActivityTargetClass);
        }
    }
    

    如上代码启动Thread后,报错:

    ActivityMain created
    ActivityTest created
    Exception in thread "Thread-0" java.lang.RuntimeException: startActivity fail, com.baiguoqing.test.activity.ActivityTarget not register
        at com.baiguoqing.android.ActivityManager.startActivity(ActivityManager.java:30)
        at com.baiguoqing.android.Activity.startActivity(Activity.java:15)
        at com.baiguoqing.test.activity.ActivityMain.onCreate(ActivityMain.java:23)
        at com.baiguoqing.android.ActivityManager.startActivity(ActivityManager.java:27)
        at com.baiguoqing.android.ActivityManager.startLauncherActivity(ActivityManager.java:20)
        at com.baiguoqing.android.Main.lambda$main$0(Main.java:12)
        at java.base/java.lang.Thread.run(Thread.java:830)
    
    Process finished with exit code 0
    

    解决方案:占坑思想,钩住关键节点,代理

    package com.baiguoqing.test.proxy;
    
    import com.baiguoqing.test.activity.ActivityMain;
    import com.baiguoqing.test.activity.ActivitySub;
    import com.baiguoqing.test.activity.ActivityTarget;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class BinderProxy implements InvocationHandler {
    
        private Object mObject;
    
        public BinderProxy(Object mObject) {
            this.mObject = mObject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("checkActivity".equals(method.getName())) {
                if (ActivityTarget.ActivityTargetClass.equals(args[0])) {
                    args[0] = ActivitySub.ActivitySubClass;
                    return method.invoke(mObject, args);
                }
            }
            try {
                return method.invoke(mObject, args);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    package com.baiguoqing.test.proxy;
    
    import com.baiguoqing.android.IBinder;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ActivityManagerProxy implements InvocationHandler {
    
        private Object mObject;
        private IBinder mBinder;
    
        public ActivityManagerProxy(Object object, IBinder binder) {
            this.mObject = object;
            this.mBinder = binder;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("getDefault".equals(method.getName())) {
                return Proxy.newProxyInstance(
                        Thread.currentThread().getContextClassLoader(),
                        new Class[]{IBinder.class},
                        new BinderProxy(mBinder));
            }
            try {
                return method.invoke(mObject, args);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    这里实现了ActivityManager和Binder类的代理,并且拦截了ActivityManager方法getDefault(),并且返回Binder的代理类;在Binder代理类中,调用checkActivity()方法时进行了修改,让其去检测占坑的Activity;

    钩子注册

    package com.baiguoqing.test.app;
    
    import com.baiguoqing.android.Application;
    import com.baiguoqing.android.IActivityManager;
    import com.baiguoqing.android.IBinder;
    import com.baiguoqing.test.proxy.ActivityManagerProxy;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class MyApp extends Application {
    
        public static final String MyAppClass = "com.baiguoqing.test.app.MyApp";
        private static final String MyAppTag = "My Application created";
    
        @Override
        public void onCreate() {
            super.onCreate();
            System.out.println(MyAppTag);
            try {
                hookActivityManager();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 钩子,用于跳过Activity检测
         */
        private void hookActivityManager() throws Exception {
            Field mManagerField = Class.forName("com.baiguoqing.android.Singleton").getDeclaredField("mManagerInstance");
            mManagerField.setAccessible(true);
            Object object1 = mManagerField.get(null);
            Method method = Class.forName("com.baiguoqing.android.ActivityManager").getDeclaredMethod("getDefault");
            method.setAccessible(true);
            IBinder binder = (IBinder) method.invoke(object1);
            IActivityManager managerProxy = (IActivityManager) Proxy.newProxyInstance(
                    Thread.currentThread().getContextClassLoader(),
                    new Class[]{IActivityManager.class},
                    new ActivityManagerProxy(object1, binder)
            );
            mManagerField.set(object1, managerProxy);
        }
    }
    
    My Application created
    ActivityMain created
    ActivityTest created
    ActivityTarget created
    
    Process finished with exit code 0
    

    总的来说,这个小系统能hook的点有很多,这里列举了一个比较简单且清晰的点;
    实际做插件开发时,四大组件的实现方式各有不同,但是思想和这个差不多,就是需要对系统原理具备充分的了解和掌握,这样才能找到合适的hook点;
    目前插件化的方案还是有很多,这里只是列举了一个最为传统好理解的实现方式;

    相关文章

      网友评论

          本文标题:插件化学习记录

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