Android版本升级下载apk文件,UI进度条显示,自动安装apk的三种方式(AsyncTask、Service和使用DownloadManager)

我们主要指的是下载一个文件,不考虑断点续传。

主要的三种方式AsyncTask、Service和使用DownloadManager

一、如果想要在后台下载任务的同时可以更新进度条UI----使用AsyncTask

asynctask效果gif

忽略安装需要密码这个细节,用oppo手机的应该知道,这个是只有oppo测试机会这样,别的手机就可以直接安装了。

要点:

  • 一个url下载链接,一个ProgressDialog用来显示进度,一个按钮(点击升级)按钮监听的方法为onUpdateClick()

  • 接着放入主要的代码

//关于进度显示
  private ProgressDialog progressDialog;
//相关属性
        progressDialog =new ProgressDialog(UpdateDialogActivity.this);
        progressDialog.setMessage("正在下载...");
        progressDialog.setIndeterminate(true);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setCancelable(true);
//升级下载按钮点击事件
 private void onUpdateClick() {
        // TODO: 2017/10/11 三种方式实现apk下载
       //第一种 asynctask
        //onProgressUpdate和onPreExecute是运行在UI线程中的,
        // 所以我们应该在这两个方法中更新progress。
        final DownloadTask downloadTask = new DownloadTask(UpdateDialogActivity.this);
        //execute 执行一个异步任务,通过这个方法触发异步任务的执行。这个方法要在主线程调用。
        downloadTask.execute(url);
        progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                downloadTask.cancel(true);
            }
        });
    }
 private  class DownloadTask extends AsyncTask<String,Integer,String> {
        private Context context;
        private PowerManager.WakeLock mWakeLock;
        public DownloadTask(Context context) {
            this.context = context;
        }
        //onPreExecute(),在execute(Params... params)方法被调用后立即执行,执行在ui线程,
        // 一般用来在执行后台任务前会UI做一些标记
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // take CPU lock to prevent CPU from going off if the user
            // presses the power button during download
            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                    getClass().getName());
            mWakeLock.acquire();
            progressDialog.show();
        }
        // doInBackground这个方法在onPreExecute()完成后立即执行,
        // 用于执行较为耗时的操作,
        // 此方法接受输入参数
        // 和返回计算结果(返回的计算结果将作为参数在任务完成是传递到onPostExecute(Result result)中),
        // 在执行过程中可以调用publishProgress(Progress... values)来更新进度信息
        //后台任务的代码块
        @Override
        protected String doInBackground(String... url) {
            InputStream input = null;
            OutputStream output = null;
            HttpURLConnection connection = null;
            try {
                URL urll=new URL(url[0]);
                Log.d("upgrade","url1:"+urll+"////url:"+url);
                connection = (HttpURLConnection) urll.openConnection();
                connection.connect();
                // expect HTTP 200 OK, so we don't mistakenly save error report
                // instead of the file
                if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                    return "Server returned HTTP " + connection.getResponseCode()
                            + " " + connection.getResponseMessage();
                }
                // this will be useful to display download percentage
                // might be -1: server did not report the length
                int fileLength = connection.getContentLength();
                // download the file
                input = connection.getInputStream();
                output = new FileOutputStream("/sdcard/new.apk");
                byte data[] = new byte[4096];
                long total = 0;
                int count;
                while ((count = input.read(data)) != -1) {
                    if (isCancelled()) {
                        input.close();
                        return null;
                    }
                    total += count;
                    // publishing the progress....
                    if (fileLength > 0) // only if total length is known
                        //在调用这个方法后,执行onProgressUpdate(Progress... values),
                        //运行在主线程,用来更新pregressbar
                        publishProgress((int) (total * 100 / fileLength));
                    output.write(data, 0, count);
                }
            } catch (Exception e) {
                return e.toString();
            } finally {
                try {
                    if (output != null)
                        output.close();
                    if (input != null)
                        input.close();
                } catch (IOException ignored) {
                }
                if (connection != null)
                    connection.disconnect();
            }
            return null;
        }
        //onProgressUpdate(Progress... values),
        // 执行在UI线程,在调用publishProgress(Progress... values)时,此方法被执行。
        @Override
        protected void onProgressUpdate(Integer... progress) {
            super.onProgressUpdate(progress);
            // if we get here, length is known, now set indeterminate to false
            progressDialog.setIndeterminate(false);
            progressDialog.setMax(100);
            progressDialog.setProgress(progress[0]);
        }

        //onPostExecute(Result result),
        // 执行在UI线程,当后台操作结束时,此方法将会被调用。
        @Override
        protected void onPostExecute(String result) {
            mWakeLock.release();
            progressDialog.dismiss();
            if (result != null)
                Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
            else
            {Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();}
//这里主要是做下载后自动安装的处理
            File file=new File("/sdcard/new.apk");
            Intent installIntent = new Intent(Intent.ACTION_VIEW);
            installIntent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(installIntent);
        }

    }

