Android 插件化系列文章目录

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


文章目录

  • Android 插件化系列文章目录
  • 一、加载插件包 dex 的类加载器
  • 二、生命周期回调方法
  • 三、代理 Activity 组件
  • 四、博客资源

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

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

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

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


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

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

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

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

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

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

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

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

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

public class ProxyActivity extends AppCompatActivity {/*** 被代理的目标 Activity 组件的全类名*/private String className = "";@Overridepublic ClassLoader getClassLoader() {return PluginManager.getInstance().getmDexClassLoader();}
}

这样就可以在 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();}

定义一个 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*/@Overridepublic void attach(Activity proxyActivity) {that = proxyActivity;}@SuppressLint("MissingSuperCall")@Overridepublic void onCreate(Bundle savedInstanceState) {}@SuppressLint("MissingSuperCall")@Overridepublic void onStart() {}@SuppressLint("MissingSuperCall")@Overridepublic void onResume() {}@SuppressLint("MissingSuperCall")@Overridepublic void onPause() {}@SuppressLint("MissingSuperCall")@Overridepublic void onStop() {}@SuppressLint("MissingSuperCall")@Overridepublic void onDestroy() {}@SuppressLint("MissingSuperCall")@Overridepublic void onSaveInstanceState(Bundle outState) {}
}

三、代理 Activity 组件


在代理 Activity 组件 ProxyActivity 中 ,

维护两个成员属性 ,

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

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

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

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

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

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

判断插件 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);
}

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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_proxy);// 注意此处的 ClassLoader 类加载器必须是插件管理器中的类加载器try {// 使用类加载器加载插件中的界面组件Class<?> clazz = getClassLoader().loadClass(className);// 使用反射创建插件界面组件 ActivityActivity 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();}}@Overrideprotected void onStart() {super.onStart();pluginActivity.onStart();}@Overrideprotected void onResume() {super.onResume();pluginActivity.onResume();}@Overrideprotected void onPause() {super.onPause();pluginActivity.onPause();}@Overrideprotected void onStop() {super.onStop();pluginActivity.onStop();}@Overrideprotected void onDestroy() {super.onDestroy();pluginActivity.onDestroy();}@Overrideprotected void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);pluginActivity.onSaveInstanceState(outState);}@Overridepublic boolean onTouchEvent(MotionEvent event) {super.onTouchEvent(event);return pluginActivity.onTouchEvent(event);}@Overridepublic void onBackPressed() {super.onBackPressed();pluginActivity.onBackPressed();}@Overridepublic ClassLoader getClassLoader() {return PluginManager.getInstance().getmDexClassLoader();}
}

四、博客资源


博客资源 :

  • GitHub : https://github.com/han1202012/Plugin

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

  1. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  2. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  3. 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  4. Android 字节码插桩全流程解析

    在Android进阶宝典 – Handler应用于线上卡顿监控中,我简单介绍了一下关于ASM实现字节码插桩来实现方法耗时的监控,但是当时只是找了一个特定的class文件,针对某个特定的方法进行插桩,但 ...

  5. Android字节码插桩

    什么是字节码插桩 字节码插桩就是在构建的过程中,通过修改已经编译完成的字节码文件,也就是class文件,来实现功能的添加. 简单来讲,我们要实现无埋点对客户端的全量统计.这里的统计概括的范围比较广泛, ...

  6. 关于android字节码插桩

    转自:https://www.jianshu.com/p/c202853059b4 基于字节码插桩可以实现面向切面的编程, 实际是在字节码中插入要执行的相关程序. 通过非侵入的方式实现切面编程. (1 ...

  7. JVM插桩之四:Java动态代理机制的对比(JDK和CGLIB,Javassist,ASM)

    一.class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件, ...

  8. 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  9. 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

最新文章

  1. 读博无门,就业碰壁,孤独当了7个月“民科”后,他的论文中了顶会
  2. R语言ggplot2可视化改变图中线条的透明度级别实战
  3. CVPR2019 日程安排
  4. 解决MAVEN项目不扫描src下的mapper.xml文件
  5. 从java里调用r jri的设置方法_从Java里调用R – JRI的设置方法
  6. Qt开发技巧:编写.pro文件,在构建流程中加入命令行的方法
  7. LeetCode:Balanced Binary Tree
  8. 读写文件--with open
  9. 获得对摄像头的访问权
  10. Caused by: java.sql.SQLException: Unable to open a test connection to the given database报错无法打开到给定数据库
  11. C#调用java类、jar包方法
  12. 从维基百科到裴松之注三国志
  13. php实现图片去除水印,PHP实现水印图片
  14. linux新建/删除子接口
  15. 信息检索(IR)笔记1: 倒排索引(Inverted Index)
  16. 扫地机器人扫水泥地板有用吗_拖地机器人好用吗—拖地机器人的优点介绍
  17. Windows fatal exception: code 0xe06d7363
  18. 怎么成为一个优秀的面试官
  19. 统计Excel数据的重复个数(两个方法)
  20. 一步一步实现中后台管理平台模板-08-登录页和用户信息保存

热门文章

  1. C++ 中内存分配和回收
  2. 在 Mac OSX 版的 LispBox 环境上安装配置 SBCL 详细过程
  3. sqlserver2000导出脚本和导出数据
  4. SessionID 的本质
  5. 通过WMIC命令远程打开远程计算机的远程桌面(Remote Desktop)功能
  6. 尚学堂requireJs课程---3、私有和公有属性和方法
  7. 2019.03.21 增删改
  8. python——模块1、模块的分类
  9. python常用的基本操作
  10. 201521123091 《Java程序设计》第3周学习总结