当前位置:   article > 正文

【Google Play】APK 扩展包 ( 2021年09月 最新处理方案 | 文件准备 | 拷贝文件至内置存储 | 解压及使用扩展文件 )_下载google paly压缩包怎么办

下载google paly压缩包怎么办

前言

在上一篇博客 【Google Play】APK 扩展包 ( 2021年09月02日最新处理方案 | 内部测试链接 | 安装 Google Play 中带 扩展文件 的 APK 安装包 | 验证下载的扩展文件 ) 中 , 成功从 Google Play 中下载了 APK 安装包 及 APK 扩展文件 ;

APK 扩展文件 , 成功下载到了 /sdcard/Android/obb/com.exapmple.app/main.6.com.example.app.obb 路径中

在这里插入图片描述





一、文件准备



在本案例中 , 需要使用到 /sdcard/Android/obb/com.exapmple.app/main.6.com.example.app.obb 文件 , 如果没有条件从 Google Play 中下载应用的话 , 可以创建 /sdcard/Android/obb/com.exapmple.app/ 目录 , 将 源码 根目录中的 main.6.com.example.app.obb 文件 , 拷贝到上述目录中 ;

在这里插入图片描述

在下图所示的路径 SD 卡下的 Android/obb 目录下创建 com.example.app 目录 , 然后将
main.6.com.example.app.obb 文件拷贝到该目录中 ;

在这里插入图片描述

在 Windows 文件系统中操作 ;

在这里插入图片描述

拷贝完毕后的 AS 中文件管理器 ;

在这里插入图片描述





二、拷贝文件至内置存储



文件拷贝前 , 声明 SD 卡权限 ;

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app">

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

    <application>
    </application>

</manifest>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

访问 SD 卡中的 /sdcard/Android/obb/ 目录 , 可以不用申请 SD 卡 运行时 动态访问权限 ; 在 AndroidManifest.xml 清单文件中声明 WRITE_EXTERNAL_STORAGE 和 READ_EXTERNAL_STORAGE 权限即可 ;


将 APK 扩展文件 , 拷贝到 Android 应用的内置存储空间的 cache 目录中 ;

即 将 /sdcard/Android/obb/com.exapmple.app/main.6.com.example.app.obb 文件 拷贝到 /data/data/com.example.app/cache/main.6.com.example.app.obb 目录中 ;


下面的类中 , 提供了 主扩展文件 和 补丁扩展文件 的 文件名拼接方法 ;
参考 【Google Play】APK 扩展包 ( 2021年09月02日最新处理方案 | 扩展文件名格式 | 扩展文件下载存放地址 ) 二、APK 扩展文件名格式 博客章节理解 ;


moveObb2Cache 方法是移动 APK 扩展文件的核心方法 , 从外置 SD 卡移动到了 应用内置存储空间 中 ;


完整的文件拷贝代码示例 :

package com.example.app;

import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 处理 APK 扩展文件
 */
public class ExpansionFileManager {

    public final static String TAG = "ExpansionFileManager";

    private Context mContext;

    public ExpansionFileManager(Context mContext) {
        this.mContext = mContext;
    }

    /**
     * 获取主扩展文件名称 main.6.com.example.app.obb
     *
     * @return
     */
    public String getMainExpansionFileName() {
        return "main." +
                BuildConfig.VERSION_CODE + "." +
                this.mContext.getApplicationInfo().packageName + "." +
                "obb";
    }

    /**
     * 获取补丁扩展文件名称 patch.6.com.example.app.obb
     *
     * @return
     */
    public String getPatchExpansionFileName() {
        return "patch." +
                BuildConfig.VERSION_CODE + "." +
                this.mContext.getApplicationInfo().packageName + "." +
                "obb";
    }

