首先, 官方google play对APK大小有限制: 50M.( https://support.google.com/googleplay/android-developer/answer/113469?hl=en )

所以想通过google play发布大数据的应用的话, 得通过扩展包, 一个叫做OBB(Opaque Binary Blob)的东西, 最大可以存储4G的数据 (国内的奇葩山寨文化就不要管提了).

OBB是app使用的数据文件, google play并不关心其内容, 下载到设备以后, 系统也不关心. 如何处理这个文件, 是由app决定的.比如app可以上传一个mp4文件作为obb, 下载以后读取该OBB文件,然后用mp4解码器播放.

SDK预置的OBB格式, 是一个(压缩的,可加密的)FAT磁盘镜像, 在运行时将OBB挂载到/mnt/XXXX/ 的位置 (XXXX是系统自动生成的字符串路径).

挂载完成以后, 记录下来挂载的位置, 就可以使用 native API 来读取文件了, 这样就可以完全脱离Java的AssetManager或者ZLib.

如何生成OBB文件呢? ADT工具下的jobb就可以.

adt-x86\sdk\tools>jobb -d E:\bin\data -o E:\bin\main.1.com.games.xxx.obb -pv 1 -pn com.games.xxx -k pswd

可以看出可以对OBB加密, 密码为"pswd"

不过这里Jobb有几个bug: 指定的文件夹太小, jobb直接崩溃. 当然它是用于大数据包的, 但是小文件测试也不行?

如果指定的文件夹是10M, 那么可以生成OBB, 但是在android上加载不了, 设备重启也加载不了, 错误代码为无法加载.
将OBB填到20M, 最后可以了.

然后就是code了, AStorageManager就是来管理obb的.

挂载OBB是异步的, 而且需要提供callback function和callback data(可选).callback data是用户定义的,可以是任何数据, 比如我这里是一个app结构指针:

static void Android_ObbCallbackFunc(const char* filename, const int32_t state, void* data)
{
Android_App* app = (Android_App*)data; if( state == AOBB_STATE_MOUNTED )
{
int isMounted = ::AStorageManager_isObbMounted(app->storage, filename);
assert( isMounted != ); const char* mntPath = ::AStorageManager_getMountedObbPath(app->storage, filename); //save persistent path data - current NDK returns tmp string that may even corrupted right after AStorageManager_getMountedObbPath() return
//https://code.google.com/p/android/issues/detail?id=41983
static char mountPath[PATH_MAX];
app->storageRoot = strcpy(mountPath, mntPath); LOGI("OBB mounted: %s", filename);
}
else if( state == AOBB_STATE_UNMOUNTED )
LOGI("OBB unmounted: %s", filename);
else if( state != AOBB_STATE_ERROR_NOT_MOUNTED )
LOGI("Android_ObbCallbackFunc: %d", state);
}

从上面的回调函数可以看到, 如果挂载成功, 就将挂载后的路径保存到app->storageRoot里, 后面的文件读取就可以使用这个路径了.

不过AStorageManager_getMountedObbPath有bug, 好像是r8的bug了, 现在已经r9了, 但我这儿有时候偶尔还是会返回乱码或者空字符串.

下面是OBB初始化的代码, 指定密码和回调函数, 以及回调数据:

static void Android_InitStorage(Android_App* app)
{
ANativeActivity* activity = app->activity;
assert(activity != NULL && activity->obbPath != NULL);
assert(app->storage == NULL);
app->storage = AStorageManager_new(); const char* obbPath = activity->obbPath; ::AStorageManager_unmountObb(app->storage, obbPath, , Android_ObbCallbackFunc, NULL); //it's a async call: handle final mount path in callbacks
::AStorageManager_mountObb(app->storage, obbPath, "pswd", Android_ObbCallbackFunc, app);
}

记得好像看过断点时的栈,  回调是在线程里调用的, 所以需要注意线程安全.

另外, 如果挂载速度不可控, 那么主线程可能需要挂起等待, 否则如果主线程跑的足够快已经开始读取, 而mount还没有完成的话, 可能会导致IO失败. 目前没有等待,也暂时没有遇到问题, 后面会继续完善.

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

更新:

理论上OBB只是APK expansion, 它可以是任何格式, app上传和下载系统并不关心. ndk提供的mount obb(Fat32镜像)在调试的时候问题比较多, 经常mount失败(AOBB_STATE_ERROR_INTERNAL和AOBB_STATE_ERROR_COULD_NOT_MOUNT)

据说(google group上有人说)是版本不匹配的原因, 但是尝试更新AndroidManifest.xml的包version和jobb -pv的版本, 比如both +1并使两者匹配, 还是经常mount不上, 对于频繁更新包文件用于测试的情况极为不利.

所以可以使用的另外一种方法是使用自定义的包格式(比如最简单的-常用的zip格式, 貌似Android SDK在java层提供了这种格式)等, 这一点跟一般PC游戏的自定义文件包格式类似.比如暴雪的MPQ格式等等.

这样就可以跳过mount,直接使用native IO来读写包文件, 因为这种方法是游戏开发中常用的方式, 所以移植起来问题不大. 只移植package系统的runtime就够用了, 工具还是在host platform上运行打包.
目前zip压缩格式已经经过测试可用.

[原] Android上使用native IO的更多相关文章

  1. [原] GLES在iOS和Android上的不同

    本来GLES提供了与native platform的接口 EGL, 然而iOS没有使用EGL接口, 而是自己搞了一套,叫做EAGL的类似东西, 虽然说大同小异,但是在做跨平台的时候还是很恶心. elg ...

  2. react native-调用react-native-fs插件时,如果数据的接口是需要验证信息的,在android上运行报错

    调用react-native-fs插件时,如果数据的接口是需要验证信息的,在android上运行报错,而在iOS上运行没问题.原因是因为接口是有验证信息的,而调用这个插件时没有传入,在iOS上会自动加 ...

  3. 系列篇|编译可在Android上运行的依赖库(一):glib库

    前言 这是系列文章,它们由<编译可在Android上运行的glib库>及其他4篇文章组成,这4篇文章在“编译依赖库”一节中列出.由于glib库依赖于其他第三方库,所以需要先将依赖的第三方库 ...

  4. 最牛逼android上的图表库MpChart(三) 条形图

    最牛逼android上的图表库MpChart三 条形图 BarChart条形图介绍 BarChart条形图实例 BarChart效果 最牛逼android上的图表库MpChart(三) 条形图 最近工 ...

  5. 最牛逼android上的图表库MpChart(二) 折线图

    最牛逼android上的图表库MpChart二 折线图 MpChart折线图介绍 MpChart折线图实例 MpChart效果 最牛逼android上的图表库MpChart(二) 折线图 最近工作中, ...

  6. 最牛逼android上的图表库MpChart(一) 介绍篇

    最牛逼android上的图表库MpChart一 介绍篇 MpChart优点 MpChart是什么 MpChart支持哪些图表 MpChart效果如何 最牛逼android上的图表库MpChart(一) ...

  7. 在Android上使用fontAwesome

    再也不用做那些讨厌的小图标了! 从网上找了些资料,总结下在android上使用fontAwesome的方法. 1.到官网上下载资源包,找到其中的字体文件fontawesome-webfont.ttf, ...

  8. android上传文件到服务器

    package com.spring.sky.image.upload.network; import java.io.DataOutputStream; import java.io.File; i ...

  9. 二十一、Android上常见度量单位【xdpi、hdpi、mdpi、ldpi】解读

    术语和概念 屏幕尺寸 屏幕的物理尺寸,以屏幕的对角线长度作为依据(比如 2.8寸, 3.5寸). 简而言之, Android把所有的屏幕尺寸简化为三大类:大,正常,和小. 程序可以针对这三种尺寸的屏幕 ...

随机推荐

  1. 【原创】QT 打印输出

    list类 qDebug 的两种用法 #include <QDebug> int main(int argc,char *argv[]) { QList<int> list; ...

  2. JDBC连接数据库:单线程、多线程、批处理插入数据的对比

    一.单线程(单条循环)插入50000条记录: 每执行一次就要访问一次数据库 import java.sql.Connection; import java.sql.DriverManager; imp ...

  3. Install SharePoint 2013 with SP1 on Windows Server 2012 R2 error - This Product requires .NF 4.5

    博客地址:http://blog.csdn.net/FoxDave 最近因为项目需要要搭建SharePoint 2013的开发环境. 准备了Windows Server 2012 R2系统和Sha ...

  4. fread 不能读取最后一个数据块

    今天遇到一个问题,fread()竟然不能读取文件中的最后一个数据块. 我定义了一个结构体: Persong { char name[10]; char phone[15]; } 以及两个函数: int ...

  5. ios UITableView背景图片设置

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPa ...

  6. 关于方法中的self参数和全局变

    先摆样例程序,自己想想执行结果是怎样的:如果注释掉global va后,执行的结果又会如何?同时注释掉global va和va = [value]+va两行呢? #a.py va = ['va1',' ...

  7. robotframework·RIDE基础

    date:2018520 day09 一.学习环境 1.安装python27 2.安装robotframework(cmd→[pip install robotframework]) 3.安装WxPy ...

  8. POJ 2369 Permutations(置换群概念题)

    Description We remind that the permutation of some final set is a one-to-one mapping of the set onto ...

  9. vuejs 在移动端调起键盘并触发‘前往’按钮

    <template> <div class="display"> <form @submit.prevent> <input @keyup ...

  10. Java并发机制和底层实现原理

    Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码转化为汇编指令在CPU上执行.Java中的并发机制依赖于JVM的实现和CPU的指令. Java语言规范第三版中 ...