Android 应用性能优化


Improving Android Application Performance David Wu dave_wu@htc.com Computer Science and Information Engineering, National Taiwan University December 22, 2011 © 2010 HTC Corporation. All rights reserved. 2 3 H!, I’m D"v!d F#l$o% @w&m"n 4 5 6 7 8 Agenda •  Performance •  Framework APIs •  UI performance •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices P'r(o)m"n*e 9 Why care about performance? 10 B"d u+e) e,p'r!e-c' 11 Activity takes a long time to launch 12 Application not responding to user events 13 Poor frame rate 14 Frame rates 15 •  To achieve 60 fps, you have 16ms to handle each frame. •  To achieve 24 fps, you have 41ms to handle each frame. •  Binder RPC call takes ~0.12ms. •  Reading a byte from flash takes ~5-25ms. •  Writing to flash takes ~5-100ms. •  TCP setup + HTTP fetch takes seconds. Numbers taken from http://www.youtube.com/watch?v=c4znvD-7VDA 16 Write performance varies Taken from http://www.youtube.com/watch?v=c4znvD-7VDA Never stall the UI thread Spawn a new thread to do work 17 Runnable Thread Future ExecutorService 18 19 new Thread(new Runnable() { @Override public void run() { // do some heavy work } }).start(); ! 20 new AsyncTask() { protected Long doInBackground(URL... urls) { final int count = urls.length; for ( int i = 0; i < count; i++ ) { Downloader.download(url); publishProgress(i); } return count; } protected void onProgressUpdate(Integer... progress) { setProgress(progress[0]); } protected void onPostExecute(Integer result) { showDialog(“Downloaded “ + result + “ files”); } } ! 21 HandlerThread mHandlerThread = new HandlerThread("WorkerThread"); Handler handler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case JOB_1: // do job #1 break; case JOB_2: // do job #2 break; } } }; ! 22 handler.sendEmptyMessage(JOB_1); handler.sendEmptyMessage(JOB_2); handler.post(new Runnable() { @Override public void run() { // do more work } }); ! 23 @Override protected void onDestroy() { mHandlerThread.quit(); super.onDestroy(); } ! 24 new AsyncQueryHandler(getContentResolver()) { @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { if (token == 0) { // get data from cursor } } }.startQuery(0, // token null, // cookie RawContacts.CONTENT_URI, null, // projection RawContacts.CONTACT_ID + " 37 Tip #3 38 ... ! ... ! 39 Defer inflation of rarely used views with 40 Tip #4 41 ! ((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE); // or View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate(); Use layoutopt to detect common problems 42 Tip #5 43 Summary for improving UI performance •  Use Hierarchy Viewer to identify bottlenecks •  Simplify complex layouts with RelativeLayout or GridLayout! •  Reuse layouts with ! •  Defer inflation of rarely used views with ! •  Use layoutopt to detect common problems 44 Agenda •  Performance •  Framework APIs •  UI performance •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices 45 Tip #1 SharedPreferences.Editor.apply(); // asynchronous SharedPreferences.Editor.commit(); // synchronous ! Asynchronous write to SharedPreferences 46 public class SharedPreferencesUtils { private static final Method sApplyMethod = findApplyMethod(); private static Method findApplyMethod() { try { Class cls = SharedPreferences.Editor.class; return cls.getMethod("apply"); } catch ( NoSuchMethodException unused ) { // fall through } return null; } public static void apply(SharedPreferences.Editor editor) { if ( sApplyMethod != null ) { try { sApplyMethod.invoke(editor); return; } catch ( InvocationTargetException unused ) { // fall through } catch ( IllegalAccessException unused ) { // fall through } } editor.commit(); } } ! 47 Tip #2 Profile your database queries with EXPLAIN QUERY PLAN! EXPLAIN QUERY PLAN SELECT * FROM .... ! 48 Tip #2 Profile your database queries with TraceView Reduce projection columns Use LIMIT clause to reduce selection rows 49 Tip #3 Minimize fillWindow time 50 Tip #4 Use index to optimize database queries 51 Tip #5 Precompile frequently used SQL statements String sql = “INSERT INTO table VALUES (?, ?)”; SQLiteStatement stmt = mDatabase.compileStatement(sql); DatabaseUtils.bindObjectToProgram(stmt, 1, 1); DatabaseUtils.bindObjectToProgram(stmt, 2, 2); stmt.execute(); stmt.close(); ! 52 Tip #6 Defer automatic requery in ContentObserver.onChange()! getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { mDirty = true; } }); ! @Override protected void onResume() { super.onResume(); if (mDirty) { // start query again mDirty = false; } } ! ContentProviderOperation! ContentProviderOperation.Builder! ContentResolver.applyBatch()! 53 Tip #7 Use bulk operations in a Transaction! SQLiteDatabase.yieldIfContendedSafely()! 54 Tip #8 Yield occasionally in a long Transaction! adb logcat -b events content_query_sample:I *:S! adb logcat -b events content_update_sample:I *:S! ! adb logcat -b events db_sample:I *:S! 55 Tip #9 Debug with event logs! I/content_query_sample( 6812): ! ![content://com.android.contacts/data,sourceid/_id,account_type=? AND account_name=? AND mimetype=? AND data2=?,,95,com.htc.example,20]! 56 content_query_sample! Uri! I/content_query_sample( 6812): ! ![content://com.android.contacts/data,sourceid/_id,account_type=? AND account_name=? AND mimetype=? AND data2=?,,95,com.htc.example,20]! 57 content_query_sample! Projection! I/content_query_sample( 6812): ! ![content://com.android.contacts/data,sourceid/_id,account_type=? AND account_name=? AND mimetype=? AND data2=?,,95,com.htc.example,20]! 58 content_query_sample! Selection! I/content_query_sample( 6812): ! ![content://com.android.contacts/data,sourceid/_id,account_type=? AND account_name=? AND mimetype=? AND data2=?,,95,com.htc.example,20]! 59 content_query_sample! Operation duration! I/content_query_sample( 6812): ! ![content://com.android.contacts/data,sourceid/_id,account_type=? AND account_name=? AND mimetype=? AND data2=?,,95,com.htc.example,20]! 60 content_query_sample! Caller package name! (only when on UI thread) I/content_update_sample( 3156): ! ![content://com.htc.friendstream.provider.FriendStreamProvider/preferences/ key/pk_synctime,insert,,39,,8]! 61 content_update_sample! Uri! I/content_update_sample( 3156): ! ![content://com.htc.friendstream.provider.FriendStreamProvider/preferences/ key/pk_synctime,insert,,39,,8]! 62 content_update_sample! Operation type! (insert, bulkInsert, update, delete) I/content_update_sample( 3156): ! ![content://com.htc.friendstream.provider.FriendStreamProvider/preferences/ key/pk_synctime,insert,,39,,8]! 63 content_update_sample! Operation duration! I/db_sample( 8888): ! ![/data/data/com.htc.usagemonitor/databases/usagemonitor.db,UPDATE billingcycle SET latest_update=?, data_rx=? WHERE _id = 1,4,com.htc.usagemonitor:RetrieverService,1]! 64 db_sample! Database file path! I/db_sample( 8888): ! ![/data/data/com.htc.usagemonitor/databases/usagemonitor.db,UPDATE billingcycle SET latest_update=?, data_rx=? WHERE _id = 1,4,com.htc.usagemonitor:RetrieverService,1]! 65 db_sample! SQL statement! I/db_sample( 8888): ! ![/data/data/com.htc.usagemonitor/databases/usagemonitor.db,UPDATE billingcycle SET latest_update=?, data_rx=? WHERE _id = 1,4,com.htc.usagemonitor:RetrieverService,1]! 66 db_sample! Operation duration! I/db_sample( 8888): ! ![/data/data/com.htc.usagemonitor/databases/usagemonitor.db,UPDATE billingcycle SET latest_update=?, data_rx=? WHERE _id = 1,4,com.htc.usagemonitor:RetrieverService,1]! 67 db_sample! Caller package name! 68 Summary for improving I/O performance •  Asynchronous write to SharedPreferences! •  Profile database queries with EXPLAIN QUERY PLAN and TraceView •  Minimize fillWindow time by reducing projection columns and using the LIMIT clause •  Use index to optimize database queries •  Precompile frequently used SQL statements using prepared statements •  Defer automatic requery in ContentObserver.onChange()! •  Use bulk operations in a transaction •  Yield occasionally in a long transaction •  Debug with event logs 69 Agenda •  Performance •  Framework APIs •  UI performance •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices ListAdapter.getView()! View.onDraw()! 70 Avoid unnecessary inflation by reusing convertView! 71 Tip #1 @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.main, parent, false); } // .... } ! Cache Views to avoid findViewById()! 72 Tip #2 @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.main, parent, false); ViewHolder holder = new ViewHolder(); holder.img = (ImageView) convertView.findViewById(R.id.image); holder.txt = (TextView) convertView.findViewById(R.id.text); convertView.setTag(holder); } ViewHolder holder = (ViewHolder) convertView.getTag(); holder.img.setImageResource(R.drawable.icon); holder.txt.setText(R.string.hello); return convertView; } private static class ViewHolder { ImageView img; TextView txt; } ! Avoid drawing the unnecessary! 73 Tip #3 74 Android draws each parent View, even if it’s behind an opaque child View. 75 “A tablet built with Tegra 2 can touch every pixel of a 1200x800 screen about 2.5 times at 60fps.” -- Dianne Hackborn https://plus.google.com/105051985738280261832/posts/2FXDCz8x93s 76 If you have a parent View that will never be seen, don’t draw it (and its background). 77 78 Activity WindowManager Logic Views PhoneWindow ViewRoot DecorView FrameLayout Application View Hierarchy 79 In most cases you don’t need to draw the window background of an Activity getWindow().setBackgroundDrawable(null); ! android:windowBackground="@null" ! Avoid runtime scaling of images! 80 Tip #4 Avoid doing animations during a scroll (marquee, AnimationDrawable, etc.)! 81 Tip #5 If you have to use animations, disable the drawing cache. ListView.setDrawableCacheEnabled(false)! 82 Use Allocation Tracker to help detect and avoid frequent garbage collection! 83 Tip #6 Object pool StringBuilder Cache using SoftReference 84 Debug with StrictMode enabled 85 Tip #7 public void onCreate() { if (DEVELOPER_MODE) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() .penaltyLog() .build()); StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); } super.onCreate(); } ! Looper.setMessageLogging()! Check main thread Looper for unnecessary activities 86 Tip #8 87 Summary for improving scrolling performance •  Avoid unnecessary inflation by reusing convertView! •  Cache Views to avoid findViewById()! •  Avoid drawing the unnecessary •  Avoid runtime scaling of images •  Avoid animations during a scroll (or disable the drawing cache) •  Use Allocation Tracker to detect and avoid frequent garbage collection •  Debug with StrictMode enabled •  Check main thread Looper for unnecessary activities! 88 Agenda •  Performance •  Framework APIs •  UI performance •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices M'm#r/ 89 90 At the system level, Android runs a modified OOM killer using the low memory driver 91 Linux OOM killer 92 OOM_ADJ 93 Android Low Memory Killer Low memory thresholds in Android (init.rc) # Define the memory thresholds at which the above process classes will! # be killed. These numbers are in pages (4k).! ! setprop ro.FOREGROUND_APP_MEM 2048! setprop ro.VISIBLE_APP_MEM 3072! setprop ro.PERCEPTIBLE_APP_MEM 4096! setprop ro.HEAVY_WEIGHT_APP_MEM 4096! setprop ro.SECONDARY_SERVER_MEM 6144! setprop ro.BACKUP_APP_MEM 6144! setprop ro.HOME_APP_MEM 6144! setprop ro.HIDDEN_APP_MEM 7168! setprop ro.EMPTY_APP_MEM 8192! ! 94 OOM_ADJ based on importance levels (init.rc) # Define the oom_adj values for the classes of processes that can be ! # killed by the kernel. These are used in ActivityManagerService. ! ! setprop ro.FOREGROUND_APP_ADJ 0 ! setprop ro.VISIBLE_APP_ADJ 1 ! setprop ro.PERCEPTIBLE_APP_ADJ 2 ! setprop ro.HEAVY_WEIGHT_APP_ADJ 3 ! setprop ro.SECONDARY_SERVER_ADJ 4 ! setprop ro.BACKUP_APP_ADJ 5 ! setprop ro.HOME_APP_ADJ 6 ! setprop ro.HIDDEN_APP_MIN_ADJ 7 ! setprop ro.EMPTY_APP_ADJ 15 ! ! 95 Process importance levels •  Persistent •  OOM_ADJ < 0 •  system_server (-16) and com.android.phone (-12) •  Foreground •  FOREGROUND_APP_ADJ = 0 •  Runs a foreground Activity •  Runs a Service executing one of onCreate(), onStartCommand(), onDestroy() •  Hosts a Service that’s bound by a foreground Activity or a foreground process •  Runs a BroadcastReceiver executing onReceive() •  Hosts a ContentProvider which is currently used by persistent or foreground processes 96 Process importance levels •  Visible •  VISIBLE_APP_ADJ = 1 •  Runs a visible Activity (not in foreground) •  Runs a Service started with startService() and the Service uses startForeground() to put itself in foreground state •  Service •  SECONDARY_SERVER_ADJ = 4 •  Runs a Service that’s been started with startService() and is not a visible process •  Background •  HIDDEN_APP_MIN_ADJ (7) .. EMPTY_APP_ADJ (15) •  Process that doesn’t hold any active application components http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html 97 Activity.onLowMemory()! Fragment.onLowMemory()! Activity.onSaveInstanceState(Bundle)! Service.onLowMemory()! ContentProvider.onLowMemory()! 98 What does this mean for the Application? 99 Agenda •  Performance •  Framework APIs •  UI performance •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices 100 At the application level, Android restricts how much memory can be allocated to each application. 101 Android defines a heap limit for each application and dictates when an OutOfMemoryError will be thrown. Terminologies •  Heap limit •  Maximum allowed footprint for the Dalvik heap •  Heap size •  Current footprint of the Dalvik heap •  Allocated •  Total number of bytes allocated on the Dalvik heap •  Free •  Heap size – Allocated •  % Used •  Allocated / Heap size * 100% •  External allocation (before Honeycomb) •  Bitmap byte[] 102 Heap size limit n  Heap size limits n  G1: 16MB n  Droid: 24MB n  Nexus One: 32MB n  Xoom: 48MB n  ActivityManager.getMemoryClass() 103 OOM Conditions •  OOM condition before Honeycomb •  Heap size + external allocation + new allocation request >= Heap limit •  OOM condition after Honeycomb •  Heap size + new allocation request >= Heap limit 104 Not representative of the memory usage of a process •  Every process forks from zygote, which has 2MB+ initial overhead •  There is more memory allocation at the native level: •  Android applications run on the Dalvik VM, but can use JNI to load native libraries. •  Dalvik level APIs called by an application can use native libraries on application’s behalf. •  If you enable hardware acceleration (default in ICS), you get 8MB overhead to use OpenGL. 105 Ranking processes in order of memory usage 106 adb shell procrank -p! PID Vss Rss Pss Uss cmdline! 3156 80272K 80220K 59228K 57624K com.htc.launcher! 1455 94540K 58728K 37488K 36060K system_server! 9000 55224K 55200K 33900K 32412K com.roguso.plurk! 6713 47912K 47880K 27719K 26788K tw.anddev.aplurk! 1624 44804K 44760K 24954K 24200K android.process.acore! 2081 44992K 44960K 23205K 21628K com.htc.android.mail! 1604 41288K 41248K 22393K 21752K com.htc.android.htcime! 1594 40912K 40844K 21588K 20284K com.htc.weatheridlescreen! 1622 39904K 39872K 21297K 20696K com.android.phone! 11961 49420K 44860K 20930K 18652K com.htc.friendstream! 6812 38756K 38696K 18285K 16964K com.htc.bg! 11725 36356K 36324K 14037K 12316K com.tam.ladyplurk! 1746 32560K 32488K 13056K 12336K com.htc.bgp! 1586 51016K 30076K 11382K 10636K com.android.systemui! 11086 30544K 30472K 10450K 9748K com.facebook.katana! 1651 29596K 29556K 10134K 9432K com.google.process.gapps! Terminologies •  VSS (Virtual Set Size) •  Total count of pages that the process can access •  RSS (Resident Set Size) •  Total count of pages in RAM that the process can access •  PSS (Proportional Set Size) •  Total count of pages that a process uses in RAM, where the size of each page is divided by the number of processes sharing it •  USS (Unique Set Size) •  Count of unshared pages that a process can access 107 Listing virtual memory areas of a process 108 adb shell procmem -p ! Vss Rss Pss Uss ShCl ShDi PrCl PrDi Name! ------- ------- ------- ------- ------- ------- ------- ------- ! 4K 4K 0K 0K 4K 0K 0K 0K /system/bin/app_process! 4K 4K 0K 0K 4K 0K 0K 0K /system/bin/app_process! 13908K 13908K 11571K 11508K 2400K 0K 11508K 0K [heap]! 0K 0K 0K 0K 0K 0K 0K 0K [heap]! 4K 4K 4K 4K 0K 0K 4K 0K [heap]! 36K 36K 0K 0K 0K 36K 0K 0K /dev/__properties__! 4K 4K 4K 4K 0K 0K 4K 0K /dev/__properties__! 12084K 12084K 8166K 8060K 4024K 0K 8060K 0K /dev/ashmem/dalvik-heap! 0K 0K 0K 0K 0K 0K 0K 0K /dev/ashmem/dalvik-heap! 212K 212K 212K 212K 0K 0K 212K 0K /dev/ashmem/dalvik-bitmap-1! 0K 0K 0K 0K 0K 0K 0K 0K /dev/ashmem/dalvik-bitmap-2! 256K 256K 256K 256K 0K 0K 256K 0K /dev/ashmem/dalvik-card-table! 12K 12K 12K 12K 0K 0K 12K 0K /dev/ashmem/dalvik-card-table! 0K 0K 0K 0K 0K 0K 0K 0K /dev/ashmem/dalvik-LinearAlloc! 2936K 2936K 2336K 2320K 616K 0K 2320K 0K /dev/ashmem/dalvik-LinearAlloc! 0K 0K 0K 0K 0K 0K 0K 0K /dev/ashmem/dalvik-LinearAlloc! 0K 0K 0K 0K 0K 0K 0K 0K /system/framework/core-junit.jar! 0K 0K 0K 0K 0K 0K 0K 0K /system/framework/ext.jar! Some important virtual memory areas •  /dev/ashmem/dalvik-heap •  Anonymous pages allocated for the heap at the Dalvik level •  [heap], [anonymous] •  Anonymous pages allocated via malloc() at the native level •  /system/framework/*.odex (release build) •  /data/dalvik-cache/*.odex (debug build) •  File-backed mmap pages 109 Listing virtual memory areas of a process 110 adb shell dumpsys meminfo ! Applications Memory Usage (kB):! Uptime: 89133197 Realtime: 106110266! ! ** MEMINFO in pid 11961 [com.htc.friendstream] **! native dalvik other total limit bitmap nativeBmp! size: 15032 8535 N/A 23567 32768 N/A N/A! allocated: 14565 5697 N/A 20262 N/A 4669 1918! free: 162 2838 N/A 3000 N/A N/A N/A! (Pss): 4105 2550 13952 20607 N/A N/A N/A! (shared dirty): 2440 1928 5532 9900 N/A N/A N/A! (priv dirty): 4044 708 12716 17468 N/A N/A N/A! ! Objects! Views: 0 ViewRoots: 0! AppContexts: 0 Activities: 0! Assets: 7 AssetManagers: 7! Local Binders: 11 Proxy Binders: 15! Death Recipients: 1! OpenSSL Sockets: 0! ! Terminologies •  Private Dirty = USS •  Amount of RAM inside the process that cannot be paged to disk and is not shared with any other processes •  The RAM that will become available to the system when the process goes away http://stackoverflow.com/questions/2298208/how-to-discover-memory-usage-of-my-application-in-android 111 112 Agenda •  Performance •  Framework APIs •  UI performance •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices Garbage collection •  Pre-Gingerbread GC: •  Stop-the-world •  Full heap collection •  Pause times often > 100ms •  Gingerbread and beyond: •  Concurrent (mostly) •  Partial collections •  Pause times usually < 5ms 113 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ Memory leaks •  GC does not prevent memory leaks •  Have a long living reference to an unused object. This prevents the unused object from being garbage collected. •  In Android, the memory leak is often a reference to Context/Activity. 114 115 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ Common cases of Context/Activity leakage •  Long-living reference to an object that is an instance of a non-static inner class of an Activity 116 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ Common cases of Context/Activity leakage •  Long-living reference to an object that is an instance of a non-static inner class of an Activity 117 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ Common cases of Context/Activity leakage •  Long-living Thread that outruns the Activity’s lifecycle 118 new Thread(new Runnable() { @Override public void run() { // do some heavy work } }).start(); ! 119 Tip #1 Use logcat to check if there is an increase of memory usage over time (look out for step functions!) 120 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ 121 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ 122 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ 123 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ 124 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ 125 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ Use Allocation Tracker to see if you’re allocating unanticipated objects over time 126 Tip #2 127 Use Eclipse Memory Analyzer (MAT) to identify possible leaks 128 Tip #3 MAT demo 129 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ http://www.youtube.com/watch?feature=player_embedded&v=_CruQY55HOk •  Use Histogram view to see the number of Activity instances. Having more than one instance of an Activity is a strong sign of Activity/Context leak. 130 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ •  The Dominator Tree view ordered by retained size helps you identify objects that retains lots of memory and cannot be freed. They are usually good starting points to find memory leaks. 131 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ •  The Dominator Tree view ordered by retained size helps you identify objects that retains lots of memory and cannot be freed. They are usually good starting points to find memory leaks. 132 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ •  The Dominator Tree view ordered by retained size helps you identify objects that retains lots of memory and cannot be freed. They are usually good starting points to find memory leaks. 133 http://dubroy.com/blog/google-io-memory-management-for-android-apps/ 134 Agenda •  Performance •  Framework APIs •  UI performance •  I/O performance •  Scrolling performance •  Memory •  How Android memory management works •  OOM killer and low memory killer •  Memory measurement of an application •  Identifying memory leaks •  Best practices Best Practices • OutOfMemoryError thrown: •  Identify whether it’s Bitmap or Java objects that takes up more of the allocations. •  If it’s Bitmap: •  Down sample your images •  Recycle unused Bitmaps early with Bitmap.recycle()! •  If it’s Java objects: •  Use techniques to avoid fragmentation •  Reduce peak usage of Java heap allocation •  Use SoftReference to implement caches (rather than holding strong reference to entire dataset) •  Use WeakReference to avoid memory leaks 135 Down sampling Bitmap 136 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, opts); final int originalWidth = opts.outWidth; final int originalHeight = opts.outHeight; final int originalDim = Math.max(originalWidth, originalHeight); opts = new BitmapFactory.Options(); opts.inSampleSize = 1; while ( originalDim > MAX_IMAGE_DIM ) { opts.inSampleSize *= 2; originalDim /= 2; } return BitmapFactory.decodeFile(path, opts); ! WeakReference 137 public class MainActivity extends Activity { static Leaky leak = null; static class Leaky { void doSomething() { System.out.println("Wheeee!!!"); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (leak == null) { leak = new Leaky(); } } } ! WeakReference 138 public class MainActivity extends Activity { static Leaky leak = null; static class Leaky { private final Context mContext; public Leaky(Context context) { super(); mContext = context; doSomethingWithOuterInstance(); } void doSomethingWithOuterInstance() { String text = mContext.getString(R.string.hello); System.out.println(text); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (leak == null) { leak = new Leaky(this); } } } ! WeakReference 139 public class MainActivity extends Activity { static Leaky leak = null; static class Leaky { private final WeakReference mContext; public Leaky(Context context) { super(); mContext = new WeakReference(context); doSomethingWithOuterInstance(); } void doSomethingWithOuterInstance() { Context context = mContext.get(); if (context != null) { String text = context.getString(R.string.hello); System.out.println(text); } } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (leak == null) { leak = new Leaky(this); } } } ! 140 D"v!d W& @w&m"n d"v!d@w&-m"n.c#m b$o0.w&-m"n.c#m 141
还剩140页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 6 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

b8gp

贡献于2014-10-10

下载需要 6 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf