Android 6.0 7.0 8.0三個版本Install Apk 採坑記錄

一個不羈的碼農發表於2018-04-16

1.為什麼會分為6.0、7.0、8.0 三個版本呢?

(1)6.0以及之前算是一個版本的問題,7.0版本新增了提高了私有檔案的安全性FileProvider是一個坑,8.0版本對於Install Apk又增加了
許可權管理又是一個坑

那麼算下來就有兩個坑了,我們一一看看我的坑是怎麼樣的。
複製程式碼

2.android 7.0的坑

(1)這個坑是一個主要的坑,我相信很多的anroid開發者都應該知道Android 7.0 新增私有檔案的安全性,那麼來看下我的程式碼:
複製程式碼
    /**
     * 通過隱式意圖呼叫系統安裝程式安裝APK
     */
    public static void install(Context context) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        // 由於沒有在Activity環境下啟動Activity,設定下面的標籤
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setDataAndType(Uri.fromFile(
                new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "myApp.apk")),
                "application/vnd.android.package-archive");
        context.startActivity(intent);
    }
複製程式碼
相信這段程式碼是我們很多6.0及以前的通過Intent隱式安裝APK的方法,當然這個方法在6.0以及前是麼有任何問題的,但是在7.0這裡就會出現
問題了
複製程式碼

Android 6.0 7.0 8.0三個版本Install Apk 採坑記錄

這就表示我們用絕對路徑去訪問檔案的時候就會出現SecurityException的異常。
大神可以直接移步官網介紹: https://developer.android.google.cn/training/secure-file-sharing/index.html

## (2)具體採坑的內容:

一、在AndroidManifest.xml裡宣告Provider
複製程式碼
<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.test.pr.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
</provider>
複製程式碼
name: android V4 包中的類FileProvider
authorities:你的檔案的Uri的域名  一般以包名.fileprovider的格式,防止重名
exported: 設定不允許匯出,我們的FileProvider應該是私有的
grantUriPermissions:允許獲取檔案的臨時訪問許可權
resourse: 設定FileProvider訪問的檔案路徑

二、配置FileProvider的檔案共享路徑

首先我們在res檔案下面建立一個xml資料夾,然後再xml中建立一個manifest中宣告的檔案file_paths.xml。這個名字看你自己定義的,但是必須和AndroidManifest.xml中宣告的一致:
複製程式碼
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path
            name="Download"
            path="test"/>
    </paths>
    .
    .
    .
</resources>
複製程式碼
這裡可以建立很多個paths,但是每個paths的name不能一樣。
name:FileProvider 提供的Uri中一個必要的,這裡的name=Download,那麼獲得Uri的連線如下所示:
複製程式碼
content://com.test.pr.fileprovider/Download
複製程式碼
接下來external-path這個標籤是最坑的地方,我來慢慢解釋,我當時出問題就是在這個問題:

     <files-path name="*name*" path="*path*" />   對應的是:Context.getFileDir()的路徑地址
     以上面的配置的path="test"來說
     得到路徑:Context.getFileDir()+"/test/"


     <cache-path name="*name*" path="*path*" />   對應路徑:Context.getCacheFir()
     以上面的配置的path="test"來說
     得到路徑:Context.getCache()+"/test/"


     <external-path name="*name*" path="*path*" />   對應路徑:Environment.getExternalStorageDirectory()
     以上面的配置的path="test"來說
     得到路徑:Environment.getExternalStorageDirectory()+"/test/"


     <external-files-path name="*name*" path="*path*" />   對應路徑:Context.getExternalStorageDirectory()
     以上面的配置的path="test"來說
     得到路徑:Context.getExternalStorageDirectory()+"/test/"


     <external-cache-path name="*name*" path="*path*" />   對應路徑: Context.getExternalCacheDir()
     以上面的配置的path="test"來說
     得到路徑:Context.getExternalCacheDir()+"/test/"
複製程式碼

PS:我再囉嗦幾句:上面的這個path標籤一定要和你儲存的檔案的位置要對應起來,如果檔案的路徑和你標籤對應的位置有錯的話,就會報這種錯誤:

錯誤: failed to find configured root that contains。。。
複製程式碼

我再把我的Install APk的程式碼如下:

public class InstallUtils {
    /**
     * 安裝apk
     *
     * @param downLoadId
     */
    public static void install(long downLoadId, Context mContext) {
        DownloadManager downloader = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
        assert downloader != null;
        File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), AppConstants.APP_NAME);
        if (!file.exists()) {
            ToastUtils.showToast(mContext, mContext.getString(R.string.install_fail));
            return;
        }
        Logger.e("" + file.length());
        //安裝
        Intent install = new Intent(Intent.ACTION_VIEW);
        //判斷是否是android 7.0及以上
        if (Build.VERSION.SDK_INT >= AppConstants.ANDROID_VERSION_7) {
            //7.0獲取儲存檔案的uri
            Uri uri = FileProvider.getUriForFile(mContext, "com.zhiyunqiao.pr.fileprovider", file);
            install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //賦予臨時許可權
            install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            //設定dataAndType
            install.setDataAndType(uri, "application/vnd.android.package-archive");
        } else {
            if (file.exists()) {
                install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                install.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            } else {
                ToastUtils.showToast(mContext, mContext.getString(R.string.install_fail));
                //清除下載成功
                PreferenceHelp.remove(mContext, "Version", "update");
            }
        }
        mContext.startActivity(install);
    }

}
複製程式碼

3.android 8.0 主要是一個許可權問題

Android 8.0更新主要是對Intent隱式安裝APK做了個安全管理:
需要在mainfist中新增一行許可權:
<!--8.0安裝apk需要許可權-->
複製程式碼
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
複製程式碼

然後在程式碼中請求下許可權即可。

相關文章