美文网首页Android技术知识Android开发探索Android开发经验谈
Android版本升级下载apk文件,UI进度条显示,自动安装a

Android版本升级下载apk文件,UI进度条显示,自动安装a

作者: 芒果味的你呀 | 来源:发表于2017-10-12 17:44 被阅读3801次

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

    主要的三种方式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);                                                               
                 }                                                                                               
             }                                                                                                   
         }                                                                                                       
     }                                                                                                           
    

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

    相关文章

      网友评论

        本文标题:Android版本升级下载apk文件,UI进度条显示,自动安装a

        本文链接:https://www.haomeiwen.com/subject/njfgyxtx.html