Reputation: 17005
To reduce the APK file size, I want to remove the drawables (images and XML files) from my project and download the proper ones from my server on the first app start.
The problem is how to load and use the downloaded drawables in my code?
This is what I found after some research:
BitmapDrawable
.NinePatchDrawable
.But I still don't know how to load and use XML drawables that have references to other drawables, like this:
<layer-list>
<item>
<bitmap android:src="@drawable/other_drawable" />
</item>
</layer-list>
Any thoughts on this or other effective ideas to reduce the APK file size would be appreciated.
Notes:
Update:
After some more research, it seems to me possible to load/use downloaded XML drawables after modifying them a little by using custom attributes for referenced drawables and a subclass of Drawable
instead of native BitmapDrawable
. So the above XML drawable example will become something like this:
<layer-list xmlns:custom="http://schemas.android.com/apk/res-auto">
<item>
<custom-bitmap custom:ref="other_drawable" />
</item>
</layer-list>
Though, it's not a very easy solution and I'm not sure it's better than doing things manually.
References:
Upvotes: 0
Views: 1864
Reputation: 1496
You could use expansion files (see http://developer.android.com/google/play/expansion-files.html) and move your resources there using the jobb tool (http://developer.android.com/tools/help/jobb.html).
You would still have to handle unavailable resources as long as the expansion file has not been downloaded though.
Upvotes: 0
Reputation: 4073
First of all, that size isn't too bad if your application have intense ui interaction. Also, in some moment the user will need download that drawables, why not in the installation from the google playstore?. Anyways, If you really want to do it, you can do the following:
For the reference in your layouts, you will need hook up from every widget that have a reference to a drawable and implement your own tag. For example, if you old layout looks like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<ImageView
android:id="@+id/someImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:src="@drawable/somePicture" />
</RelativeLayout>
Now should look like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.custom"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.custom.RemoteImageView
android:id="@+id/someImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
custom:remoteSrc="somePicture" />
</RelativeLayout>
Remember add the custom tag definition in your attrs.xml
. In the implementation of the class you can get the image (if exist) from some cache like this:
public class RemoteImageView extends ImageView {
private String remoteSrc;
private DiskLruImageCache diskCache;
public RemoteImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public RemoteImageView(Context context, AttributeSet attrs) {
super(context, attrs);
diskCache = new DiskLruImageCache(this, "cache", 1024 * 1024 * 30, CompressFormat.PNG, 70);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.RemoteImageView, 0, 0);
try {
remoteSrc = a.getString(R.styleable.RemoteImageView_remoteSrc);
Bitmap cachedImage;
if (!diskCache.containsKey(remoteSrc)) {
//Download it from Internet and save it into the cache
//...
diskCache.put(remoteSrc, bitmapThatYouDownload);
}
cachedImage = diskCache.getBitmap(remoteSrc);
//Use the cachedImage as the bitmap for this view.
} finally {
a.recycle();
}
}
public RemoteImageView(Context context) {
super(context);
}
}
To see about DiskLruImageCache
you can check this links:
You can do some tricks with this like have a two-levels cache, the first one with a memory cache and the second one with a disk cache. Already exist libraries that does stuff like this, but how you need your custom tags, you can either modify those libraries or use the cache library in your classes like I show you above.
For the AnimationDrawable
's like animation-list you will need build the animation in runtime. For example like this:
AnimationDrawable animation = new AnimationDrawable();
animation.addFrame(new BitmapDrawable(getResources(),diskCache.get(myFirstFreame)), 100);
animation.addFrame(new BitmapDrawable(getResources(),diskCache.get(mySecondFrame)), 1000);
animation.addFrame(new BitmapDrawable(getResources(),diskCache.get(myThirdFrame)), 1000);
animation.setOneShot(false);
ImageView imageAnim = (ImageView) findViewById(R.id.img);
imageAnim.setBackgroundDrawable(animation);
Remember check if myFirstFrame
,mySecondFrame
and myThirdFrame
are already in the cache. If not then download them and add them.
Hope this help, but if I was you, I would keep that size, personally isn't so much, I've developed a tumblr client with a lot of animation and ui features, and the apk size was around 15 MiB.
Upvotes: 2
Reputation: 11314
I doubt if your drawable goes this much (20MB) here i mean to drawable is xml Files only. Coz these can not be downloaded later, Have you properly optimize your code for Re-useability?
but you are free with the Images/raw's like audio files or something like this
Note: If you are looking for downloadable xml drawables
then answer is no it is not possible it must be withing apk and also you can follow the following to optimize
As your question says how to use Downloaded drawable to you code
so the answer is You can not go with xml drawable but yes you can do these things with images
and there is clear method of setting drawable
from file or creating Bitmap
from file
Upvotes: 1
Reputation: 192
XML Files and drawables are only to help developers, these are not the essential part of the programming in android. If you want to download from server you can do this. what you have to do is Create Layouts and other things manually in Java files. i-e
RelativeLayout rl =new Relativelayout(Context);// To create Layout
Button button = new Button(Context); // Create Components Like this
button.setText("abc");// Set Properties
Now add components to layout as a child.
rl.addView(button,new Layoutparams(Layoutparams.WRAP_CONTENT,Layoutparams.WRAP_CONTENT));
In onCreate return this Layout!
For drawables include very important in your drawable and download others through HTTP Calls.
Hope it helps.
Upvotes: 2
Reputation: 1
As per your requirement you can not change the xml drawables at runtime.
Other that if you still need this functionality you can download it on your application's splash screen and set it runtime as you have suggest using BitmapDrawable.
But as per my opinion this is not right way to use this, as when you create your activity that time your view already render on setContentView(layoutID). So when you call your activity it will display the blank views without images.
And from other resources i have read that you can not change the resources file like drawable, raw,strings at runtime as it's id's can't create on runtime.
Upvotes: 0