Android之DownloadManager實(shí)現(xiàn)軟件更新并自動(dòng)安裝

為之于未有,治之于未亂。

在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)下載,不受程序是否退出的影響。
  • 安裝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ú)法安裝,這讓我百思不得其解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,828評(píng)論 25 709
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,525評(píng)論 19 139
  • 公司開發(fā)時(shí)候,應(yīng)該最常用的就是APP升級(jí)功能,倒不是說(shuō)的是熱修復(fù)等技術(shù),而是普通的檢測(cè)到服務(wù)器版本比本地手機(jī)版本高...
    青蛙要fly閱讀 4,750評(píng)論 12 86
  • DownloadManager實(shí)現(xiàn)下載、安裝更新app功能 一款app必不可少的功能,就是自動(dòng)檢測(cè)版本實(shí)現(xiàn)下載、安...
    一點(diǎn)愁閱讀 1,859評(píng)論 0 5
  • 有一朵黑玫瑰 它是愛人心頭的珍珠 我把玫瑰給了你 把自己變成了一朵黑 死在愛情的泥土里 做了一次風(fēng)流的鬼 玫瑰已不...
    栗春初閱讀 344評(píng)論 0 3

友情鏈接更多精彩內(nèi)容