导论:
上篇博客提到增量更新,这篇就要说说热修复,那么到底什么区别呢?通俗的理解:
1:你软件有新的功能或者升级一下一些更改,但是下载完整的APK又比较大,这时为了省流量才会用到增量更新,这个叫增量包,格式为.patch文件
2:用户使用你的应用某个地方老是出现bug,那么怎么办,不会还是发布完整APK下载使用吧,当然不会,这时就需要修补bug的补丁了,类似windows 漏洞更新,这个叫补丁包,格式为.apatch
为了学习更多的知识,我找到某个大牛的demo,里面介绍的是阿里百川的热修复框架Andfix,当然你也可以自己写框架,除非你强过阿里的技术大牛,在这里还会了解一点第三方服务器存储,友盟统计,极光消息推送Jpush,多线程下载等知识,也是为了让大家学到更多的知识,若有疑问,还请海涵.
原理:
1:启动应用获取友盟在线参数来判断当前应用是否有补丁下载,有则下载到SDcard里并且通过阿里热修复框架AndFix自动修补补丁
2:使用极光Jpush推送消息到应用,若收到消息,就有补丁需要下载,若没有收到消息,就通过友盟在线参数来判断有无补丁
步骤
1:在app/module build.gradle中添加依赖,有多线程下载,Jpush,友盟,阿里andfix
dependencies {
....
compile 'com.mani:thindownloadmanager:1.0.0'
compile files('libs/jpush-android-2.1.0.jar')
compile files('libs/umeng-onlineconfig_v1.0.0.jar')
compile 'com.alipay.euler:andfix:0.3.1@aar'
}
2:导入AndFix的so库文件以及极光推送的so库文件到jniLibs目录里,具体地址自己自行百度
3:配置友盟在线参数以及极光推送自定义消息的内容在这里忽略,官方有开发文档可以参考
4:制作补丁包放在第三方服务器上
A:下载apkpatch工具:
https://github.com/alibaba/AndFix/raw/master/tools/apkpatch-1.0.3.zip
B:将old.apk,new.apk,Jks签名文件拷贝到解压apkpatch工具的目录下,打开命令行输入命令生成补丁包,格式如下
生成差异文件命令 : apkpatch.bat -f new.apk -t old.apk -o output1 -k debug.keystore -p android -a androiddebugkey -e android
-f <new.apk> :新版本
-t <old.apk> : 旧版本
-o <output> : 输出目录
-k <keystore>: 打包所用的keystore
-p <password>: keystore的密码
-a <alias>: keystore 用户别名
-e <alias password>: keystore 用户别名密码
C:将生成的补丁.apach放到服务器上
D:根据服务器存储信息去配置友盟后台和定义极光推送消息路径
5:在自定义Application类进行初始化andfix PatchManager和Jpush JPushInterface
public class BaseApplication extends Application {
public static String VERSION_NAME;
public static PatchManager mPatchManager;
@Override
public void onCreate() {
super.onCreate();
try {
VERSION_NAME = this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionName;
mPatchManager = new PatchManager(this);
mPatchManager.init(VERSION_NAME);
mPatchManager.loadPatch();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
JPushInterface.init(this);
}
}
6:比对本地应用版本和远程服务器的版本及补丁版本,若有更新,就下载到本地SDcard,最后调用andfix PatchManager自动修补补丁
private static final int THREAD_COUNT = 3; //下载的线程数
private ThinDownloadManager mDownloadManager;
private LocalPreferencesHelper mLocalPreferencesHelper;
private static class SingletonHolder {
public static final RepairBugUtil INSTANCE = new RepairBugUtil();
}
//单例模式
public static RepairBugUtil getInstance() {
return SingletonHolder.INSTANCE;
}
//使用ThinDownloadManager多线程下载和PatchManager修补补丁
public void downloadAndLoad(Context context, final PatchBean bean, String downloadUrl) {
if (mLocalPreferencesHelper == null) {
mLocalPreferencesHelper = new LocalPreferencesHelper(context, SPConst.SP_NAME);
}
Uri downloadUri = Uri.parse(downloadUrl);
Uri destinationUri = Uri.parse(Environment.getExternalStorageDirectory()
.getAbsolutePath() + bean.url);
//下载方法
DownloadRequest downloadRequest = new DownloadRequest(downloadUri)
.setDestinationURI(destinationUri)
.setPriority(DownloadRequest.Priority.HIGH)
.setDownloadListener(new DownloadStatusListener() {
public void onDownloadComplete(int id) {
try {
String patchFileString = Environment.getExternalStorageDirectory()
.getAbsolutePath() + bean.url;
//修补补丁
BaseApplication.mPatchManager.addPatch(patchFileString);
File f = new File(patchFileString);
if (f.exists()) {
boolean result = new File(patchFileString).delete();
}
} catch (IOException e) {
} catch (Throwable throwable) {
}
}
public void onDownloadFailed(int id, int errorCode, String errorMessage) {
}
public void onProgress(int id, long totalBytes, int progress) {
}
});
//多线程下载
mDownloadManager = new ThinDownloadManager(THREAD_COUNT);
mDownloadManager.add(downloadRequest);
}
//比对远程服务器应用和补丁版本和本地的区别,若版本相同,但是补丁不一样,那么就下载新的补丁
public void comparePath(Context context, PatchBean RemoteBean) throws Exception {
String pathInfo = mLocalPreferencesHelper.getString(SPConst.PATH_INFO);
final PatchBean localBean = GsonUtils.getInstance().parseIfNull(PatchBean.class, pathInfo);
if (BaseApplication.VERSION_NAME.equals(RemoteBean.app_v)) {
if (localBean == null && !TextUtils.isEmpty(RemoteBean.path_v)
|| localBean.app_v.equals(RemoteBean.app_v) &&
!localBean.path_v.equals(RemoteBean.path_v)) {
downloadAndLoad(context, RemoteBean,
SPConst.URL_PREFIX + RemoteBean.url);
String json = GsonUtils.getInstance().parse(RemoteBean);
mLocalPreferencesHelper.saveOrUpdate(SPConst.PATH_INFO, json);
}
}
}
//释放线程
public void release() {
if (mDownloadManager != null) {
mDownloadManager.release();
}
}
7:在MainActivity中进行友盟补丁检测和极光推送的自定义消息的处理
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerMessageReceiver();
initUmeng();
getUmengParamAndFix();
}
//获取友盟后台定义键对应的值并下载补丁后修补
private void getUmengParamAndFix() {
//获取友盟在线参数对应key的values
String pathInfo = OnlineConfigAgent.getInstance().getConfigParams(this, UMENG_ONLINE_PARAM);
if (!TextUtils.isEmpty(pathInfo)) {
PatchBean onLineBean = GsonUtils.getInstance().parseIfNull(PatchBean.class, pathInfo);
try {
RepairBugUtil.getInstance().comparePath(this, onLineBean);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//初始化友盟
private void initUmeng() {
OnlineConfigAgent.getInstance().updateOnlineConfig(this);
OnlineConfigAgent.getInstance().setDebugMode(true);
}
//注销Jpush广播
protected void onDestroy() {
unregisterReceiver(mMessageReceiver);
RepairBugUtil.getInstance().release();
super.onDestroy();
}
private WeakHandler mHandler = new WeakHandler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == MSG_WHAT_DOWNLOAD) {
String message = (String) msg.obj;
if (TextUtils.isEmpty(message)) return false;
try {
//处理Jpush推送消息,下载补丁完成修补
PatchBean bean = GsonUtils.getInstance().parse(PatchBean.class, message);
RepairBugUtil.getInstance().comparePath(MainActivity.this, bean);
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
});
private MessageReceiver mMessageReceiver;
public static final String MESSAGE_RECEIVED_ACTION = "com.aile.andfix.MESSAGE_RECEIVED_ACTION";
public static final String KEY_MESSAGE = "message";
//注册Jpush广播
public void registerMessageReceiver() {
mMessageReceiver = new MessageReceiver();
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(MESSAGE_RECEIVED_ACTION);
registerReceiver(mMessageReceiver, filter);
}
//创建Jpush广播处理器,接收Jpush推送消息内容
public class MessageReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) {
String message = intent.getStringExtra(KEY_MESSAGE);
Message msg = new Message();
msg.what = MSG_WHAT_DOWNLOAD;
msg.obj = message;
mHandler.sendMessage(msg);
}
}
}
8:关于极光Jpush广播的的创建及注册AndroidManifest.xml相关问题和友盟统计后台设置可以自行进入相关官网学习开发文档
好了,结束.