    /**
     * 将 obb 文件移动到内置存储中
     * 将 /sdcard/Android/obb/com.exapmple.app/main.6.com.example.app.obb
     * 移动到 /data/data/com.example.app/cache/main.6.com.example.app.obb 内置存储中
     */
    public void moveObb2Cache() {
        // /sdcard/Android/obb/com.exapmple.app/main.6.com.example.app.obb
        String srcFileName = Environment.getExternalStorageDirectory() +
                "/Android/obb/" +
                this.mContext.getApplicationInfo().packageName +
                "/" +
                getMainExpansionFileName();

        // /data/data/com.example.app/cache/main.6.com.example.app.obb
        String dstFileName = this.mContext.getCacheDir() +
                "/" +
                getMainExpansionFileName();

        Log.i(TAG, "移动文件 : srcFileName = " +
                srcFileName +
                " , dstFileName = " +
                dstFileName);

        File srcFile = new File(srcFileName);
        File dstFile = new File(dstFileName);

        Log.i(TAG, "srcFile = " + srcFile.exists() + " , dstFile = " + dstFile.exists());

        FileInputStream fis = null;
        try {
            fis = new FileInputStream(srcFileName);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(dstFileName);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        byte buffer[] = new byte[1024 * 16];
        int readLen = 0;

        try {
            while ((readLen = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, readLen);
            }
            fis.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            Log.i(TAG, "文件移动完成");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108




三、解压及使用扩展文件



使用 zip 压缩文件工具类 , 对文件进行压缩 , 解压缩 操作 ;

将拷贝到 /data/data/com.example.app/cache/main.6.com.example.app.obb 目录下的文件 , 解压到 /data/data/com.example.app/cache/unzip 目录中 ;

执行下面的代码即可完成文件的移动 及 解压 ;

                // 移动文件
                ExpansionFileManager manager = new ExpansionFileManager(MainActivity.this);
                manager.moveObb2Cache();

                // 解压文件
                File cacheObb = new File(getCacheDir(), manager.getMainExpansionFileName());
                File unzipDir = new File(getCacheDir(), "unzip");
                ZipUtils.unZip(cacheObb, unzipDir);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

文件压缩解压工具类 : 主要调用其 unZip 方法 ;

package com.example.app;

import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class ZipUtils {

    public static final String TAG = "ZipUtils";

    /**
     * 删除文件, 如果有目录, 则递归删除
     */
    private static void deleteFile(File file){
        if (file.isDirectory()){
            File[] files = file.listFiles();
            for (File f: files) {
                deleteFile(f);
            }
        }else{
            file.delete();
        }
    }

    /**
     * 解压文件
     * @param zip 被解压的压缩包文件
     * @param dir 解压后的文件存放目录
     */
    public static void unZipApk(File zip, File dir) {
        try {
            // 如果存放文件目录存在, 删除该目录
            deleteFile(dir);
            // 获取 zip 压缩包文件
            ZipFile zipFile = new ZipFile(zip);
            // 获取 zip 压缩包中每一个文件条目
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            // 遍历压缩包中的文件
            while (entries.hasMoreElements()) {
                ZipEntry zipEntry = entries.nextElement();
                // zip 压缩包中的文件名称 或 目录名称
                String name = zipEntry.getName();
                // 如果 apk 压缩包中含有以下文件 , 这些文件是 V1 签名文件保存目录 , 不需要解压 , 跳过即可
                if (name.equals("META-INF/CERT.RSA") || name.equals("META-INF/CERT.SF") || name
                        .equals("META-INF/MANIFEST.MF")) {
                    continue;
                }
                // 如果该文件条目 , 不是目录 , 说明就是文件
                if (!zipEntry.isDirectory()) {
                    File file = new File(dir, name);
                    //创建目录
                    if (!file.getParentFile().exists()) {
                        file.getParentFile().mkdirs();
                    }
                    // 向刚才创建的目录中写出文件
                    FileOutputStream fos = new FileOutputStream(file);
                    InputStream is = zipFile.getInputStream(zipEntry);
                    byte[] buffer = new byte[2048];
                    int len;
                    while ((len = is.read(buffer)) != -1) {
                        fos.write(buffer, 0, len);
                    }
                    is.close();
                    fos.close();
                }
            }

            // 关闭 zip 文件
            zipFile.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解压文件
     * @param zip 被解压的压缩包文件
     * @param dir 解压后的文件存放目录
     */
    public static void unZip(File zip, File dir) {
        try {
            // 如果存放文件目录存在, 删除该目录
            deleteFile(dir);
            // 获取 zip 压缩包文件
            ZipFile zipFile = new ZipFile(zip);
            // 获取 zip 压缩包中每一个文件条目
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            // 遍历压缩包中的文件
            while (entries.hasMoreElements()) {
                ZipEntry zipEntry = entries.nextElement();
                // zip 压缩包中的文件名称 或 目录名称
                String name = zipEntry.getName();

                Log.i(TAG, "解压文件 " + name);

                // 如果该文件条目 , 不是目录 , 说明就是文件
                if (!zipEntry.isDirectory()) {
                    File file = new File(dir, name);
                    //创建目录
                    if (!file.getParentFile().exists()) {
                        file.getParentFile().mkdirs();
                    }
                    // 向刚才创建的目录中写出文件
                    FileOutputStream fos = new FileOutputStream(file);
                    InputStream is = zipFile.getInputStream(zipEntry);
                    byte[] buffer = new byte[2048];
                    int len;
                    while ((len = is.read(buffer)) != -1) {
                        fos.write(buffer, 0, len);
                    }
                    is.close();
                    fos.close();
                }
            }

            // 关闭 zip 文件
            zipFile.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            Log.i(TAG, "文件解压完成");
        }
    }

    /**
     * 压缩目录为zip
     * @param dir 待压缩目录
     * @param zip 输出的zip文件
     * @throws Exception
     */
    public static void zip(File dir, File zip) throws Exception {
        zip.delete();
        // 对输出文件做CRC32校验
        CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(
                zip), new CRC32());
        ZipOutputStream zos = new ZipOutputStream(cos);
        //压缩
        compress(dir, zos, "");
        zos.flush();
        zos.close();
    }

    /**
     * 添加目录/文件 至zip中
     * @param srcFile 需要添加的目录/文件
     * @param zos   zip输出流
     * @param basePath  递归子目录时的完整目录 如 lib/x86
     * @throws Exception
     */
    private static void compress(File srcFile, ZipOutputStream zos,
                                 String basePath) throws Exception {
        if (srcFile.isDirectory()) {
            File[] files = srcFile.listFiles();
            for (File file : files) {
                // zip 递归添加目录中的文件
                compress(file, zos, basePath + srcFile.getName() + "/");
            }
        } else {
            compressFile(srcFile, zos, basePath);
        }
    }

    private static void compressFile(File file, ZipOutputStream zos, String dir)
            throws Exception {
        // temp/lib/x86/libdn_ssl.so
        String fullName = dir + file.getName();
        // 需要去掉temp
        String[] fileNames = fullName.split("/");
        //正确的文件目录名 (去掉了temp)
        StringBuffer sb = new StringBuffer();
        if (fileNames.length > 1){
            for (int i = 1;i<fileNames.length;++i){
                sb.append("/");
                sb.append(fileNames[i]);
            }
        }else{
            sb.append("/");
        }
        //添加一个zip条目
        ZipEntry entry = new ZipEntry(sb.substring(1));
        zos.putNextEntry(entry);
        //读取条目输出到zip中
        FileInputStream fis = new FileInputStream(file);
        int len;
        byte data[] = new byte[2048];
        while ((len = fis.read(data, 0, 2048)) != -1) {
            zos.write(data, 0, len);
        }
        fis.close();
        zos.closeEntry();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202

文件解压完成后的效果 :

在这里插入图片描述





四、博客资源



GitHub 源码地址 : https://github.com/han1202012/APK_Expansion_File

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/262551
推荐阅读
相关标签
  

闽ICP备14008679号