转载

dynamic-load-apk插件原理整理

因为当前项目功能越来越多,编译速度越来越慢(公司电脑配置也挺差的...),并且方法数已超出65535的限制了,虽然通过multidex暂时解决了,但是这并不是一个好的解决方式。所以通过插件来加快编译速度以及解决方法数的限制,算是一个越来越重要的任务了,工作中还有很多新需求,所以趁放假的2天研究了下现在比较流行的插件框架dynamic-load-apk,并整理了下。

框架github地址: https://github.com/singwhatiwanna/dynamic-load-apk

lib module的svn地址: https://github.com/singwhatiwanna/dynamic-load-apk/trunk/DynamicLoadApk/lib

一、加载apk总流程 :

//插件文件 File plugin = new File(apkPath); PluginItem item = new PluginItem(); //插件文件路径 item.pluginPath = plugin.getAbsolutePath(); //PackageInfo = PackageManager.getPackageArchiveInfo item.packageInfo = DLUtils.getPackageInfo(this, item.pluginPath); //launcherActivity if (item.packageInfo.activities != null && item.packageInfo.activities.length > 0) {    item.launcherActivityName = item.packageInfo.activities[0].name; } //launcherService if (item.packageInfo.services != null && item.packageInfo.services.length > 0) {  item.launcherServiceName = item.packageInfo.services[0].name; } //加载apk信息 DLPluginManager.getInstance(this).loadApk(item.pluginPath); 

二、loadApk信息过程 :

1、createDexClassLoader:

