插件式开发是什么,有什么优缺点,测试有什么注意点,这个今天就来谈一谈。
在开始前先来复习一下基础知识
反射
反射,在程序运行的时候才根据名字去动态加载类,方法,相比于import的方式,不需要在编译的时候就必须知道,而是运行的时候一个动态的过程,因此,具有非常好的灵活性。
.dex文件
这是安卓的二进制文件,java最终输出是.class或者.jar,而android则是对许许多的.class进行优化,最终形成.dex,这个文件也是本文中最重要的东西
DexClassLoader
我们知道java默认的是ClassLoader,是用来加载类文件的,加载Dex文件用什么呢,当然是google给我们开发的DexClassLoader啦,它的继承关系如下:
loadclass的过程
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {
ClassNotFoundException suppressed = null;
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
suppressed = e;
}
if (clazz == null) {
try {
clazz = findClass(className);
} catch (ClassNotFoundException e) {
e.addSuppressed(suppressed);
throw e;
}
}
}
return clazz;
}
于是解释是这样的,
1.传入你要load的类名
2.去已加载的类看下有没有已经加载了的,有就直接返回
3.没有去父类的classloader查找有没有加载
4.还是没有,通过findclass去正真加载你的自定义类
好了,基础就这么多,下面说说好处,其实就是两个字,灵活。
那么对应于商业应用来说有两点优势:
第一点,按需下载,比如QQ的主题,打包整个主题资源显然不合适,所以按用户需要自己去下载,然后动态反射加载这些主题功能。
第二点,无痕更新,一个安装包发出去了,就收不回来了,修复BUG只能等待下一次更新,但是有了插件就不一样了,把重点模块的逻辑以插件dex的形式存放,有BUG,马上修,马上推送,用户甚至不用退出应用,只需要一次返回,就完成更新了,非常爽快。
缺点:好像没发现影响很大的缺点,汗…
ok,回到测试当中来,其实在我看来loadclass这个过程完全是交给系统做的,所以本身没有什么BUG,但是我们要注意的地方是load周边逻辑,包括:
怎么比对插件新旧版本 新旧插件交替的过程中旧的插件处理,新的插件有没有正确加载 新旧apk版本有没有正确使用对应的dex插件主要测试方式还是通过log的形式展现,其二就是通过load目录对应的md5值判断
最后,来一段实现的不太规范的小小demo:
插件实现了抽象类,调用的时候只需要反射加载代码并转为定义的抽象类,这样就可以直接调用方法了,有点多态的味道。
package com.cloudhuan.dexloader;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Display;
import dalvik.system.DexClassLoader;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mock_Dex mock_dex = new Mock_Dex();
DexClassLoader dexClassLoader = new DexClassLoader(mock_dex.getDexPath(),this.getDir("tmp_dex", 0).toString(),null,this.getParent().getClassLoader());
try {
Class clazz = dexClassLoader.loadClass("com.cloudhuan.plugin");
MyRun myRun = (MyRun) clazz.newInstance();
myRun.run();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Mock_Dex{
String dexPath;
public String getDexPath(){
return dexPath;
}
}
abstract class MyRun{
public abstract void run();
}