XAPK安装需求
xapk是将apk和其他文件打包成一个压缩文件,以.xapk格式结尾。调试时可以将.xapk后缀直接改为zip后缀,然后使用解压缩工具解压xapk包。通常能看到多个apk文件和一些资源配置文件,以Spotify.xapk包为例。
xapk安装
1、adb install-multiple调试是否可安装
2、代码实现
- 解压xapk文件到一个新目录:unzip the file to a new folder.
上层可使用原生API方法ZipFile类或ZipInputStream类进行解压缩。
- 执行多次apk安装:install multiple.
- private static boolean installXApk(Context context, File outputDirectory) {
- if (outputDirectory == null || !outputDirectory.exists()) return false;
- Log.d(TAG, "installXApk outputDirectory = " + outputDirectory.getPath());
- PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
- // 创建SessionParams对象,设置APK文件的安装参数
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- //params.setAppPackageName(""); // 如果需要,设置目标应用的包名
- // 尝试创建新的安装会话
- int sessionId;
- try {
- sessionId = packageInstaller.createSession(params);
- } catch (IOException e) {
- Log.e(TAG, "Failed to create session", e);
- return false;
- }
- File[] apkFiles = outputDirectory.listFiles();
- if (apkFiles == null) {
- return false;
- }
- // 打开安装会话
- PackageInstaller.Session session = null;
- try {
- session = packageInstaller.openSession(sessionId);
- } catch (IOException e) {
- e.printStackTrace();
- }
- for (File apkFile : apkFiles) {
- if (!apkFile.getName().endsWith(".apk")) continue;
- Log.d(TAG, "installApk: name = " + apkFile.getName());
- OutputStream out = null;
- try {
- session = packageInstaller.openSession(sessionId);
- out = session.openWrite(apkFile.getName(), 0, apkFile.length());
- FileInputStream in = new FileInputStream(apkFile);
- byte[] buffer = new byte[65536];
- int c;
- while ((c = in.read(buffer)) != -1) {
- out.write(buffer, 0, c);
- }
- session.fsync(out);
- } catch (IOException e) {
- Log.e(TAG, "Failed to write APK to session", e);
- if (session != null) session.abandon(); //如果写入失败,放弃会话
- return false;
- } finally {
- try {
- if (out != null) {
- out.close();
- }
- } catch (IOException e) {
- Log.e(TAG, "Failed to close output stream", e);
- }
- }
- }
- if (session != null) {
- PendingIntent intent = PendingIntent.getBroadcast(context, 0, new Intent("com.example.install"), PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
- session.commit(intent.getIntentSender());
- Log.d(TAG, "installXApk commit sessionId = " + sessionId);
- }
- return true;
- }
复制代码
多用户安装
通常我们安装apk都是在给当前用户安装,该需求必要能做到跨用户安装,满足如下要求:
- 在user用户下为当前用户安装apk
- 在当前用户下为其他用户安装apk
1、在满足android:sharedUserId="android.uid.system"和体系platform签名的情况下,通过 pm install --user 指定用户来安装apk的方式理论可行。但在实现过程中会遇到data目录selinux权限以及违反neverallow规则等一系列标题。
2、PackageInstaller.Session传递apk路径方式默认安装在主用户下,无法直接为子用户安装apk。
目前找到的跨用户方式是通过机器已存在的packageName包名进行安装。
- public static boolean silentInstallAsUser(Context context, String packageName, int userId) {
- Log.i(TAG, "silentInstallAsUser packageName = " + packageName + ", userId = " + userId);
- PackageManager pm = context.getPackageManager();
- try {
- int status = pm.installExistingPackageAsUser(packageName, userId);
- if (status == PackageManager.INSTALL_SUCCEEDED) {
- Log.d(TAG,"Install succeed");
- return true;
- } else {
- Log.d(TAG,"Install failed, result code = " + status);
- return false;
- }
- } catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
- }
- return false;
- }
复制代码 3、最终思路是通过区分两种情况(主用户安装/跨用户安装)来执行不同安装:
- public boolean installMultuserApp(int userId, String path) {
- boolean ret = false;
- try {
- int currentUserId = ActivityManager.getCurrentUser();
- if (userId == 0) { //主用户静默安装
- ret = SilentInstallManager.silentInstallApk(mContext, path);
- } else { //跨用户静默安装
- String packageName = SilentInstallManager.getPackageName(mContext, path);
- if (!TextUtils.isEmpty(packageName)) {
- boolean hasPackageName = SilentInstallManager.isAppInstalled(mContext, packageName);
- Log.i(TAG, "installMultuserApp hasPackageName = " + hasPackageName);
- if (hasPackageName) { //若存在该包名则直接安装
- ret = SilentInstallManager.silentInstallAsUser(mContext, packageName, userId);
- } else { //若不存在该包名则先安装在主用户,再安装到其它用户,最后再卸载主用户的包名
- boolean mainStatus = SilentInstallManager.silentInstallApk(mContext, path);
- if (mainStatus) {
- //Thread.sleep(8000);
- ret = SilentInstallManager.silentInstallAsUser(mContext, packageName, userId);
- }
- //最后再卸载主用户的包名
- SilentInstallManager.silentUninstallAsUser(mContext, packageName, 0);
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return ret;
- }
复制代码 4、参考链接
- https://android.stackexchange.com/questions/221204/how-to-install-xapk-apks-or-multiple-apks-via-adb
- https://alysoninno.cn/android/install-apk-splits-package
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |