android調用系統下載任務(DownloadManager)實現軟件版本更新

        做移動軟件開發,必然要涉及軟件版本升級。版本檢測什麼的我就不多說了,網上一大堆,這裏主要是在獲取新版本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的下載及自動安裝操作完成。如果對你有幫助,點個贊再走吧。(手動滑稽)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章