上一篇文章中我們講解了Android app中的輪詢操作,講解的內容主要包括:我們在App中使用輪詢操作的情景,作用以及實現方式等。一般而言我們使用輪詢操作都是通過定時任務的形式請求服務器并更新用戶界面,輪詢操作都有一定的使用生命周期,即在一定的頁面中啟動輪詢操作,然后在特定的情況下關閉輪詢操作,這點需要我們尤為注意,我們還介紹了使用Timer和Handler實現輪詢操作的實例,更多關于App中輪詢操作的信息,可參考我的:[Android產品研發(十三)–>App輪詢操作](http://blog.csdn.net/qq_23547831/article/details/51719389)
本文將講解app的升級與更新。一般而言用戶使用App的時候升級提醒有兩種方式獲得:
* 一種是通過App Store獲取
* 一種是打開應用之后提醒用戶更新升級
而更新操作一般是在用戶點擊了升級按鈕之后開始執行的,這里的升級操作也分為兩種形式:
* 一般升級
* 強制升級
**app升級操作**:
* App Store升級
在App Store中升級需要為App Store上傳新版App,我們在新版本完成之后都會上傳到App Store中,不同的應用市場審核的時間不同,一般除了第一次上傳時間較長之外,其余的審核都是挺快的,一般不會超過半天(不排除例外情況奧),在審核完成之后就相當于完成了這個應用市場的發布了,也就是發布上線了。這時候如果用戶安裝了這個應用市場,那么就能看到我們的App有新版本的升級提醒了。
* 應用內升級
除了可以在應用市場升級,我們還可以在應用內升級,在應用內升級主要是通過調用服務器端接口獲取應用的升級信息,然后通過獲取的服務器升級應用信息與本地的App版本比對,若服務器下發的最新的App版本高于本地的版本號,則說明有新版本發布,那么我們就可以執行更新操作了,否則忽略掉即可。
應用內升級其實已經有好多第三方的SDK了,常見的友盟,百度App開發工具包都已經集成了升級的功能,部分SDK廠商還提供增量更新的功能。增量更新的內容不是我們這里的討論重點,想了解更多增量更新的內容可參考:[淺談Android增量升級](http://blog.csdn.net/hmg25/article/details/8100896)
這里我們先簡單介紹一下友盟的App升級功能,友盟其實已經有了App升級的API,我們只需要簡單的調用即可。
* 友盟更新接口API
~~~
/**
* 請求友盟更新API,判斷是否彈出更新彈窗
*/
public static void updateVersion(final Activity mContext, final MainActivity.UpdateCallback updateCallback, final boolean isShow) {
UmengUpdateAgent.setUpdateListener(new UmengUpdateListener() {
@Override
public void onUpdateReturned(int updateStatus, UpdateResponse updateInfo) {
switch (updateStatus) {
//判斷是否有新版本需要更新
case UpdateStatus.Yes: // has update
try {
//在線讀取更新參數
String value = MobclickAgent.getConfigParams(mContext, "FORCE_UPDATE_MIXVERSION");
if (value != null && !value.trim().equals("")) {
int versionCode = Config.changeVersionNameToCode(value);
if (versionCode != 0) {
String localVersionName = getVersionName(mContext);
int localVersionCode = Config.changeVersionNameToCode(localVersionName);
//判斷當前版本號于友盟中的最低版本號,若當前版本號小于最低版本號,則強制更新,否則非強制更新
if (localVersionCode <= versionCode) {
// 彈窗更新彈窗
updateCallback.onUpdateSuccess(updateInfo);
} else {
UmengUpdateAgent.setUpdateAutoPopup(true);
UmengUpdateAgent.showUpdateDialog(mContext, updateInfo);
}
} else {
UmengUpdateAgent.setUpdateAutoPopup(true);
UmengUpdateAgent.showUpdateDialog(mContext, updateInfo);
}
} else {
UmengUpdateAgent.setUpdateAutoPopup(true);
UmengUpdateAgent.showUpdateDialog(mContext, updateInfo);
}
} catch (Exception e) {
e.printStackTrace();
}
break;
case UpdateStatus.No: // has no update
if (isShow) {
Config.showToast(mContext, "您當前使用的友友用車已是最新版本");
}
break;
}
}
});
UmengUpdateAgent.setUpdateAutoPopup(false);
UmengUpdateAgent.forceUpdate(mContext);
UmengUpdateAgent.setChannel(ChannelUtil.getChannel(mContext));
}
~~~
以上是友盟的升級API,在調用之前需要先繼承友盟的SDK,這樣經過調用之后我們就可以通過友盟實現更新接口的提示功能了,默認的友盟提供了靜默安裝,更新提示彈窗,強制更新等幾種,可以根據自身App的需求來確定更新的方式。
如果不喜歡使用第三方的更新方式,我們也可以通過調用服務器接口的方式實現自己的更新彈窗提示,主要的邏輯也是通過判斷服務器下發的最新App版本號與本地版本號對比,若服務器端的App版本號大于本地的App版本號,則說明當前App不是最新的版本,需要升級,這里我們簡單看一下友友用車中自定義的更新接口實現:
~~~
/**
* 檢測App是否需要更新
*
* @param mContext
* @param isShow 若不需要更新是否需要彈出文案
*/
public static void queryAppBaseVersionInfo(final Activity mContext, final boolean isOneUpdate, final boolean isShow) {
try {
// 若當前網絡異常,則直接return
if (!Config.isNetworkConnected(mContext)) {
// 關閉進度條
dismissProgress(isShow);
return;
}
// 控制變量,App更新接口進程生命周期中只會調用一次
if (isQueryAppUpdated && isOneUpdate) {
return;
}
L.i("開始調用請求是否需要版本更新的接口....");
ExtInterface.QueryAppBaseVersionInfoNL.Request.Builder request = ExtInterface.QueryAppBaseVersionInfoNL.Request.newBuilder();
request.setClientChannel(CHANNEL_Android);
// 查詢最新的版本信息,不需要傳入版本號
// request.setVersionCode(VersionUtils.getVersionName(mContext));
NetworkTask task = new NetworkTask(Cmd.CmdCode.QueryAppBaseVersionInfo_VALUE);
task.setBusiData(request.build().toByteArray());
NetworkUtils.executeNetwork(task, new HttpResponse.NetWorkResponse<UUResponseData>() {
@Override
public void onSuccessResponse(UUResponseData responseData) {
if (responseData.getRet() == 0) {
try {
isQueryAppUpdated = true;
ExtInterface.QueryAppBaseVersionInfoNL.Response response = ExtInterface.QueryAppBaseVersionInfoNL.Response.parseFrom(responseData.getBusiData());
if (response.getRet() == 0) {
L.i("請求檢測App是否更新接口成功,開始解析返回結果");
// 解析檢測結果
parserUpdateResule(mContext, response, isShow);
} else {
if (isShow) {
showDefaultNetworkSnackBar(mContext);
}
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
if (isShow) {
showDefaultNetworkSnackBar(mContext);
}
}
}
}
@Override
public void onError(VolleyError errorResponse) {
L.e("請求檢測更新接口失敗....");
if (isShow) {
showDefaultNetworkSnackBar(mContext);
}
}
@Override
public void networkFinish() {
L.i("請求檢測更新接口完成....");
// 關閉進度條
dismissProgress(isShow);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
~~~
該接口只會在App打開時調用一次,判斷App是否需要更新,然后在請求服務器成功之后,會解析請求結果,我們繼續看一下我們的解析邏輯:
~~~
/**
* 解析更新檢查結果
*
* @param response
*/
private static void parserUpdateResule(Activity mContext, ExtInterface.QueryAppBaseVersionInfoNL.Response response, boolean isShow) {
if (mContext == null) {
return;
}
// 判斷是否需要更新
ExtInterface.AppBaseVersionInfo appBaseVersionInfo = response.getAppBaseVersionInfo();
// 若當前更新是否有效
if (appBaseVersionInfo.getIsDel() == ENEFFECT) {
return;
}
String updateVersionCode = appBaseVersionInfo.getVersionCode();
int updateCode = changeVersionNameToCode(updateVersionCode);
int localCode = changeVersionNameToCode(VersionUtils.getVersionName(mContext));
// 本地應用版本號小于更新的應用版本號,則需要更新
L.i("本地版本號:" + localCode + " " + VersionUtils.getVersionName(mContext) + " 遠程版本號:" + updateCode
+ " " + updateVersionCode);
if (localCode < updateCode) {
// 顯示更新文案
L.i("開始顯示更新彈窗...");
showUpdateDialog(mContext, appBaseVersionInfo);
}
// 不需要更新
else {
if (isShow) {
Config.showToast(mContext, mContext.getResources().getString(R.string.about_new));
}
}
}
~~~
解析更新接口信息的時候,會判斷App的更新操作是普通更新還是強制更新,若是強制更新的話,則沒有取消按鈕,并且更新彈窗不可關閉。若是普通的更新的話則有暫不更新按鈕,點擊暫不更新更新彈窗會取消,但是當下次打開App的時候,彈窗提醒還是會彈窗。
普通更新包含暫不更新和立即更新兩個按鈕操作:

強制更新只有立即更新按鈕,彈窗不可取消:

**app更新操作**:
app的更新操作就是下載App并安裝了,下面我們還是分兩部分看,應用市場的更新與應用內更新
- App store更新App
在應用市場中更新App很簡單就是執行簡單的下載操作,然后順著App的提醒,一步步安裝即可,這里沒有什么需要注意的地方。
- 應用內更新
應用內更新操作主要是當用戶點擊了更新按鈕之后執行的,下載,安裝等邏輯,下面我們看一下友友用車應用內更新的實踐。
應用內更新主要包含了:普通更新和強制更新兩種,其中普通更新彈窗可以選擇更新也可以選擇忽略,而強制更新只能選擇更新,并且更新彈窗不可取消。
下面的代碼是執行下載操作的核心邏輯:
~~~
/**
* 開始執行下載動作
*/
private static void doDownLoad(final Activity mContext, String downloadUrl, final String actionButtonMsg, final boolean isFocusUpdate) {
// 強制更新
if (isFocusUpdate) {
DownLoadDialog.updateRela.setVisibility(View.VISIBLE);
DownLoadDialog.progressBar.setProgress(0);
DownLoadDialog.progressBar.start();
DownLoadDialog.updatePercent.setText("0%");
DownLoadDialog.materialDialog.getPositiveButton().setEnabled(false);
DownLoadDialog.materialDialog.getPositiveButton().setText("下載中");
}
Config.showToast(mContext, "開始下載安裝包.......");
// 刪除下載的apk文件
doDeleteDownApk(mContext);
L.i("安裝包下載地址:" + downloadUrl);
DownloadManager.getInstance().cancelAll();
DownloadManager.downloadId = DownloadManager.getInstance().add(DownloadManager.getDownLoadRequest(mContext, downloadUrl, new DownloadStatusListenerV1() {
@Override
public void onDownloadComplete(DownloadRequest downloadRequest) {
L.i("onDownloadComplete_____...");
// 設置按鈕是否可點擊
showPositiveText(false, actionButtonMsg);
if (isFocusUpdate) {
// 更新進度條顯示
DownLoadDialog.updatePercent.setText("100%");
DownLoadDialog.progressBar.stop();
} else {
String title = "正在下載友友用車...";
String content = "下載成功";
DownloadNotification.showNotification(mContext, title, content, DownloadNotification.notofyId);
// 關閉通知欄消息
UUApp.notificationManager.cancel(DownloadNotification.notofyId);
}
// 下載完成,執行安裝邏輯
doInstallApk(mContext);
// 退出App
UUApp.getInstance().exit();
}
@Override
public void onDownloadFailed(DownloadRequest downloadRequest, int errorCode, String errorMessage) {
L.i("onDownloadFiled______...");
L.i("errorMessage:" + errorMessage);
// 設置按鈕是否可點擊
showPositiveText(false, actionButtonMsg);
if (isFocusUpdate) {
// DownLoadDialog.progressBar.stop();
DownLoadDialog.updatePercent.setText("更新失敗");
} else {
String title = "正在下載友友用車...";
String content = "下載失敗";
DownloadNotification.showNotification(mContext, title, content, DownloadNotification.notofyId);
}
}
@Override
public void onProgress(DownloadRequest downloadRequest, long totalBytes, long downloadedBytes, int progress) {
if (lastProgress != progress) {
lastProgress = progress;
L.i("onProgress_____progress:" + progress + " totalBytes:" + totalBytes + " downloadedBytes:" + downloadedBytes);
// 設置按鈕是否可點擊
showPositiveText(true, actionButtonMsg);
// 強制更新則更新進度條
if (isFocusUpdate) {
String content = downloadedBytes * 100 / totalBytes + "%";
float result = progress / (float)100.00;
DownLoadDialog.progressBar.setProgress(result);
DownLoadDialog.updatePercent.setText(content);
} else {
String title = "正在下載友友用車...";
String content = downloadedBytes * 100 / totalBytes + "%";
DownloadNotification.showNotification(mContext, title, content, DownloadNotification.notofyId);
}
}
}
}));
}
~~~
這里的下載操作包含了三個回調方法:
* onDownloadComplete()
* onDownloadFailed()
* onProgress()
其中onDownlaodComplete方法在下載完成時回調,onDownloadFailed方法在下載失敗是回調,而onProgress方法則用于刷新下載進程,我們在onProcess方法中更新通知欄下載進度,具體我們可以看一下更新通知欄消息的方法:
~~~
/**
* 更新通知欄顯示
* @param title
* @param content
* @param notifyId
*/
public static void showNotification(Activity mContext, String title, String content, int notifyId) {
NotificationCompat.Builder mNotifyBuilder = new NotificationCompat.Builder(mContext)
.setSmallIcon(R.mipmap.icon)
.setContentTitle(title)
.setContentText(content)
.setSmallIcon(Android.R.drawable.stat_sys_download);
Notification notification = mNotifyBuilder.build();
// notification.flags = Notification.FLAG_NO_CLEAR;
UUApp.notificationManager.notify(notifyId, notification);
}
~~~
而在onDownloadFailed方法中,執行的代碼邏輯是提示用戶下載失敗,
而在onDownloadComplete方法中,執行安裝下載apk文件的操作,我們可以繼續看一下我們是如何執行安裝邏輯的。
~~~
/**
* 執行安裝apk文件
*/
private static void doInstallApk(Activity mContext) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(Uri.fromFile(new File(DownloadManager.getApkPath(mContext))),
"application/vnd.Android.package-archive");
mContext.startActivity(intent);
}
~~~
這段代碼會調用Android的安裝apk程序,這樣我們就執行了下載文件的安裝操作,不同的手機安裝程序及展示界面略有不同。
**總結**:
* App升級操作分為兩種,在應用市場提示升級和在應用內提示升級,而在應用內提示升級可以繼承第三方升級API(如:友盟),也可以自己實現;
* 應用升級的提示主要邏輯是根據服務器端的APK版本號與本地的應用版本號對比,若服務器端的應用版本號高于本地版本號,則說明應用需要升級;
* 應用升級可以分為普通升級和強制升級兩種,一般不太建議使用強制升級(用戶體驗很差),除非是一些嚴重的線上bug;
* App的更新操作包含下載與安裝兩部分,下載操作時可以選擇繼承第三方服務,也可以自己實現。
另外對產品研發技術,技巧,實踐方面感興趣的同學可以參考我的:
[android產品研發(一)-->實用開發規范 ](http://blog.csdn.net/qq_23547831/article/details/51534013)
[android產品研發(二)-->啟動頁優化 ](http://blog.csdn.net/qq_23547831/article/details/51541277)
[android產品研發(三)-->基類Activity ](http://blog.csdn.net/qq_23547831/article/details/51546974)
[android產品研發(四)-->減小Apk大小](http://blog.csdn.net/qq_23547831/article/details/51559066)
[android產品研發(五)-->多渠道打包](http://blog.csdn.net/qq_23547831/article/details/51569261)
[Android產品研發(六)–>Apk混淆](http://blog.csdn.net/qq_23547831/article/details/51581491)
[android產品研發(七)-->Apk熱修復](http://blog.csdn.net/qq_23547831/article/details/51587927)
[Android產品研發(八)–>App數據統計](http://blog.csdn.net/qq_23547831/article/details/51612429)
[Android產品研發(九)–>App網絡傳輸協議](http://blog.csdn.net/qq_23547831/article/details/51655330)
[Android產品研發(十)–>不使用靜態變量保存數據](http://blog.csdn.net/qq_23547831/article/details/51685310)
[Android產品研發(十一)–>應用內跳轉scheme協議](http://blog.csdn.net/qq_23547831/article/details/51685310)
[Android產品研發(十二)–>App長連接實現](http://blog.csdn.net/qq_23547831/article/details/51719389)
[Android產品研發(十三)–>App輪詢操作](http://blog.csdn.net/qq_23547831/article/details/51764773)
- 前言
- (一)–>實用開發規范
- (二)-->啟動頁優化
- (三)-->基類Activity
- (四)-->減小Apk大小
- (五)-->多渠道打包
- (六)-->Apk混淆
- (七)-->Apk熱修復
- (八)-->App數據統計
- (九)-->App網絡數據解析
- (十)-->盡量不使用靜態變量保存數據
- (十一)-->應用內跳轉Scheme協議
- (十二)-->App長連接實現
- (十三)-->App輪詢操作
- (十四)-->App升級與更新
- (十五)-->內存對象序列化
- (十六)-->開發者選項
- (十七)-->Hybrid開發
- (十八)-->webview問題集錦
- (十九)-->Android studio中的單元測試
- (二十)-->代碼Review
- (二十一)-->Android中的UI優化
- (二十二)-->Android實用調試技巧
- (二十三)-->Android中保存靜態秘鑰實踐
- (二十四)-->內存泄露場景與檢測
- (二十五)-->MVC/MVVM/MVP簡單理解