Android技术知识Android开发探索Android开发经验谈

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

2017-10-12  本文已影响3801人  芒果味的你呀

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

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

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

asynctask效果gif

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

要点:

//关于进度显示
  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);                                                               
             }                                                                                               
         }                                                                                                   
     }                                                                                                       
 }                                                                                                           

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

上一篇下一篇

猜你喜欢

热点阅读