关于android 热修复,网上资料林林总总,比如AndFix、HotFix、DroidFix、Tinker等,这些都是从apk层面来实现怎么对线上app进行热修复的;但是有时候我们不仅仅需要热修复apk,当接入第三方SDK(jar)的话,获取也对需要SDK进行热修复,毕竟如果SDK有bug的话,APK替换修复好的SDK,然后发布新的版本代价太大。目前正好手头有这个需求,就查看资料捣鼓了一下,以博客的形式记录下来,供以后查看翻阅
SDK的热修复原理比App的热修复简单的多,核心原理或者步骤简单来说就两条:
1)下载服务器的jar
2)通过反射机制加载jar,创建相关对象并且操作之。
原理很简单,但是真正实现起来也有好几点需要注意:
1)宿主Apk配置上网以及SDK读写权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2)提供的jar包需要处理成dex格式的jar包,处理的步骤如下:
2.1)把打包好的jar放入android安装目录的platform-tools文件目录下(当然其他盘的任何目录都可以)。
2.2)cd 到android安装目录的build-tools文件如图:
在此我选择23.0.0版本的文件夹:在该文件夹下有dx.bat和\lib\dx.jar两个文件,这两个文件正式转成dex格式的jar文件所需的工具。打开cmd,切换到23.0.0目录执行如下命令即可:dx - -dex - -output=转换后.jar,转换前.jar
如此就把jar转换成DexClassLoader可以加载的dex格式的jar了(hotfix.jar)。然后就可以交给宿主APK使用了。
3) 宿主Apk通过DexClassLoader加载hotfix.jar,通过反射创建对象并使用之。
为了方便说明,在此不实现jar的下载逻辑了,直接把hotfix.jar放在sdk卡根目录测试。
3.1)通过反射机制创建对象并使用之,代码如下:
private Object instance;// 反射形式创建的全局变量
private File file;//获取jar文件
private File dexOutputDir;
private DexClassLoader classLoader;
private Class sdkClass;
public void method1() {
file = new File(Environment
.getExternalStorageDirectory().toString()
+ File.separator
+ "hotfix.jar");
dexOutputDir = getDir("dex", 0);
classLoader = new DexClassLoader(file.getAbsolutePath(),
dexOutputDir.getAbsolutePath(), null, getClassLoader());
//加载sdk中提供的一个接口实现类
sdkClass = classLoader.loadClass("cn.com.hotfix.intefimpl.HotFixImpl");
//通过反射创建对象
instance = sdkClass.newInstance();
//通过反射获取hotFix方法
Method method = sdkClass.getMethod("hotFix", null);
//调用hotFix方法,并且获取hotFix方法返回的字符串;
String src = (String) method.invoke(instance, null);
Toast.makeText(MainActivity.this, src,
Toast.LENGTH_SHORT).show();
} catch (Exception exception) {
// Handle exception gracefully here.
exception.printStackTrace();
}
}
此种方式通过反射机制创建一个对象,并且通过反射机制创建对象,然后通过反射机制获取Method方法来完成业务逻辑。当然这种方法很麻烦,你得告诉宿主apk的方法的名称(比如上面的,参数的类型),主要是代码维护起来麻烦!所以也可以采用第二种方式:插件化方式给宿主提供jar!
3.2)反射机制+插件化的方式
这种方式仍然离不开反射机制,这个暂且不提;先说说插件化的要求:
宿主定制好所需接口,SDK实现该接口;这样当宿主apk引入第三方SDK的时候,结合反射机制,能很好的来对接SDK.下面以一个简单的demo来作说明。
第一步:宿主定制接口:
如图所示,HotFixSuZhu创建了cn.com.hotfix.interf.IHotFix的接口,该接口很简单:
public interface IHotFix {
public String hotFix();
}
现在宿主Apk已经定制好接口,那么SDK实现这个接口,然后打包成dex格式的jar提供给宿主使用就可以了!SDK结构如下:
SDK同样需要创建一个cn.com.hotfix.interf包,该包下面同样需要创建跟宿主一模一样的IHotFix接口,这点从接入方式来看有点像AIDL客户端的使用方式:在使用AIDL的时候同样需要在客户端引入AIDL接口的全限定名以及接口文件。同时SDK的HotFixImpl实现了IHotFix接口:
public class HotFixImpl implements IHotFix {
@Override
public String hotFix() {
return "hotFix test";
}
}
到此为止,宿主接口创建好了,SDK也实现了宿主创建的接口;那么就看看宿主怎么使用SDK(注意SDK在打包成jar并转换成dex格式的时候:切记不要把SDK项目下的cn.com.hotfix.interf.IHotFix也打包):
private IHotFix hotFixImpl;//创建对象
private File file;//获取jar文件
private File dexOutputDir;
private DexClassLoader classLoader;
private Class sdkClass;
public void method1() {
file = new File(Environment
.getExternalStorageDirectory().toString()
+ File.separator
+ "hotfix.jar");
dexOutputDir = getDir("dex", 0);
classLoader = new DexClassLoader(file.getAbsolutePath(),
dexOutputDir.getAbsolutePath(), null, getClassLoader());
//加载sdk中提供的一个接口实现类
sdkClass = classLoader.loadClass("cn.com.hotfix.intefimpl.HotFixImpl");
//创建对象
hotFixImpl = (IHotFix) sdkClass.newInstance();
Toast.makeText(MainActivity.this, hotFixImpl.hotFix(),
Toast.LENGTH_SHORT).show();
}
到此位置,SDK的热更新机制使用完毕,无非就是远程加载sdk,利用反射机制来创建一个SDK的对象来完成工作。这种更新机制可用于于及时解决线上app第三方SDK出现的bug,避免apk重新发布让用户下载安装的这种不好的体验。
相对于SDK热修复的原理来说,此篇博文倒是显得啰嗦了,希望对大家有所帮助。不当之处或者有什么别的实现方式,欢迎交流。(点此下载demo)
今天闲着没事翻阅《android内核剖析这本书》的时候才发现原来此书也有关于android插件化开发的说明,详见该书第二章《java基础》篇的DexClassLoader的介绍。