1. 程式人生 > >關於Android應用內安裝app然後開啟其他app的一些記錄(相容至Android O)

關於Android應用內安裝app然後開啟其他app的一些記錄(相容至Android O)

        前段時間個人專案裡需要加入一個預覽word文件的功能,大致流程就是把這個word下載下來,然後用Tencent的X5核心開啟。很簡單對吧,可是讓人頭疼的是將這個功能整合至專案後,一直提示預覽失敗。然而我自己單獨寫的集成了X5核心的demo確是完美開啟,很無語。由於交付在即,於是乎有一個想法:將這個demo作為一個外掛app放在專案裡,然後在需要用到這個功能的時候安裝然後使用。搜尋引擎上有很多這樣的程式碼,可是比較凌亂,相容度差,甚至部分都沒有實踐過。所以這裡總結了一下

       應用內安裝主要需要注意的有兩點:

       1. 在Android N 之後的裝置對於許可權操控比較嚴格,尤其是資料讀取共享這一塊,需要藉助FileProvider

       2. 在Android O 之後的裝置對於應用內安裝app這個部分做了限制,為了保護使用者安全,需要開發者向用戶去動態申請

       話不多說,上關鍵程式碼 :

       provider的註冊:

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

       關於file_paths :

             在res資料夾下建立xml資料夾,然後新增這個file_paths,接著檔案內新增下面程式碼

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <root-path
        name="root_path"
        path="." />
</paths>

       上述步驟完成後,將目標app放在assets下 ,接下來就是安裝的部分了:為了適配O之後的裝置,我們需要在清單配置檔案內宣告一個許可權:

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

        然後是如下常見許可權:

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>

       這樣基本工作就做完了,接下是讀取assets下的app 及 安裝:

 public static void prepareInstall(Context context) {
        AssetManager assets = context.getAssets();
        try {
            InputStream stream = assets.open("app-debug.apk");
            if (stream == null) {
                Log.e("error", "no file");
                return;
            }
            String folder = "/mnt/sdcard/sm/";
            File f = new File(folder);
            if (!f.exists()) {
                f.mkdir();
            }
            String apkPath = "/mnt/sdcard/sm/app-debug.apk";
            File file = new File(apkPath);
            file.createNewFile();
            writeStreamToFile(stream, file);
            installApk(context, new File(apkPath));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    private static void writeStreamToFile(InputStream stream, File file) {
        try {
            OutputStream output = null;
            try {
                output = new FileOutputStream(file);
            } catch (FileNotFoundException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            try {
                try {
                    final byte[] buffer = new byte[1024];
                    int read;
                    while ((read = stream.read(buffer)) != -1)
                        output.write(buffer, 0, read);
                    output.flush();
                } finally {
                    output.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } finally {
            try {
                stream.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }


    private static void installApk(Context context, File file) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri data;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            data = FileProvider.getUriForFile(context,
                    "**.provider",
                    file);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            data = Uri.fromFile(file);
        }
        intent.setDataAndType(data, "application/vnd.android.package-archive");
        context.startActivity(intent);
    }

       注意!!!, 申請許可權的時候,除了讀寫以外還要額外新增請求安裝未知來源程式的許可權,也是為O之後的裝置準備的:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
       val haveInstallPermission = packageManager.canRequestPackageInstalls()
       if (!haveInstallPermission) {
           val packageURI = Uri.parse("package:$packageName")
           val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI)
           startActivity(intent)
       }
}

       這樣下來,基本就ok了。然後開啟app很簡單:

val packageName = "目標應用的包名"
val activity = "目標應用的class類名"
val component = ComponentName(packageName, activity)
val intent = Intent()
intent.component = component
startActivity(intent)

       完工!至於為什麼貼兩種語言,主要是最近和女票分手了,很傷心,不想翻譯了