2024年Go最全Google Play渠道超过100M?尝试APK分包!(1),一个小例子彻底搞懂Golang的MVP模式到底是什么

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

} else {
File file = new File(outPathString + File.separator + szName);
FileUtils.createDirectoryIfNeeded(file.getParent());
file.createNewFile();
FileOutputStream out = new FileOutputStream(file);
int len;
byte[] buffer = new byte[1024];
while ((len = inZip.read(buffer)) != -1) {
out.write(buffer, 0, len);
out.flush();
}
out.close();
}
}
inZip.close();
}

public static String createDirectoryIfNeeded(String folderPath) {
File folder = new File(folderPath);
if (!folder.exists() || !folder.isDirectory()) {
folder.mkdirs();
}
return folderPath;
}

解压完成后,就可以通过输出文件的路径来访问到我们需要访问的大容量资源了,文件的读取在这里就不展开了。

  • 下载obb

从Google Play下载和安装App有一定概率会下载到不包含obb文件的apk,或者obb文件被人为删除了。这种情况下,需要开发者到谷歌提供的下载地址处下载相应的obb文件。可是要怎么获取到下载地址呢,这里使用了官方的Downloader Library

这个库可以通过Android Sdk Manager下载到,打开manager后勾上Google Play Licensing Library packageGoogle Play APK Expansion Library package点下载即可。可是在我兴高采烈准备大干一场的时候,发现它竟然编译不过[捂脸]。这个库和上面说的APK Expansion Zip Library一样,由于年代久远又年久失修,基本不能使用了。折腾了一些时间后,魔改了一个版本,才终于可以使用。 这里提供一个编译好的jar包google_apk_expand_helper。具体代码如下:

//随机byte数组,随便填就好
private static final byte[] salt = new byte[]{18, 22, -31, -11, -54, 18, -101, -32, 43, 2, -8, -4, 9, 5, -106, -17, 33, 44, 3, 1};

private static final String TAG = “Obb”;

public static void getObbUrl(Context context, String publicKey) {
final APKExpansionPolicy aep = new APKExpansionPolicy(
context,
new AESObfuscator(salt,
context.getPackageName(),
Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID)
));
aep.resetPolicy();

final LicenseChecker checker = new LicenseChecker(context, aep, publicKey);

checker.checkAccess(new LicenseCheckerCallback() {
@Override
public void allow(int reason) {
Log.i(TAG, “allow:” + reason);
if (aep.getExpansionURLCount() > 0) {
//这里就是获取到的地址
String url = aep.getExpansionURL(0);
}
}

@Override
public void dontAllow(int reason) {
Log.i(TAG, “dontAllow:” + reason);
}

@Override
public void applicationError(int errorCode) {
Log.i(TAG, “applicationError:” + errorCode);
}
});
}

上述方法中需要提供参数publicKey,这个publicKey可以在GooglePlayConsole中找到。

  • 小结

掌握了上述的方法我们就已经完成了Apk分包的主要流程了,以下内容将举例说明如果通过配置gradle文件进行多渠道打包,如何在每次打包的时候自动将大容量资源文件压缩成obb等。

多渠道与自动化

  • 例子

假设我们现在需要发布一个超过100M的安装包到GooglePlay以及应用宝,对于GooglePlay来说,我们需要生成小于100M的apk文件和obb文件,而对于应用宝来说,只需要生成一个完整的apk即可。

那么问题来了,我们不可能说在打包GooglePlay的时候将资源文件手动移除并修改资源引用的相关逻辑,然后再在打包应用宝的时候将他们放回来,这样做会大大增加开发者的工作量并且增大出错的可能性。那有没有办法在单个工程项目下既能打包GooglePlay的包又可以打包应用宝的包呢?答案是有的,build.gradle中的sourceSets就可以解决这样的问题。

  • 利用sourceSets隔离渠道资源和资源引用代码

假设我们有一个splash.mp4文件,在应用宝中渠道包中,它被放在了res/raw/目录下。而在googlePlay渠道包中,它被放置在obb文件里,我们可以这么处理。

首先,在src目录下创建两个新的目录googlePlay和tencent,并在他们的目录下新建java,res和assest文件夹。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在app级别的build.gradle文件中添加GooglePlay和应用宝的渠道信息:

android {
flavorDimensions “default”
productFlavors {

GooglePlay { dimension “default” }
Tencent { dimension “default” }

/** 在AndroidManifest.xml中加入

**/

productFlavors.all { flavor ->
flavor.manifestPlaceholders = [CHANNEL_NAME: name]
}

}
}

紧接其后添加sourceSets配置,指定不同渠道的资源和代码地址,其中main为共有资源和代码,其余的为对应渠道包的资源和代码:

sourceSets {
main {
java.srcDirs = [‘src/main/java’]
assets.srcDirs = [‘src/main/assets’]
res.srcDirs = [‘src/main/res’]
}
GooglePlay {
java.srcDirs = [‘src/googlePlay/java’]
res.srcDirs = [‘src/googlePlay/res’]
assets.srcDirs = [‘src/googlePlay/assets’]
}
Tencent {
java.srcDirs = [‘src/tencent/java’]
res.srcDirs = [‘src/tencent/res’]
assets.srcDirs = [‘src/tencent/assets’]
}
}

