導論:
上篇博客提到增量更新,這篇就要說說熱修復,那麼到底什麼區別呢?通俗的理解:
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相關問題和友盟統計後臺設置可以自行進入相關官網學習開發文檔
好了,結束.