Overview of the Android app memory optimization image optimization _android

Source: Internet
Author: User

Objective

In the case of Android, where the RAM is on top of the G, there's no need to worry too much about the app's consumption of Android memory, but what I do in the real world is the education of primary school App,app buttons, backgrounds, animation transformations are basically all pictures, on 2K screen (resolution 2048* 1536 a background picture will occupy memory 12M, switch back and forth several times memory consumption will increase to hundreds of megabytes, in order to not affect the visual effect of the app, it is necessary to use a variety of means to reduce the memory consumption of the app.

Through the Ddms App memory Footprint View tool analysis found that the most memory of the app is the picture, each activity in the picture occupies a majority of memory, this article focuses on sharing the image of memory optimization.

Do not set the button's background to selector

In the layout file and code, you can set the button background to selector, so that the positive and negative selection of buttons to achieve, but the actual trace found that if the button's background is set to selector, When the button is initialized, the positive and negative images are loaded in memory (the Android source code can be viewed, the methods of the class are Drawable.java createFromXmlInner parsed and the final method is called Drawable inflate ), The equivalent of a button that takes up the memory used by two images of the same size, if there is a lot of buttons on an interface or a big button, the memory of the button will be very large, you can set the button in the layout file only the normal state of the background picture, and then in the code to monitor the button click State, When the button is pressed to set the button to reverse the effect of the picture, when lifted back to the normal state of the background, the implementation of the following way:

 public class Imagebuttonclickutils {
 private imagebuttonclickutils () {

 }

 /**
  * Set the positive and negative effects of the button
  * 
  * *
 /public static void Setclickstate (view view, Final int normalresid, final int pressresid) {
  View.setontouchlis Tener (New Ontouchlistener () {
  @Override public
  boolean Ontouch (View V, motionevent event) {
   switch ( Event.getaction ()) {case
   motionevent.action_down:{
   v.setbackgroundresource (PRESSRESID);
   }
   break;
   Case motionevent.action_move:{
   V.setbackgroundresource (PRESSRESID);
   }
   break;
   Case motionevent.action_up:{
   V.setbackgroundresource (NORMALRESID);
   }
   break;
   default:{

   } break
   ;

   In order not to affect the onclick callback of the Listener button, the return value should be false to
   false;}}

This way you can solve the problem that the same button consumes twice times the memory. If you think that for a button to provide a positive and negative selection of two of images will cause the size of the APK, you can implement the button click in the following way to reverse the election effect, this way there is no button to occupy twice times the memory of the situation, It also reduces the volume of apk (the Tintcolor in Android 5.0 can achieve similar results):

 ImageButton personalinfobtn = (ImageButton) Findviewbyid (R.id.personalbtnid);
 Personalinfobtn.setontouchlistener (New Ontouchlistener () {
 @SuppressLint ("clickableviewaccessibility")
 @ Override Public
 Boolean Ontouch (View V, motionevent event) {
  int action = Event.getaction ();

  if (action = = Motionevent.action_down) {
  ((ImageButton) v). Setcolorfilter (Getresources (). GetColor (0x50000000));
  else if (action = = MOTIONEVENT.ACTION_UP | | action = = motionevent.action_cancel) {
  (ImageButton) v). Clearcolorfilter ();
  }

  In order not to affect the onclick callback of the Listener button, the return value should be false to
  false;
 

Put the background picture on a non-UI thread to draw and increase the efficiency of the app

On a high resolution tablet device, drawing a large background of the picture will affect the efficiency of the program, in serious cases and without the hardware acceleration when the use of handwriting, the equivalent of cards, and finally our solution is to draw the background picture SurfaceView , which is equivalent to drawing in a non-UI thread, Does not affect the UI thread to do other things:

Import Android.content.Context;
Import Android.content.res.TypedArray;
Import Android.graphics.Bitmap;
Import Android.graphics.BitmapFactory;
Import Android.graphics.Canvas;
Import Android.graphics.Matrix;
Import Android.graphics.PixelFormat;
Import Android.util.AttributeSet;
Import Android.util.DisplayMetrics;
Import Android.view.SurfaceHolder;

Import Android.view.SurfaceView;

Import COM.EEBBK.HANZILEARNING.ACTIVITY.R; public class Rootsurfaceview extends Surfaceview implements Surfaceholder.callback, runnable{private float mviewwidth =
 0;
 private float mviewheight = 0;
 private int mresourceid = 0;
 Private context mcontext = null;
 Private volatile Boolean isrunning = false;

 Private Surfaceholder msurfaceholder = null;
 Public Rootsurfaceview (context, AttributeSet attrs, int defstyleattr) {Super (context, attrs, defstyleattr);
 Initrootsurfaceview (context, Attrs, defstyleattr, 0); Public Rootsurfaceview (context, AttributeSet attrs) {Super (context, ATTRS);
 Initrootsurfaceview (context, attrs, 0, 0); private void Initrootsurfaceview (context context, AttributeSet attrs, int defstyleattr, int defstyleres) {Mcontext =
 Context
 Displaymetrics displaymetrics = Context.getresources (). Getdisplaymetrics ();
 TypedArray a = Context.obtainstyledattributes (Attrs, R.styleable.rootsurfaceview, defstyleattr, defstyleres);
 int n = a.getindexcount ();
 Mviewwidth = Displaymetrics.widthpixels;
 Mviewheight = Displaymetrics.heightpixels;
  for (int index=0; index<n; index++) {int attr = A.getindex (index);
  Switch (attr) {case r.styleable.rootsurfaceview_background:{Mresourceid = A.getresourceid (attr, 0);
  } break;
  Case r.styleable.rootsurfaceview_view_width:{mviewwidth = a.getdimension (attr, displaymetrics.widthpixels);
  } break;
  Case r.styleable.rootsurfaceview_view_height:{mviewheight = a.getdimension (attr, displaymetrics.heightpixels);
  } break;
  default:{} break;
 } a.recycle (); Msurfaceholder = GetholDer ();
 Msurfaceholder.addcallback (this);
 Msurfaceholder.setformat (pixelformat.translucent); Private Bitmap Getdrawbitmap (context context, float width, float height) {Bitmap Bitmap = Bitmapfactory.decoderesour
 CE (getresources (), Mresourceid);
 Bitmap Resultbitmap = Zoomimage (Bitmap, width, height);
 return resultbitmap; @Override public void surfacechanged (Surfaceholder arg0, int arg1, int arg2, int arg3) {System.out.println ("Rootsur
 Faceview surfacechanged ");
 @Override public void surfacecreated (Surfaceholder holder) {drawbackground (holder);
 System.out.println ("Rootsurfaceview surfacecreated");
 @Override public void surfacedestroyed (Surfaceholder holder) {isrunning = false;
 System.out.println ("Rootsurfaceview surfacedestroyed");
 } @Override protected void Onattachedtowindow () {Super.onattachedtowindow ();
 System.out.println ("Rootsurfaceview Onattachedtowindow"); } @Override protected void Ondetachedfromwindow () {Super.ondetachedfromwindow ();
 System.out.println ("Rootsurfaceview Ondetachedfromwindow"); @Override public void Run () {while (isrunning) {synchronized (Msurfaceholder) {if!msurfaceholder.getsurface (
  ). IsValid ()) {continue;
  } drawbackground (Msurfaceholder);
  } isrunning = false;
 Break
 } private void Drawbackground (Surfaceholder holder) {Canvas Canvas = Holder.lockcanvas ();
 Bitmap Bitmap = Getdrawbitmap (Mcontext, Mviewwidth, mviewheight);
 Canvas.drawbitmap (bitmap, 0, 0, NULL);
 Bitmap.recycle ();
 Holder.unlockcanvasandpost (canvas);  public static Bitmap Zoomimage (Bitmap bgimage, float newwidth, float newheight) {Float width = bgimage.getwidth (
 );
 float height = bgimage.getheight ();
 Matrix matrix = new Matrix ();
 float scalewidth = newwidth/width;
 float scaleheight = newheight/height;
 Matrix.postscale (ScaleWidth, ScaleHeight);
 Bitmap Bitmap = Bitmap.createbitmap (bgimage, 0, 0, (int) width, (int) height, matrix, true); if (bitmap!= bgimage) {bgimage.Recycle ();
 Bgimage = null;
 return bitmap; }
}

Define custom properties for the custom view in the Res/values/attr.xml file:

<declare-styleable name= "Rootsurfaceview" >
 <attr name= "background" format= "reference"/>
 < attr name= "View_width" format= "Dimension"/> <attr name=
 "View_height" format= "Dimension"/>
</ Declare-styleable>

There is no need to use hardware-accelerated interfaces to recommend turning off hardware acceleration

By Ddms's heap tracking, it is found that more memory is consumed when hardware acceleration is turned on than when hardware acceleration is turned off, but some interfaces that turn on or off hardware acceleration do not have much impact on the efficiency of the program. In this case, you can consider shutting down the hardware acceleration of the corresponding activity in the Androidmanifest.xml file, like this:

<!--set interface-->
<activity
 android:name= ". Settingactivity "
 android:hardwareaccelerated=" false "
 android:screenorientation=" Sensorlandscape "
 android:theme= "@style/translucent_notitle" >
</activity>

Note: If you use WebView, video playback, handwriting, animation, and so on, turn off hardware acceleration will seriously affect the efficiency of the program, this situation can only shut down the activity of some view hardware acceleration, the entire activity of the hardware acceleration does not shut down.

If a view in an activity needs to turn off hardware acceleration, but the entire activity cannot be turned off, you can call the view level to turn off hardware acceleration:

View.setlayertype | | Call the method
Setlayertype (View.layer_type_software, NULL) in the constructor that defines the View;

Try to use less animationdrawable, if you have to be able to customize the picture switcher instead of animationdrawable

Animationdrawable is also a large memory consumption, picture frame number of more memory consumption, specific can view animationdrawable source code, in animationdrawable instantiation, The drawable Createfromxmlinner method invokes the Animationdrawable inflate method, which has a while loop that reads all the frames at once. That is, in the initialization of the time to read all the frames in memory, how many pictures, it will consume the corresponding size of memory.

Although the memory consumed by Animationdrawable can be freed in the following way, when you exit the interface using the animationdrawable and go back to using it to play the animation, you will report an exception using the image that you have recycled. This should be the Android to the image processing mechanism, although the activity was finish, but the activity used in the picture or in memory, if it is recycled, the next entry will report the exception information:

/**
 * Free animationdrawable occupied memory
 * * 
 *
/@SuppressWarnings ("unused")
private void Freeanimationdrawable (animationdrawable animationdrawable) {
 animationdrawable.stop (); 
 for (int i = 0; i < animationdrawable.getnumberofframes (); ++i) {
 drawable frame = animationdrawable.getframe (i); 
   if (frame instanceof bitmapdrawable) {(
  bitmapdrawable) frame). Getbitmap (). Recycle ();
 } 
 Frame.setcallback (null);
 } 

 Animationdrawable.setcallback (null);
}

Usually I will customize a imageview to achieve animationdrawable function, according to the time interval between the pictures to set the imageview background picture, so always just a imageview instance, replace only its background, Memory consumption is much smaller than animationdrawable:

/** * Picture Dynamic Switcher * * */public class Animimageview {private static final int msg_start = 0XF1;
 private static final int msg_stop = 0xf2;
 private static final int state_stop = 0xf3;

 private static final int state_running = 0xf4;
 /* Running state/private int mstate = state_running;
 Private ImageView Mimageview;
 /* Picture Resource ID list */private list<integer> mresourceidlist = null;
 /* Timed task */private Timer mtimer = null;
 Private Animtimertask mtimetask = null;
 /* Record Playback position * * Private int mframeindex = 0;

 /* Play form * * Private Boolean islooping = false;
 Public Animimageview () {Mtimer = new Timer ();
 /** * Set Animation playback resource * */public void setanimation (Hanziimageview imageview, list<integer> resourceidlist) {
 Mimageview = ImageView;
 Mresourceidlist = resourceidlist;
 /** * Start playing animation * @param loop @param duration Animation playback time interval */public void Start (Boolean loop, int duration) {
 Stop ();
 Islooping = loop;
 Mframeindex = 0;
 Mstate = state_running; Mtimetask = New AnImtimertask ();
 Mtimer.schedule (Mtimetask, 0, duration);
  /** * Stop Animation * */public void Stop () {if (mtimetask!= null) {mframeindex = 0;
  Mstate = State_stop;
  Mtimer.purge ();
  Mtimetask.cancel ();
  Mtimetask = null;
 Mimageview.setbackgroundresource (0); }/** * Timer task * * */class Animtimertask extends TimerTask {@Override public void run () {if (Mframeindex < 0 | |
  Mstate = = state_stop) {return;
  } if (Mframeindex < Mresourceidlist.size ()) {Message msg = Animhanlder.obtainmessage (msg_start,0,0,null);
  Msg.sendtotarget ();
  }else{mframeindex = 0;
   if (!islooping) {Message msg = Animhanlder.obtainmessage (msg_stop,0,0,null);
  Msg.sendtotarget (); Private Handler Animhanlder = new Handler () {public void Handlemessage (android.os.Message msg) {switch (msg.what) {case msg_start:{if (mframeindex >=0 && Mframeindex < Mresourceidlist.size () && mSt ate = = state_running) {Mimageview.setimAgeresource (Mresourceidlist.get (Mframeindex));
   mframeindex++;
  }} break;
   Case msg_stop:{if (mtimetask!= null) {mframeindex = 0;
   Mtimer.purge ();
   Mtimetask.cancel ();
   Mstate = State_stop;
   Mtimetask = null;
   Mimageview.setimageresource (0);
  }} break;
  Default:break;
}
  }
 }; }

Other optimization methods

1, as far as possible in the activity of the small picture and background merged, a small picture both waste layout time, but also to increase the memory footprint;

2. Do not set the default background picture for the activity in the subject of the activity, which can cause the memory of the activity to double:

<!--never set the default background for the activity in the topic

<style name= "Activity_style" parent= @android: Theme.Holo.Light.NoActionBar ">
<item name=" Android:background "> @drawable/*</item>
</style>

3. For pictures or layouts that appear only when needed, you can use the viewstub tag to pass the sdk/ The Hierarchyviewer.bat view layout file in the Tools directory finds that the components that use the viewstub tag do not consume the layout time, and the instantiation of the code when it needs to be displayed helps increase the efficiency of the activity's layout and save the memory that the activity consumes.

Summarize

The above is the entire content of this article, I hope for you to develop Android can help, if there are questions can be a message discussion.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.