将splash.mp4放到tencent/res/raw/文件夹下,并为不同渠道的java文件夹新建包名文件夹以及ResourcesHelper.java,完成后的目录结构如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有两点需要注意的地方:

一是java包下必须创建包名文件夹,否则会无法引用到项目下的类。该例子中就是com.example.obbtest包。

二是AndroidStudio中可以通过左下角的Build Variants窗口选择当前需要编译的渠道包类型,当选择GooglePlay时会发现tencent下的java文件失效了。所以,如果需要修改某渠道下的java文件,请先通过Build Variants切换到指定渠道。

最后,针对不同渠道的ResourcesHelper.java采用不同的资源获取方式:

GooglePlay版本:

public class ResourcesHelper {
public static void playSplashVideoResource(VideoView videoView){
String filePath = ObbHelper.getCurrentObbFileFolder()+“raw/”+“splash.mp4”;
videoView.setVideoPath(filePath);
}
}

tencent版本:

public class ResourcesHelper {
public static void playSplashVideoResource(VideoView videoView) {
int resource = R.raw.splash;
String uri = “android.resource://” + videoView.getContext().getApplicationContext().getPackageName() + “/” + resource;
videoView.setVideoURI(Uri.parse(uri));
}
}

通过sourceSets隔离渠道资源和资源引用代码在这里就完成了,针对更加复杂的场景,就需要小伙伴根据实际情况进行扩展和修改了。下面我们来看一下如何在构建时自动将资源打包成obb文件。

  • 构建时生成obb文件

要在构建时生成obb文件就必须通过添加gradle脚本来实现。我们先在项目目录下新建一个脚本文件flavour.gradle。

然后,要想打包obb文件,就必须知道现在构建的是哪个渠道的包,那要怎么拿到现在的渠道呢,请看代码:

def String getCurrentFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()

Pattern pattern;

if (tskReqStr.contains(“assemble”))
pattern = Pattern.compile(“assemble(\w+)(Release|Debug)”)
else
pattern = Pattern.compile(“generate(\w+)(Release|Debug)”)

Matcher matcher = pattern.matcher(tskReqStr)

if (matcher.find())
return matcher.group(1).toLowerCase()
else {
println “NO MATCH FOUND”
return “”
}
}

我们知道obb的本质就是zip文件,所以只要在flavour.gradle中添加压缩文件的方法,就可以达到生成obb的效果了。由于笔者的Groovy语言不精通,所以这里使用java代码来解决,在flavour.gradle中添加:

import java.util.regex.Matcher
import java.util.regex.Pattern
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream

ext {
zipObb = this.&zipObb
getCurrentFlavor = this.&getCurrentFlavor
}

//外部压缩方法入口,参数是所有需要压缩文件的目录以及输出路径,同样没有添加压缩密码逻辑,小伙伴们需要的自己添加吧
def static zipObb(File[] fs, String zipFilePath) {
if (fs == null) {
throw new NullPointerException(“fs == null”);
}
ZipOutputStream zos = null;
try {
zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFilePath)));
for (File file : fs) {
if (file == null || !file.exists()) {
continue;
}
compress(file, zos, file.getName());
}
zos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if(zos != null){
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

//内部递归压缩方法
def static compress(File sourceFile, ZipOutputStream zos, String name) throws Exception {
byte[] buf = new byte[2048];
if (sourceFile.isFile()) {
// 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
zos.putNextEntry(new ZipEntry(name));
// copy文件到zip输出流中
int len;
FileInputStream inputStream = new FileInputStream(sourceFile);
while ((len = inputStream.read(buf)) != -1) {
zos.write(buf, 0, len);
}
// Complete the entry
zos.closeEntry();
inputStream.close();
} else {
File[] listFiles = sourceFile.listFiles();
if (listFiles == null || listFiles.length == 0) {
// 需要保留原来的文件结构时,需要对空文件夹进行处理
zos.putNextEntry(new ZipEntry(name + “/”));
// 没有文件,不需要文件的copy
zos.closeEntry();
} else {
for (File file : listFiles) {
compress(file, zos, name + “/” + file.getName());
}
}
}
}
def String getCurrentFlavor() {

}

我们已经在flavour.gradle中添加了获取当前渠道和压缩文件的方法了,现在回到app下的build.gradle文件中,通过判断当前渠道是否GooglePaly,对需要压缩的所有文件进行压缩,并输出到googlePlay渠道包apk的同级目录下:

apply from: “…/flavour.gradle”

//添加到文件最后
//自动打包扩展文件obb
task zipObb(type: JavaExec) {
//判断是否GooglePlay渠道包,获取渠道包的时候做了小写处理
if (getCurrentFlavor().equals(“googleplay”)) {

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

(img-IfRLgQ9p-1715876225992)]
[外链图片转存中…(img-2frO0vao-1715876225992)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值