private DexClassLoader createDexClassLoader(String dexPath) {     dexOutputPath = mContext.getDir("dex", Context.MODE_PRIVATE).getAbsolutePath();     DexClassLoader loader = new DexClassLoader(dexPath,      dexOutputPath,  //getDir("dex", Context.MODE_PRIVATE)      mNativeLibDir,  //optimizedDirectory=getDir("pluginlib", Context.MODE_PRIVATE)      mContext.getClassLoader());  //host.Appliceation.getClassLoader()     return loader; } 

2、createAssetManager:

private AssetManager createAssetManager(String dexPath) {  try {   AssetManager assetManager = AssetManager.class.newInstance();   //通过反射调用addAssetPath方法,将apk资源加载到AssetManager   Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);   addAssetPath.invoke(assetManager, dexPath);   return assetManager;  } catch (Exception e) {   e.printStackTrace();   return null;  } } 

后面会重写DLProxyActivity的getAssets()方法,返回此处生成的AssetManager,从而实现从插件apk加载资源:

@Override public AssetManager getAssets() {     return impl.getAssets() == null ? super.getAssets() : impl.getAssets(); }

3、createResources:

private Resources createResources(AssetManager assetManager) {  //通过刚创建的assetManager以及宿主程序的Resources创建Plugin的Resources  Resources superRes = mContext.getResources();  Resources resources = new Resources(assetManager,    superRes.getDisplayMetrics(),    superRes.getConfiguration());  return resources; } 

后面会重写DLProxyActivity的getResources()方法,返回此处生成的Resources,从而实现从插件apk加载资源:

@Override public Resources getResources() {     return impl.getResources() == null ? super.getResources() : impl.getResources(); }

4、创建pluginPackage并通过插件的packageName保存插件信息:

pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo);

mPackagesHolder.put(packageInfo.packageName, pluginPackage);

5、copySoLib(拷贝so文件到应用的pluginlib目录下):SoLibManager.getSoLoader().copyPluginSoLib(mContext, dexPath, mNativeLibDir);

三、调用插件

1、要向插件Intent传递可序列化对象,必须通过DLIntent,设置Bundle的ClassLoader:

@Override public Intent putExtra(String name, Parcelable value) {  setupExtraClassLoader(value);  return super.putExtra(name, value); } @Override public Intent putExtra(String name, Serializable value) {  setupExtraClassLoader(value);  return super.putExtra(name, value); } private void setupExtraClassLoader(Object value) {  ClassLoader pluginLoader = value.getClass().getClassLoader();  DLConfigs.sPluginClassloader = pluginLoader;  setExtrasClassLoader(pluginLoader); //设置Bundle的ClassLoader } 

2、startPluginActivity:插件內部的activity之间相互调用,需要使用此方法。

public int startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode) {  if (mFrom == DLConstants.FROM_INTERNAL) {   dlIntent.setClassName(context, dlIntent.getPluginClass());   performStartActivityForResult(context, dlIntent, requestCode);   return DLPluginManager.START_RESULT_SUCCESS;  }  String packageName = dlIntent.getPluginPackage();  //验证intent的包名  if (TextUtils.isEmpty(packageName)) {   throw new NullPointerException("disallow null packageName.");  }  //检测插件是否加载  DLPluginPackage pluginPackage = mPackagesHolder.get(packageName);  if (pluginPackage == null) {   return START_RESULT_NO_PKG;  }  //要调用的插件Activity的class完整路径  final String className = getPluginActivityFullPath(dlIntent, pluginPackage);  //Class.forName  Class<?> clazz = loadPluginClass(pluginPackage.classLoader, className);  if (clazz == null) {   return START_RESULT_NO_CLASS;  }  //获取代理Activity的class,DLProxyActivity/DLProxyFragmentActivity  Class<? extends Activity> proxyActivityClass = getProxyActivityClass(clazz);  if (proxyActivityClass == null) {   return START_RESULT_TYPE_ERROR;  }  //put extra data  dlIntent.putExtra(DLConstants.EXTRA_CLASS, className);  dlIntent.putExtra(DLConstants.EXTRA_PACKAGE, packageName);  dlIntent.setClass(mContext, proxyActivityClass);  //通过context启动宿主Activity  performStartActivityForResult(context, dlIntent, requestCode);  return START_RESULT_SUCCESS; } 

四、Activity生命周期的管理

插件apk中的activity其实就是一个普通的对象,不是真正意义上的activity(没有在宿主程序中注册且没有完全初始化),不具有activity的性质,因为系统启动activity是要做很多初始化工作的,而我们在应用层通过反射去启动activity是很难完成系统所做的初始化工作的,所以activity的大部分特性都无法使用包括activity的生命周期管理,这就需要我们自己去管理。

DL采用了接口机制,将activity的大部分生命周期方法提取出来作为一个接口(DLPlugin),然后通过代理activity(DLProxyActivity)去调用插件activity实现的生命周期方法,这样就完成了插件activity的生命周期管理,并且没有采用反射,当我们想增加一个新的生命周期方法的时候,只需要在接口中声明一下同时在代理activity中实现一下即可。

public interface DLPlugin {  public void onCreate(Bundle savedInstanceState);  public void onStart();  public void onRestart();  public void onActivityResult(int requestCode, int resultCode, Intent data);  public void onResume();  public void onPause();  public void onStop();  public void onDestroy();  public void attach(Activity proxyActivity, DLPluginPackage pluginPackage);  public void onSaveInstanceState(Bundle outState);  public void onNewIntent(Intent intent);  public void onRestoreInstanceState(Bundle savedInstanceState);  public boolean onTouchEvent(MotionEvent event);  public boolean onKeyUp(int keyCode, KeyEvent event);  public void onWindowAttributesChanged(LayoutParams params);  public void onWindowFocusChanged(boolean hasFocus);  public void onBackPressed();  public boolean onCreateOptionsMenu(Menu menu);  public boolean onOptionsItemSelected(MenuItem item); } 

DLBasePluginActivity的部分实现:

public class DLBasePluginActivity extends Activity implements DLPlugin {  /**   * 代理activity,可以当作Context来使用,会根据需要来决定是否指向this   */  protected Activity mProxyActivity;  /**   * 等同于mProxyActivity,可以当作Context来使用,会根据需要来决定是否指向this<br/>   * 替代this来使用(应为this指向的是插件中的Activity,已经不是常规意义上的activity,所以this是没有意义的)   * 如果是DLPlugin中已经覆盖的Activity的方法,就不需使用that了,直接调用this即可   */  protected Activity that;  protected DLPluginManager mPluginManager;  protected DLPluginPackage mPluginPackage;  protected int mFrom = DLConstants.FROM_INTERNAL;  @Override  public void attach(Activity proxyActivity, DLPluginPackage pluginPackage) {   mProxyActivity = (Activity) proxyActivity;   that = mProxyActivity;   mPluginPackage = pluginPackage;  }  @Override  public void onCreate(Bundle savedInstanceState) {   if (savedInstanceState != null) {    mFrom = savedInstanceState.getInt(DLConstants.FROM, DLConstants.FROM_INTERNAL);   }   if (mFrom == DLConstants.FROM_INTERNAL) {    super.onCreate(savedInstanceState);    mProxyActivity = this;    that = mProxyActivity;   }   mPluginManager = DLPluginManager.getInstance(that);  }  @Override  public void setContentView(View view) {   if (mFrom == DLConstants.FROM_INTERNAL) {    super.setContentView(view);   } else {    mProxyActivity.setContentView(view);   }  }  ...... } 

在代理类DLProxyActivity中的实现:

public class DLProxyActivity extends Activity implements DLAttachable {  protected DLPlugin mRemoteActivity;  private DLProxyImpl impl = new DLProxyImpl(this);  @Override  protected void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   impl.onCreate(getIntent());  }  @Override  public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager) {   mRemoteActivity = remoteActivity;  }  @Override  public AssetManager getAssets() {   return impl.getAssets() == null ? super.getAssets() : impl.getAssets();  }  @Override  public Resources getResources() {   return impl.getResources() == null ? super.getResources() : impl.getResources();  }  @Override  public Theme getTheme() {   return impl.getTheme() == null ? super.getTheme() : impl.getTheme();  }  @Override  public ClassLoader getClassLoader() {   return impl.getClassLoader();  }  @Override  protected void onActivityResult(int requestCode, int resultCode, Intent data) {   mRemoteActivity.onActivityResult(requestCode, resultCode, data);   super.onActivityResult(requestCode, resultCode, data);  }  @Override  protected void onStart() {   mRemoteActivity.onStart();   super.onStart();  }  ...... } 

总结:

插件主要的2个问题就是资源加载以及Activity生命周期的管理。

资源加载

通过反射调用AssetManager的addAssetPath方法,我们可以将一个插件apk中的资源加载到AssetManager中,然后再通过AssetManager来创建一个新的Resources对象,然后就可以通过这个Resources对象来访问插件apk中的资源了。

Activity生命周期管理

采用接口机制,将activity的大部分生命周期方法提取出来作为一个接口(DLPlugin),然后通过代理activity(DLProxyActivity)去调用插件activity实现的生命周期方法,这样就完成了插件activity的生命周期管理。

正文到此结束
Loading...