Introduction
Google Play currently requires that your APK file be no more than
50MB. For most applications, this is plenty of space for all the
application's code and assets. However, some apps need more space for
high-fidelity graphics, media files, or other large assets. Previously,
if your app exceeded 50MB, you had to host and download the additional
resources yourself when the user opens the app. Hosting and serving the
extra files can be costly, and the user experience is often less than
ideal. To make this process easier for you and more pleasant for users,
Google Play allows you to attach two large expansion files that
supplement your APK.
Google Play hosts the expansion files for your application and
serves them to the device at no cost to you. The expansion files are
saved to the device's shared storage location (the SD card or
USB-mountable partition; also known as the "external" storage) where
your app can access them. On most devices, Google Play downloads the
expansion file(s) at the same time it downloads the APK, so your
application has everything it needs when the user opens it for the first
time. In some cases, however, your application must download the files
from Google Play when your application starts.
Overview
Each time you upload an APK using the Google Play Android Developer
Console, you have the option to add one or two expansion files to the
APK. Each file can be up to 2GB and it can be any format you choose, but
we recommend you use a compressed file to conserve bandwidth during the
download. Conceptually, each expansion file plays a different role:
- The main expansion file is the primary expansion file for additional resources required by your application.
- The patch expansion file is optional and intended for small updates to the main expansion file.
Screen:
Please see below screen for how apk expansion work.
Each expansion file you upload can be any format you choose (ZIP, PDF, MP4, etc.).
[main|patch]...obb
There are three components to this scheme:
main or patch
Specifies whether the file is the main or patch expansion file. There can be only one main file and one patch file for each APK.
<expansion-version>
This is an integer that matches the version code of the APK with which the expansion is first associated.
Developer Console allows you to re-use an uploaded expansion file with a new APK,
<package-name>
Your application's Java-style package name.
For example, suppose your APK version is 314159 and your package
name is com.example.app. If you upload a main expansion file, the file
is renamed to:
main.314159.com.example.app.obb
Ex. main.1.packagename.obb.zip name used in my project.
Storage location
When Google Play downloads your expansion files to a device, it saves
them to the system's shared storage location. To ensure proper
behavior, you must not delete, move, or rename the expansion files. In
the event that your application must perform the download from Google
Play itself, you must save the files to the exact same location.
The specific location for your expansion files is:
<shared-storage>/Android/obb/<package-name>/
<shared-storage> is the path to the shared storage space, available from getExternalStorageDirectory().
<package-name> is your application's Java-style package name, available from getPackageName().
Ex: sdcard/Android/obb/packagename/main.1.packagename.obb
If you must unpack the contents of your expansion files, do not
delete the .obb expansion files afterwards and do not save the unpacked
data in the same directory
For example, we've provided a library
project called the APK Expansion Zip Library that reads your data
directly from the ZIP file.
Note: Unlike APK files, any files saved on the shared storage can be read by the user and other applications.
Download process
Most of the time, Google Play downloads and saves your expansion
files at the same time it downloads the APK to the device. However, in
some cases Google Play cannot download the expansion files or the user
might have deleted previously downloaded expansion files. To handle
these situations, your app must be able to download the files itself
when the main activity starts, using a URL provided by Google Play.
The download process from a high level looks like this:
- User selects to install your app from Google Play.
- If Google Play is able to download the expansion files (which is
the case for most devices), it downloads them along with the APK.
If Google Play is unable to download the expansion files, it downloads the APK only.
- When the user launches your application, your app must check whether the expansion files are already saved on the device.
1. If yes, your app is ready to go.
2. If no, your app must download the expansion files over HTTP from
Google Play. Your app must send a request to the Google Play
client using the Google Play's Application Licensing service, which
responds with the name, file size, and URL for each expansion file. With
this information, you then download the files and save them to the
proper storage location.
Caution: It is critical that you include the
necessary code to download the expansion files from Google Play in the
event that the files are not already on the device when your application
starts. As discussed in the following section about Downloading the
Expansion Files, we've made a library available to you that greatly
simplifies this process and performs the download from a service with a
minimal amount of code from you.
Use Download Library to implement your download behavior. Its Very Easy to use & Implement Downloading behavior using Download Library. you can also take reference from Sample Demo App.
Downloading the Expansion files
In most cases, Google Play downloads and saves your expansion files to
the device at the same time it installs or updates the APK. This way,
the expansion files are available when your application launches for the
first time. However, in some cases your app must download the expansion
files itself by requesting them from a URL provided to you in a
response from Google Play's Application Licensing service.
The basic logic you need to download your expansion files is the following:
1. When your application starts, look for the expansion files on the
shared storage location (in the Android/obb/<package-name>/
directory).
a. If the expansion files are there, you're all set and your application can continue.
b. If the expansion files are not there:
1.) Perform a request using Google Play's Application Licensing to get your app's expansion file names, sizes, and URLs.
2.) Use the URLs provided by Google Play to download the
expansion files and save the expansion files. You must save the files to
the shared storage location (Android/obb/<package-name>/) and use
the exact file name provided by Google Play's response.
Note:
The URL that Google Play provides for your
expansion files is unique for every download and each one expires
shortly after it is given to your application.
Note: Whether your application is free or not,
Google Play returns the expansion file URLs only if the user acquired
your application from Google Play.
Sample Demo App:
You can Download Sample App from SDK Manager. And Take Reference from it.
You have to download below Library from SDK Manager in Eclipse.
Google Play Licensing Library
Google Play APK Expansion Library
|
Download from SDK Manager |
You have to add total three Library in your Project for APK Expansion
1) APK Expansion Zip Library (SDK/extras/google/play_apk_expansion/zip_file)
2) Application Licensing (
SDK/extras/google/market_licensing)
3) Downloader Library (
SDK/extras/google/play_apk_expansion/downloader_library)
APK Expansion Zip Library
This is an optional library that
helps you read your expansion
files when they're saved as ZIP files. Using this library allows you to easily read resources from
your ZIP expansion files as a virtual file system.
Downloader Library
To use APK expansion files with your application and provide the best user experience with
minimal effort on your behalf, we recommend you use the Downloader Library that's included in the
Google Play APK Expansion Library package. This library downloads your expansion files in a
background service, shows a user notification with the download status, handles network
connectivity loss, resumes the download when possible, and more.
Application Licensing
If the files are not on the device, use Google Play's
Application Licensing service to request URLs
for the expansion files, then download and save them.
Downloaded Sample Demo Available at below paths in your system.
ex. /Android SDK / extras/ google / play_apk_expansion/
Declaring user permissions
Note: By default, the Downloader Library requires API level 4, but the APK Expansion Zip Library requires API level 5.
Implementing the downloader service
In order to perform downloads in the background, the Downloader
Library provides its own Service subclass called DownloaderService that
you should extend. In addition to downloading the expansion files for
you, the DownloaderService also:
All you need to do is create a class in your application that
extends the DownloaderService class and override three methods to
provide specific application details:
getPublicKey()
This must return a string that is the Base64-encoded RSA public
key for your publisher account, available from the profile page on the
Developer Console (see Setting Up for Licensing).
getSALT()
This must return an array of random bytes that the licensing
Policy uses to create an Obfuscator. The salt ensures that your
obfuscated SharedPreferences file in which your licensing data is saved
will be unique and non-discoverable.
getAlarmReceiverClassName()
This must return the class name of the BroadcastReceiver in your
application that should receive the alarm indicating that the download
should be restarted (which might happen if the downloader service
unexpectedly stops).
For example, here's a complete implementation of DownloaderService:
public class SampleDownloaderService extends DownloaderService {
// You must use the public key belonging to your publisher account
public static final String BASE64_PUBLIC_KEY = "YourLVLKey";
// You should also modify this salt
public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98,
-100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84
};
@Override
public String getPublicKey() {
return BASE64_PUBLIC_KEY;
}
@Override
public byte[] getSALT() {
return SALT;
}
@Override
public String getAlarmReceiverClassName() {
return SampleAlarmReceiver.class.getName();
}
}
Notice: You must update the BASE64_PUBLIC_KEY value
to be the public key belonging to your publisher account. You can find
the key in the Developer Console under your profile information. This is
necessary even when testing your downloads.
Remember to declare the service in your manifest file:
...
Note: You have to add your BASE64 public key in SampleDownloaderService in your app.
This key you can get when you create app in Google Market.
Implementing the alarm receiver
In order to monitor the progress of the file downloads and restart the
download if necessary, the DownloaderService schedules an RTC_WAKEUP
alarm that delivers an Intent to a BroadcastReceiver in your
application. You must define the BroadcastReceiver to call an API from
the Downloader Library that checks the status of the download and
restarts it if necessary.
You simply need to override the onReceive() method to call DownloaderClientMarshaller.startDownloadServiceIfRequired().
For example:
public class SampleAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent,
SampleDownloaderService.class);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
}
Notice that this is the class for which you must return the name in your
service's getAlarmReceiverClassName() method (see the previous
section).
Remember to declare the receiver in your manifest file:
...
Activity for Expansion Handling
public class VideoDownloaderActivity extends Activity implements
IDownloaderClient {
/**
* Here is where you place the data that the validator will use to determine
* if the file was delivered correctly. This is encoded in the source code
* so the application can easily determine whether the file has been
* properly delivered without having to talk to the server. If the
* application is using LVL for licensing, it may make sense to eliminate
* these checks and to just rely on the server.
*/
private static final XAPKFile[] xAPKS = { new XAPKFile(true, // true
// signifies
// a main
// file
1, // the version of the APK that the file was uploaded
// against
191696333L // the length of the file in bytes
),
private class CopyTask extends AsyncTask<Void, Void, Boolean> {
private ProgressDialog mProgressDialog;
private Context context;
public CopyTask(Context context) {
this.context = context;
}
/**
* Display the dialog first when the webservice call.
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
if (context != null) {
mProgressDialog = ProgressDialog.show(context, "",
context.getString(R.string.copy_video_message), true,
false);
}
}
/**
* Call the webservice and parse the data from the service in
* background.
*/
@Override
protected Boolean doInBackground(Void... params) {
// return wsLinks.executeService(currentPage);
for (XAPKFile xf : xAPKS) {
String fileName = Helpers.getExpansionAPKFileName(
VideoDownloaderActivity.this, xf.mIsMain,
xf.mFileVersion);
if (!Helpers.doesFileExist(VideoDownloaderActivity.this,
fileName, xf.mFileSize, false))
return true;
fileName = Helpers.generateSaveFileName(
VideoDownloaderActivity.this, fileName);
ZipResourceFile zrf;
try {
zrf = APKExpansionSupport.getAPKExpansionZipFile(
VideoDownloaderActivity.this, 1, 0);
File path = new File(
Environment.getExternalStorageDirectory()
+ File.separator
+ getString(R.string.app_name));
if (!path.exists()) {
// create a File object for the
// parent directory
File photoDirectory = new File(
path.getAbsolutePath());
// have the object build the
// directory structure, if needed.
photoDirectory.mkdirs();
} else {
if (path.isDirectory()) {
String[] children = path.list();
Log.v(LOG_TAG, "file length:" + children.length);
for (int i = 0; i < children.length; i++) {
new File(path, children[i]).delete();
}
}
}
for (ZipEntryRO entry : zrf.getAllEntries()) {
Log.v(LOG_TAG, "name:" + entry.mFileName);
DataInputStream is = new DataInputStream(
zrf.getInputStream(entry.mFileName));
// AssetFileDescriptor af =
// zrf.getAssetFileDescriptor(entry.mFileName);
long length = entry.mCompressedLength;
byte[] buf = new byte[1024 * 256];
Log.v("assetfilediscripter", "" + length);
// FileOutputStream fos = new FileOutputStream("");
// OutputStream stream = new BufferedOutputStream(new
// FileOutputStream("/storage/sdcard0/Download/sample.mp3"));
String[] bits = entry.mFileName.split("/");
String lastOne = bits[bits.length-1];
Log.v(LOG_TAG, "path:" + path + ":"+ lastOne);
OutputStream stream = new BufferedOutputStream(
new FileOutputStream(path + "/"
+ lastOne));
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int len = 0;
while ((len = is.read(buffer)) != -1) {
stream.write(buffer, 0, len);
}
if (stream != null)
stream.close();
// break;
}
return true;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
return true;
}
/**
* onPostExecute method after called webService and Set the data into
* adapter after background task is complete. this method set footerView
* into listView according to totalPAge.
*/
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (mProgressDialog != null && mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
if (result) {
finish();
Intent intent = new Intent(VideoDownloaderActivity.this,
VideoListActivity.class);
startActivity(intent);
} else {
errorDialog(context, context.getString(R.string.copy_failed));
}
}
}
}
Testing Your Expansion Files
Before publishing your application, there are two things you should test: Reading the expansion files and downloading the files.
Testing file reads
Before you upload your application to Google Play, you should test
your application's ability to read the files from the shared storage.
All you need to do is add the files to the appropriate location on the
device shared storage and launch your application:
1.) On your device, create the appropriate directory on the shared storage where Google Play will save your files.
For
example, if your package name is com.example.android, you need to
create the directory Android/obb/com.example.android/ on the shared
storage space. (Plug in your test device to your computer to mount the
shared storage and manually create this directory.)
2.) Manually add the expansion files to that directory. Be sure that
you rename your files to match the file name format that Google Play
will use.
For example, regardless of the file type, the main expansion file
for the com.example.android application should be
main.0300110.com.example.android.obb. The version code can be whatever
value you want. Just remember:
- The main expansion file always starts with main and the patch file starts with patch.
- The package name always matches that of the APK to which the file is attached on Google Play.
3.) Now that the expansion file(s) are on the device, you can install and run your application to test your expansion file(s).
Here are some reminders about handling the expansion files:
- Do not delete or rename the .obb expansion files (even if you
unpack the data to a different location). Doing so will cause Google
Play (or your app itself) to repeatedly download the expansion file.
- Do not save other data into your obb/ directory. If you must
unpack some data, save it into the location specified by
getExternalFilesDir().
Testing file download
Because your application must sometimes manually download the expansion
files when it first opens, it's important that you test this process to
be sure your application can successfully query for the URLs, download
the files, and save them to the device.
To test your application's implementation of the manual download
procedure, you must upload your application to Google Play as a "draft"
to make your expansion files available for download:
1.) Upload your APK and corresponding expansion files using the Google Play Developer Console
2.) Fill in the necessary application details (title, screenshots,
etc.). You can come back and finalize these details before publishing
your application.
Click the Save button. Do not click Publish. This saves the
application as a draft, such that your application is not published for
Google Play users, but the expansion files are available for you to test
the download process.
3.) Install the application on your test device using the Eclipse tools or adb.
4.) Launch the app.
If everything works as expected, your application should begin
downloading the expansion files as soon as the main activity starts.
References: http://developer.android.com/google/play/expansion-files.html
Customn DropDown Spinner in Android