Android版本升級(jí)下載apk文件,UI進(jìn)度條顯示,自動(dòng)安裝apk的三種方式(AsyncTask、Service和使用DownloadManager)

我們主要指的是下載一個(gè)文件,不考慮斷點(diǎn)續(xù)傳。

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

一、如果想要在后臺(tái)下載任務(wù)的同時(shí)可以更新進(jìn)度條UI----使用AsyncTask

asynctask效果gif

忽略安裝需要密碼這個(gè)細(xì)節(jié),用oppo手機(jī)的應(yīng)該知道,這個(gè)是只有oppo測(cè)試機(jī)會(huì)這樣,別的手機(jī)就可以直接安裝了。

要點(diǎn):

  • 一個(gè)url下載鏈接,一個(gè)ProgressDialog用來(lái)顯示進(jìn)度,一個(gè)按鈕(點(diǎn)擊升級(jí))按鈕監(jiān)聽(tīng)的方法為onUpdateClick()

  • 接著放入主要的代碼

//關(guān)于進(jìn)度顯示
  private ProgressDialog progressDialog;
//相關(guān)屬性
        progressDialog =new ProgressDialog(UpdateDialogActivity.this);
        progressDialog.setMessage("正在下載...");
        progressDialog.setIndeterminate(true);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setCancelable(true);
//升級(jí)下載按鈕點(diǎn)擊事件
 private void onUpdateClick() {
        // TODO: 2017/10/11 三種方式實(shí)現(xiàn)apk下載
       //第一種 asynctask
        //onProgressUpdate和onPreExecute是運(yùn)行在UI線程中的,
        // 所以我們應(yīng)該在這兩個(gè)方法中更新progress。
        final DownloadTask downloadTask = new DownloadTask(UpdateDialogActivity.this);
        //execute 執(zhí)行一個(gè)異步任務(wù),通過(guò)這個(gè)方法觸發(fā)異步任務(wù)的執(zhí)行。這個(gè)方法要在主線程調(diào)用。
        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)方法被調(diào)用后立即執(zhí)行,執(zhí)行在ui線程,
        // 一般用來(lái)在執(zhí)行后臺(tái)任務(wù)前會(huì)UI做一些標(biāo)記
        @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這個(gè)方法在onPreExecute()完成后立即執(zhí)行,
        // 用于執(zhí)行較為耗時(shí)的操作,
        // 此方法接受輸入?yún)?shù)
        // 和返回計(jì)算結(jié)果(返回的計(jì)算結(jié)果將作為參數(shù)在任務(wù)完成是傳遞到onPostExecute(Result result)中),
        // 在執(zhí)行過(guò)程中可以調(diào)用publishProgress(Progress... values)來(lái)更新進(jìn)度信息
        //后臺(tái)任務(wù)的代碼塊
        @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+"http:////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
                        //在調(diào)用這個(gè)方法后,執(zhí)行onProgressUpdate(Progress... values),
                        //運(yùn)行在主線程,用來(lái)更新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),
        // 執(zhí)行在UI線程,在調(diào)用publishProgress(Progress... values)時(shí),此方法被執(zhí)行。
        @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),
        // 執(zhí)行在UI線程,當(dāng)后臺(tái)操作結(jié)束時(shí),此方法將會(huì)被調(diào)用。
        @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();}
//這里主要是做下載后自動(dòng)安裝的處理
            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

每個(gè)Android App都會(huì)有版本更新的功能,而下載功能Google官方推薦使用 DownloadManager服務(wù)

使用最簡(jiǎn)單的一種

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();//設(shè)置可以被掃描到
                request.setVisibleInDownloadsUi(true);// 設(shè)置下載可見(jiàn)
                request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);//下載完成后通知欄任然可見(jiàn)
                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", "開(kāi)始下載任務(wù):" + Id + " ...");

如果想同樣實(shí)現(xiàn)下載完安裝,要使用廣播.當(dāng)DownloadManager下載完成后會(huì)發(fā)出一個(gè)廣播 android.intent.action.DOWNLOAD_COMPLETE,創(chuàng)建一個(gè)廣播接收者,處理自動(dòng)提示安裝:

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中進(jìn)行注冊(cè)
      <receiver android:name=".receiver.DownLoadBroadcastReceiver">
            <intent-filter android:priority="20" >
                <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
            </intent-filter>
        </receiver>
