【Android 插件化】“ 插桩式 “ 插件化框架 ( 代理 Activity 组件开发 )

举报
韩曙亮 发表于 2022/01/10 23:06:17 2022/01/10
【摘要】 Android 插件化系列文章目录 【Android 插件化】插件化简介 ( 组件化与插件化 ) 【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 ) 【Android 插件化...

Android 插件化系列文章目录

【Android 插件化】插件化简介 ( 组件化与插件化 )
【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )
【Android 插件化】插件化原理 ( 类加载器 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )




参考 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 ) 中给出的实现思路 , 逐步实现 “ 插桩式 “ 插件化框架 ;

【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 ) 博客中 , 开发了 DexClassLoader 类加载器加载插件包 , 并使用 AssetManager 加载插件包资源的模块 ;

本博客中开发开发本地的 Activity 桩 , 即空壳 Activity , 用于持有插件界面组件 , 并在生命周期中回调插件界面 Activity 组件的对应生命周期方法 ;




一、加载插件包 dex 的类加载器



在 插件化框架 中定义一个代理 Activity , ProxyActivity , 该 Activity 只是个空壳 , 持有从 apk 加载的 PluginActivity 类对象 , 在 ProxyActivity 声明周期方法中调用对应 PluginActivity 类的生命周期方法

将 ProxyActivity 中要加载的全类名 , 设置在成员属性中 ;

/**
 * 被代理的目标 Activity 组件的全类名
 */
private String className = "";

  
 
  • 1
  • 2
  • 3
  • 4

如果要使用类加载器加载 插件包 apk 中的 ProxyActivity , 则不能使用应用本身的类加载器 , 插件管理器 PluginManager 中的类加载器已经加载了插件包 apk 中的 dex 文件 , 因此可以获取到 PluginActivity 字节码对象 ;

// 创建 DexClassLoader
mDexClassLoader = new DexClassLoader(
        loadPath, // 加载路径
        optimizedDirectory.getAbsolutePath(), // apk 解压缓存目录
        null,
        context.getClassLoader() // DexClassLoader 加载器的父类加载器
);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在支持 插件化的工程中 , " 宿主 " 模块 和 " 插件 " 模块 都要依赖该 " 插件化框架 " ;

调用插件化框架中的 PluginManager 单例对象中的类加载器 , 加载插件包 apk 中的 PluginActivity 类对象 ;

/**
 * 插件化框架核心类
 */
public class PluginManager {
	/**
	 * 类加载器
	 * 用于加载插件包 apk 中的 classes.dex 文件中的字节码对象
	 */
	private DexClassLoader mDexClassLoader;
	
