动态加载框架DL分析
动态加载框架DL分析
插件化开发,主要解决三个问题
1.动态加载未安装的apk,dex,jar等文件
2.activity生命周期的问题,还有service
3.Android的资源调用的问题
简单说一下怎样解决这三个问题,让插件化开发成为可能
1.解决未安装的apk比较简单,用DexClassLoader就可以解决(原始的jar要用dx转换一下,不能直接加载)
2.activity在未安装的apk中只是一个普通的类,生命周期不会被系统管理。解决这个问题就是在宿主apk注册代理activity,
这个activity只是一个壳,什么也没干。就是用来调用一下未安装apk的activity的生命周期。这里或许不能一下看懂,下面会详细讲解
3.android的资源都是单例的,java中获取资源都是通过Resources,而Resources又是通过AssetManager创建的。所以通过反射拿到AssetManager,
然后创建一个未加载apk的Resources,就能解决这个问题。
接下来看一下DLPluginManager。这是个管理类。先看一下怎样加载未安装的插件。
public DLPluginPackage loadApk(final String dexPath, boolean hasSoLib) {
mFrom = DLConstants.FROM_EXTERNAL;
PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(dexPath,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
if (packageInfo == null) {
return null;
}
DLPluginPackage pluginPackage = preparePluginEnv(packageInfo, dexPath);
if (hasSoLib) {
copySoLib(dexPath);
}
return pluginPackage;
}
loadApk这个方文件法有两个参数,一个就是要加载dex的路径,另一个是否有so库。然后跟踪到preparePluginEnv这个方法。
如果有so库,就拷贝到mContext.getDir("pluginlib", Context.MODE_PRIVATE).getAbsolutePath();这个目录/data/data/包名/pluginlib这个目录
private DLPluginPackage preparePluginEnv(PackageInfo packageInfo, String dexPath) {
DLPluginPackage pluginPackage = mPackagesHolder.get(packageInfo.packageName);
if (pluginPackage != null) {
return pluginPackage;
}
DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
AssetManager assetManager = createAssetManager(dexPath);
Resources resources = createResources(assetManager);
// create pluginPackage
pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo);
mPackagesHolder.put(packageInfo.packageName, pluginPackage);
return pluginPackage;
}
mPackagesHolder是缓存,先从缓存换取,缓存没有就调用createDexClassLoader加载dex,同时把Resources也获取了,这点之后再说。动态加载的结果都保存到
DLPluginPackage这个类。
private DexClassLoader createDexClassLoader(String dexPath) {
File dexOutputDir = mContext.getDir("dex", Context.MODE_PRIVATE);
dexOutputPath = dexOutputDir.getAbsolutePath();
DexClassLoader loader = new DexClassLoader(dexPath, dexOutputPath, mNativeLibDir, mContext.getClassLoader());
return loader;
}
这里说明一下DexClassLoader四个参数
dexPath dex所在的路径
dexOutputPath 优化dex的存放路径
mNativeLibDir 表示dex要调用so库的路径,上面说了,有so库就copy到该路径。
mContext.getClassLoader() 加载自己的DexClassLoader。这里用到宿主context的DexClassLoader。
我们再看一下上面说到的创建AssetManager和createResources
private AssetManager createAssetManager(String dexPath) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, dexPath);
return assetManager;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private Resources createResources(AssetManager assetManager) {
Resources superRes = mContext.getResources();
Resources resources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
return resources;
}
先通过反射获取AssetManager,然后再创建一个新的Resources。因为宿主的Resources不等于动态加载的dex文件的Resources。
接下来看一下DLProxyActivity这个类,这个类要在宿主apk上注册。用来控制未安装的apk的activity的生命周期等的各种回调函数。
回调的函数都在DLPlugin这个接口
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);
}
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 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);
}
}
这里有mRemoteActivity和DLProxyImpl。mRemoteActivity一般都是未安装apk的activity。DLProxyImpl就是一个代理处理未加载activity的类。
这个类负责解析插件apk的资源、ClassLoader、通过反射加载插件Activity,DLProxyImpl加载了插件Activity之后又会调用Proxy Activity的
attach方法将插件Activity实例传递给Proxy Activity,也就是mRemoteActivity。
这里看一下DLProxyImpl这个类
public void onCreate(Intent intent) {
// set the extra's class loader
intent.setExtrasClassLoader(DLConfigs.sPluginClassloader);
mPackageName = intent.getStringExtra(DLConstants.EXTRA_PACKAGE);
mClass = intent.getStringExtra(DLConstants.EXTRA_CLASS);
Log.d(TAG, "mClass=" + mClass + " mPackageName=" + mPackageName);
mPluginManager = DLPluginManager.getInstance(mProxyActivity);
mPluginPackage = mPluginManager.getPackage(mPackageName);
mAssetManager = mPluginPackage.assetManager;
mResources = mPluginPackage.resources;
initializeActivityInfo();
handleActivityInfo();
launchTargetActivity();
}
先是给intent set一个加载器。这个加载器放在DLConfigs这个类中,其实首先重写了intent为DLintent这个类,这个类putExtra的时候就会
把classloader存放到DLConfigs中,然后接收的activity就能从DLConfigs获取到classloader.然后就可以获取各种需要的信息。接下来看一下
initializeActivityInfo(), handleActivityInfo(); launchTargetActivity();这三个方法
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;
}
}
}
}
}
}
}
这里获取ActivityInfo和修正theme。
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.
}
这里设置mProxyActivity的主题
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();
}
}
这里获取mPluginActivity插件的activity,attach方法调用了两次,一次把插件activity给代理activity。另一次把代理activity给插件activity
然后创建一个bundle,调用插件activity的onCreate方法。这样插件activity就启动起来了。
PluginActivity里面创造了that语法。是插件就使用that,that是指代理activity,this代表自己。自己不是插件,就用this。
最后:
1.service和activity原理差不多,但是没有使用that语法。PluginFragmentActivity也不说了。
2.使用dl开发,插件中会依赖dl.jar包,但不能打包到apk。否则会发生内链接错误。内存中有多个相同的类是不允许的。
demo中放到external-jar目录下,然后再.classpath加上<classpathentry kind="lib" path="external-jars/dl-lib.jar"/>
3.有哪里说得不对,请指出。
动态加载框架DL分析的更多相关文章
- Android 使用动态加载框架DL进行插件化开发
http://blog.csdn.net/t12x3456/article/details/39958755/ 转载自: 时之沙: http://blog.csdn.net/t12x3456
- Android动态加载框架汇总
几种动态加载的比较 1.Tinker 用途:热修复 GitHub地址:https://github.com/Tencent/tinker/ 使用:http://www.jianshu.com/p/f6 ...
- APK动态加载框架(DL)解析
转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/39937639 (来自singwhatiwanna的csdn博客) 前言 好久 ...
- 携程Android App的插件化和动态加载框架
携程Android App的插件化和动态加载框架已上线半年,经历了初期的探索和持续的打磨优化,新框架和工程配置经受住了生产实践的考验.本文将详细介绍Android平台插件式开发和动态加载技术的原理和实 ...
- Android RocooFix热修复动态加载框架介绍
RocooFix Another hotfix framework 之前的HotFix项目太过简单,也有很多同学用Nuwa遇到很多问题,作者也不再修复,所以重新构建了一套工具. Bugfix 2016 ...
- Android HotFix动态加载框架介绍
HotFix(Deprecated) https://github.com/dodola/HotFix 请关注 RocooFix 我重新写了一个RocooFix框架,解决了Nuwa因为Gradle1. ...
- APK动态加载框架 https://github.com/singwhatiwanna/dynamic-load-apk
https://github.com/singwhatiwanna/dynamic-load-apk
- 携程Android App插件化和动态加载实践
携程Android App的插件化和动态加载框架已上线半年,经历了初期的探索和持续的打磨优化,新框架和工程配置经受住了生产实践的考验.本文将详细介绍Android平台插件式开发和动态加载技术的原理和实 ...
- Adroid动态加载Apk-插件化技术框架(动态代理方案)
技术:Android + java +动态加载+插件化 概述 为什么要使用插件化?在开发中,一个项目只会越做越大.初始版本可能是单一功能,后续可能加上各种风马牛不相及的功能.所以我认为插件化可以使 ...
随机推荐
- android adb命令
adb查看logcat adb logcat <TAG>:* *:S 查看指定TAG的Log adb查看最上层activity:linux: adb shell dumpsys activ ...
- mac的webdriver自动化
下载webdriver-chrome的连接:http://chromedriver.storage.googleapis.com/index.html
- j2ee部分
j2ee部分 1.BS与CS的联系与区别. C/S是Client/Server的缩写.服务器通常采用高性能的PC.工作站或小型机,并采用大型数据库系统,如Oracle.Sybase.InFORMix或 ...
- PHP扩展开发及内核应用
目录中汉字部分代表已经翻译完成的章节,带链接的表示已经发布的,未待链接的表示正在校正即将发布的. PHP的生命周期 让我们从SAPI开始 PHP的启动与终止 PHP的生命周期 线程安全 小结 PHP变 ...
- Base64 的那些事儿
一.Base64是什么? Base64是一种编码的格式.是将信息流(字节流)按照一定的规范,重新组合,显示出完全不相关内容的编码格式. ps.定义是我自己总结的,我觉得对于知识的定义,只要简洁,不错误 ...
- MapReduce 简介
2. MapReduce 简介 MapReduce 实际上是分为两个过程 map 过程 : 数据的读取 reduce 过程 : 数据的计算 并行计算是一个非常复杂的过程, mapreduce是一个并行 ...
- Linux下man手册使用
在 linux 环境下,为了查看一下 free 函数的原型,执行 man free 命令,发现显示结果为系统的 free 命令帮助,而不是库文件中的 free 函数帮助.查阅了一下,发现 man 的使 ...
- 关于缺少nvToolsExt64_1.lib时的PhysX的处理
我本人之所以缺少这个文件是因为我的PhysX如果你的PhysX是从UE4源代码中提取的,那么可能会出现如下错误,提示找不到nvToolsExt64_1.lib(本机是64bit的操作系统) 那就执行u ...
- Lucene.net
模糊查询-〉数据库全文检索-〉Lucene 一元分词(lucene内置) Analyzer analyzer = new CJKAnalyzer(); TokenStream tokenStream ...
- android控件的对齐方式(转)
<?xml version="1.0" encoding="utf-8"?> <!-- android:layout_above 将该控件的底 ...