Android之DownloadManager实现软件更新并自动安装

为之于未有,治之于未乱。

在Android开发中,软件更新可以说是必不可少的,而隔壁的iOS喝着咖啡,无情的嘲笑着这个他们不用再实现的功能,因为苹果会无情的拒绝所有包含软件更新,哪怕是版本检测的APP。作为苦逼的Android攻城狮,我们还是老老实实地研究一下如何实现软件更新吧。

一、软件升级的实现思路

  • 下载APK

    • 应用内下载文件
      应用内实现文件下载,如果退出程序,下载将结束。
    • Service下载文件
      利用Service实现文件后台下载,不受程序是否退出的影响。
  • 安装APK

    • 手动安装
    • 自动安装

对于用户而言,后台下载并自动安装必然是一个好的使用体验。使用Service去实现文件下载的话,还需要我们自己维护网络请求,这可是非常麻烦又头痛的事情,有没有一个更方便快捷的方式呢?下面将介绍一种简单粗暴的实现方法!

二、DownloadManager实现文件下载

  • 使用DownloadManager实现APK下载
    通过DownloadManager来实现文件下载,我们就可以完全不用考虑网络请求、异步问题了,系统帮我们处理了所有事情,你可以继续操作App,即便是退出程序也不会影响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("更新失败");
       }

   }

三、自动安装

这里的自动安装是指下载完成后,自动弹出安装界面,而不是静默安装APK。同时这里不得不提的一个大坑就是Android6.0这个分水岭,6.0以后的实现方式有所不同。

  • 自定义Receiver接收系统广播,实现软件自动安装。
    Android6.0之前的版本我们可以通过Intent的方式来实现,但是我们会发现6.0之后的手机上,下载完成后没有任何效果,解决办法就是通过打开文件的形式实现自动安装。
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);
            }

        }
    }

    /**
     * 通过downLoadId查询下载的apk,解决6.0以后安装的问题
     * @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中注册广播
<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>
  • 还未实现功能:安装成功后删除apk文件。

大功告成。

小插曲:当我把文件名称"juyoubang.apk"写成静态常量来引用的时候,下载并安装时,会提示文件已损坏,无法安装,这让我百思不得其解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,568评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • 公司开发时候,应该最常用的就是APP升级功能,倒不是说的是热修复等技术,而是普通的检测到服务器版本比本地手机版本高...
    青蛙要fly阅读 4,514评论 12 86
  • DownloadManager实现下载、安装更新app功能 一款app必不可少的功能,就是自动检测版本实现下载、安...
    一点愁阅读 1,602评论 0 5
  • 有一朵黑玫瑰 它是爱人心头的珍珠 我把玫瑰给了你 把自己变成了一朵黑 死在爱情的泥土里 做了一次风流的鬼 玫瑰已不...
    栗春初阅读 298评论 0 3