没有安装的apk插件,是没有运行环境的,宿主app需要通过 代理类 去加载没有安装的apk插件(主要加载class和资源文件),将宿主的环境传递给插件,这种方式就叫做占位/插桩,所以叫
占位式(插桩式)插件化
插件中启动Activity
流程
第一步,加载插件
关键点
- DexClassloader (
插件的
DexClassloader,new 创建); - AssetManager(
反射
创建,AssetManager 被 final 修饰); - Resources (
插件的
Resources,new 创建); - PackageManager、PackageInfo 和 ActivityInfo;
- 了解系统源码,反射的使用等等;
具体步骤
MainActivity
/**
* 1. 加载插件
*/
public void loadPlugin(View view) {
PluginManager.getInstance(this).loadPlugin();
}
PluginManager
/**
* 加载插件 class and resources
*/
public void loadPlugin() {
try {
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");
if (!file.exists()) {
Log.d(TAG, "插件包 不存在...");
return;
}
String pluginPaht = file.getAbsolutePath();
// >>>> 下面是加载插件里面的 class
// dexClassLoader需要一个缓存目录 /data/data/当前应用的包名/pDir
File fileDir = context.getDir("pDir", Context.MODE_PRIVATE);
// Activity class
dexClassLoader = new DexClassLoader(pluginPaht, fileDir.getAbsolutePath(), null, context.getClassLoader());
// >>>> 下面是加载插件里面的layout
// 加载资源
AssetManager assetManager = AssetManager.class.newInstance();
// 我们要执行此方法,为了把插件包的路径 添加进去
// public final int addAssetPath(String path)
Method addAssetPathMethod = assetManager.getClass().getMethod("addAssetPath", String.class); // 他是类类型 Class
addAssetPathMethod.invoke(assetManager, pluginPaht); // 插件包的路径 pluginPaht
Resources r = context.getResources(); // 宿主的资源配置信息
// 特殊的 Resources,加载插件里面的资源的 Resources
resources = new Resources(assetManager, r.getDisplayMetrics(), r.getConfiguration()); // 参数2 3 资源配置信息
} catch (Exception e) {
e.printStackTrace();
}
}
第二步,启动插件里面的Activity
MainActivity
/**
* 2. 启动插件里面的Activity
*/
public void startPluginActivity(View view) {
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "p.apk");
String path = file.getAbsolutePath();
// 获取插件包 里面的 Activity
PackageManager packageManager = getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
ActivityInfo activityInfo = packageInfo.activities[0];
// 占位 代理Activity
Intent intent = new Intent(this, ProxyActivity.class);
intent.putExtra("className", activityInfo.name);
startActivity(intent);
}
ProxyActivity
/**
* 代理的Activity 代理/占位 插件里面的 Activity
*/
public class ProxyActivity extends Activity {
@Override
public Resources getResources() {
return PluginManager.getInstance(this).getResources();
}
@Override
public ClassLoader getClassLoader() {
return PluginManager.getInstance(this).getClassLoader();
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 真正的加载 插件里面的 Activity
String className = getIntent().getStringExtra("className");
try {
Class mPluginActivityClass = getClassLoader().loadClass(className);
// 实例化 插件包里面的 Activity
Constructor constructor = mPluginActivityClass.getConstructor(new Class[]{});
Object mPluginActivity = constructor.newInstance(new Object[]{});
ActivityInterface activityInterface = (ActivityInterface) mPluginActivity;
// 注入
activityInterface.insertAppContext(this);
Bundle bundle = new Bundle();
bundle.putString("appName", "我是宿主传递过来的信息");
// 执行插件里面的onCreate方法
activityInterface.onCreate(bundle);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void startActivity(Intent intent) {
String className = intent.getStringExtra("className");
Intent proxyIntent = new Intent(this, ProxyActivity.class);
proxyIntent.putExtra("className", className); // 包名+TestActivity
//让 Activity 进栈(本例子中的 TestActivity )
super.startActivity(proxyIntent);
}
}
PluginActivity
public class PluginActivity extends BaseActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.plugin_main);
// this 会报错,因为插件没有安装,也没有组件的环境,所以必须使用宿主环境
Toast.makeText(appActivity, "我是插件", Toast.LENGTH_SHORT).show();
findViewById(R.id.bt_start_activity).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(appActivity, TestActivity.class));
}
});
}
}
BaseActivity
public class BaseActivity extends Activity implements ActivityInterface {
public Activity appActivity; // 宿主的环境
@Override
public void insertAppContext(Activity appActivity) {
this.appActivity = appActivity;
}
public void setContentView(int resId) {
appActivity.setContentView(resId);
}
public View findViewById(int layoutId) {
return appActivity.findViewById(layoutId);
}
@Override
public void startActivity(Intent intent) {
Intent intentNew = new Intent();
intentNew.putExtra("className", intent.getComponent().getClassName());
appActivity.startActivity(intentNew);
}
@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 onDestroy() {
}
}
ActivityInterface
/**
* 标准
*/
public interface ActivityInterface {
/**
* 把宿主(app)的环境 给 插件
* @param appActivity
*/
void insertAppContext(Activity appActivity);
// 生命周期方法
void onCreate(Bundle savedInstanceState);
void onStart();
void onResume();
void onDestroy();
}
TestActivity
public class TestActivity extends BaseActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
}
}
包结构