动态加载框架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.有哪里说得不对,请指出。

时间: 2024-10-05 18:04:37

动态加载框架DL分析的相关文章

Android动态加载框架DL的架构与基本原理解析

转载请注明出处,本文来自[ Mr.Simple的博客 ]. 我正在参加博客之星,点击这里投我一票吧,谢谢~ 前言 最近这一两年,Android App使用插件化技术开发的数量越来越大,其实还是业务地快速膨胀导致,需求越来越多,App越来越臃肿.虽然手机的内存空间不断地的增大,但是太大的安装包给用户也造成了心理压力.于是大家都会想到插件化的开发方式,把App做成一个平台,而不是一个独立的app.平台上可以集成各种各样的功能,功能模块也插件的形式添加进来,这些插件不需要安装,只需要用户按需下载到某个

Android 使用动态加载框架DL进行插件化开发

如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456    (来自时之沙的csdn博客) 概述: 随着应用的不断迭代,应用的体积不断增大,项目越来越臃肿,冗余增加.项目新功能的添加,无法确定与用户匹配性,发生严重异常往往牵一发而动全身,只能紧急发布补丁版本,强制用户进行更新.结果频繁的更新,反而容易降低用户使用黏性.或者是公司业务的不断发展,同系的应用越来越多,传统方式需要通过用户量最大的主项目进行引导下载并安装. 怎么办?参考浏览器-插件开发模式: 一.

APK动态加载框架(DL)解析

意义 这里说说这个开源项目的意义.首先要说的是动态加载技术(或者说插件化)在技术驱动型的公司中扮演这相当重要的角色,当项目越来越庞大的时候,需要通过插件化来减轻应用的内存和cpu占用,还可以实现热插拔,即在不发布新版本的情况下更新某些模块. 我 几个月前开始进行这项技术的研究,当时查询了很多资料,没有找到很好的开源.目前淘宝.微信等都有成熟的动态加载框架,包括apkplug,但是它们都是 不开源的.还有github上有一个开源项目AndroidDynamicLoader,其思想是通过Fragme

几种APK动态加载框架对比

APK动态加载框架主要有这几种:CJFrameForAndroid .DL. CJFrameForAndroid的使用注意: ●让插件应用中的Activity继承CJActivity,并且一切使用this调用的方法都使用that替代.例如this.setContentView();需要改为that.setContentView();●插件中涉及到的Android权限,须在APP项目清单中具有声明.●插件Activity跳转时,推荐使用CJActivityUtils类来辅助跳转.若一定要start

Android动态加载那些事儿

基础 1.Java 类加载器 类加载器(class loader)是 Java?中的一个很重要的概念.类加载器负责加载 Java 类的字节代码到 Java 虚拟机中.本文首先详细介绍了 Java 类加载器的基本概念,包括代理模式.加载类的具体过程和线程上下文类加载器等,接着介绍如何开发自己的类加载器,最后介绍了类加载器在 Web 容器和 OSGi?中的应用. 2.反射原理 Java 提供的反射機制允許您於執行時期動態載入類別.檢視類別資訊.生成物件或操作生成的物件,要舉反射機制的一個應用實例,就

携程Android App插件化和动态加载实践

携程Android App的插件化和动态加载框架已上线半年,经历了初期的探索和持续的打磨优化,新框架和工程配置经受住了生产实践的考验.本文将详细介绍Android平台插件式开发和动态加载技术的原理和实现细节,回顾携程Android App的架构演化过程,期望我们的经验能帮助到更多的Android工程师. 需求驱动 2014年,随着业务发展需要和携程无线部门的拆分,各业务产品模块归属到各业务BU,原有携程无线App开发团队被分为基础框架.酒店.机票.火车票等多个开发团队,从此携程App的开发和发布

Adroid动态加载Apk-插件化技术框架(动态代理方案)

技术:Android + java +动态加载+插件化 概述 为什么要使用插件化?在开发中,一个项目只会越做越大.初始版本可能是单一功能,后续可能加上各种风马牛不相及的功能.所以我认为插件化可以使得业务分离的更彻底,一人负责哪几个模块,问题也能快速定位.但是也会带来问题:插件和插件之间的交互的复杂性更高.底层支持库因为多个插件需要使用相同的代码可能会变得很大.所以插件化看似解耦了程序员的职责,实际上对于代码质量的要求更高.   实现插件化,最快的方法就是找一个第三方框架.但是要想真正理解,需要真

Win8 Metro动态加载内容框架

制作背景 为了参加ImagineCup 2013 世界公民类比赛,我们设计制作了一个可动态扩展的幼教类App.这个App需要能动态加载内容,内容包括带动画可交互的电子书,动画,视频,游戏. 技术支持 2012年10月第一次:因为SVG性能问题,将SVG换为cocos2d-x JSBind,可惜cocos2d-x JSBind不完善,最后换为cocos2d-x html5.11月第二次:cocos2d-x html5性能问题,破产.12月第三次:取消HTML5,转为使用XAML+JS模式. (微软

插件式换肤框架搭建 - 资源加载源码分析

1. 概述 大部分控件我们都会使用,但是我们未必知道其资源加载的原理,目前换肤的框架比较多我们可以随随便便拿过来用,但早在几年前这些资料是比较少的,如果想做一个换肤的框架那就只能自己一点一点啃源码. 如果说我们现在不去用第三方的开源框架,要做一个换肤的功能,摆在我们面前的其实只有一个问题需要解决,那就是如何读取另外一个皮肤apk中的资源. 所有分享大纲:2017Android进阶之路与你同行 视频讲解地址:http://pan.baidu.com/s/1bC3lAQ 2. 资源加载源码分析 2.