i have an app size approximately < 200 MB size which contain assets folder with .jpg image files.
Step 1:
Manifist Permission Provided
<!-- Required to access Google Play Licensing -->
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
<!-- Required to download files from Google Play -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- Required to poll the state of the network connection and respond to changes -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Required to check whether Wi-Fi is enabled -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- Required to read and write the expansion files on shared storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Step 2:
I have used dependencies instead of import module
build.gradle;
repositories {
maven { url 'https://dl.bintray.com/alexeydanilov/apk-expansion' }
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
// APK Expansion library
compile 'com.google.android.gms:play-services:7.5.0'
compile files('libs/commons-io-2.4.jar')
// try using library too
// compile project(':aPKExpansionZipLibrary')
// compile project(':applicationLicensing')
// compile project(':downloaderLibrary')
compile 'com.danikula.expansion:expansion:1.1@aar'
compile 'com.danikula.expansion:license:1.5@aar'
compile 'com.danikula.expansion:zip:1.1@aar'
}
Step 3:
Downloader Service Class
public class ExampleDownloaderService extends DownloaderService {
// BASE64_PUBLIC_KEY from developer profile public static final String BASE64_PUBLIC_KEY = "..............oAtvtNLZ/tNm3okOpR7GsT58dMBsc.............";
// i put it random .... i am not clear till now why so random public static final byte[] SALT = new byte[] {1, 4, -1, -1, 14, 42, -79, -21, 13, 2, -8, -11, 62, 1, -10, -101, -19, 41, -12, 18};
@Override
public String getPublicKey() {
return BASE64_PUBLIC_KEY;
}
@Override
public byte[] getSALT() {
return SALT;
}
@Override
public String getAlarmReceiverClassName() {
return ExampleAlarmReceiver.class.getName();
}
}
Step 4:
Alaram Receiver Class:
public class ExampleAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, ExampleDownloaderService.class);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
}
Step 5:
I Have create two helper classes
Uitilities
public class Utilities {
private static final String TAG = "Utilities";
// The shared path to all app expansion files
public final static String EXP_PATH = "/Android/obb/";
public final static String EXP_DATA_PATH = "/Android/data/";
public final static long EXP_FILE_SIZE = 211434558L; // 154577244L
public final static int MAIN_VERSION = 2; // 1
public final static int PATCH_VERSION = 0;
public static final String PREFS_NAME = "FishGuidePrefsFile";
public static InputStream getAssetsPathInputStream(Context context, String internalPath) throws IOException {
// Get a ZipResourceFile representing a merger of both the main and
// patch files
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(context, MAIN_VERSION, PATCH_VERSION);
internalPath = "assets/" + internalPath;
// Get an input stream for a known file inside the expansion file ZIPs
return getAssetInputStream(context, internalPath);
}
public static AssetFileDescriptor getAssetsFileDescriptor(Context context, String internalPath) throws IOException {
// Get a ZipResourceFile representing a merger of both the main and
// patch files
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(context, MAIN_VERSION, PATCH_VERSION);
internalPath = "assets/" + internalPath;
// Get an input stream for a known file inside the expansion file ZIPs
return expansionFile.getAssetFileDescriptor(internalPath);
}
public static ZipResourceFile getAssetsZipResourceFile(Context context)
throws IOException {
// Get a ZipResourceFile representing a merger of both the main and
// patch files
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(context, MAIN_VERSION, PATCH_VERSION);
return expansionFile;
}
public static String[] getAPKExpansionFiles(Context ctx) {
return getAPKExpansionFiles(ctx, MAIN_VERSION, PATCH_VERSION);
}
public static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) {
String packageName = ctx.getPackageName();
Vector<String> ret = new Vector<String>();
Log.w(TAG, "packageName: " + packageName);
Log.w(TAG, "Environment.getExternalStorageState(): " + Environment.getExternalStorageState());
Log.w(TAG, "Environment.getExternalStorageDirectory(): " + Environment.getExternalStorageDirectory());
Log.w(TAG, "ctx.getFilesDir(): " + ctx.getFilesDir());
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// Build the full path to the app's expansion files
File root = Environment.getExternalStorageDirectory();
File expPath = new File(root.toString() + EXP_PATH + packageName);
// Check that expansion file path exists
if (expPath.exists()) {
if (mainVersion > 0) {
String strMainPath = expPath + File.separator + "main."
+ mainVersion + "." + packageName + ".obb";
File main = new File(strMainPath);
if (main.isFile()) {
ret.add(strMainPath);
}
}
if (patchVersion > 0) {
String strPatchPath = expPath + File.separator + "patch."
+ mainVersion + "." + packageName + ".obb";
File main = new File(strPatchPath);
if (main.isFile()) {
ret.add(strPatchPath);
}
}
}
}
String[] retArray = new String[ret.size()];
ret.toArray(retArray);
return retArray;
}
public static void unzipExpansionFile(String expPath, File outputDir) {
ZipHelper zh = new ZipHelper();
zh.unzip(expPath, outputDir);
}
public static File getExternalDataPath(Context ctx) {
String packageName = ctx.getPackageName();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// Build the full path to the app's expansion files
File root = Environment.getExternalStorageDirectory();
File expDataPath = new File(root.toString() + EXP_DATA_PATH + packageName);
// Check that expansion file path exists
// if (!expDataPath.exists()) {
// expDataPath.mkdirs();
// }
Log.d(TAG, "External Data Path: " + expDataPath.toString());
return expDataPath;
}
return null;
}
public static String getFullExternalDataPath(Context ctx, String assetPath) {
File baseDir = getExternalDataPath(ctx);
if (!assetPath.startsWith("assets")) {
assetPath = "assets/" + assetPath;
}
return baseDir + File.separator + assetPath;
}
public static InputStream getAssetInputStream(Context ctx, String path) {
try {
if (!path.startsWith("assets")) {
path = "assets/" + path;
}
File baseDir = getExternalDataPath(ctx);
String assetFilePath = baseDir + File.separator + path;
File assetFile = new File(assetFilePath);
Log.d(TAG, "Loading AssetInputStream for: " + assetFilePath
+ " => Exists: " + assetFile.exists());
return new FileInputStream(assetFilePath);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static void cleanUpOldData(Context ctx) {
try {
String packageName = ctx.getPackageName();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// Build the full path to the app's expansion files
File root = Environment.getExternalStorageDirectory();
File expPath = new File(root.toString() + EXP_PATH + packageName);
File expDataPath = new File(root.toString() + EXP_DATA_PATH + packageName);
// clean up the expanded data under EXP_DATA_PATH
boolean isDeleted = deleteDirectory(expDataPath);
Log.d(TAG, "Expanded data path deleted: " + isDeleted);
// clean up the obb file under EXP_PATH
int currentMainVersion = MAIN_VERSION - 1;
int currentPatchVersion = PATCH_VERSION - 1;
// Check that expansion file path exists
if (expPath.exists()) {
if (currentMainVersion > 0) {
String strMainPath = expPath + File.separator + "main." + currentMainVersion + "." + packageName + ".obb";
File main = new File(strMainPath);
if (main.exists()) {
isDeleted = main.delete();
}
}
Log.d(TAG, "Main OBB file deleted: " + isDeleted);
if (currentPatchVersion > 0) {
String strPatchPath = expPath + File.separator + "patch." + currentPatchVersion + "." + packageName + ".obb";
File patch = new File(strPatchPath);
if (patch.exists()) {
isDeleted = patch.delete();
}
Log.d(TAG, "Patch OBB file deleted: " + isDeleted);
}
}
}
} catch (Exception e) {
Log.e(TAG, "Unable to clean up old app data");
}
}
public static boolean deleteDirectory(File path) {
if (path.exists()) {
File[] files = path.listFiles();
if (files == null) {
return true;
}
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
deleteDirectory(files[i]);
} else {
files[i].delete();
}
}
}
return (path.delete());
}
// public static String getFullExternalDataPath(Context ctx, String
// assetPath) {
// return "file:///android_asset/" + assetPath;
// }
//
// public static InputStream getAssetInputStream(Context ctx, String path) {
// try {
// AssetManager mgr = ctx.getAssets();
// return mgr.open(path, AssetManager.ACCESS_BUFFER);
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// } catch (Exception e) {
// // TODO: handle exception
// }
// return null;
// }
}
andother Class ZipHelper
public class ZipHelper {
private static final String TAG = "CoralSea.ZipHelper";
boolean zipError=false;
public boolean isZipError() {
return zipError;
}
public void setZipError(boolean zipError) {
this.zipError = zipError;
}
public void unzip(String archive, File outputDir) {
try {
Log.d(TAG,"ZipHelper.unzip() - File: " + archive);
ZipFile zipfile = new ZipFile(archive);
for (Enumeration e = zipfile.entries(); e.hasMoreElements(); ) {
ZipEntry entry = (ZipEntry) e.nextElement();
unzipEntry(zipfile, entry, outputDir);
}
}
catch (Exception e) {
Log.e(TAG,"ZipHelper.unzip() - Error extracting file " + archive+": "+ e);
setZipError(true);
}
}
private void unzipEntry(ZipFile zipfile, ZipEntry entry, File outputDir) throws IOException {
if (entry.isDirectory()) {
createDirectory(new File(outputDir, entry.getName()));
return;
}
File outputFile = new File(outputDir, entry.getName());
if (!outputFile.getParentFile().exists()){
createDirectory(outputFile.getParentFile());
}
//Log.d("control","ZipHelper.unzipEntry() - Extracting: " + entry);
BufferedInputStream inputStream = new BufferedInputStream(zipfile.getInputStream(entry));
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));
try {
copy(inputStream, outputStream);
}
catch (Exception e) {
Log.e(TAG,"ZipHelper.unzipEntry() - Error: " + e);
setZipError(true);
}
finally {
outputStream.close();
inputStream.close();
}
}
private void createDirectory(File dir) {
Log.d(TAG,"ZipHelper.createDir() - Creating directory: "+dir.getName());
if (!dir.exists()){
if(!dir.mkdirs()) throw new RuntimeException("Can't create directory "+dir);
}
else Log.d(TAG,"ZipHelper.createDir() - Exists directory: "+dir.getName());
}
// Copies src file to dst file.
// If the dst file does not exist, it is created
private void copy(File src, File dst) throws IOException {
InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst);
copy(in, out);
}
// Copies src file to dst file.
// If the dst file does not exist, it is created
private void copy(InputStream in, OutputStream out) throws IOException {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
}
Step 6:
Spalsh Screen work
Finally: Process to create expansion file:
- Create New Folder and rename with main.2.com.example
- Put all the images inside the folder
- Compress this folder with 0 compression
- New generated File as main.2.com.example.zip & Rename with main.2.com.example.obb
So my Questions are:
1.How can i make the separate expansion file as the size of my apk is much greater than 50MB but less than 200MB?
a>Which classes are required to handle the expansion file zip and download?
b>Where should they have been called?
- My original source code took the assets folder directory to get image path, Should i update path from Android/OBB/ [package name]/ directory.
3.What are the memory optimization factor while use expansion file?
I have already checked other Questions bit similar to mine but none of it solve any of my purpose!!!