做移動軟件開發,必然要涉及軟件版本升級。版本檢測什麼的我就不多說了,網上一大堆,這裏主要是在獲取新版本APK地址後的下載操作。
第一步:判斷任務是否已經存在如果存在,先清除原任務
if (downloadId != 0) { //根據任務ID判斷是否存在相同的下載任務,如果有則清除
clearCurrentTask(mContext, downloadId);
}
downloadId = downLoadApk(mContext, url, describeStr);
第二步:開啓下載任務
public static long downLoadApk(Context context, String url, String describeStr) {
// 得到系統的下載管理
DownloadManager manager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
Uri uri = Uri.parse(url);
// 以下兩行代碼可以讓下載的apk文件被直接安裝而不用使用Fileprovider,系統7.0或者以上才啓動。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
StrictMode.VmPolicy.Builder localBuilder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(localBuilder.build());
}
DownloadManager.Request requestApk = new DownloadManager.Request(uri);
// 設置在什麼網絡下下載
requestApk.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
// 下載中和下載完後都顯示通知欄
requestApk.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
if (saveFile.exists()) { //判斷文件是否存在,存在的話先刪除
saveFile.delete();
}
requestApk.setDestinationUri(Uri.fromFile(saveFile));
// 表示允許MediaScanner掃描到這個文件,默認不允許。
requestApk.allowScanningByMediaScanner();
// 設置下載中通知欄的提示消息
requestApk.setTitle("XXXX更新下載");
// 設置設置下載中通知欄提示的介紹
requestApk.setDescription(describeStr);
// 7.0以上的系統適配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
requestApk.setRequiresDeviceIdle(false);
requestApk.setRequiresCharging(false);
}
// 啓動下載,該方法返回系統爲當前下載請求分配的一個唯一的ID
long downLoadId = manager.enqueue(requestApk);
return downLoadId;
}
開啓下載任務後返回了任務ID,這是任務的唯一標識。
第三步:下載完成後調用廣播,通知系統去執行安裝操作點擊任務欄時判斷任務是否下載完成
if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
DownloadApkUtils.installApk(context);
} else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {
// 如果還未完成下載,用戶點擊Notification ,跳轉到下載中心
Intent viewDownloadIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
viewDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(viewDownloadIntent);
}
第四部:檢測和安裝APK
public static void installApk(Context context) {
downloadId = 0;
Intent intent = new Intent(Intent.ACTION_VIEW);
try {
String[] command = {"chmod", "777", saveFile.getAbsolutePath()};
ProcessBuilder builder = new ProcessBuilder(command);
builder.start();
} catch (Exception ignored) {
ignored.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, "<Your Package Name>.fileprovider", saveFile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(saveFile), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
下面給出關鍵類的代碼:
(1)DownloadApkUtils:
public class DownloadApkUtils {
private static File saveFile;
private static long downloadId = 0;
public static void startDownload(Context mContext, String url, String describeStr) {
initFile();
if (downloadId != 0) { //根據任務ID判斷是否存在相同的下載任務,如果有則清除
clearCurrentTask(mContext, downloadId);
}
downloadId = downLoadApk(mContext, url, describeStr);
}
private static void initFile() {
if (saveFile == null)
saveFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), "XXXX.apk");
}
public static long downLoadApk(Context context, String url, String describeStr) {
// 得到系統的下載管理
DownloadManager manager = (DownloadManager) context.getSystemService(context.DOWNLOAD_SERVICE);
Uri uri = Uri.parse(url);
// 以下兩行代碼可以讓下載的apk文件被直接安裝而不用使用Fileprovider,系統7.0或者以上才啓動。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
StrictMode.VmPolicy.Builder localBuilder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(localBuilder.build());
}
DownloadManager.Request requestApk = new DownloadManager.Request(uri);
// 設置在什麼網絡下下載
requestApk.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
// 下載中和下載完後都顯示通知欄
requestApk.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
if (saveFile.exists()) { //判斷文件是否存在,存在的話先刪除
saveFile.delete();
}
requestApk.setDestinationUri(Uri.fromFile(saveFile));
// 表示允許MediaScanner掃描到這個文件,默認不允許。
requestApk.allowScanningByMediaScanner();
// 設置下載中通知欄的提示消息
requestApk.setTitle("XXXX更新下載");
// 設置設置下載中通知欄提示的介紹
requestApk.setDescription(describeStr);
// 7.0以上的系統適配
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
requestApk.setRequiresDeviceIdle(false);
requestApk.setRequiresCharging(false);
}
// 啓動下載,該方法返回系統爲當前下載請求分配的一個唯一的ID
long downLoadId = manager.enqueue(requestApk);
return downLoadId;
}
/**
* 下載前先移除前一個任務,防止重複下載
*
* @param downloadId
*/
public static void clearCurrentTask(Context mContext, long downloadId) {
DownloadManager dm = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
try {
dm.remove(downloadId);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
}
public static void installApk(Context context) {
downloadId = 0;
Intent intent = new Intent(Intent.ACTION_VIEW);
try {
String[] command = {"chmod", "777", saveFile.getAbsolutePath()};
ProcessBuilder builder = new ProcessBuilder(command);
builder.start();
} catch (Exception ignored) {
ignored.printStackTrace();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, "<Your Package Name>.fileprovider", saveFile);
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(saveFile), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
}
要想安裝要有權限,當然還要有讀寫權限以及網絡請求權限,這裏就不列出來了
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
(2)下載的廣播接收者
public class DownloadReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
DownloadApkUtils.installApk(context);
} else if (intent.getAction().equals(DownloadManager.ACTION_NOTIFICATION_CLICKED)) {
// 如果還未完成下載,用戶點擊Notification ,跳轉到下載中心
Intent viewDownloadIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
viewDownloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(viewDownloadIntent);
}
}
}
既然用到了廣播接收這,記得在配置文件中註冊;
<receiver android:name=".receiver.DownloadReceiver">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_SERVICE"/>
</intent-filter>
</receiver>
執行下載操作的Activity中註冊廣播
private boolean isRegisterReceiver = false;
/**
* 註冊下載成功的廣播監聽
*/
private void setReceiver() {
if (!isRegisterReceiver) {
DownloadReceiver receiver = new DownloadReceiver();
IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
this.registerReceiver(receiver, intentFilter);
isRegisterReceiver = true;
}
}
都知道android的適配是很煩人的,但是不得不適配。下載過程中涉及了文件的讀寫,那就必須要適配7.0 使用Provider。
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.retrofitdemo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
資源文件夾下創建xml文件夾,創建file_paths。內容如下
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
最後就是調用了,由於前面準備工作做得很好,這一步就非常簡單了,只要一行代碼
DownloadApkUtils.startDownload(MainActivity.this,"下載地址","下載任務描述");
到此版本更新時APK的下載及自動安裝操作完成。如果對你有幫助,點個贊再走吧。(手動滑稽)