当前位置: 首页 > 知识库问答 >
问题:

是否可以在Android设备上合并/安装拆分的APK文件(又名“app bundle”),而不需要root?

杨赞
2023-03-14

在过去,我曾经问过关于app-bundle/split apk文件的共享或备份的问题。

这似乎是一个几乎不可能完成的任务,我只能弄清楚如何安装拆分的APK文件,即使这样也只能通过adb:

adb install-multiple apk1 apk2 ...

我被告知实际上应该可以将多个拆分的APK文件合并到一个我可以安装的文件中(这里),但没有给出如何做到这一点。

事实上,这是一个如此重大的问题,以至于我不知道有任何备份应用可以处理拆分的APK文件(应用包),这包括钛应用。

我拿了一个使用app-bundles的示例app,叫做“爱彼迎”。

看看它拥有的文件,这些就是Play Store决定下载的:

  • “META-INF”
  • “resources.arsc”
  • “AndroidManifest.xml”
  • 对于具有“xxxhdpi”的文件夹,我还获得“res”文件夹。

事情是,由于这些都存在于多个地方,我不知道我怎么能合并他们。

>

  • 将所有这些合并到一个APK文件中的方法是什么?

    编辑:可悲的是,这里的所有解决方案都不起作用,不管有没有root,即使我找到了一个成功做到这一点的应用程序(有没有root),名为“SAI(Split APKs Installer)”(我想它的存储库就在这里,是我花了一大笔钱后找到的)。

    我要给你一笔新的赏金。无论谁发布了一个新的答案,请表明它在使用和不使用root时都是有效的。如果需要,请在Github上显示(这里只是重要的内容)。我知道这个应用程序是开源的,但对我来说,如何在这里实现它并与其他人共享是很重要的,因为目前这里显示的东西不起作用,并且需要root,尽管它并不是真的需要。

    这一次,我不会授予赏金,直到我看到一些确实有效的东西(以前我没有时间,并授予它的答案,我认为应该工作)。

  • 共有1个答案

    澹台鸿熙
    2023-03-14

    请查一下这个。当我们发送

    adb install-multiple apk1 apk2 ...
    

    它将此代码称为install-multiple

     std::string install_cmd;
        if (_use_legacy_install()) {
            install_cmd = "exec:pm";
        } else {
            install_cmd = "exec:cmd package";
        }
    
        std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
        for (i = 1; i < first_apk; i++) {
            cmd += " " + escape_arg(argv[i]);
        }
    

    它又调用pm.java或一种执行PackageManagerService代码的新方法,两者都是相似的

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

    >

  • 创建带有参数-s的会话,返回会话ID。

    (install-create,-s,52488426)52488426--APK总大小。

    在该会话中写入具有大小、名称和路径的拆分APK

    使用会话id提交会话

    (安装-提交,824704264)

    我已经把爱彼迎apk放在我的SDCard里了。

    OnePlus5:/sdcard/com.airbnb.android-1 $ ll
    total 51264
    -rw-rw---- 1 root sdcard_rw 44334187 2019-04-01 14:20 base.apk
    -rw-rw---- 1 root sdcard_rw  1262034 2019-04-01 14:20 split_config.en.apk
    -rw-rw---- 1 root sdcard_rw   266117 2019-04-01 14:20 split_config.hdpi.apk
    -rw-rw---- 1 root sdcard_rw  6626088 2019-04-01 14:20 split_config.x86.apk
    
    final InstallParams installParams = makeInstallParams(52488426l);
    
                try {
                    int sessionId = runInstallCreate(installParams);
    
                    runInstallWrite(44334187,sessionId, "1_base.apk", "/sdcard/com.airbnb.android-1/base.apk");
    
                    runInstallWrite(1262034,sessionId, "2_split_config.en.apk", "/sdcard/com.airbnb.android-1/split_config.en.apk");
    
                    runInstallWrite(266117,sessionId, "3_split_config.hdpi.apk", "/sdcard/com.airbnb.android-1/split_config.hdpi.apk");
    
                    runInstallWrite(6626088,sessionId, "4_split_config.x86.apk", "/sdcard/com.airbnb.android-1/split_config.x86.apk");
    
    
                    if (doCommitSession(sessionId, false )
                            != PackageInstaller.STATUS_SUCCESS) {
                    }
                    System.out.println("Success");
    
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
    
    private int runInstallCreate(InstallParams installParams) throws RemoteException {
        final int sessionId = doCreateSession(installParams.sessionParams);
        System.out.println("Success: created install session [" + sessionId + "]");
        return sessionId;
    }
    
    private int doCreateSession(PackageInstaller.SessionParams params)
            throws RemoteException {
    
        int sessionId = 0 ;
        try {
            sessionId = packageInstaller.createSession(params);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sessionId;
    }
    
    private int runInstallWrite(long size, int sessionId , String splitName ,String path ) throws RemoteException {
        long sizeBytes = -1;
    
        String opt;
        sizeBytes = size;
        return doWriteSession(sessionId, path, sizeBytes, splitName, true /*logSuccess*/);
    }
    
    
    private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName,
                               boolean logSuccess) throws RemoteException {
        if ("-".equals(inPath)) {
            inPath = null;
        } else if (inPath != null) {
            final File file = new File(inPath);
            if (file.isFile()) {
                sizeBytes = file.length();
            }
        }
    
        final PackageInstaller.SessionInfo info = packageInstaller.getSessionInfo(sessionId);
    
        PackageInstaller.Session session = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            session = packageInstaller.openSession(sessionId);
    
            if (inPath != null) {
                in = new FileInputStream(inPath);
            }
    
            out = session.openWrite(splitName, 0, sizeBytes);
    
            int total = 0;
            byte[] buffer = new byte[65536];
            int c;
            while ((c = in.read(buffer)) != -1) {
                total += c;
                out.write(buffer, 0, c);
            }
            session.fsync(out);
    
            if (logSuccess) {
                System.out.println("Success: streamed " + total + " bytes");
            }
            return PackageInstaller.STATUS_SUCCESS;
        } catch (IOException e) {
            System.err.println("Error: failed to write; " + e.getMessage());
            return PackageInstaller.STATUS_FAILURE;
        } finally {
            try {
                out.close();
                in.close();
                session.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    
    private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
        PackageInstaller.Session session = null;
        try {
            try {
                session = packageInstaller.openSession(sessionId);
            } catch (IOException e) {
                e.printStackTrace();
            }
            session.commit(PendingIntent.getBroadcast(getApplicationContext(), sessionId,
                    new Intent("android.intent.action.MAIN"), 0).getIntentSender());
            System.out.println("install request sent");
    
            Log.d(TAG, "doCommitSession: " + packageInstaller.getMySessions());
    
            Log.d(TAG, "doCommitSession: after session commit ");
    
            return 1;
        } finally {
            session.close();
        }
    }
    
    
    
    private static class InstallParams {
        PackageInstaller.SessionParams sessionParams;
    }
    
    private InstallParams makeInstallParams(long totalSize ) {
        final PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        final InstallParams params = new InstallParams();
        params.sessionParams = sessionParams;
        String opt;
        sessionParams.setSize(totalSize);
        return params;
    }
    
    04-01 16:04:40.626  4886  4886 D Pm      : run() called with: args = [[install-create, -S, 52488426]]
    04-01 16:04:41.862  4897  4897 D Pm      : run() called with: args = [[install-write, -S, 44334187, 824704264, 1_base.apk, -]]
    04-01 16:04:56.036  4912  4912 D Pm      : run() called with: args = [[install-write, -S, 1262034, 824704264, 2_split_config.en.apk, -]]
    04-01 16:04:57.584  4924  4924 D Pm      : run() called with: args = [[install-write, -S, 266117, 824704264, 3_split_config.hdpi.apk, -]]
    04-01 16:04:58.842  4936  4936 D Pm      : run() called with: args = [[install-write, -S, 6626088, 824704264, 4_split_config.x86.apk, -]]
    04-01 16:05:01.304  4948  4948 D Pm      : run() called with: args = [[install-commit, 824704264]]
    

  •  类似资料:
    • 无法在我的手机上安装已签名的发行版APK,也无法将其上载到Play Store上。 我创建了一个带有V1和V2 JAR签名复选框的签名发行版APK。当我尝试将APK放在我的手机上并安装它时: 未安装应用程序 没有JAR签名。 我使用的是Android Studio3.3版本,我正在处理一个Play Store Jar签名错误(重复选中V1和V2复选框,并且都选中了)。 播放存储错误: 这可能是一个

    • 几天前,我在尝试安装时遇到问题。我的银河系S2上的apk。我选择我的手机作为目标,单击“确定”,然后在控制台中出现以下错误: 无法安装AvatarRun。设备“device number”上的apk:超时启动已取消! 在不更改代码并再次运行的情况下,我也可以得到错误: 无法安装AvatarRun。设备“device number”上的apk:找不到设备com。Androidddmlib。Insta

    • 我在尝试以编程方式安装apk并在安装后重新启动Android emulator时遇到了一些问题。我指的是这条线。 这是我的代码: 有没有办法安装apk而不启动意图?因为我正在AsyncTask的中执行上述方法。然后在中,我需要显示一个片段,说明安装成功。 然而,对于上面的代码,在调用时,它只是关闭了我的所有片段。

    • 我想通过将文件写入内部存储来检查设备上是否安装了应用程序。稍后,当用户甚至重新安装应用程序时,我可以检查文件是否存在(我会将文件写入一些用户不会在意的系统文件夹),以查看用户是否以前安装过应用程序。我只是想知道这是可行的,还是Android反对这样做?我需要用户的任何权限才能执行此操作吗? 我不想使用任何硬件特定的标识符,如IMEI或Android ID。我也不想使用实例ID,因为它会在重新安装应

    • 我正在写一个jar,打算与Spring和Ehcache一起使用。Spring要求为每个元素定义一个缓存,所以我计划为jar定义一个Ehcache,最好是作为jar中的一个资源,可以导入应用程序的主要Ehcache配置。然而,我对示例Ehcache配置文件的阅读和我的谷歌搜索并没有找到任何导入子Ehcache配置文件的方法。 有没有办法导入一个子Ehache配置文件,或者有没有其他方法来解决这个问题

    • 本文向大家介绍Android安装apk文件并适配Android 7.0详解,包括了Android安装apk文件并适配Android 7.0详解的使用技巧和注意事项,需要的朋友参考一下 Android安装apk文件并适配Android 7.0详解 首先在AndroidManifest.xml文件,activity同级节点注册provider: 将apk文件下载到此路径: 在res目录xml文件夹下创