編輯:Android編程入門
一直關注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、極光的初始化)
在程序的入口類進行友盟補丁的檢測:
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 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加載補丁包的方法:
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); }
判斷是否有補丁包需要下載的方法:
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
我們常常會用到上傳頭像,或者發帖子的時候選擇本地圖片上傳的功能.這個很常見今天因為app的需求我研究了下.現在分享下.其實不論是通過拍照還是從相冊選取都會用到Intent
NDK的發布,使“Java+C”的開發方式終於轉正,成為官方支持的開發方式。NDK將是Android平台支持C開發的開端,今天我們開始ndk的學習
1.推送方式基礎知識: 在移動互聯網時代以前的手機,如果有事情發生需要通知用戶,則會有一個窗口彈出,將告訴用戶正在發生什麼事情。可能是未接電話的提示,日歷的
例子是從《Android系統源代碼情景分析》第二章抄過來的,在學習的過程中還是遇到了不少的問題。個人體會:在學習第二章之前應該把《Linux設備驅動程序》這本書至少前四章