為之于未有,治之于未亂。
在Android開發(fā)中,軟件更新可以說(shuō)是必不可少的,而隔壁的iOS喝著咖啡,無(wú)情的嘲笑著這個(gè)他們不用再實(shí)現(xiàn)的功能,因?yàn)樘O果會(huì)無(wú)情的拒絕所有包含軟件更新,哪怕是版本檢測(cè)的APP。作為苦逼的Android攻城獅,我們還是老老實(shí)實(shí)地研究一下如何實(shí)現(xiàn)軟件更新吧。
一、軟件升級(jí)的實(shí)現(xiàn)思路
-
下載APK
- 應(yīng)用內(nèi)下載文件
應(yīng)用內(nèi)實(shí)現(xiàn)文件下載,如果退出程序,下載將結(jié)束。 - Service下載文件
利用Service實(shí)現(xiàn)文件后臺(tái)下載,不受程序是否退出的影響。
- 應(yīng)用內(nèi)下載文件
-
安裝APK
- 手動(dòng)安裝
- 自動(dòng)安裝
對(duì)于用戶而言,后臺(tái)下載并自動(dòng)安裝必然是一個(gè)好的使用體驗(yàn)。使用Service去實(shí)現(xiàn)文件下載的話,還需要我們自己維護(hù)網(wǎng)絡(luò)請(qǐng)求,這可是非常麻煩又頭痛的事情,有沒有一個(gè)更方便快捷的方式呢?下面將介紹一種簡(jiǎn)單粗暴的實(shí)現(xiàn)方法!
二、DownloadManager實(shí)現(xiàn)文件下載
- 使用DownloadManager實(shí)現(xiàn)APK下載
通過(guò)DownloadManager來(lái)實(shí)現(xiàn)文件下載,我們就可以完全不用考慮網(wǎng)絡(luò)請(qǐng)求、異步問(wèn)題了,系統(tǒng)幫我們處理了所有事情,你可以繼續(xù)操作App,即便是退出程序也不會(huì)影響APK的下載。
/**
* 下載新版本
*
* @param context
* @param url
*/
public static void downLoadAPK(Context context, String url) {
if (TextUtils.isEmpty(url)) {
return;
}
try {
String serviceString = Context.DOWNLOAD_SERVICE;
final DownloadManager downloadManager = (DownloadManager) context.getSystemService(serviceString);
Uri uri = Uri.parse(url);
DownloadManager.Request request = new DownloadManager.Request(uri);
request.allowScanningByMediaScanner();
request.setVisibleInDownloadsUi(true);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setMimeType("application/vnd.android.package-archive");
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/juyoubang/","juyoubang.apk");
if (file.exists()){
file.delete();
}
request.setDestinationInExternalPublicDir(Environment.getExternalStorageDirectory().getAbsolutePath()+"/juyoubang/", "juyoubang.apk");
long refernece = downloadManager.enqueue(request);
SharePreHelper.getIns().setLongData("refernece", refernece);
} catch (Exception exception) {
ToastUtils.init(context).show("更新失敗");
}
}
三、自動(dòng)安裝
這里的自動(dòng)安裝是指下載完成后,自動(dòng)彈出安裝界面,而不是靜默安裝APK。同時(shí)這里不得不提的一個(gè)大坑就是Android6.0這個(gè)分水嶺,6.0以后的實(shí)現(xiàn)方式有所不同。
- 自定義Receiver接收系統(tǒng)廣播,實(shí)現(xiàn)軟件自動(dòng)安裝。
Android6.0之前的版本我們可以通過(guò)Intent的方式來(lái)實(shí)現(xiàn),但是我們會(huì)發(fā)現(xiàn)6.0之后的手機(jī)上,下載完成后沒有任何效果,解決辦法就是通過(guò)打開文件的形式實(shí)現(xiàn)自動(dòng)安裝。
public class UpdataBroadcastReceiver extends BroadcastReceiver {
@SuppressLint("NewApi")
public void onReceive(Context context, Intent intent) {
long myDwonloadID = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
long refernece = SharePreHelper.getIns().getLongData("refernece", 0);
if (refernece != myDwonloadID) {
return;
}
DownloadManager dManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Uri downloadFileUri = dManager.getUriForDownloadedFile(myDwonloadID);
installAPK(context,downloadFileUri);
}
private void installAPK(Context context,Uri apk ) {
if (Build.VERSION.SDK_INT < 23) {
Intent intents = new Intent();
intents.setAction("android.intent.action.VIEW");
intents.addCategory("android.intent.category.DEFAULT");
intents.setType("application/vnd.android.package-archive");
intents.setData(apk);
intents.setDataAndType(apk, "application/vnd.android.package-archive");
intents.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intents);
} else {
File file = queryDownloadedApk(context);
if (file.exists()) {
openFile(file, context);
}
}
}
/**
* 通過(guò)downLoadId查詢下載的apk,解決6.0以后安裝的問(wèn)題
* @param context
* @return
*/
public static File queryDownloadedApk(Context context) {
File targetApkFile = null;
DownloadManager downloader = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
long downloadId = SharePreHelper.getIns().getLongData("refernece", -1);
if (downloadId != -1) {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
query.setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL);
Cursor cur = downloader.query(query);
if (cur != null) {
if (cur.moveToFirst()) {
String uriString = cur.getString(cur.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (!TextUtils.isEmpty(uriString)) {
targetApkFile = new File(Uri.parse(uriString).getPath());
}
}
cur.close();
}
}
return targetApkFile;
}
private void openFile(File file, Context context) {
Intent intent = new Intent();
intent.addFlags(268435456);
intent.setAction("android.intent.action.VIEW");
String type = getMIMEType(file);
intent.setDataAndType(Uri.fromFile(file), type);
try {
context.startActivity(intent);
} catch (Exception var5) {
var5.printStackTrace();
Toast.makeText(context, "沒有找到打開此類文件的程序", Toast.LENGTH_SHORT).show();
}
}
private String getMIMEType(File var0) {
String var1 = "";
String var2 = var0.getName();
String var3 = var2.substring(var2.lastIndexOf(".") + 1, var2.length()).toLowerCase();
var1 = MimeTypeMap.getSingleton().getMimeTypeFromExtension(var3);
return var1;
}
}
- 最后記得在AndroidManifest.xml中注冊(cè)廣播
<receiver android:name=".receiver.UpdataBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
<!--<action android:name="android.intent.action.PACKAGE_INSTALL" />-->
</intent-filter>
</receiver>
- 還未實(shí)現(xiàn)功能:安裝成功后刪除apk文件。
大功告成。
小插曲:當(dāng)我把文件名稱"juyoubang.apk"寫成靜態(tài)常量來(lái)引用的時(shí)候,下載并安裝時(shí),會(huì)提示文件已損壞,無(wú)法安裝,這讓我百思不得其解。