1. 程式人生 > >Android利用FileDownloader實現APP自動更新並且安裝

Android利用FileDownloader實現APP自動更新並且安裝

在Android中APP自動更新、安裝是必然的,最近呢我們公司也有這樣一個需求開始呢本菜鳥是打算用AppUpdate但是呢看了他的專案後發現真的是個好東西但是我不打算這樣做打算自己來實現更新、安裝。無意之間在一個專案中發現了FileDownloader這個真是個好東西那麼我立馬下手果然…做出來了 而且還挺簡單…

第三方依賴說明

一、網路請求:Retrofit2+RxJava

 	implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    implementation 'com.squareup.okhttp3:okhttp:3.9.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
    implementation 'io.reactivex.rxjava2:rxjava:2.1.5'

二、檔案下載:FileDownloader

implementation 'com.liulishuo.filedownloader:library:1.7.5'

更新APP

一、查詢APP和伺服器版本是否一樣(這裡你可以和你們後臺小哥協商)

  RequestManager.getInstance().getApi.getAppMsg().compose(RxThreadUtlis.rxSchedulerHelper())
                .subscribe(new RequestSubscribe() {
                    @Override
                    protected
void onRequestScceed(RequestBaseBean response) { if (response instanceof AppUpdateBean) { AppUpdateBean appUpdateBean = (AppUpdateBean) response; if (appUpdateBean != null) { //比較APP版本和伺服器版本是否一樣
if (appUpdateBean.getData().get(0).getAppMsgId() != PackageUtlis.packageCode(AppApdateActivity.this)) { new AlertDialog.Builder(AppApdateActivity.this) .setTitle("應用更新") .setMessage("檢查到新版本啦,請安裝更新~") .setPositiveButton("立即更新", (dialog, which) -> { dialog.dismiss(); downloadAPK(); }).setNeutralButton("稍後提醒", (dialog, which) -> dialog.dismiss()).create() .show(); } } } } @Override protected void onRequestErro(RequestBaseBean response) { } });

二、FileDownloader簡單使用下載

1、初始化FileDownloader

FileDownloader.setupOnApplicationOnCreate(this);

2、獲取檔案路徑(如果檔案存在則刪除不存在則建立)

private String apkPath() {
        String path = null;
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
            File file = new File(Environment.getExternalStorageDirectory().toString()+File.separator+"AppApdate");
            if (file.exists() && file.isDirectory()){
                for (File fileDir : file.listFiles()) {
                    if (fileDir.isFile()) {
                        fileDir.delete(); // 刪除所有檔案
                    }
                }
            }else {
                file.mkdir();
            }
            path = file.getAbsolutePath();
        }
        return path;
    }

3、使用FileDownloader下載

 FileDownloader.getImpl().create(Constant.APK_URL).setPath(apkPath()+File.separator+"app.apk")
                .setListener(new FileDownloadLargeFileListener() {
                    @Override
                    protected void pending(BaseDownloadTask task, long soFarBytes, long totalBytes) {
                        Log.e(DOWNLOAD_TAG,"pending");
                    }

                    @Override
                    protected void progress(BaseDownloadTask task, long soFarBytes, long totalBytes) {
                        Log.e(DOWNLOAD_TAG,"progress----"+(soFarBytes * 100 / totalBytes));
                        progressBar.setProgress((int) (soFarBytes * 100 / totalBytes));
                        tv_pg_d.setText("下載進度:"+(int) (soFarBytes * 100 / totalBytes)+"%  /  "+100+"%");
                        tv_pg_m.setText("已完成:"+soFarBytes/1024/1024+"MB / "+totalBytes/1024/1024+"MB");

                    }

                    @Override
                    protected void paused(BaseDownloadTask task, long soFarBytes, long totalBytes) {
                        Log.e(DOWNLOAD_TAG,"paused");

                    }

                    @Override
                    protected void completed(BaseDownloadTask task) {
                        Log.e(DOWNLOAD_TAG,"completed----------"+task.getPath());
                        progressBar.setProgress(100);
                        tv_pg_d.setText("下載進度:"+"100% / 100%");
                        installAPK(task.getPath());

                    }

                    @Override
                    protected void error(BaseDownloadTask task, Throwable e) {
                        Toast.makeText(AppApdateActivity.this,"下載錯誤",Toast.LENGTH_SHORT).show();

                    }

                    @Override
                    protected void warn(BaseDownloadTask task) {
                        Log.e(DOWNLOAD_TAG,"warn--:在下載佇列中(正在等待/正在下載)已經存在相同下載連線與相同儲存路徑的任務");
                    }
                }).start();

4、安裝APK

   /**
     * 安裝APK
     * @param path
     */
    private void installAPK(String path) {
        File fileApk = new File(path);
        if (!fileApk.exists()) {
            Toast.makeText(IndexAc.this, "純氧健身更新失敗", Toast.LENGTH_SHORT).show();
            return;
        }
        Intent intent = new Intent(Intent.ACTION_VIEW);

        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        Uri apkUri = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            apkUri = FileProvider.getUriForFile(IndexAc.this, BuildConfig.APPLICATION_ID + ".provider", fileApk);
            //新增這一句表示對目標應用臨時授權該Uri所代表的檔案
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            apkUri = Uri.fromFile(fileApk);
        }

        intent.setDataAndType(apkUri,
                "application/vnd.android.package-archive");

        startActivity(intent);
    }

注:APK安裝在7.0之後的版本執行以前apk安裝程式碼會出現android.os.FileUriExposedException

原因:“私有目錄被限制訪問”是指在Android7.0中為了提高私有檔案的安全性,面向 Android N 或更高版本的應用私有目錄將被限制訪問。 而7.0的” StrictMode API 政策” 是指禁止向你的應用外公開 file:// URI。 如果一項包含檔案 file:// URI型別 的 Intent 離開你的應用,應用失敗,並出現 FileUriExposedException 異常。 之前程式碼用到的Uri.fromFile就是商城一個file://的Uri在7.0之後,我們需要使用FileProvider來解決

5、配置:FileProvider

  • AndroidManifest.xml中application中新增
  <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
  • 建立xml檔案file_paths
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path
            name="files_root"
            path="" />
    </paths>
</resources>

path:需要臨時授權訪問的路徑(.代表所有路徑)
name:就是你給這個訪問路徑起個名字

新增許可權(Android 6.0 後記得加上執行時許可權哦~WRITE_EXTERNAL_STORAGE 6.0後需要動態申請)

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!--安裝許可權-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

這樣更新就完成啦~
(注:此方法僅供參考個人嘗試是成功了的)