Android黑科技动态加载(四)之动态启动插件Activity
16lz
2021-01-23
目录
Android黑科技动态加载(一)之Java中的ClassLoader
Android黑科技动态加载(二)之Android中的ClassLoader
Android黑科技动态加载(三)之动态加载资源
Android黑科技动态加载(四)之插件化开发
项目地址
android_plugin_activity.gif如果做插件化
Android动态加载技术三个关键问题详解一文中指出插件化需要解决的三个问题. 其中两个问题已经在前面博客中解决了. 现在剩下的问题就是如何去启动Activity.
启动已经安装的插件的Activity很简单, 只需要使用隐式启动就可以了
那对于未安装的插件的Activity, 我们使用一种思想叫做代理Activity
代理Activity
代理Activity的思想就是通过代理Activity占坑, 然后我们资源或者业务逻辑之类的都是加载插件Activity的. 这样我们就能把插件Activity加"启动起来".
编码
首先我们需要新建一个Bean来存放已经加载的插件APK信息
/** * 用来存放已经加载的插件APK信息 */public class PluginApk { public PackageInfo packageInfo; public Resources resources; public ClassLoader classLoader; public PluginApk(Resources resources) { this.resources = resources; }}
然后我们根据之前加载资源的方式去加载插件APK的信息
/** * 创建一个Entity保存APK的信息 * * @param apkPath * @return */private PluginApk createApk(String apkPath) { PluginApk pluginApk = null; try { // 事实就是跟前面那样动态加载资源的原理是一样的 AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPathMethod = assetManager.getClass().getMethod("addAssetPath", String.class); addAssetPathMethod.invoke(assetManager, apkPath); Resources resources = new Resources(assetManager, mContext.getResources().getDisplayMetrics(), mContext.getResources().getConfiguration()); pluginApk = new PluginApk(resources); pluginApk.classLoader = createDexClassLoader(apkPath); } catch (Exception e) { e.printStackTrace(); } return pluginApk;}/** * 查询APK的包信息 * * @param apkPath * @return */private PackageInfo queryPackageInfo(String apkPath) { PackageManager packageManager = mContext.getPackageManager(); PackageInfo packageInfo = packageManager.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES); return packageInfo;}/** * 加载并创建APK的信息 * * @param apkPath * @return */public PluginApk loadApk(String apkPath) { PackageInfo packageInfo = queryPackageInfo(apkPath); // 获取未安装的插件APK包信息 if (packageInfo == null || TextUtils.isEmpty(packageInfo.packageName)) { return null; } PluginApk pluginApk = sMap.get(packageInfo.packageName); // 从缓存中获取 if (pluginApk == null) { pluginApk = createApk(apkPath); // 缓存中不存在, 则开始创建APK信息 if (pluginApk != null) { // 缓存 pluginApk.packageInfo = packageInfo; sMap.put(packageInfo.packageName, pluginApk); } else { throw new NullPointerException("plugin apk is null"); } } return pluginApk;}
至此, 我们的插件APK资源就已经获取完毕了. 下面我们就开始编写代理Activity的逻辑吧.
/** * 代理Activity, 真正启动的Activity是这个, 但是加载的资源是插件Activity的 */public class ProxyActivity extends Activity { LifeCircleController mPluginController = new LifeCircleController(this); // 用于管理代理Activity生命周倩和资源的类 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPluginController.onCreate(getIntent().getExtras()); } @Override public Resources getResources() { Resources resources = mPluginController.getResources(); return null != resources ? resources : super.getResources(); } @Override public AssetManager getAssets() { AssetManager assets = mPluginController.getAssets(); return null != assets ? mPluginController.getAssets() : super.getAssets(); } @Override public ClassLoader getClassLoader() { ClassLoader loader = mPluginController.getClassLoader(); return null != loader ? loader : super.getClassLoader(); }}
说白, 代理Activity的逻辑不多, 就是一个空壳. 重要的逻辑都写在
LifeCircleController
中.
/** * 代理Activity生命周期以及资源管理类 */public class LifeCircleController implements Pluginable { public static final String KEY_PLUGIN_CLASS_NAME = "plugin_class_name"; // 用来传递需要需要启动的Activity的类名 public static final String KEY_PACKAGE_NAME = "package_name"; // Activity所在插件APK的包名 Activity mActivityProxy; PluginActivity mPluginActivity; PluginApk mPluginApk; public LifeCircleController(ProxyActivity activityProxy) { this.mActivityProxy = activityProxy; } /** * 加载插件Activity * * @param classLoader * @param pluginName * @return */ private PluginActivity loadPluginActivity(ClassLoader classLoader, String pluginName) { PluginActivity activity = null; try { Class cls = classLoader.loadClass(pluginName); activity = (PluginActivity) cls.newInstance(); } catch (Exception e) { e.printStackTrace(); } return activity; } /** * 代理获取资源 * * @return */ public Resources getResources() { return null != mPluginApk ? mPluginApk.resources : null; } /** * 代理获取资源 * * @return */ public AssetManager getAssets() { return mPluginApk.resources.getAssets(); } /** * 解析需要启动的Activity * * @param extras */ @Override public void onCreate(Bundle extras) { String pluginName = extras.getString(KEY_PLUGIN_CLASS_NAME); String packageName = extras.getString(LifeCircleController.KEY_PACKAGE_NAME); mPluginApk = ActivityManager.getInstance().getPluginApk(packageName); // 获取加载的插件APK信息 mPluginActivity = loadPluginActivity(mPluginApk.classLoader, pluginName); // 加载插件Activity mPluginActivity.attach(mActivityProxy); // 绑定代理Activity到插件Activity中, 使其能够调用代理Activity对应的应用资源等方法 mPluginActivity.onCreate(null); // 代理生命周期 } // 代理生命周期 @Override public void onRestart() { mPluginActivity.onRestart(); } @Override public void onStart() { mPluginActivity.onStart(); } @Override public void onResume() { mPluginActivity.onResume(); } @Override public void onPause() { mPluginActivity.onPause(); } @Override public void onStop() { mPluginActivity.onStop(); } @Override public void onDestroy() { mPluginActivity.onDestroy(); } public ClassLoader getClassLoader() { return mPluginApk.classLoader; }}
其中我们提供一个专门同步生命周期的接口
public interface Pluginable { void onCreate(Bundle var1); void onRestart(); void onStart(); void onResume(); void onPause(); void onStop(); void onDestroy();}
还有一个Attachable接口
public interface Attachable { void attach(T data);}
下面的就是所有插件Activity都必须继承的父类
/** * 所有插件Activity都必须继承的基类 */public class PluginActivity extends Activity implements Pluginable, Attachable { private Activity mProxyActivity; @Override public Window getWindow() { return mProxyActivity.getWindow(); } @Override public View findViewById(int id) { return mProxyActivity.findViewById(id); } /** * 使用代理Activity去设置加载到的资源, 因为代理Activity本身就是优先使用插件APK的ClassLoader和Resource, 所以该方法会加载到插件APK的布局 * * @param layoutResID */ @Override public void setContentView(int layoutResID) { mProxyActivity.setContentView(layoutResID); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { } @Override public void onRestart() { } @Override public void onStart() { } @Override public void onResume() { } @Override public void onPause() { } @Override public void onStop() { } @Override public void onDestroy() { } @Override public void attach(Activity data) { this.mProxyActivity = data; }}
然后Plugin相关类需要导出Jar包, 这样主包跟插件包才能使用同样的类
更多相关文章
- 巧解Android时区加载过慢的问题
- android的异步加载与UI
- 【Gradle】Android Gradle 插件
- Android工程手动增加插件包方法
- Android 加载模型
- Android加载Gif和ImageView的通用解决方案:android-gif-drawable(1
- Android 资源(resource)学习小结