1. 程式人生 > >android app版本升級(DownloadManager、適配6.0、7.0)

android app版本升級(DownloadManager、適配6.0、7.0)

說明:

1.本文使用系統DownloadManager在通知欄更新下載進度  2.動態許可權使用第三方庫EasyPermissions(https://github.com/googlesamples/easypermissions)  3.下載完成的App安裝適配7.0  4.提示下載框(AlertDialog)是依附於Activity(UpdateActivity)的,這樣做是為了解決“進入首頁後,開啟自動檢測升級,檢測到有升級的版本就隨時彈框提示使用者,但此時使用者可能已經在操作APP進入其他頁面,怎麼保證彈框可以正常彈出?”這一問題並適配各大機型

升級流程圖

MianActivity原始碼

public class MainActivity extends BaseActivity         implements EasyPermissions.PermissionCallbacks, CheckUpdateManager.RequestPermissions { //...     private Version mVersion;//版本控制bean     private static final int RC_EXTERNAL_STORAGE = 0x04;//儲存許可權 //...

    //BaseActivity方法,返回佈局檔案     @Override     protected int getContentView() {         return R.layout.activity_main_ui;     }

     //BaseActivity方法,初始化data     @Override     protected void initData() {         super.initData();

        // 檢查版本升級         checkUpdate();

    }

    //CheckUpdateManager回撥介面 CheckUpdateManager封裝了網路請求      @Override     public void call(Version version) {         this.mVersion = version;         requestExternalStorage(); }

    @AfterPermissionGranted(RC_EXTERNAL_STORAGE)     public void requestExternalStorage() {         if (EasyPermissions.hasPermissions(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {             DownloadService.startService(this, mVersion.getDownloadUrl());         } else {             EasyPermissions.requestPermissions(this, "", RC_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);         }     }

    @Override     public void onPermissionsGranted(int requestCode, List<String> perms) {

    }

    @Override     public void onPermissionsDenied(int requestCode, List<String> perms) {

        for (String perm : perms) {             if (perm.equals(Manifest.permission.READ_EXTERNAL_STORAGE)) {                 //DialogHelper:Alerdialog分裝類                 DialogHelper.getConfirmDialog(this, "溫馨提示", "需要開啟您手機的儲存許可權才能下載安裝,是否現在開啟", "去開啟", "取消", true, new DialogInterface.OnClickListener() {                     @Override                     public void onClick(DialogInterface dialog, int which) {                         startActivity(new Intent(Settings.ACTION_APPLICATION_SETTINGS));                     }                 }, null).show();

            } else {             //SharedPreferences中儲存授權狀態                 Setting.updateLocationPermission(getApplicationContext(), false);             }         }

    }

    @Override     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {         EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);     }

    private void checkUpdate() {             CheckUpdateManager manager = new          manager.checkUpdate();     }

} CheckUpdateManager 原始碼 public class CheckUpdateManager {

    private ProgressDialog mWaitDialog;     private Context mContext;     private boolean mIsShowDialog;

    public CheckUpdateManager(Context context, boolean showWaitingDialog) {         this.mContext = context;         mIsShowDialog = showWaitingDialog;         if (mIsShowDialog) {             mWaitDialog = DialogHelper.getProgressDialog(mContext);             mWaitDialog.setMessage("正在檢查中...");             mWaitDialog.setCancelable(false);             mWaitDialog.setCanceledOnTouchOutside(false);         }     }

    public void checkUpdate() {         if (mIsShowDialog) {             mWaitDialog.show();         }         OSChinaApi.checkUpdate(new TextHttpResponseHandler() {             @Override             public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {                 if (mIsShowDialog) {                     DialogHelper.getMessageDialog(mContext, "網路異常,無法獲取新版本資訊").show();                 }             }

            @Override             public void onSuccess(int statusCode, Header[] headers, String responseString) {                         //此處省略若干程式碼                             int curVersionCode = TDevice.getVersionCode(AppContext                                     .getInstance().getPackageName());                                     //version:伺服器解析後實體bean                             if (curVersionCode < version.getCode()) {                                 UpdateActivity.show((Activity) mContext, version);                                                          } else {                                 if (mIsShowDialog) {                                     DialogHelper.getMessageDialog(mContext, "已經是新版本了").show();                                 }                             }     

            }

            @Override             public void onFinish() {                 super.onFinish();                 if (mIsShowDialog) {                     mWaitDialog.dismiss();                 }             }         });     }

}

UpdateActivity原始碼 public class UpdateActivity extends BaseActivity implements View.OnClickListener,         EasyPermissions.PermissionCallbacks {     @Bind(R.id.tv_update_info)     TextView mTextUpdateInfo;     private Version mVersion;     private static final int RC_EXTERNAL_STORAGE = 0x04;//儲存許可權

    public static void show(Activity activity, Version version) {         Intent intent = new Intent(activity, UpdateActivity.class);         intent.putExtra("version", version);         activity.startActivityForResult(intent, 0x01);     }

    @Override     protected int getContentView() {         return R.layout.activity_update;     }

    @SuppressWarnings("deprecation")     @Override     protected void initData() {         super.initData();         setTitle("");         getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);         mVersion = (Version) getIntent().getSerializableExtra("version");         mTextUpdateInfo.setText(Html.fromHtml(mVersion.getMessage()));     }

    @OnClick({R.id.btn_update, R.id.btn_close})     @Override     public void onClick(View v) {         switch (v.getId()) {             case R.id.btn_update:                 if (!TDevice.isWifiOpen()) {                     DialogHelper.getConfirmDialog(this, "當前非wifi環境,是否升級?", new DialogInterface.OnClickListener() {                         @Override                         public void onClick(DialogInterface dialog, int which) {                             requestExternalStorage();                                                 }                     }, new DialogInterface.OnClickListener() {                         @Override                         public void onClick(DialogInterface dialog, int which) {                             finish();                         }                     }).show();                 } else {                     requestExternalStorage();

                }                 break;             case R.id.btn_close:                 finish();                 break;         }

    }

    @AfterPermissionGranted(RC_EXTERNAL_STORAGE)     public void requestExternalStorage() {         if (EasyPermissions.hasPermissions(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {

            AppUpgradeManager.getInstance(this, mVersion).startDown();             finish();         } else {             EasyPermissions.requestPermissions(this, "需要開啟對您手機的儲存許可權才能下載安裝", RC_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE);         }     }

    @Override     public void onPermissionsGranted(int requestCode, List<String> perms) {

    }

    @Override     public void onPermissionsDenied(int requestCode, List<String> perms) {     //當權限視窗不能彈出式呼叫-使用者勾選了不再提醒         if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {             DialogHelper.getConfirmDialog(UpdateActivity.this, "溫馨提示", "需要開啟對您手機的儲存許可權才能下載安裝,是否現在開啟", "去開啟", "取消", true, new DialogInterface.OnClickListener() {                 @Override                 public void onClick(DialogInterface dialog, int which) {                     startActivity(new Intent(Settings.ACTION_APPLICATION_SETTINGS));                 }             }, new DialogInterface.OnClickListener() {                 @Override                 public void onClick(DialogInterface dialog, int which) {                     finish();                 }             }).show();         } else {             finish();         }     }

    @Override     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {         EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);     }

}

AppUpgradeManager原始碼 public class AppUpgradeManager {     private volatile static AppUpgradeManager sAppUpgradeManager;     private DownloadManager downloader;     private Context appContext;     private NotificationClickReceiver mNotificationClickReceiver;     private DownloadReceiver mDownloaderReceiver;     private String apkName = AppConfig.APP_NAME;     //apk下載檔案的路徑     private String downloadApkPath;     //   伺服器返回的版本資訊     private Version latestVersion;

    public AppUpgradeManager(Context context, Version version) {         appContext = context.getApplicationContext();         latestVersion = version;         mDownloaderReceiver = new DownloadReceiver();         mNotificationClickReceiver = new NotificationClickReceiver();     }

    public static AppUpgradeManager getInstance(Context context, Version version) {         if (sAppUpgradeManager == null) {             synchronized (AppUpgradeManager.class) {                 if (sAppUpgradeManager == null) {                     sAppUpgradeManager = new AppUpgradeManager(context, version);                 }             }         }         return sAppUpgradeManager;     }

    public void startDown() {         //確定apk下載的絕對路徑         String dirPath = AppConfig.DEFAULT_SAVE_FILE_PATH_PUBLIC;         //AppConfig.DEFAULT_SAVE_FILE_PATH_PUBLIC=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();

        dirPath = dirPath.endsWith(File.separator) ? dirPath : dirPath + File.separator;         downloadApkPath = dirPath + apkName;

        //先檢查本地是否已經有需要升級版本的安裝包,如有就不需要再下載         File targetApkFile = new File(downloadApkPath);         if (targetApkFile.exists()) {             PackageManager pm = appContext.getPackageManager();             PackageInfo info = pm.getPackageArchiveInfo(downloadApkPath, PackageManager.GET_ACTIVITIES);             if (info != null) {                 String versionCode = String.valueOf(info.versionCode);                 //比較已下載到本地的apk安裝包,與伺服器上apk安裝包的版本號是否一致                 if (String.valueOf(latestVersion.getCode()).equals(versionCode)) {                     installApk();                     return;                 }             }         }         //要檢查本地是否有安裝包,有則刪除重新下         File apkFile = new File(downloadApkPath);         if (apkFile.exists()) {             apkFile.delete();         }

        if (downloader == null) {             downloader = (DownloadManager) appContext.getSystemService(Context.DOWNLOAD_SERVICE);         }         //開始下載         DownloadManager.Query query = new DownloadManager.Query();         long downloadTaskId = TDevice.getDownloadTaskId(appContext);         query.setFilterById(downloadTaskId);         Cursor cur = downloader.query(query);         // 檢查下載任務是否已經存在         if (cur.moveToFirst()) {             int columnIndex = cur.getColumnIndex(DownloadManager.COLUMN_STATUS);             int status = cur.getInt(columnIndex);             if (DownloadManager.STATUS_PENDING == status || DownloadManager.STATUS_RUNNING == status || DownloadManager.STATUS_PAUSED == status) {                 cur.close();                 Toast.makeText(appContext, "更新任務已在後臺進行中,無需重複更新", Toast.LENGTH_LONG).show();                 return;             }         }         cur.close();         DownloadManager.Request task = new DownloadManager.Request(Uri.parse(latestVersion.getDownloadUrl()));         //定製Notification的樣式         String title = "最新版本:" + latestVersion.getCode();         task.setTitle(title);         task.setDescription("本次更新:\n1.增強系統穩定性\n2.修復已知bug");         task.setVisibleInDownloadsUi(true);         //設定是否允許手機在漫遊狀態下下載         //task.setAllowedOverRoaming(false);         //限定在WiFi下進行下載         //task.setAllowedNetworkTypes(Request.NETWORK_WIFI);         task.setMimeType("application/vnd.android.package-archive");         // 在通知欄通知下載中和下載完成         // 下載完成後該Notification才會被顯示         if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {             // 3.0(11)以後才有該方法             //在下載過程中通知欄會一直顯示該下載的Notification,在下載完成後該Notification會繼續顯示,直到使用者點選該Notification或者消除該Notification             task.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);         }         // 可能無法建立Download資料夾,如無sdcard情況,系統會預設將路徑設定為/data/data/com.android.providers.downloads/cache/xxx.apk         if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {             task.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkName);         } // 自定義檔案路徑 //task.setDestinationUri()         downloadTaskId = downloader.enqueue(task);         //TDevice SharedPreferences封裝類         TDevice.saveDownloadTaskId(appContext, downloadTaskId);         //註冊下載完成廣播         appContext.registerReceiver(mDownloaderReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));         appContext.registerReceiver(mNotificationClickReceiver, new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED));

    }

    private void installApk() {         if (TextUtils.isEmpty(downloadApkPath)) {             Toast.makeText(appContext, "APP安裝檔案不存在或已損壞", Toast.LENGTH_LONG).show();             return;         }         File apkFile = new File(Uri.parse(downloadApkPath).getPath());         if (!apkFile.exists()) {             Toast.makeText(appContext, "APP安裝檔案不存在或已損壞", Toast.LENGTH_LONG).show();             return;         }

        Intent intent = new Intent(Intent.ACTION_VIEW);         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {             intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);             Uri contentUri = FileProvider.getUriForFile(appContext, "net.xxx.app.provider", apkFile);             intent.setDataAndType(contentUri, "application/vnd.android.package-archive");         } else {             intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);         }         appContext.startActivity(intent);     }

    /**      * 下載完成的廣播      */     class DownloadReceiver extends BroadcastReceiver {         @Override         public void onReceive(Context context, Intent intent) {             if (downloader == null) {                 return;             }             long completeId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);             long downloadTaskId = TDevice.getDownloadTaskId(context);             if (completeId != downloadTaskId) {                 return;             }

            DownloadManager.Query query = new DownloadManager.Query();             query.setFilterById(downloadTaskId);             Cursor cur = downloader.query(query);             if (!cur.moveToFirst()) {                 return;             }

            int columnIndex = cur.getColumnIndex(DownloadManager.COLUMN_STATUS);             if (DownloadManager.STATUS_SUCCESSFUL == cur.getInt(columnIndex)) {                 installApk();             } else {                 Toast.makeText(appContext, "下載App最新版本失敗!", Toast.LENGTH_LONG).show();             }             // 下載任務已經完成,清除             TDevice.removeDownloadTaskId(context);             cur.close();         }

    }

    /**      * 點選通知欄下載專案,下載完成前點選都會進來,下載完成後點選不會進來。      */     public class NotificationClickReceiver extends BroadcastReceiver {         @Override         public void onReceive(Context context, Intent intent) {             long[] completeIds = intent.getLongArrayExtra(                     DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS);             //正在下載的任務ID             long downloadTaskId = TDevice.getDownloadTaskId(context);             if (completeIds == null || completeIds.length <= 0) {                 openDownloadsPage(appContext);                 return;             }

            for (long completeId : completeIds) {                 if (completeId == downloadTaskId) {                     openDownloadsPage(appContext);                     break;                 }             }         }

        /**          * Open the Activity which shows a list of all downloads.          *          * @param context 上下文          */         private void openDownloadsPage(Context context) {             Intent pageView = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);             pageView.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);             context.startActivity(pageView);         }     } } Android7.0適配 1.在manifest檔案application標籤中中加入

<provider             android:name="android.support.v4.content.FileProvider"             android:authorities="net.xxx.app.provider"              /**注意引數統一性 FileProvider.getUriForFile(appContext, "net.xxx.app.provider", apkFile);*/                         android:exported="false"             android:grantUriPermissions="true">             <meta-data                 android:name="android.support.FILE_PROVIDER_PATHS"                 android:resource="@xml/provider_paths" />         </provider> 2.res下的xml資料夾中的provider_paths原始碼

<?xml version="1.0" encoding="utf-8"?> <paths>     <!--path:需要臨時授權訪問的路徑(.代表所有路徑) name:就是你給這個訪問路徑起個名字-->     <external-path         name="external_files"         path="." /> </paths>