- 左边控制栏选择 Release managerment ,然后选择 App Release,最后选择Internal test 的MANAGE INTERNAL TEST发布内部测试版本。
- 在内部测试里创建新的发布版本:将GooglePlay版本的Apk上传,上传完毕后,点击Apk右侧添加更多按钮,将obb文件提交上去,注意obb文件的命名版本号必须与上传的apk的版本号一致,否则会收到提交版本失败的错误。推荐大家使用不可能用在线上版本的versionCode进行测试,比如手机号码、女朋友生日等,以免后续提交正式版本时版本号被占用(不知道为什么GooglePlay的内部测试和正式发布的版本号竟然不能重复)。
-
填写剩下内容并发。,回到内部测试管理界面,选择管理测试者,将需要测试的Google账号提交上去,并将“Opt-in URL”的地址复制下来。
-
在测试机上登录测试账号,在浏览器里打开刚刚的“Opt-in URL”地址,即可加入内测,并可以通过Google Play App下载测试版本的App。
-
下载完成后,可以在/Android/obb/App包名/下看到一份崭新的obb文件。
解压和下载
- 解压
第一次安装完app后,需要将obb文件进行解压并将解压后的文件存储到我们定义的文件夹里(可以是data/data/包名/files/也可以是内置存储下自定义的项目文件夹)。要想解压obb文件,第一步是获取obb文件的本地路径,具体代码如下:
public static String getObbFilePath(Context context) {
try {
return Environment.getExternalStorageDirectory().getAbsolutePath()
- “/Android/obb/”
- context.getPackageName()
- File.separator
- “main.”
- context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode
- “.”
- context.getPackageName()
- “.obb”;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return null;
}
}
拿到obb文件路径后,可以开始进行解压了:
public static void unZipObb(Context context) {
String obbFilePath = getObbFilePath(context);
if (obbFilePath == null) {
return;
} else {
File obbFile = new File(obbFilePath);
if (!obbFile.exists()) {
//下载obb文件
} else {
File outputFolder = new File(“yourOutputFilePath”);
if (!outputFolder.exists()) {
//目录未创建 没有解压过
outputFolder.mkdirs();
unZip(obbFile, outputFolder.getAbsolutePath());
} else {
//目录已创建 判断是否解压过
if (outputFolder.listFiles() == null) {
//解压过的文件被删除
unZip(obbFile, outputFolder.getAbsolutePath());
}else {
//此处可添加文件对比逻辑
}
}
}
}
}
谷歌官方有提供解压obb文件的库供开发者使用,叫做APK Expansion Zip Library,感兴趣的小伙伴可以在一下路径下查看。
/extras/google/google_market_apk_expansion/zip_file/
笔者不推荐使用该库,原因是这个库已经编写了有一些年头了,当时编译的sdk版本比较低,有一些兼容性的bug需要开发者修改代码后才能使用。所以这里使用的upzip方法是用最普通的ZipInputStream和FileOutputStream解压zip包的方式来实现的:
//这里没有添加解压密码逻辑,小伙伴们可以自己修改添加以下
public static void unzip(File zipFile, String outPathString) throws IOException {
FileUtils.createDirectoryIfNeeded(outPathString);
ZipInputStream inZip = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry zipEntry;
String szName;
while ((zipEntry = inZip.getNextEntry()) != null) {
szName = zipEntry.getName();
if (zipEntry.isDirectory()) {
szName = szName.substring(0, szName.length() - 1);
File folder = new File(outPathString + File.separator + szName);
folder.mkdirs();
} 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 package和Google 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”);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后
简历首选内推方式,速度快,效率高啊!然后可以在拉钩,boss,脉脉,大街上看看。简历上写道熟悉什么技术就一定要去熟悉它,不然被问到不会很尴尬!做过什么项目,即使项目体量不大,但也一定要熟悉实现原理!不是你负责的部分,也可以看看同事是怎么实现的,换你来做你会怎么做?做过什么,会什么是广度问题,取决于项目内容。但做过什么,达到怎样一个境界,这是深度问题,和个人学习能力和解决问题的态度有关了。大公司看深度,小公司看广度。大公司面试你会的,小公司面试他们用到的你会不会,也就是岗位匹配度。
选定你想去的几家公司后,先去一些小的公司练练,学习下面试技巧,总结下,也算是熟悉下面试氛围,平时和同事或者产品PK时可以讲得头头是道,思路清晰至极,到了现场真的不一样,怎么描述你所做的一切,这绝对是个学术性问题!
面试过程一定要有礼貌!即使你觉得面试官不尊重你,经常打断你的讲解,或者你觉得他不如你,问的问题缺乏专业水平,你也一定要尊重他,谁叫现在是他选择你,等你拿到offer后就是你选择他了。
金九银十面试季,跳槽季,整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。**
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-zoBpVZJt-1712539129638)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!