還要加權(quán)限:
   <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

雖說(shuō)代碼量很少,但是也確實(shí)會(huì)有問(wèn)題遇到

問(wèn)題1:No Activity found to handle Intent

解決:首先不要單獨(dú)設(shè)置data和type, 要同時(shí)setDataAndType(data, "application/vnd.android.package-archive")。其次最多的可能是下載文件路徑的問(wèn)題,好好檢查文件路徑是否錯(cuò)誤或是否不可讀。最簡(jiǎn)單的方法就是把a(bǔ)pk的路徑固定死

問(wèn)題2:權(quán)限問(wèn)題,targetSdkVersion >=23需要獲取權(quán)限才能自動(dòng)安裝

解決:

方法一:把build.gradle 文件中的targetSdkVersion < 23。這種方式也是最簡(jiǎn)單的。

方法二:動(dòng)態(tài)的獲取權(quán)限:代碼如下

// getPersimmions();方法
 @TargetApi(23)
    private void getPersimmions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            ArrayList<String> permissions = new ArrayList<String>();
            /*
             * 讀寫(xiě)權(quán)限和電話狀態(tài)權(quán)限非必要權(quán)限(建議授予)只會(huì)申請(qǐng)一次,用戶同意或者禁止,只會(huì)彈一次
             */
            // 讀寫(xiě)權(quán)限
            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) { // 如果應(yīng)用沒(méi)有獲得對(duì)應(yīng)權(quán)限,則添加到列表中,準(zhǔn)備批量申請(qǐng)
            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中我們開(kāi)啟一個(gè)線程執(zhí)行下載任務(wù)(service和你的app其實(shí)是在一個(gè)線程中,因此不想阻塞主線程的話必須開(kāi)啟新的線程。

//在這里根據(jù)url進(jìn)行下載文件,并通過(guò)receiver把需要更新的progressbar的值放在bundle傳過(guò)去
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();
        }

    }
}
//記得注冊(cè)<service android:name=".DownloadService"/>

activity中這樣調(diào)用DownloadService

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

activity中定義一個(gè)廣播接收器繼承ResultReceiver,ResultReceiver允許我們接收來(lái)自service中發(fā)出的廣播

 //使用ResultReceiver接收來(lái)自DownloadService的下載進(jìn)度通知                                                                
 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)就是根據(jù)你的進(jìn)度可以設(shè)置現(xiàn)在的進(jìn)度值。                                                                     
             //(false)就是滾動(dòng)條的當(dāng)前值自動(dòng)在最小到最大值之間來(lái)回移動(dòng),形成這樣一個(gè)動(dòng)畫(huà)效果                                                    
             progressDialog.setIndeterminate(false);                                                         
             progressDialog.setProgress(progress);                                                           
             if (progress == 100) {                                                                          
                 progressDialog.dismiss();         
          //自動(dòng)安裝下載的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);                                                               
             }                                                                                               
         }                                                                                                   
     }                                                                                                       
 }                                                                                                           

如果對(duì)您有用,給個(gè)贊鼓勵(lì)一下唄~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,466評(píng)論 25 708
  • Android Studio JNI流程首先在java代碼聲明本地方法 用到native關(guān)鍵字 本地方法不用去實(shí)現(xiàn)...
    MigrationUK閱讀 11,953評(píng)論 7 123
  • 小時(shí)候幻想仗劍走天涯 如今我到處流浪四海為家。 她羨慕著別人乖巧的發(fā)卡 渴望著別人些許的關(guān)懷 最后假笑得像只厭惡的...
    BoombayahKAI閱讀 401評(píng)論 1 3
  • 上個(gè)月休年假,我去了上海一趟看望我的閨蜜W姑娘,我在虹橋火車站地鐵口等著她來(lái)接我,我心里算了一下,我們大概有一年多...
    達(dá)達(dá)令閱讀 820評(píng)論 3 8
  • 你曾許下過(guò)一生只愛(ài)一個(gè)人的諾言嗎?在青春時(shí)光里,我以為我會(huì)愛(ài)那個(gè)女孩一生一世。可是,后來(lái)卻發(fā)現(xiàn)我卻不明白什么是愛(ài),...
    Toooony閱讀 378評(píng)論 0 1