關於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)
完工!至於為什麼貼兩種語言,主要是最近和女票分手了,很傷心,不想翻譯了