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

插件化学习记录

作者: 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点;
目前插件化的方案还是有很多,这里只是列举了一个最为传统好理解的实现方式;

相关文章

  • Small插件化实践-踩坑记

    本文主要记录学习Small插件化过程中遇到的问题,欢迎大家一起讨论学习和指正! Small插件化实践 1.动态替换...

  • 插件化学习记录

    1.插件化知识相关 插件化简介(此处省略若干字,,,) 这篇文章主要是自己学习的记录,不实用~~~ 反射、动态代理...

  • Android基础知识:项目架构基础概述

    1、前言 这篇记录一下架构方面的相关知识总结以及自己学习后对模块化、组件化和插件化这三化概念的理解。 2、MVC、...

  • 编辑器--VS Code

    VSCode 自用插件记录 记录使用的插件,供那天突然还电脑时候回来看 XML Tools 格式化xml文件等v...

  • 前端国际化需求

    目前比较流行的国际化插件是 i18n 。我现在记录,自身的学历过程 我所学习的是vue-i18n 的插件,基于Vu...

  • 插件化学习

    atlas学习传送门github demo地址atlas插件化源码解析atlas插件化官方文档atlas原理细节解析

  • 记:初学习插件化和热更新

    本篇文章学习于以下链接,主意为用自己方式理解和记录动态加载技术插件化讲解:转载于https://segmentfa...

  • Android Studio 基本使用

    AndroidStudio学习记录 1. 插件的使用。 plugins.jetbrains.com插件网站。 2....

  • android 插件化学习(一、汇总)

    本篇是学习插件化的第一篇,无技术干货,纯扯淡向。主要内容是对于市面上插件化框架的简单分析和建立起对于插件化的最基础...

  • RePlugin插件化框架的学习

    现状 最近在接触插件化方面的技术,学习后赶紧坐下笔记,给入门的朋友看, 一起学习,一起进步。当前比较热门的插件化框...

网友评论

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

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