Tuesday, June 14, 2016

Setting up a Google Play Expansion File (.obb) for easy Android WebView reference

Scoping the problem

A PhoneGap app I'm working on has a lot of image and sound files.  We have the iOS version working fine, but the Android version is roughly 200MB, a full 100MB over the maximum binary size allowed by Google Play.

Google Play tells us that we can create a separate ".obb" (Opaque Binary Blob) expansion file where we can offload some of that content.  The OBB is uploaded along with the APK,

Ok, sounds great.  But before creating the OBB, I know I'm going to have to access the files in the OBB from HTML passed to an Android WebView.  Ideally,  we want simple file references without writing a lot of code.  For example:

<img src="file://SomePathToOBB/photos/photo1.png">

I didn't want to have to unpack the OBB, so after doing some Stack Overflowing, here and here, I decided to pursue this SO suggestion:
It is a good alternative to use JOBB tools to pack expansion file and use StorageManager to mount it. After that the files inside expansion file should be accessible as regular files in the file system. The unpacking of expansion file is not needed in this case.
An OBB file can be of any type (.pdf, .zip, etc), but a file created with the JOBB utility can be mounted using StorageManager and requires no extra work at runtime (like extracting from a zip file).

Creating the .obb using JOBB & loading on a test device

To create the .obb, I moved directories of image files into a directory named materials, and then ran the following* from the command line on my Mac:
/Users/mikem/Library/Android/sdk/tools/jobb -d materials -o my.obb -pn com.gwhizmobile.example -pv 1
* com.gwhizmobile.example represents the app package name

Easy!  The OBB was created as my.obb.  According to my reading of the docs, when I upload it to Google Play, it will automagically rename the file into something like main.1.com.gwhizmobile.example.obb.  However, I wanted to be able to test it on my Nexus 5 test device, so I renamed it into the proper format, and put it into a directory structure with the following path:
./obb/com.gwhizmobile.example/main.1.com.gwhizmobile.example.obb
This structure allowed me to use the Android File Transfer utility for Mac to copy the obb directory onto my device (in the Android folder)

So at this stage, I have the .obb file built and residing on my Nexus 5 where it is supposed to be.

Using Storage Manager to mount the .obb and get contents path

Next, we need to add some code that allows us to mount the obb.  Between here and here, I generated this little snippet of code**:
    
  String pathReference = null;

  final StorageManager storageManager = (StorageManager) context.getSystemService(context.STORAGE_SERVICE);
 
  final String packageName = "com.gwhizmobile.example";
  final String packageVersion = "1";

  final File mainFile = new 
 File(Environment.getExternalStorageDirectory() +  "/Android/obb/" + packageName + "/"+ "main." + packageVersion + "." + packageName + ".obb");

  storageManager.mountObb(mainFile.getAbsolutePath(), null, new  OnObbStateChangeListener() {

     @Override

     public void onObbStateChange(String path, int state) {
       super.onObbStateChange(path, state);
       if (state == OnObbStateChangeListener.MOUNTED) {
         pathReference = storageManager.getMountedObbPath(path);
       }
    }
  });

** Since this is a PhoneGap app, this code was encapsulated within a simple homegrown Cordova plugin.

Final integration steps

The generated pathReference value looks something like file:///mnt/obb/123e187987192397831.  This is the prefix we can use in the HTML, as in:
 <img src="file:///mnt/obb/123e187987192397831/photos/photo1.png">
Works. Like. A. Champ.

A few other notes

  1. Although we created the .obb by pointing it at the materials directory, that directory name is not included in the filename path.
  2. GooglePlay will not allow you to upload the OBB file with your first APK!?  However, after doing another build process the OBB can be loaded.
  3. Note that the packageVersion in the code above will need to change since GooglePlay will rename the OBB file to the version of the APK loaded at the same time as the OBB.