Android组件化、插件化详细讲解

来自:网络
时间:2022-12-26
阅读:
目录

什么是组件化(通俗易懂)

通俗易懂来讲就是,拆成多个module开发就是组件化。

App的部分功能模块在打包时并不以传统⽅式打包进apk⽂件中,⽽是以另⼀种形式⼆次封装进apk内部,或者放在⽹络上适时下载,在需要的时候动态对这些功能模块进⾏加载,称之为插件化。这些单独⼆次封装的功能模块apk,就称作插件,初始安装的apk称作宿主。插件化是组件化的更进⼀步推进。

插件化基础之反射:

反射的写法

    try {
            Class utilClass = Class.forName("com.hencoder.demo.hidden.Util");
            Constructor utilConstructor = utilClass.getDeclaredConstructors()[0];
            utilConstructor.setAccessible(true);
            Object util = utilConstructor.newInstance();
            Method shoutMethod = utilClass.getDeclaredMethod("shout");
            shoutMethod.setAccessible(true);
            shoutMethod.invoke(util);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

反射的⽬的

Java既然提供了可⻅性关键字public、private等等,⽤来限制代码之间的可⻅性,为什么⼜要提供反射功能?可⻅性特性的⽀持不是为了代码不被坏⼈使⽤,⽽是为了程序开发的简洁性。安全性的话,可⻅性的⽀持提供的是Safety 的安全,⽽不是Security的安全。即,可⻅性的⽀持让程序更不容易写出bug,⽽不是更不容易被⼈⼊侵。反射的⽀持可以让开发者在可⻅性的例外场景中,可以突破可⻅性限制来调⽤⾃⼰需要的API。这是基于对开发者在使⽤反射时已经⾜够了解和谨慎的假设的。所以,可⻅性的⽀持不是为了防御外来者⼊侵,因此反射功能的⽀持并没有什么不合理。

关于DEX:

  • class:java编译后的⽂件,每个类对应⼀个class⽂件
  • dex:Dalvik EXecutable把class打包在⼀起,⼀个dex可以包含多个class⽂件
  • odex:Optimized DEX针对系统的优化,例如某个⽅法的调⽤指令,会把虚拟的调⽤转换为使⽤具体的index,这样在执⾏的时候就不⽤再查找了
  • oat:Optimized Androidfile Type。使⽤AOT策略对dex预先编译(解释)成本地指令,这样再运⾏阶段就不需再经历⼀次解释过程,程序的运⾏可以更快
  • AOT:Ahead-Of-Time compilation预先编译

插件化原理:动态加载

通过⾃定义ClassLoader来加载新的dex⽂件,从⽽让程序员原本没有的类可以被使⽤,这就是插件化的原理。

例如:把Utils拆到单独的项⽬,打包apk作为插件引⼊:

 File f = new File(getCacheDir() + "/demo-debug.apk");
        if (!f.exists()) {
            try { 
                InputStream is = getAssets().open("apk/demo-debug.apk");
                int size = is.available();
                byte[] buffer = new byte[size];
                is.read(buffer);
                is.close();
                FileOutputStream fos = new FileOutputStream(f);
                fos.write(buffer);
                fos.close();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    DexClassLoader classLoader = new DexClassLoader(f.getPath(),
    getCodeCacheDir().getPath(), null, null);
      try {
            Class oldClass = classLoader.loadClass("com.hencoder.demo.hidden.Util");
            Constructor utilConstructor = oldClass.getDeclaredConstructors()[0];
            utilConstructor.setAccessible(true);
            Object util = utilConstructor.newInstance();
            Method shoutMethod = oldClass.getDeclaredMethod("shout");
            shoutMethod.setAccessible(true);
            shoutMethod.invoke(util);
            Class activityClass = classLoader.loadClass("com.hencoder.demo.MainActivity");
            startActivity(new Intent(this, activityClass));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

问题⼀:未注册的组件(例如Activity)不能打开

  • 解决⽅式⼀:代理Activity
  • 解决⽅式⼆:欺骗系统
  • 解决⽅式三:重写gradle打包过程,合并AndroiManifest.xml

问题⼆:资源⽂件⽆法加载

解决⽅式:⾃定义AssetManager和Resources对象

   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;
    }

插件化有什么用?

  • 早期:解决dex 65535问题。⾕歌后来也出了multidex⼯具来专⻔解决
  • 懒加载来减少软件启动速度:有可能,实质上未必会快
  • 减⼩安装包⼤⼩:可以
  • 项⽬结构拆分,依赖完全隔离,⽅便多团队开发和测试,解决了组件化耦合度太⾼的问题:这个使⽤模块化就够了,况且模块化解耦不够的话,插件化也解决不了这个问题
  • 动态部署:可以
  • 热修复:可以
返回顶部
顶部