二、使用DownloadManager

每个Android App都会有版本更新的功能,而下载功能Google官方推荐使用 DownloadManager服务

使用最简单的一种

download.gif
 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
                request.setDescription("下载中");
                request.setTitle("我的下载");
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

                }
                request.allowScanningByMediaScanner();//设置可以被扫描到
                request.setVisibleInDownloadsUi(true);// 设置下载可见
                request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);//下载完成后通知栏任然可见
                request.setDestinationInExternalPublicDir(
                        Environment.DIRECTORY_DOWNLOADS, "my.apk");
                manager = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
               // manager.enqueue(request);
                long Id = manager.enqueue(request);
                //listener(Id);
                SharedPreferences sPreferences = getActivity().getSharedPreferences(
                        "downloadapk", 0);
                sPreferences.edit().putLong("apk",Id).commit();//保存此次下载ID
                Log.d("shengji", "开始下载任务:" + Id + " ...");

如果想同样实现下载完安装,要使用广播.当DownloadManager下载完成后会发出一个广播 android.intent.action.DOWNLOAD_COMPLETE,创建一个广播接收者,处理自动提示安装:

public class DownLoadBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        long completeId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        Log.d("shengji","下载完成后的ID:"+completeId);
        SharedPreferences sPreferences =context.getSharedPreferences(
                "downloadapk", 0);
        long Id = sPreferences.getLong("apk", 0);
        if (Id==completeId){
            DownloadManager  manager =
                    (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
            Intent installIntent=new Intent(Intent.ACTION_VIEW);
            Uri downloadFileUri = manager
                    .getUriForDownloadedFile(completeId);
            installIntent.setDataAndType(downloadFileUri,
                    "application/vnd.android.package-archive");
            installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(installIntent);
        }
    }
}
在AndroidManifet中进行注册
      <receiver android:name=".receiver.DownLoadBroadcastReceiver">
            <intent-filter android:priority="20" >
                <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
            </intent-filter>
        </receiver>
还要加权限:
   <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

虽说代码量很少,但是也确实会有问题遇到

问题1:No Activity found to handle Intent

解决:首先不要单独设置data和type, 要同时setDataAndType(data, "application/vnd.android.package-archive")。其次最多的可能是下载文件路径的问题,好好检查文件路径是否错误或是否不可读。最简单的方法就是把apk的路径固定死

问题2:权限问题,targetSdkVersion >=23需要获取权限才能自动安装

解决:

方法一:把build.gradle 文件中的targetSdkVersion < 23。这种方式也是最简单的。

方法二:动态的获取权限:代码如下

// getPersimmions();方法
 @TargetApi(23)
    private void getPersimmions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            ArrayList<String> permissions = new ArrayList<String>();
            /*
             * 读写权限和电话状态权限非必要权限(建议授予)只会申请一次,用户同意或者禁止,只会弹一次
             */
            // 读写权限
            if (addPermission(permissions, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                permissionInfo += "Manifest.permission.WRITE_EXTERNAL_STORAGE Deny \n";
            }

            if (permissions.size() > 0) {
                requestPermissions(permissions.toArray(new String[permissions.size()]), SDK_PERMISSION_REQUEST);
            }
        }
    }
    @TargetApi(23)
    private boolean addPermission(ArrayList<String> permissionsList, String permission) {
        if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { // 如果应用没有获得对应权限,则添加到列表中,准备批量申请
            if (shouldShowRequestPermissionRationale(permission)){
                return true;
            }else{
                permissionsList.add(permission);
                return false;
            }

        }else{
            return true;
        }
    }

    @TargetApi(23)
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        // TODO Auto-generated method stub
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    }

