Is it possible to merge/install split APK files (AKA “app bundle”), on Android device itself, without root?

后端 未结 8 1254
北恋
北恋 2020-12-09 02:50

Background

In the past, I\'ve asked about sharing or backup of app-bundle / split apk files, here .

This seems like an almost impossible task, which I coul

8条回答
  •  离开以前
    2020-12-09 03:26

    If you have root, you can use this code.

    Please get the read/write sdcard permission.(via runtime permissions or permission granted from settings app) before executing this code. airbnb apk was successfully installed after running this code.

    Calling this function with args "/split-apks/" , I have placed the airbnb split apks in a directory in /sdcard/split-apks/.

    installApk("/split-apks/");
    
    
     public void installApk(String apkFolderPath)
    {
        PackageInstaller packageInstaller =  getPackageManager().getPackageInstaller();
        HashMap nameSizeMap = new HashMap<>();
        long totalSize = 0;
    
        File folder = new File(Environment.getExternalStorageDirectory().getPath()+ apkFolderPath);
        File[] listOfFiles = folder.listFiles();
        for (int i = 0; i < listOfFiles.length; i++) {
            if (listOfFiles[i].isFile()) {
                System.out.println("File " + listOfFiles[i].getName());
                nameSizeMap.put(listOfFiles[i].getName(),listOfFiles[i].length());
                totalSize += listOfFiles[i].length();
            }
        }
    
        String su = "/system/xbin/su";
    
    
        final String[] pm_install_create = new String[]{su, "-c", "pm" ,"install-create", "-S", Long.toString(totalSize) };
        execute(null, pm_install_create);
    
        List sessions = packageInstaller.getAllSessions();
    
        int sessId = sessions.get(0).getSessionId();
    
        String sessionId = Integer.toString(sessId);
    
    
        for(Map.Entry entry : nameSizeMap.entrySet())
        {
            String[] pm_install_write = new String[]{su, "-c", "pm" ,"install-write", "-S", Long.toString(entry.getValue()),sessionId, entry.getKey(), Environment.getExternalStorageDirectory().getPath()+apkFolderPath+ entry.getKey()};
    
            execute(null,pm_install_write);
    
        }
    
        String[] pm_install_commit  = new String[]{su, "-c", "pm" ,"install-commit", sessionId};
    
    
        execute(null, pm_install_commit);
    
    }
    public String execute(Map environvenmentVars, String[] cmd) {
    
        boolean DEBUG = true;
        if (DEBUG)
            Log.d("log","command is " + Arrays.toString(cmd));
    
        try {
            Process process = Runtime.getRuntime().exec(cmd);
            if (DEBUG)
                Log.d("log", "process is " + process);
    
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            if (DEBUG)
                Log.d("log", "bufferreader is " + reader);
    
            if (DEBUG)
                Log.d("log", "readline " + reader.readLine());
            StringBuffer output = new StringBuffer();
    
            char[] buffer = new char[4096];
            int read;
    
            while ((read = reader.read(buffer)) > 0) {
                output.append(buffer, 0, read);
            }
    
            reader.close();
    
            process.waitFor();
            if (DEBUG)
                Log.d("log", output.toString());
    
            return output.toString();
    
        }
    
        catch (Exception e)
        {
            e.printStackTrace();
        }
    
        return null;
    
    }
    

    EDIT: same code, but in Kotlin, as it's shorter:

    sample usage:

    Foo.installApk(context,fullPathToSplitApksFolder)
    

    Example:

            AsyncTask.execute {
                Foo.installApk(this@MainActivity,"/storage/emulated/0/Download/split")
            }
    

    Code:

    object Foo {
        @WorkerThread
        @JvmStatic
        fun installApk(context: Context, apkFolderPath: String) {
            val packageInstaller = context.packageManager.packageInstaller
            val nameSizeMap = HashMap()
            var totalSize: Long = 0
            val folder = File(apkFolderPath)
            val listOfFiles = folder.listFiles().filter { it.isFile && it.name.endsWith(".apk") }
            for (file in listOfFiles) {
                Log.d("AppLog", "File " + file.name)
                nameSizeMap[file] = file.length()
                totalSize += file.length()
            }
            val su = "su"
            val pmInstallCreate = arrayOf(su, "-c", "pm", "install-create", "-S", totalSize.toString())
            execute(pmInstallCreate)
            val sessions = packageInstaller.allSessions
            val sessionId = Integer.toString(sessions[0].sessionId)
            for ((file, value) in nameSizeMap) {
                val pmInstallWrite = arrayOf(su, "-c", "pm", "install-write", "-S", value.toString(), sessionId, file.name, file.absolutePath)
                execute(pmInstallWrite)
            }
            val pmInstallCommit = arrayOf(su, "-c", "pm", "install-commit", sessionId)
            execute(pmInstallCommit)
        }
    
        @WorkerThread
        @JvmStatic
        private fun execute(cmd: Array): String? {
            Log.d("AppLog", "command is " + Arrays.toString(cmd))
            try {
                val process = Runtime.getRuntime().exec(cmd)
                Log.d("AppLog", "process is $process")
                val reader = BufferedReader(InputStreamReader(process.inputStream))
                Log.d("AppLog", "bufferreader is $reader")
                Log.d("AppLog", "readline " + reader.readLine())
                val output = StringBuilder()
                val buffer = CharArray(4096)
                var read: Int
                while (true) {
                    read = reader.read(buffer)
                    if (read <= 0)
                        break
                    output.append(buffer, 0, read)
                }
                reader.close()
                process.waitFor()
                Log.d("AppLog", output.toString())
                return output.toString()
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return null
        }
    }
    

提交回复
热议问题