    /**
     * 获取类加载器
     * @return
     */
    public DexClassLoader getmDexClassLoader() {
        return mDexClassLoader;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

设置 代理界面组件 ProxyActivity 中的类加载器为 插件化框中 中的 插件管理器 PluginManager 中的类加载器 ;

public class ProxyActivity extends AppCompatActivity {

    /**
     * 被代理的目标 Activity 组件的全类名
     */
    private String className = "";

    @Override
    public ClassLoader getClassLoader() {
        return PluginManager.getInstance().getmDexClassLoader();
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这样就可以在 ProxyActivity 中调用 getClassLoader() 方法获取插件管理器中的 DexClassLoader , 用于加载插件中的字节码类对象 ;




二、生命周期回调方法



定义一个接口 , 接口中定义 Activity 组件的生命周期 ;

package com.example.plugin_core;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;

import androidx.annotation.NonNull;

public interface PluginActivityInterface {

    /**
     * 绑定代理 Activity
     * @param proxyActivity
     */
    void attach(Activity proxyActivity);

    void onCreate(Bundle savedInstanceState);
    void onStart();
    void onResume();
    void onPause();
    void onStop();
    void onDestroy();
    void onSaveInstanceState(Bundle outState);
    boolean onTouchEvent(MotionEvent event);
    void onBackPressed();

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

定义一个 Activity 基类 BaseActivity , 继承 AppCompatActivity , 实现了 PluginActivityInterface , 其中涉及到的生命周期函数重复了 , 如 AppCompatActivity 中的 public void onCreate(Bundle savedInstanceState) 方法与 PluginActivityInterface 接口中的 public void onCreate(Bundle savedInstanceState) 方法是重复的 , 这里在每个方法前面加上 @SuppressLint("MissingSuperCall") 注解 , 忽略该报错 ;

所有的插件包中的 Activity 都要集继承该 BaseActivity ;

这样写的目的是为了方便在代理 Activity 中可以随意调用插件包中的 Activity 类的生命周期函数 , 这些生命周期函数都是 protected 方法 , 不能直接调用 , 否则每个方法调用时 , 还要先反射修改访问性 , 才能调用 ;

package com.example.plugin_core;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity implements PluginActivityInterface {

    /**
     * 注入的 Activity
     */
    private Activity that;

    /**
     * 注入代理 Activity
     * 在 ProxyActivity 中将代理 Activity 组件注入进来
     * @param proxyActivity
     */
    @Override
    public void attach(Activity proxyActivity) {
        that = proxyActivity;
    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onCreate(Bundle savedInstanceState) {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onStart() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onResume() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onPause() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onStop() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onDestroy() {

    }

    @SuppressLint("MissingSuperCall")
    @Override
    public void onSaveInstanceState(Bundle outState) {

    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67



三、代理 Activity 组件



在代理 Activity 组件 ProxyActivity 中 ,

维护两个成员属性 ,

/**
 * 被代理的目标 Activity 组件的全类名
 */
private String className = "";

  
 
  • 1
  • 2
  • 3
  • 4

插件包类的 全类名 , 需要通过反射获取该类的字节码对象 ;

/**
 * 插件包中的 Activity 界面组件
 */
private PluginActivityInterface pluginActivity;

  
 
  • 1
  • 2
  • 3
  • 4

插件包中的 Activity 组件类 , 借助反射获取该类 , 在 Activity 的各个声明周期函数中 , 需要调用该 PluginActivityInterface 的各个对应接口 ;


在 onCreate 方法中 , 先获取类加载器 , 并反射 插件 Activity 字节码对象 ; 并使用反射创建 Activity 类对象 ;

// 使用类加载器加载插件中的界面组件
Class<?> clazz = getClassLoader().loadClass(className);
// 使用反射创建插件界面组件 Activity
Activity activity = (Activity) clazz.newInstance();

  
 
  • 1
  • 2
  • 3
  • 4

判断插件 Activity 是否是 PluginActivityInterface 类型的 , 如果是强转为 PluginActivityInterface 类型对象 , 并开始注入上下文 Activity , 在插件类中凡是涉及到调用上下文的地方 , 一律调用该注入的上下文对象 , 也就是代理 ProxyActivity 的上下文 ;

// 判断 Activity 组件是否是 PluginActivityInterface 接口类型的
if (activity instanceof PluginActivityInterface){
    // 如果是 PluginActivityInterface 类型 , 则强转为该类型
    this.pluginActivity = (PluginActivityInterface) activity;
    // 上下文注入
    // 将该 ProxyActivity 绑定注入到 插件包的 PluginActivity 类中
    // 该 PluginActivity 具有运行的上下文
    // 一旦绑定注入成功 , 则被代理的 PluginActivity 也具有了上下文
    pluginActivity.attach(this);
    // 调用
    pluginActivity.onCreate(savedInstanceState);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

ProxyActivity 完整代码示例 :

package com.example.plugin_core;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;

/**
 * 该 Activity 只是个空壳 ;
 * 主要用于持有从 apk 加载的 Activity 类
 * 并在 ProxyActivity  声明周期方法中调用对应 PluginActivity 类的生命周期方法
 */
public class ProxyActivity extends AppCompatActivity {

    /**
     * 被代理的目标 Activity 组件的全类名
     */
    private String className = "";

    /**
     * 插件包中的 Activity 界面组件
     */
    private PluginActivityInterface pluginActivity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_proxy);

        // 注意此处的 ClassLoader 类加载器必须是插件管理器中的类加载器
        try {
            // 使用类加载器加载插件中的界面组件
            Class<?> clazz = getClassLoader().loadClass(className);
            // 使用反射创建插件界面组件 Activity
            Activity activity = (Activity) clazz.newInstance();

            // 判断 Activity 组件是否是 PluginActivityInterface 接口类型的
            if (activity instanceof PluginActivityInterface){
                // 如果是 PluginActivityInterface 类型 , 则强转为该类型
                this.pluginActivity = (PluginActivityInterface) activity;

                // 上下文注入
                // 将该 ProxyActivity 绑定注入到 插件包的 PluginActivity 类中
                // 该 PluginActivity 具有运行的上下文
                // 一旦绑定注入成功 , 则被代理的 PluginActivity 也具有了上下文
                pluginActivity.attach(this);

                // 调用
                pluginActivity.onCreate(savedInstanceState);
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

    }

    @Override
    protected void onStart() {
        super.onStart();
        pluginActivity.onStart();
    }

    @Override
    protected void onResume() {
        super.onResume();
        pluginActivity.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        pluginActivity.onPause();
    }

    @Override
    protected void onStop() {
        super.onStop();
        pluginActivity.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        pluginActivity.onDestroy();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        pluginActivity.onSaveInstanceState(outState);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        return pluginActivity.onTouchEvent(event);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        pluginActivity.onBackPressed();
    }

    @Override
    public ClassLoader getClassLoader() {
        return PluginManager.getInstance().getmDexClassLoader();
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116




四、博客资源



博客资源 :

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/117485680

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。