一直关注App的热修复的技术发展,之前做的应用也没用使用到什么热修复开源框架。在App的热修复框架没有流行之前,做的应用上线后发现一个小小的Bug,就要马上发一个新的版本。我亲身经历过一周发两个版本,真的折腾用户的节奏~~所以,要开始考虑引入热修复。下面记录使用开源框架阿里巴巴的AndFix过程。
这里说的不是热修复怎么实现修bug的原理,这里说的是怎么使用AndFix。如果你想了解更多的andFix实现原理,你可以参考下面的文章:
在gradle文件中增加相应的依赖。这里我使用thindownlaodmanager来下载补丁,使用极光推送来推送自定义消息下载补丁通知,使用友盟在线参数来获取补丁包的信息。也许你会问为了修复一个补丁而增加这么多的依赖,值得吗?我认为还可以吧,因为我的项目一般会使用到这些。
AndFix的引入是: compile ‘com.alipay.euler:andfix:0.3.1@aar‘
导入AndFix的so库文件以及极光推送的so库文件;
极光推送集成参考文档:http://docs.jpush.io/client/android_sdk/
注意:导入AndFix的so文件时,可以先阅读这下个:https://github.com/zhonghanwen/AndFix-Ndk-Build-ADT
配置友盟在线参数的参数以及推光推送自定义的内容
友盟在线参数
极光推送自定义消息(自定义消息有长度限制,所以补丁的下载url写成拼接形式:站点+下载资源名称)
定义相对应的Bean
在启动的自定义Application类进行初始化工作(AndFix、极光的初始化)
在程序的入口类进行友盟补丁的检测:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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(); } } } |
再加上推送推送的自定义内容的处理:(当推送消息过来的时候应用处于运行状态的时候,程序会处理消息进行下载补丁包)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | 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 { PatchBean bean = GsonUtils.getInstance().parse(PatchBean. class , message); RepairBugUtil.getInstance().comparePath(MainActivity. this , bean); } catch (Exception e) { e.printStackTrace(); } } return false ; } }); //for receive customer msg from jpush server private MessageReceiver mMessageReceiver; public static final String MESSAGE_RECEIVED_ACTION = "com.zhw.andfix.MESSAGE_RECEIVED_ACTION" ; public static final String KEY_MESSAGE = "message" ; public void registerMessageReceiver() { mMessageReceiver = new MessageReceiver(); IntentFilter filter = new IntentFilter(); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); filter.addAction(MESSAGE_RECEIVED_ACTION); registerReceiver(mMessageReceiver, filter); } public class MessageReceiver extends BroadcastReceiver { @Override 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); } } } |
补丁包的生成
apkpatch -m <apatch_path...> -o <output> -k <keystore> -p <***> -a <alias> -e <***>
自己测试一下成不成啦~
通过ThinDownloadManager下载补丁包,下载成功后使用AndFix加载补丁包的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | 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() { @Override public void onDownloadComplete( int id) { // add patch at runtime try { // .apatch file path String patchFileString = Environment.getExternalStorageDirectory() .getAbsolutePath() + bean.url; BaseApplication.mPatchManager.addPatch(patchFileString); Log.d(TAG, "apatch:" + patchFileString + " added." ); //复制且加载补丁成功后,删除下载的补丁 File f = new File(patchFileString); if (f.exists()) { boolean result = new File(patchFileString).delete(); if (!result) Log.e(TAG, patchFileString + " delete fail" ); } // mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, false); } catch (IOException e) { Log.e(TAG, "" , e); } catch (Throwable throwable) { } } @Override public void onDownloadFailed( int id, int errorCode, String errorMessage) { //下载失败的时候,标注标记位,等下次重新打开应用的时候重新下载 // mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, true); Log.e(TAG, "onDownloadFailed" ); } @Override public void onProgress( int id, long totalBytes, int progress) { Log.e(TAG, "progress:" + progress); } }); mDownloadManager = new ThinDownloadManager(THREAD_COUNT); mDownloadManager.add(downloadRequest); } |
判断是否有补丁包需要下载的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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)) { //远程的应用版本跟本地保存的应用版本一样,但补丁不一样,则需要下载重新 /** *第一种情况:当本地记录的Bean为空的时候(刚安装的时候可能为空)并且远程的Bean的path_v不为空的时候需要下载补丁。 * 第二种情况:当本地记录的path_v和远程Bean的path_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); } /*else { mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, false); }*/ } } |
项目GitHub地址:https://github.com/zhonghanwen/AndFix-Bad-Practices
http://www.cnblogs.com/common1140/p/5287040.html
原文:http://www.cnblogs.com/wsfjlagr/p/5293953.html