1. 程式人生 > >Android線上應用內更新(站內更新) 適配6.0、7.0、8.0

Android線上應用內更新(站內更新) 適配6.0、7.0、8.0

概要
線上應用內更新 在APP開發中是最基礎的一項功能。主要可以分為以下幾步:

  1. 獲取當前版本資訊;
  2. 獲取後臺線上版本資訊;
  3. 版本對比,提示更新
  4. 點選取消,跳過更新,進入app
  5. 點選確定,開始下載
  6. 下載結束,提示安裝

正文
這裡略過1—4步驟,主要講下載及安裝部分。

檔案的下載有很多中方式與第三方框架,這裡由於僅僅是下載應用,所有綜合考慮,強烈建議使用安卓 SDK 自帶下載框架 DownloadManager 。

1.最基礎也是重要的 申請檔案讀寫許可權、網路許可權,8.0系統需要獲取“未知應用安裝許可權”

<!-- 網路許可權 -->
<uses-permission
android:name="android.permission.INTERNET" />
<!-- 檔案讀寫許可權 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 8.0系統 獲取 未知應用安裝許可權 --> <uses-permission android:name
="android.permission.REQUEST_INSTALL_PACKAGES" />

2.檢測版本與安裝許可權

由於 8.0 “未知應用安裝許可權”被除掉,替換為“未知來源應用安裝許可權”,預設是關閉狀態,需要使用者手動開啟才能安裝最新版本安裝包,所以這裡需要判斷是否為 8.0 系統,然後獲取是否有該許可權。
為了使用者體驗比較好,這裡我在 onResume 中進行了版本判斷。其中 haveInstallPermission 為 boolean 型別,預設為true。

//版本判斷
if (Build.VERSION.SDK_INT >= Build.VERSION
_CODES.O) { //獲取是否有安裝未知來源應用的許可權 haveInstallPermission = MainActivity.this.getPackageManager().canRequestPackageInstalls(); }

3.封裝下載請求

這裡由於註釋很詳細,就不多做解釋,直接上程式碼。關於 DownloadManager.Request 的屬性詳解可以檢視這裡

public void downloadAPK() {
   //已存在 -- 刪除
   File apkFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "xxxx.apk");
   if (apkFile != null && apkFile.exists()) {
       apkFile.delete();
   }
   //下載 (AppCont.Update_URL 是你的app下載地址)
   DownloadManager.Request request = new DownloadManager.Request(Uri.parse(AppCont.Update_URL));
   //設定title
 request.setTitle(GetResourcesUtil.getString(R.string.app_name));
   // 完成後顯示通知欄
 request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
   //設定儲存路徑 --這個是應用專用的,軟體解除安裝後,下載的檔案將隨著解除安裝全部被刪除
   request.setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, "xxx.apk");
   //設定 檔案型別
   request.setMimeType("application/vnd.android.package-archive");
   downloadManager.enqueue(request);
}

4.開始下載

在第二步中拿到了 是否有安裝未知來源應用的許可權 ,可以在使用者點選確定下載最新版本時去判斷,如果沒有許可權,跳轉到許可權授權介面;如果有許可權,則直接下載更新。

if (!haveInstallPermission) {
    ToastUtils.showShort("請開啟安裝未知來源應用的許可權");
    //沒有許可權 在 yourAppPackageName 設定你的app包名
    Uri packageURI = Uri.parse("package:" + yourAppPackageName);
    Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
    MainActivity.this.startActivityForResult(intent, 010);
}else {
    diaglog.dismiss();
    ToastUtils.showShort("正在下載....");
    //下載更新
    updateUtil = new UpdateUtil(MainActivity.this);
    updateUtil.downloadAPK();
}

5.設定廣播接收器,監聽下載完成

downloadmanager 在下載完成時,會發送一條廣播,我們可以設定接收器,接受這條廣播,並做下載完成後的操作。
這裡先做廣播接收器

public class DownloadManagerReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        //下載完成
        if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
            //獲取 downloadmanager 下載任務id
           long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
           //安裝應用
            UpdateUtil updateUtil = new UpdateUtil(context);
            updateUtil.installApk(id);
        }
    }
}

// AndroidManifest.xml
<receiver android:name="com.xxxx.xxxx.util.DownloadManagerReceiver">
    <intent-filter>
        <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
    </intent-filter>
</receiver>

在更新提示的介面註冊廣播接收器。建議在 onResume() 中註冊,在onPause() 中解除註冊。

@Override
protected void onResume() {
    super.onResume();
    //註冊廣播
    downloadManagerReceiver = new DownloadManagerReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
    registerReceiver(downloadManagerReceiver, intentFilter);
    }
}
@Override
protected void onPause() {
    super.onPause();
    //解除註冊
    unregisterReceiver(downloadManagerReceiver);
}

6.安裝app

在安裝 app 的時候需要獲取 apk 儲存地址,這裡需要注意一下,需要做6.0以下(不包含6.0)/6.0/7.0以上(包含7.0)三種版本的適配。

6.0以下與6.0系統的區別 與 適配:
6.0以下(不包含6.0)系統的可以直接獲取儲存路徑;getUriForDownloadedFile得到 值為: content://downloads/my_downloads/10
Android6.0以下,getUriForDownloadedFile得到的值為:file:///storage/emulated/0/Android/data/packgeName/files/Download/xxx.apk

7.0適配 :
由於7.0對檔案訪問許可權做出了限制,必須通過 FileProvider 封裝後訪問。具體詳解可以檢視這裡

public Uri getDownloadPath(long downloadId) {
   Uri path = null;
   if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
       //6.0 以下
       path = downloadManager.getUriForDownloadedFile(downloadId);
   } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
       //6.0 — 7.0
       path = Uri.fromFile(queryDownloadedApk(downloadId));
   } else {
       //7.0 以上
       path = FileProvider.getUriForFile(context, "com.xxx.xxxx", new File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "xxxx.apk"));
   }
   return path;
}
private File queryDownloadedApk(long downloadId) {
    File downloadFilePath = null;
    DownloadManager.Query query = new DownloadManager.Query();
    Cursor cursor = null;
    try {
        cursor = downloadManager.query(query.setFilterById(downloadId));
        if (cursor != null || cursor.moveToFirst()) {
            String uriString = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
            if (!TextUtils.isEmpty(uriString)) {
                downloadFilePath = new File(Uri.parse(uriString).getPath());
            }
        }
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    return downloadFilePath;
}

獲取到apk儲存路徑後,可以進行最後的步驟安裝了,這裡很簡單,直接上程式碼。

public void installApk(long id) {
    //在上面已經獲取到apk儲存路徑
    Uri uri = getDownloadPath(id);
    //跳轉安裝
    Intent intentInstall = new Intent();
    intentInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intentInstall.setAction(Intent.ACTION_VIEW);
    intentInstall.setDataAndType(uri, "application/vnd.android.package-archive");
    context.startActivity(intentInstall);
}