组件化Android开发Android开发经验谈

从启动一个未安装Apk的页面入门插件化开发

2017-09-07  本文已影响125人  fushuang

原文链接:http://blog.csdn.net/qq_34306963/article/details/77883671

什么是插件化开发

一个Android应用在开发到了一定阶段以后,功能模块将会越来越多,APK安装包也越来越大,用户在使用过程中也没有办法选择性的加载自己需要的功能模块。此时可能就需要考虑如何分拆整个应用了。
举个栗子,好比小时候玩的小霸王游戏机,你刚买回家的只是一个插卡带的机器,就好比最初下载的apk,当你发现你想玩某一款游戏的时候,就去买专门的卡带插进去即可,而不是非得把游戏一下子全买下来.

插件化优点:

插件化的两种方式

1.安装式:通过网络下载插件apk,安装(这样似乎和重新装了一个apk没啥区别感觉),中间可以通过一个webview 动态更新,便于加载新的模块.通过js调用intent启动即可,因为是安装的apk,系统会为apk注入上下文,运行比较方便

2.不安装式:因为apk没有安装,在宿主apk中,无法加载安装包中的资源文件,类文件,注入content,系统也不会为apk管理生命周期,需要通过宿主apk协助加载,本文重点讨论这种方式

话不多说,切入正题......

可是怎么启动一个未安装的apk页面呢??

先po出用于展示插件页面的代理activity吧(当然是在宿主app里的) ,先简单说说里面的几个关键的东西是干啥的,一会在细说(提前抛出一个坑,所有的关于context 的东西,必须重写,因为系统没有入住context,需要调用我们宿主的context)

 private String className;
    private ProxyInterface realActivity;

    @Override
    public void startActivity(Intent intent) {
        Intent in = new Intent(this,ProxyActivity.class);
        String className = intent.getStringExtra("className");
        in.putExtra("className", className);
        super.startActivity(in);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {

         super.onCreate(savedInstanceState);
         className = getIntent().getStringExtra("className");

        //这种不能获得,因为类在包外面
//        getClassLoader().loadClass(className)

        try {
            //activity 构造私有,反射构建
            Class loadClass = getClassLoader().loadClass(className);
            Constructor constructor = loadClass.getConstructor(new Class[]{});
            realActivity = ((ProxyInterface) constructor.newInstance(new Object[]{}));
            realActivity.onAttach(this);
            /**
             * 定义标准
             * 传递生命周期
             */
            Bundle bundle = new Bundle();
            realActivity.onCreate(bundle);

        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    @Override
    protected void onStart() {
        super.onStart();
        realActivity.onStart();
    }

    @Override
    protected void onResume() {
        super.onPause();
        realActivity.onResume();
    }

    @Override
    protected void onStop() {
        super.onStop();
        realActivity.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        realActivity.onDestroy();
    }

    @Override
    protected void onPause() {
        super.onPause();
        realActivity.onPause();
    }


    @Override
    public ClassLoader getClassLoader() {
        return PluginManager.getInstance().dexClassLoader;
    }

    @Override
    public Resources getResources() {
        return PluginManager.getInstance().resources;
    }
}

大概...需要三个东西吧

1.重写类加载器

在插件apk中,系统并不认为你是一个应用程序,所以不会为你分配虚拟机,木有虚拟机,就不会有类加载机制,那么所有的类,在宿主app中是不可以通过反射或者etClassLoader().loadClass("类名")去加载的.
那..这就有点尴尬了,好在...可以通过重写类加载器去加载文件里的类

  File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
  dexClassLoader = new DexClassLoader(path, dexOutFile.getAbsolutePath(), null,  context.getClassLoader());

DexClassLoader 构造 加载器一共四个参数,所需条件并不是很难获得

  1. dexPath 加载dex文件的绝对路径,dex在安装包内,就传插件安装包路径就行
  2. optimizedDirectory 加载优化路径
  3. 引用类库路径
  4. 父加载器

2.重新资源Resource对象

//构建新包的资源加载器
        //对于AssetManager因为构造方法添加了Hide注解,所以必须通过反射获取
        try {
            AssetManager assetManager = AssetManager.class.newInstance();
            Method method = AssetManager.class.getMethod("addAssetPath", String.class);
            method.invoke(assetManager,path);
            resources = new Resources(assetManager
            ,context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
        } catch (Exception e) {
            e.printStackTrace();
        }

3.在插件apk重写几个关于context 的方法,由注入的context调用

getResources
getWindowManager
getWindow
getLayoutInflater
getClassLoader
findViewById
setContentView

4.最后一步.....

从宿主的mainActivity中跳转到 代理ProxyActivity中,并且传入要加载的插件apk中启动页面的类名,完事.....

4.关于插件apk中页面的跳转

是不能够通过startActivity调用的,原因..还是因为context 为null ,跳转需要将目标页面的类名再次传入ProxyActivity ,也就是每次启动插件页面,其实都是开启一个ProxyActivity,本质上是启动ProxyActivity ,只是加载了插件中的布局,class 而已...关于activity的启动原理呢..还需深入了解一下

最后贴个github地址 :https://github.com/fushuangdage/ProxyDemo

上一篇 下一篇

猜你喜欢

热点阅读