目錄
Android黑科技動(dòng)態(tài)加載(一)之Java中的ClassLoader
Android黑科技動(dòng)態(tài)加載(二)之Android中的ClassLoader
Android黑科技動(dòng)態(tài)加載(三)之動(dòng)態(tài)加載資源
Android黑科技動(dòng)態(tài)加載(四)之插件化開(kāi)發(fā)
如果做插件化
Android動(dòng)態(tài)加載技術(shù)三個(gè)關(guān)鍵問(wèn)題詳解一文中指出插件化需要解決的三個(gè)問(wèn)題. 其中兩個(gè)問(wèn)題已經(jīng)在前面博客中解決了. 現(xiàn)在剩下的問(wèn)題就是如何去啟動(dòng)Activity.
啟動(dòng)已經(jīng)安裝的插件的Activity很簡(jiǎn)單, 只需要使用隱式啟動(dòng)就可以了
那對(duì)于未安裝的插件的Activity, 我們使用一種思想叫做代理Activity
代理Activity
代理Activity的思想就是通過(guò)代理Activity占坑, 然后我們資源或者業(yè)務(wù)邏輯之類(lèi)的都是加載插件Activity的. 這樣我們就能把插件Activity加"啟動(dòng)起來(lái)".
編碼
首先我們需要新建一個(gè)Bean來(lái)存放已經(jīng)加載的插件APK信息
/**
* 用來(lái)存放已經(jīng)加載的插件APK信息
*/
public class PluginApk {
public PackageInfo packageInfo;
public Resources resources;
public ClassLoader classLoader;
public PluginApk(Resources resources) {
this.resources = resources;
}
}
然后我們根據(jù)之前加載資源的方式去加載插件APK的信息
/**
* 創(chuàng)建一個(gè)Entity保存APK的信息
*
* @param apkPath
* @return
*/
private PluginApk createApk(String apkPath) {
PluginApk pluginApk = null;
try {
// 事實(shí)就是跟前面那樣動(dòng)態(tài)加載資源的原理是一樣的
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;
}
/**
* 查詢(xún)APK的包信息
*
* @param apkPath
* @return
*/
private PackageInfo queryPackageInfo(String apkPath) {
PackageManager packageManager = mContext.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES);
return packageInfo;
}
/**
* 加載并創(chuàng)建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); // 緩存中不存在, 則開(kāi)始創(chuàng)建APK信息
if (pluginApk != null) {
// 緩存
pluginApk.packageInfo = packageInfo;
sMap.put(packageInfo.packageName, pluginApk);
} else {
throw new NullPointerException("plugin apk is null");
}
}
return pluginApk;
}
至此, 我們的插件APK資源就已經(jīng)獲取完畢了. 下面我們就開(kāi)始編寫(xiě)代理Activity的邏輯吧.
/**
* 代理Activity, 真正啟動(dòng)的Activity是這個(gè), 但是加載的資源是插件Activity的
*/
public class ProxyActivity extends Activity {
LifeCircleController mPluginController = new LifeCircleController(this); // 用于管理代理Activity生命周倩和資源的類(lèi)
@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();
}
}
說(shuō)白, 代理Activity的邏輯不多, 就是一個(gè)空殼. 重要的邏輯都寫(xiě)在
LifeCircleController
中.
/**
* 代理Activity生命周期以及資源管理類(lèi)
*/
public class LifeCircleController implements Pluginable {
public static final String KEY_PLUGIN_CLASS_NAME = "plugin_class_name"; // 用來(lái)傳遞需要需要啟動(dòng)的Activity的類(lèi)名
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();
}
/**
* 解析需要啟動(dòng)的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中, 使其能夠調(diào)用代理Activity對(duì)應(yīng)的應(yīng)用資源等方法
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;
}
}
其中我們提供一個(gè)專(zhuān)門(mén)同步生命周期的接口
public interface Pluginable {
void onCreate(Bundle var1);
void onRestart();
void onStart();
void onResume();
void onPause();
void onStop();
void onDestroy();
}
還有一個(gè)Attachable接口
public interface Attachable<T> {
void attach(T data);
}
下面的就是所有插件Activity都必須繼承的父類(lèi)
/**
* 所有插件Activity都必須繼承的基類(lèi)
*/
public class PluginActivity extends Activity implements Pluginable, Attachable<Activity> {
private Activity mProxyActivity;
@Override
public Window getWindow() {
return mProxyActivity.getWindow();
}
@Override
public View findViewById(int id) {
return mProxyActivity.findViewById(id);
}
/**
* 使用代理Activity去設(shè)置加載到的資源, 因?yàn)榇鞟ctivity本身就是優(yōu)先使用插件APK的ClassLoader和Resource, 所以該方法會(huì)加載到插件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相關(guān)類(lèi)需要導(dǎo)出Jar包, 這樣主包跟插件包才能使用同樣的類(lèi)