Android 插件化“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 插件化“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )相关的知识,希望对你有一定的参考价值。
android 插件化系列文章目录
【Android 插件化】插件化简介 ( 组件化与插件化 )
【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )
【Android 插件化】插件化原理 ( 类加载器 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )
文章目录
一、编译 " 插件 " 模块
首先编译 " 插件 " 模块 , 生成 APK 安装包 ;
二、首次编译运行 " 宿主 " 模块
编译运行 " 宿主 " 模块 , 目的是为了生成 getExternalFilesDir(null).getAbsolutePath()
目录 ;
该目录在应用首次运行时自动生成 , 应用卸载后 , 自动删除 ;
该目录的绝对路径名称如下 :
/sdcard/Android/data/kim.hsl.plugin/files
拷贝插件包 : 将 上一章节 编译的插件包 apk 拷贝到 /sdcard/Android/data/kim.hsl.plugin/files
目录中 ;
在 " Device FIle Explorer " 面板中 , 右键点击 /sdcard/Android/data/kim.hsl.plugin/files
目录 , 点击 " Upload " ,
再弹出的对话框中 , 选择编译生成的 apk 安装包 , 上传到该目录中 ;
上传完成 ;
三、第二次运行 " 宿主 " 模块
第一次运行 " 宿主 " 模块 后 , 生成 /sdcard/Android/data/kim.hsl.plugin/files
目录 , 将 " 插件 " 模块编译后的插件包拷贝到该目录中 ;
第二次运行时 , 在 onCreate 方法中就会加载解析该插件包 , 解析 dex 文件与资源文件 ;
点击跳转按钮 , 即可跳转到插件模块 Activity 中 ;
四、" 宿主 " 模块代码
package kim.hsl.plugin;
import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.example.plugin_core.PluginManager;
import com.example.plugin_core.ProxyActivity;
import pub.devrel.easypermissions.EasyPermissions;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "getClassLoader() : " +
getClassLoader());
Log.i(TAG, "getClassLoader().getParent() : " +
getClassLoader().getParent());
Log.i(TAG, "getClassLoader().getParent().getParent() : " +
getClassLoader().getParent().getParent());
EasyPermissions.requestPermissions(
this,
"权限申请原理对话框 : 描述申请权限的原理",
100,
// 下面是要申请的权限 可变参数列表
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
);
/*
加载 " 插件 " 模块的 apk 文件
先将该插件包拷贝到
*/
String path = getExternalFilesDir(null).getAbsolutePath() + "/plugin-debug.apk";
Log.i(TAG, "path : " + path);
PluginManager.getInstance().loadPlugin(this, path);
}
public void onClick(View view) {
// 跳转到 plugin_core 模块的代理 Activity
// 首先要获取 " 插件 " 模块中的入口 Activity 类
Intent intent = new Intent(this, ProxyActivity.class);
// 获取 " 插件 " 模块中的 Activity 数组信息
ActivityInfo[] activityInfos = PluginManager.getInstance().getmPackageInfo().activities;
// 获取的插件包中的 Activity 不为空 , 才进行界面跳转
if (activityInfos.length > 0) {
Log.i(TAG, "CLASS_NAME : " + activityInfos[0].toString());
// 这里取插件包中的第 0 个 Activity
// 次序就是在 AndroidManifest.xml 清单文件中定义 Activity 组件的次序
// 必须将 Launcher Activity 定义在第一个位置
// 不能在 Launcher Activity 之前定义 Activity 组件
// 传入的是代理的目标组件的全类名
intent.putExtra("CLASS_NAME", activityInfos[0].name);
startActivity(intent);
}
}
}
五、" 插件 " 模块代码
package kim.hsl.plugin;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.example.plugin_core.BaseActivity;
public class PluginActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_plugin);
}
}
六、" 依赖库 " 模块代码
1、插件 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();
}
2、插件 Activity 基类
package com.example.plugin_core;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
/**
* " 插件 " 模块中的 Activity 类都继承该类
* 具体的 Activity 业务类的父类
*/
public class BaseActivity extends AppCompatActivity implements PluginActivityInterface {
/**
* 注入的 Activity , 代理该 Activity 类作为上下文
*/
private Activity proxyActivity;
/**
* 注入代理 Activity
* 在 ProxyActivity 中将代理 Activity 组件注入进来
* @param proxyActivity
*/
@Override
public void attach(Activity proxyActivity) {
this.proxyActivity = proxyActivity;
}
@Override
public void setContentView(int layoutResID) {
// 调用代理 Activity 中的 setContentView 方法
if (proxyActivity != null) {
proxyActivity.setContentView(layoutResID);
}else{
super.setContentView(layoutResID);
}
}
@Override
public void setContentView(View view) {
// 调用代理 Activity 中的 setContentView 方法
if (proxyActivity == null) {
proxyActivity.setContentView(view);
}else{
super.setContentView(view);
}
}
@Override
public <T extends View> T findViewById(int id) {
if (proxyActivity == null) {
return proxyActivity.findViewById(id);
}else{
return super.findViewById(id);
}
}
@Override
public void startActivity(Intent intent) {
if (proxyActivity == null) {
intent.putExtra("className", intent.getComponent().getClassName());
proxyActivity.startActivity(intent);
}else{
super.startActivity(intent);
}
}
@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) {
}
}
3、代理 Activity ( 桩 )
package com.example.plugin_core;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
/**
* 插件化框架核心类
*/
public class PluginManager {
/**
* 类加载器
* 用于加载插件包 apk 中的 classes.dex 文件中的字节码对象
*/
private DexClassLoader mDexClassLoader;
/**
* 从插件包 apk 中加载的资源
*/
private Resources mResources;
/**
* 插件包信息类
*/
private PackageInfo mPackageInfo;
/**
* 加载插件的上下文对象
*/
private Context mContext;
/**
* PluginManager 单例
*/
private static PluginManager instance;
private PluginManager(){
}
/**
* 获取单例类
* @return
*/
public static PluginManager getInstance(){
if (instance == null) {
instance = new PluginManager();
}
return instance;
}
/**
* 加载插件
* @param context 加载插件的应用的上下文
* @param loadPath 加载的插件包地址
*/
public void loadPlugin(Context context, String loadPath) {
this.mContext = context;
// DexClassLoader 的 optimizedDirectory 操作目录必须是私有的
// ( 模式必须是 Context.MODE_PRIVATE )
File optimizedDirectory = context.getDir("cache_plugin", Context.MODE_PRIVATE);
// 创建 DexClassLoader
mDexClassLoader = new DexClassLoader(
loadPath, // 加载路径
optimizedDirectory.getAbsolutePath(), // apk 解压缓存目录
null,
context.getClassLoader() // DexClassLoader 加载器的父类加载器
);
// 加载资源
try {
// 通过反射创建 AssetManager
AssetManager assetManager = AssetManager.class.newInstance();
// 通过反射获取 AssetManager 中的 addAssetPath 隐藏方法
Method addAssetPathMethod = assetManager.
getClass().
getDeclaredMethod("addAssetPath", String.class);
// 调用反射方法
addAssetPathMethod.invoke(assetManager, loadPath);
// 获取资源
mResources = new Resources(
assetManager,
context.getResources().getDisplayMetrics(),
context.getResources().getConfiguration()
);
// 获取插件包中的 Activity 类信息
mPackageInfo = context.getPackageManager().getPackageArchiveInfo(
loadPath, // 加载的插件包路径
PackageManager.GET_ACTIVITIES); // 获取的包信息类名
} catch (IllegalAccessException e) {
// 调用 AssetManager.class.newInstance() 反射构造方法异常
e.printStackTrace();
} catch (InstantiationException e) {
// 调用 AssetManager.class.newInstance() 反射构造方法异常
e.printStackTrace();
} catch (NoSuchMethodException e) {
// getDeclaredMethod 反射方法异常
e.printStackTrace();
} catch (InvocationTargetException e) {
// invoke 执行反射方法异常
e.printStackTrace();
}
}
/**
* 获取类加载器
* @return
*/
public DexClassLoader getmDexClassLoader() {
return mDexClassLoader;
}
/**
* 获取插件包中的 Package 信息
* @return
*/
public PackageInfo getmPackageInfo() {
return mPackageInfo;
}
/**
* 获取插件包中的资源
* @return
*/
public Resources getmResources() {
return mResources;
}
}
4、插件管理器
package com.example.plugin_core;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
/**
* 插件化框架核心类
*/
public class PluginManager {
/**
* 类加载器
* 用于加载插件包 apk 中的 classes.dex 文件中的字节码对象
*/
private DexClassLoader mDexClassLoader;
/**
* 从插件包 apk 中加载的资源
*/
private Resources mResources;
/**
* 插件包信息类
*/
private PackageInfo mPackageInfo;
/**
* 加载插件的上下文对象
*/
private Context mContext;
/**
* PluginManager 单例
*/
private static PluginManager instance;
private PluginManager(){
}
/**
* 获取单例类
* @return
*/
public static PluginManager getInstance(){
if (instance == null) {
instance = new PluginManager();
}
return instance;
}
/**
* 加载插件
* @param context 加载插件的应用的上下文
* @param loadPath 加载的插件包地址
*/
public void loadPlugin(Context context, String loadPath) {
this.mContext = context;
// DexClassLoader 的 optimizedDirectory 操作目录必须是私有的
// ( 模式必须是 Context.MODE_PRIVATE )
File optimizedDirectory = context.getDir("cache_plugin", Context.MODE_PRIVATE);
Android 插件化“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )
Android 插件化“ 插桩式 “ 插件化框架 ( 代理 Activity 组件开发 )
Android 插件化“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
Android 插件化“ 插桩式 “ 插件化框架 ( 原理与实现思路 )