承接上一篇,继续分析DynamicLoadApk 源码。 4.2 DLPluginPackage 插件信息对应的实体类,主要属性如下:
public String packageName;
public String defaultActivity;
public DexClassLoader classLoader;
public AssetManager assetManager;
public Resources resources;
public PackageInfo packageInfo;
packageName为插件的包名; defaultActivity为插件的 Launcher Main Activity; classLoader为加载插件的 ClassLoader; assetManager为加载插件资源的 AssetManager; resources利用assetManager中已经加载的资源创建的Resources,代理组件中会从这个Resources中读取资源。 packageInfo被PackageManager解析后的插件信息。 这些信息都会在DLPluginManager#loadApk(…)时初始化。
4.3 DLAttachable.java/DLServiceAttachable.java DLServiceAttachable 与 DLAttachable 类似,下面先分析 DLAttachable.java。 DLAttachable 是一个接口,主要作用是以统一所有不同类型的代理 Activity,如DLProxyActivity、DLProxyFragmentActivity,方便作为同一接口统一处理。 DLProxyActivity和DLProxyFragmentActivity都实现了这个类。
DLAttachable 目前只有一个接口
public interface DLAttachable {
/**
* when the proxy impl ( {@see DLProxyImpl#launchTargetActivity()} ) launch
* the plugin activity , dl will call this method to attach the proxy activity
* and pluginManager to the plugin activity. the proxy activity will load
* the plugin's resource, so the proxy activity is a resource delegate for
* plugin activity.
*
* @param proxyActivity a instance of DLPlugin, {@see DLBasePluginActivity}
* and {@see DLBasePluginFragmentActivity}
* @param pluginManager DLPluginManager instance, manager the plugins
*/
public void attach(DLPlugin proxyActivity, DLPluginManager pluginManager);
}
抽象函数,表示将插件Activity和代理Activity绑定在一起,其中的proxyActivity参数就是指插件Activity。 同样 DLServiceAttachable 类似,作用是统一所有不同类型的代理 Service,实现插件Service和代理Service的绑定。虽然目前只有DLProxyService。
public interface DLServiceAttachable {
public void attach(DLServicePlugin remoteService, DLPluginManager pluginManager);
}
4.4 DLPlugin.java/DLServicePlugin.java
DLPlugin 与 DLServicePlugin 类似,下面先分析 DLPlugin.java。 DLPlugin 是一个接口,包含Activity生命周期、触摸、菜单等抽象函数 DLBase*Activity 都实现了这个类,这样插件的 Activity 间接实现了此类。 主要作用是统一所有不同类型的插件 Activity,如Activity、FragmentActivity,方便作为同一接口统一处理,所以这个类叫DLPluginActivity更合适。
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);
}
同样 DLServicePlugin 主要作用是统一所有不同类型的插件 Service,方便作为统一接口统一处理,目前包含Service生命周期等抽象函数。
public interface DLServicePlugin {
public void onCreate();
public void onStart(Intent intent, int startId);
public int onStartCommand(Intent intent, int flags, int startId);
public void onDestroy();
public void onConfigurationChanged(Configuration newConfig);
public void onLowMemory();
public void onTrimMemory(int level);
public IBinder onBind(Intent intent);
public boolean onUnbind(Intent intent);
public void onRebind(Intent intent);
public void onTaskRemoved(Intent rootIntent);
public void attach(Service proxyService, DLPluginPackage pluginPackage);
}
4.5 DLProxyActivity.java/DLProxyFragmentActivity.java 代理 Activity,他们是在宿主 Manifest 中注册的组件,也是启动插件 Activity 时,真正被启动的 Activity,他们的内部会完成插件 Activity 的初始化和启动。 这两个类大同小异,所以这里只分析DLProxyActivity。 首先来看下它的成员变量。 (1). DLPlugin mRemoteActivity 表示真正需要启动的插件Activity。这个属性名应该叫做pluginActivity更合适。 上面我们已经介绍了,DLPlugin是所有插件Activity都间接实现了的接口。 接下来在代理Activity的生命周期、触摸、菜单等函数中我们都会同时调用 mRemoteActivity 的相关函数,模拟插件Activity的相关功能。
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();
}
@Override
protected void onRestart() {
mRemoteActivity.onRestart();
super.onRestart();
}
@Override
protected void onResume() {
mRemoteActivity.onResume();
super.onResume();
}
@Override
protected void onPause() {
mRemoteActivity.onPause();
super.onPause();
}
@Override
protected void onStop() {
mRemoteActivity.onStop();
super.onStop();
}
@Override
protected void onDestroy() {
mRemoteActivity.onDestroy();
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
mRemoteActivity.onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
mRemoteActivity.onRestoreInstanceState(savedInstanceState);
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected void onNewIntent(Intent intent) {
mRemoteActivity.onNewIntent(intent);
super.onNewIntent(intent);
}
@Override
public void onBackPressed() {
mRemoteActivity.onBackPressed();
super.onBackPressed();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
return mRemoteActivity.onTouchEvent(event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
super.onKeyUp(keyCode, event);
return mRemoteActivity.onKeyUp(keyCode, event);
}
@Override
public void onWindowAttributesChanged(LayoutParams params) {
mRemoteActivity.onWindowAttributesChanged(params);
super.onWindowAttributesChanged(params);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
mRemoteActivity.onWindowFocusChanged(hasFocus);
super.onWindowFocusChanged(hasFocus);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
mRemoteActivity.onCreateOptionsMenu(menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
mRemoteActivity.onOptionsItemSelected(item);
return super.onOptionsItemSelected(item);
}
@Override
public ComponentName startService(Intent service) {
return super.startService(service);
}
}
(2). DLProxyImpl impl 主要封装了插件Activity的公用逻辑,如初始化插件 Activity 并和代理 Activity 绑定、获取资源等。
DLProxyImpl 主要封装了插件Activity的公用逻辑,如初始化插件 Activity 并和代理 Activity 绑定、获取资源等,相当于把DLProxyActivity和DLProxyFragmentActivity的公共实现部分提出出来,核心逻辑位于下面介绍的 onCreate() 函数。 主要函数:
1) DLProxyImpl(Activity activity) 构造函数,参数为代理 Activity。
public DLProxyImpl(Activity activity) {
mProxyActivity = activity;
}
2) public void onCreate(Intent intent) onCreate 函数,会在代理 Activity onCreate 函数中被调用,流程图如下:
public void onCreate(Intent intent) {
// set the extra's class loader
//设置intent的ClassLoader
intent.setExtrasClassLoader(DLConfigs.sPluginClassloader);
//获取PackageName
mPackageName = intent.getStringExtra(DLConstants.EXTRA_PACKAGE);
//获得待启动Activity的ClassName
mClass = intent.getStringExtra(DLConstants.EXTRA_CLASS);
Log.d(TAG, "mClass=" + mClass + " mPackageName=" + mPackageName);
mPluginManager = DLPluginManager.getInstance(mProxyActivity);
//获得插件信息PluginPackage
mPluginPackage = mPluginManager.getPackage(mPackageName);
//从PluginPackage中获取AssetManager
mAssetManager = mPluginPackage.assetManager;
//从PluginPackage中获取Resources
mResources = mPluginPackage.resources;
initializeActivityInfo();
handleActivityInfo();
launchTargetActivity();
}
3) protected void launchTargetActivity() 加载待启动插件 Activity 完成初始化流程,并通过DLPlugin和DLAttachable接口的 attach 函数实现和代理 Activity 的双向绑定。流程图见上图虚线框部分。
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
protected void launchTargetActivity() {
try {
Class<?> localClass = getClassLoader().loadClass(mClass);
Constructor<?> localConstructor = localClass.getConstructor(new Class[] {});
Object instance = localConstructor.newInstance(new Object[] {});
mPluginActivity = (DLPlugin) instance;
((DLAttachable) mProxyActivity).attach(mPluginActivity, mPluginManager);
Log.d(TAG, "instance = " + instance);
// attach the proxy activity and plugin package to the mPluginActivity
mPluginActivity.attach(mProxyActivity, mPluginPackage);
Bundle bundle = new Bundle();
bundle.putInt(DLConstants.FROM, DLConstants.FROM_EXTERNAL);
mPluginActivity.onCreate(bundle);
} catch (Exception e) {
e.printStackTrace();
}
}
4) private void initializeActivityInfo() 获得待启动插件的 ActivityInfo,其中对插件Activity的主题做了一些处理
private void initializeActivityInfo() {
PackageInfo packageInfo = mPluginPackage.packageInfo;
if ((packageInfo.activities != null) && (packageInfo.activities.length > 0)) {
if (mClass == null) {
mClass = packageInfo.activities[0].name;
}
//Finals 修复主题BUG
int defaultTheme = packageInfo.applicationInfo.theme;
for (ActivityInfo a : packageInfo.activities) {
if (a.name.equals(mClass)) {
mActivityInfo = a;
// Finals ADD 修复主题没有配置的时候插件异常
if (mActivityInfo.theme == 0) {
if (defaultTheme != 0) {
mActivityInfo.theme = defaultTheme;
} else {
if (Build.VERSION.SDK_INT >= 14) {
mActivityInfo.theme = android.R.style.Theme_DeviceDefault;
} else {
mActivityInfo.theme = android.R.style.Theme;
}
}
}
}
}
}
}
5) private void handleActivityInfo() 设置代理 Activity 的主题等信息。 其他的 get* 函数都是获取一些插件相关信息,会被代理 Activity 调用。 同样 DLServiceProxyImpl 主要封装了插件Service的公用逻辑,如初始化插件 Service 并和代理 Activity 绑定。
private void handleActivityInfo() {
Log.d(TAG, "handleActivityInfo, theme=" + mActivityInfo.theme);
if (mActivityInfo.theme > 0) {
mProxyActivity.setTheme(mActivityInfo.theme);
}
Theme superTheme = mProxyActivity.getTheme();
mTheme = mResources.newTheme();
mTheme.setTo(superTheme);
// Finals适配三星以及部分加载XML出现异常BUG
try {
mTheme.applyStyle(mActivityInfo.theme, true);
} catch (Exception e) {
e.printStackTrace();
}
// TODO: handle mActivityInfo.launchMode here in the future.
}
4.7 DLBasePluginActivity.java/DLBasePluginFragmentActivity.java 插件 Activity 基类,插件中的Activity都要继承 DLBasePluginActivity/DLBasePluginFragmentActivity 之一(目前尚不支持 ActionBarActivity)。
主要作用是根据是否被代理,确定一些函数直接走父类逻辑还是代理 Activity 或是空逻辑。
DLBasePluginActivity继承自Activity,同时实现了DLPlugin接口。这两个类大同小异,所以这里只分析DLBasePluginActivity。 主要变量:
/**
* 代理activity,可以当作Context来使用,会根据需要来决定是否指向this
*/
protected Activity mProxyActivity;
/**
* 等同于mProxyActivity,可以当作Context来使用,会根据需要来决定是否指向this<br/>
* 可以当作this来使用
*/
protected Activity that;
protected DLPluginManager mPluginManager;
protected DLPluginPackage mPluginPackage;
mProxyActivity为代理 Activity,通过attach(…)函数绑定。 that与mProxyActivity等同,只是为了和this指针区分,表示真实的Context,这里真实指的是被代理情况下为代理 Activity,未被代理情况下等同于 this。
Attach方法,设置代理的Activity
@Override
public void attach(Activity proxyActivity, DLPluginPackage pluginPackage) {
Log.d(TAG, "attach: proxyActivity= " + proxyActivity);
mProxyActivity = (Activity) proxyActivity;
that = mProxyActivity;
mPluginPackage = pluginPackage;
}
根据是否被代理,确定一些函数直接走父类逻辑还是代理 Activity 或是空逻辑。(此处贴部分代码)
/**
* @param dlIntent
* @return may be {@link #START_RESULT_SUCCESS},
* {@link #START_RESULT_NO_PKG}, {@link #START_RESULT_NO_CLASS},
* {@link #START_RESULT_TYPE_ERROR}
*/
public int startPluginActivityForResult(DLIntent dlIntent, int requestCode) {
if (mFrom == DLConstants.FROM_EXTERNAL) {
if (dlIntent.getPluginPackage() == null) {
dlIntent.setPluginPackage(mPluginPackage.packageName);
}
}
return mPluginManager.startPluginActivityForResult(that, dlIntent, requestCode);
}
public int startPluginService(DLIntent dlIntent) {
if (mFrom == DLConstants.FROM_EXTERNAL) {
if (dlIntent.getPluginPackage() == null) {
dlIntent.setPluginPackage(mPluginPackage.packageName);
}
}
return mPluginManager.startPluginService(that, dlIntent);
}
4.8 DLBasePluginService.java 插件 Service 基类,插件中的 Service 要继承这个基类,主要作用是根据是否被代理,确定一些函数直接走父类逻辑还是代理 Service 或是空逻辑。 PS:截止目前这个类还是不完善的,至少和DLBasePluginActivity对比,还不支持非代理的情况(贴部分代码)
@Override
public void attach(Service proxyService, DLPluginPackage pluginPackage) {
// TODO Auto-generated method stub
LOG.d(TAG, TAG + " attach");
mProxyService = proxyService;
mPluginPackage = pluginPackage;
that = mProxyService;
mFrom = DLConstants.FROM_EXTERNAL;
}
protected boolean isInternalCall() {
return mFrom == DLConstants.FROM_INTERNAL;
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
LOG.d(TAG, TAG + " onBind");
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
LOG.d(TAG, TAG + " onCreate");
}
4.9 DLIntent.java 继承自 Intent,封装了待启动组件的 PackageName 和 ClassName。
public class DLIntent extends Intent {
private String mPluginPackage;
private String mPluginClass;
public DLIntent() {
super();
}
public DLIntent(String pluginPackage) {
super();
this.mPluginPackage = pluginPackage;
}
public DLIntent(String pluginPackage, String pluginClass) {
super();
this.mPluginPackage = pluginPackage;
this.mPluginClass = pluginClass;
}
public DLIntent(String pluginPackage, Class<?> clazz) {
super();
this.mPluginPackage = pluginPackage;
this.mPluginClass = clazz.getName();
}
public String getPluginPackage() {
return mPluginPackage;
}
public void setPluginPackage(String pluginPackage) {
this.mPluginPackage = pluginPackage;
}
public String getPluginClass() {
return mPluginClass;
}
public void setPluginClass(String pluginClass) {
this.mPluginClass = pluginClass;
}
public void setPluginClass(Class<?> clazz) {
this.mPluginClass = clazz.getName();
}
@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);
}
}
4.10 SoLibManager.java 调用SoLibManager拷贝 so 库到 Native Library 目录。 主要函数: (1) copyPluginSoLib(Context context, String dexPath, String nativeLibDir) 函数中以ZipFile形式加载插件,循环读取其中的文件,如果为.so结尾文件、符合当前平台 CPU 类型且尚未拷贝过最新版,则新建Runnable拷贝 so 文件。
/**
* copy so lib to specify directory(/data/data/host_pack_name/pluginlib)
*
* @param dexPath plugin path
* @param nativeLibDir nativeLibDir
*/
public void copyPluginSoLib(Context context, String dexPath, String nativeLibDir) {
String cpuName = getCpuName();
String cpuArchitect = getCpuArch(cpuName);
sNativeLibDir = nativeLibDir;
Log.d(TAG, "cpuArchitect: " + cpuArchitect);
long start = System.currentTimeMillis();
try {
ZipFile zipFile = new ZipFile(dexPath);
Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry zipEntry = (ZipEntry) entries.nextElement();
if (zipEntry.isDirectory()) {
continue;
}
String zipEntryName = zipEntry.getName();
if (zipEntryName.endsWith(".so") && zipEntryName.contains(cpuArchitect)) {
final long lastModify = zipEntry.getTime();
if (lastModify == DLConfigs.getSoLastModifiedTime(context, zipEntryName)) {
// exist and no change
Log.d(TAG, "skip copying, the so lib is exist and not change: " + zipEntryName);
continue;
}
mSoExecutor.execute(new CopySoTask(context, zipFile, zipEntry, lastModify));
}
}
} catch (IOException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
Log.d(TAG, "### copy so time : " + (end - start) + " ms");
}
获取CPU类型
/**
* get cpu name, according cpu type parse relevant so lib
*
* @return ARM、ARMV7、X86、MIPS
*/
private String getCpuName() {
try {
FileReader fr = new FileReader("/proc/cpuinfo");
BufferedReader br = new BufferedReader(fr);
String text = br.readLine();
br.close();
String[] array = text.split(":\\s+", 2);
if (array.length >= 2) {
return array[1];
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
获取CPU架构
@SuppressLint("DefaultLocale")
private String getCpuArch(String cpuName) {
String cpuArchitect = DLConstants.CPU_ARMEABI;
if (cpuName.toLowerCase().contains("arm")) {
cpuArchitect = DLConstants.CPU_ARMEABI;
} else if (cpuName.toLowerCase().contains("x86")) {
cpuArchitect = DLConstants.CPU_X86;
} else if (cpuName.toLowerCase().contains("mips")) {
cpuArchitect = DLConstants.CPU_MIPS;
}
return cpuArchitect;
}
4.11 DLUtils.java 这个类中大都是无用或是不该放在这里的函数,也许是大版本升级及维护人过多后对工具函数的维护不够所致。
至此,DynamicLoadApk 源码已解析完毕。