三、使用service(IntentService和ResultReceiver)

service.gif

IntentService继承自service,在IntentService中我们开启一个线程执行下载任务(service和你的app其实是在一个线程中,因此不想阻塞主线程的话必须开启新的线程。

//在这里根据url进行下载文件,并通过receiver把需要更新的progressbar的值放在bundle传过去
public class DownloadService extends IntentService {
    public static final int UPDATE_PROGRESS = 8344;
    public DownloadService() {
        super("DownloadService");
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        String urlToDownload = intent.getStringExtra("url");
        ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");
        HttpURLConnection connection ;
        try {
            URL url = new URL(urlToDownload);
            connection = (HttpURLConnection) url.openConnection();
            connection.connect();
            // this will be useful so that you can show a typical 0-100% progress bar
            int fileLength = connection.getContentLength();
            Log.d("test","fileLength:"+fileLength);
            // download the file
            InputStream input = connection.getInputStream();
            OutputStream output = new FileOutputStream("/sdcard/new.apk");
            byte data[] = new byte[2048];
            long total = 0;
            int count;
            while ((count = input.read(data)) != -1) {
                total += count;
                // publishing the progress....
                Bundle resultData = new Bundle();
                resultData.putInt("progress" ,(int) (total * 100 / fileLength));
                receiver.send(UPDATE_PROGRESS, resultData);
                output.write(data, 0, count);
            }
            output.flush();
            output.close();
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
//记得注册<service android:name=".DownloadService"/>

activity中这样调用DownloadService

  progressDialog.show();                                                
  Intent intent = new Intent(this, DownloadService.class);              
  intent.putExtra("url",url);                                           
  intent.putExtra("receiver", new DownloadReceiver(new Handler()));     
  startService(intent);                                                 

activity中定义一个广播接收器继承ResultReceiver,ResultReceiver允许我们接收来自service中发出的广播

 //使用ResultReceiver接收来自DownloadService的下载进度通知                                                                
 private class DownloadReceiver extends ResultReceiver {                                                     
     public DownloadReceiver(Handler handler) {                                                              
         super(handler);                                                                                     
     }                                                                                                       

     @Override                                                                                               
     protected void onReceiveResult(int resultCode, Bundle resultData) {                                     
     super.onReceiveResult(resultCode, resultData);                                                          
         if (resultCode == DownloadService.UPDATE_PROGRESS) {                                                
             int progress = resultData.getInt("progress");                                                   
             //(true)就是根据你的进度可以设置现在的进度值。                                                                     
             //(false)就是滚动条的当前值自动在最小到最大值之间来回移动,形成这样一个动画效果                                                    
             progressDialog.setIndeterminate(false);                                                         
             progressDialog.setProgress(progress);                                                           
             if (progress == 100) {                                                                          
                 progressDialog.dismiss();         
          //自动安装下载的apk                                                          
                 File file=new File("/sdcard/new.apk");                                                      
                 Intent installIntent = new Intent(Intent.ACTION_VIEW);                                      
                 installIntent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
                 installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                                      
                 startActivity(installIntent);                                                               
             }                                                                                               
         }                                                                                                   
     }                                                                                                       
 }                                                                                                           

如果对您有用,给个赞鼓励一下呗~

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,255评论 25 707
  • Android Studio JNI流程首先在java代码声明本地方法 用到native关键字 本地方法不用去实现...
    MigrationUK阅读 11,825评论 7 123
  • 小时候幻想仗剑走天涯 如今我到处流浪四海为家。 她羡慕着别人乖巧的发卡 渴望着别人些许的关怀 最后假笑得像只厌恶的...
    BoombayahKAI阅读 384评论 1 3
  • 上个月休年假,我去了上海一趟看望我的闺蜜W姑娘,我在虹桥火车站地铁口等着她来接我,我心里算了一下,我们大概有一年多...
    达达令阅读 798评论 3 8
  • 你曾许下过一生只爱一个人的诺言吗?在青春时光里,我以为我会爱那个女孩一生一世。可是,后来却发现我却不明白什么是爱,...
    Toooony阅读 271评论 0 1