Android热修复、插件化、组件化

2022-01-12 15:33:36 浏览数 (2)

模块化:项目按照独立的模块进行划分

组件化:将项目按照单一的组件来进行划分结构

项目组件化的重要环节在于,将项目按照模块来进行拆分,拆分成一个个业务module和其他支撑module(lib),各个业务module之间互不依赖,互相解耦!每个业务module都可以安排不同的开发人员团队来进行开发,不强制使用一种开发模式,MVP可以,MVC也可以!然后各个业务module之间通过路由机制进行跳转和传递!

热修复

  • 主要用来修复代码、修复bug、添加独立的功能,他的原理主要是操作PathClassLoader、DexClassLoader。
  • PathClassLoader是类加载器,DexClassLoader可以从.jar和.apk类型的文件内部加载classes.dex文件就好了。他们都是classloder的子类。
  • classloder是什么呢?与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class文件加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader。
  • 一个ClassLoader可以包含多个dex文件,每个dex文件是一个Element(元素),多个dex文件排列成一个有序的数组dexElements,当找类的时候,会按顺序遍历dex文件,然后从当前遍历的dex文件中找类,如果找类则返回,如果找不到从下一个dex文件继续查找。
  • 那么这样的话,就可以在这个dexElements中去做一些事情,比如,在这个数组的第一个元素放置我们的patch.jar,里面包含修复过的类,这样的话,当遍历findClass的时候,我们修复的类就会被查找到,从而替代有bug的类。
  • 原理简单说就是当打开的时候使用ClassLoader动态加载,然后使用反射机制来调用插件中的类和方法,一般都会搭配一套插件框架来配合使用。

插件化

减少体积、添加功能、提高打开速度(多个dex,效果不理想)

把插件apk放在asset里,或者网络下载,保存在本地,可以通过dexClassLoader加载

代码语言:javascript复制
public class MainActivity extends AppCompatActivity {


  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);


    /*try {
      Class utilsClass = Class.forName("com.pluginnable_plugin.Utils");
      Constructor utilsConstructor = utilsClass.getDeclaredConstructors()[0];
      utilsConstructor.setAccessible(true);
      Object utils = utilsConstructor.newInstance();
      Method shoutMethod = utilsClass.getDeclaredMethod("shout");
      shoutMethod.setAccessible(true);
      shoutMethod.invoke(utils);
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }*/


    File apk = new File(getCacheDir()   "/plugin-debug.apk");
    try (Source source = Okio.source(getAssets().open("plugin-debug.apk"));
         BufferedSink sink = Okio.buffer(Okio.sink(apk))) {
      sink.writeAll(source);
    } catch (IOException e) {
      e.printStackTrace();
    }
    DexClassLoader classLoader = new DexClassLoader(apk.getPath(), getCacheDir().getPath(), null, null);
    try {
      Class utilsClass = classLoader.loadClass("com.demo.pluginnable_plugin.Utils");
      Constructor utilsConstructor = utilsClass.getDeclaredConstructors()[0];
      Object utils = utilsConstructor.newInstance();
      Method shoutMethod = utilsClass.getDeclaredMethod("shout");
      shoutMethod.invoke(utils);
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
}

AndFix

原理:

方法的替换,把有bug的方法替换成补丁文件中的方法。

优点:

重大bug,需要紧急修复 可以下次迭代修复的bug 影响用户体验的行为

无需重启 缺点:

  • 无法添加新类(内部类也不行)和新的字段、新的方法?自己试了方法可以
  • 资源文件无法替换 试了下换原有的图片可以,但是新增的不行
  • 不能修改xml布局文件 不能
  • 加固后的包补丁无法使用,如果要加固,需要加固前的包来生成补丁,不过这样生成的补丁也很容易破解
  • 不能对同一个方法修复两次,否则App根本跑不起来
  • 对加载过的补丁文件要做名字修改 如果名字重叠 就不会再次加载

补丁加载的时机:

可以放在自定义Application的onCreate方法中,也可以放在button的点击事件中,也可以放在监听网络变化的广播中。

操作:

通过命令生成补丁

代码语言:javascript复制
 -a,--alias <alias>     keystore entry alias.
 -e,--epassword <***>   keystore entry password.
 -f,--from <loc>        new Apk file path.
 -k,--keystore <loc>    keystore path.
 -n,--name <name>       patch name.
 -o,--out <dir>         output dir.
 -p,--kpassword <***>   keystore password.
 -t,--to <loc>          old Apk file path.

阿里百川

http://www.tuicool.com/articles/viEJfeE

主要好处是可以对补丁很好的管理,例如停止发布、继续发布、发布回滚等等

0 人点赞