1. 程式人生 > >Android7.0、8.0安裝apk以及安裝apk彈出“選擇開啟方式”的解決方案

Android7.0、8.0安裝apk以及安裝apk彈出“選擇開啟方式”的解決方案

目錄

問題描述

解決方案

問題描述

解決方案

問題描述

解決方案

最近在做一款APP,做自動更新的時候,安裝apk遇到了一些問題:

  • FileUriExposedException異常;

  • 無法跳轉到APP安裝頁面,無法進行版本更新升級;

  • 在下載完成,進行安裝的時候,總是彈出“請選擇以下開啟方式”讓使用者選擇一個開啟方式進行安裝,一旦選擇不對則無法安裝。

下面我們逐一分析解決。

Android7.0安裝apk導致的FileUriExposedException異常

問題描述

在Android7.0中為了提高私有檔案的安全性,Google做了私有目錄限制訪問,面向Android-N或者更高版本的私有目錄被限制訪問,有點類似於IOS的沙盒機制,此設定可防止私有檔案的元資料洩漏。禁止像你的應用外公開file://uri,如果某一項包含file://uri型別的Intent被外部執行的時候,比如說安裝App(從file://uri獲取apk檔案進行安裝)、拍照(向file://uri存取照片)就會導致安全異常FileUriExposedException,下面是我的錯誤程式碼以及報錯日誌:

安裝App的程式碼

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse("file://" + apkfile.toString()),
                    "application/vnd.android.package-archive");
mContext.startActivity(intent);

解決方案

方案一,使用嚴格模式(在Application中新增以下程式碼)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.setVmPolicy(builder.build());
}

方案二,使用FileProvider

首先在配置清單檔案(AndroidManifest.xm)中測註冊一個provider

    <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.houbin.test.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
    </provider>

注意:

  • exported:要求必須為false,為true則會報安全異常。
  • grantUriPermissions:true,表示授予 URI 臨時訪問許可權。
  • authorities 元件標識,按照江湖規矩,都以包名開頭,避免和其它應用發生衝突。

然後資源目錄(res)下建立xml目錄,並在xml目錄下建立一個xml檔案,名字要跟AndroidMaifest.xml檔案中註冊的provider引用的resource保持一致。

建立file_paths.xml檔案:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="files_root"
        path="Android/data/com.herenit.webdoc/" />
    <external-path
        name="external_storage_root"
        path="." />
</paths>

<external-path/>代表的根目錄: Environment.getExternalStorageDirectory()

<external-path name="files_root"  path="Android/data/com.houbin.test/" />

表示路徑為:Environment.getExternalStorageDirectory()+"Android/data/com.houbin.test/",起名字為files_root。

最後要在程式碼中做Android7.0相容,使用FileProvider代替file://url的Uri

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    Uri contentUri = FileProvider.getUriForFile(mContext,BuildConfig.APPLICATION_ID+ ".fileprovider", apkfile);
    intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
    mContext.startActivity(intent);
}

其中BuildConfig.APPLICATION_ID+".fileprovider",表示“APP包名.fileprovider”,即前面AndroidManifest.xml註冊的provider的authorities節點的值com.houbin.test.fileprovider。

注意,在FileProvider.getUriForFile()方法的原始碼已經明確指出,需要Intent設定flag新增許可權FLAG_GRANT_READ_URI_PERMISSION和FLAG_GRANT_WRITE_URI_PERMISSION許可權,如果這裡缺少intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);這句程式碼時,還是會丟擲FileUriExposedException異常的。

Android8.0安裝apk無法跳轉到正常的APP安裝頁面

問題描述

當用戶要從除了官方應用商店之外的來源安裝App時(安裝位置來源的APP):

Android8.0(Android-O)之前:可以被安裝,或者需要開啟系統設定當中“安裝未知應用”許可權,或者會有彈窗給使用者一個提示。;Android8.0之後,Google進一步加強了許可權管理,“安裝未知應用”許可權的永久開關被移除掉,每次當用戶安裝位置來源的App時,都需要單獨授權並且對軟體許可權進行手動確認,這樣的設計避免了被引誘安裝帶來的危害。當我們在Android8.0上安裝位置來源Apk時,如果不進行設定或者程式碼控制,是不會跳轉到系統App安裝介面,就會導致App無法安裝。

解決方案

方案一,使用者自己設定

例如在華為8.0系統手機上,使用者可以通過開啟 “設定->安全和隱私->更多安全設定->安裝未知應用”找到要更新的應用,然後點選開啟操作介面,設定“安裝未知應用”狀態為“允許”即可,如下圖所示:

         

是不是很不方便?如果我們的軟體以這樣的方式給使用者升級更新,那就體驗太差了。請看另一種解決辦法。

方案二,程式碼相容Android8.0

首先在AndroidManifest.xml清單檔案中新增安裝未知來源的許可權

 <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

然後在程式碼中相容Android8.0

if (android.os.Build.VERSION.SDK_INT >= 26) {
    boolean hasInstallPermission = mContext.getPackageManager().canRequestPackageInstalls();
    if (!hasInstallPermission) {
        //請求安裝未知應用來源的許可權
        ActivityCompat.requestPermissions((Activity) mContext, new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}, 6666);
    }
}

這樣在系統安裝App頁面的時候,會彈出選擇框,讓使用者手動選擇是否同意安裝位置來源的應用,這個是無法避免的。如果到這一步,使用者還要選擇“禁止”,那在下實在沒辦法了~ ~ ~

安裝apk時彈出“選擇開啟方式”讓使用者選擇而不是直接跳轉到APP安裝介面

問題描述

當Android系統版本比較高的時候,在有些機型上,會出現這樣的情況,每次安裝下載好的APK檔案時候,不會直接跳轉到系統的安裝介面,而是先彈出一個“開啟方式的彈窗”讓使用者選擇開啟,如果選擇的是打包安裝程式之類的應用來開啟APK,那麼會成功跳轉到系統安裝Apk介面,否則不會安裝成功。

解決方案

遇到這種情況,請檢查一下安裝apk的程式碼,是不是缺少開啟相應Activity的Action?為了保險起見,在寫安裝apk程式碼的時候,要有如下程式碼,否則有可能就會出現上述問題

intent.setAction(Intent.ACTION_VIEW);

安裝apk程式碼示例

   /**
     * 安裝APK檔案
     */
    private void installApk() {
        File apkfile = new File(mSavePath, apkName);
        if (!apkfile.exists()) {
            return;
        }
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Uri contentUri = FileProvider.getUriForFile(mContext, BuildConfig.APPLICATION_ID + ".fileprovider", apkfile);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
            //相容8.0
            if (android.os.Build.VERSION.SDK_INT >= 26) {
                boolean hasInstallPermission = mContext.getPackageManager().canRequestPackageInstalls();
                if (!hasInstallPermission) {
                    //請求安裝未知應用來源的許可權
                    ActivityCompat.requestPermissions((Activity) mContext, new String[]{Manifest.permission.REQUEST_INSTALL_PACKAGES}, 6666);
                }
            }
        } else {
            // 通過Intent安裝APK檔案
            intent.setDataAndType(Uri.parse("file://" + apkfile.toString()),
                    "application/vnd.android.package-archive");
        }
        if (mContext.getPackageManager().queryIntentActivities(intent, 0).size() > 0) {
            mContext.startActivity(intent);
        }
    }

個人總結的一點小東西,歡迎各位大神批評指正