Android 性能优化总结

wzlx2000 7年前
   <h2><strong>将从以下几个方面总结Android的应用性能优化</strong></h2>    <h2><strong>性能</strong></h2>    <ul>     <li> <p>框架API</p> </li>     <li> <p>UI 性能</p> </li>     <li> <p>I/O性能</p> </li>     <li> <p>屏幕滚动性能</p> </li>    </ul>    <h2><strong>内存</strong></h2>    <ul>     <li> <p>Android 如何管理内存</p>      <ul>       <li> <p>OOM终结 & 低内存终结</p> </li>       <li> <p>应用内存使用监测</p> </li>      </ul> </li>     <li> <p>识别内存泄露</p> </li>     <li> <p>最佳实践</p> </li>    </ul>    <p>糟糕的用户体验</p>    <ul>     <li> <p>Activity 启动时间过长</p> </li>     <li> <p>应用无反应(ANR)</p> </li>     <li> <p>帧速率差</p> </li>    </ul>    <p>关于帧</p>    <p>帧速率</p>    <ul>     <li> <p>为了保证能达到60fps,最多只有16ms去处理每一帧</p> </li>     <li> <p>而保证能达到24fps,最多只有41ms去处理每一帧</p> </li>    </ul>    <p>常见操作耗时</p>    <ul>     <li> <p>Binder RPC 调用大约花费 0.12ms</p> </li>     <li> <p>从闪存读取一个字节大约花费 5-25ms</p> </li>     <li> <p>写内容到闪存大约花费 5-100ms</p> </li>     <li> <p>TCP 初始化以及HTTP提取通常花费秒级的时间</p> </li>    </ul>    <p>往磁盘写内容的时候,会随着磁盘的剩余空间的较少而导致写速率不断减低</p>    <p>永远不要做阻塞UI线程的事情,用一个新的线程去做可能会影响UI体验的事情</p>    <p>四种可以异步的实现:</p>    <ol>     <li> <p>Runnable</p> </li>     <li> <p>Thread</p> </li>     <li> <p>Future</p> </li>     <li> <p>ExecutorService</p> </li>    </ol>    <ul>     <li> <p>使用Thread</p> </li>    </ul>    <pre>  <code class="language-java">new Thread(new Runnable() {    @Override    public void run() {        // do some heavy work    }  }).start();</code></pre>    <ul>     <li> <p>使用内置AsyncTask</p> </li>    </ul>    <pre>  <code class="language-java">new AsyncTask<URL, Integer, Integer>() {    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”);    }  }</code></pre>    <ul>     <li> <p>使用HandlerThread</p> </li>    </ul>    <pre>  <code class="language-java">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;      }    }  };      handler.sendEmptyMessage(JOB_1);  handler.sendEmptyMessage(JOB_2);      handler.post(new Runnable() {    @Override    public void run() {        // do more work    }  });      @Override  protected void onDestroy() {    mHandlerThread.quit();    super.onDestroy();  }</code></pre>    <ul>     <li> <p>使用AsyncQueryHandler</p> </li>    </ul>    <pre>  <code class="language-java">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 + "<?", // selection          new String[] { "888" }, // selectionArgs          RawContacts.DISPLAY_NAME_PRIMARY + " ASC" // orderby          );</code></pre>    <ul>     <li> <p>使用IntentService</p> </li>    </ul>    <pre>  <code class="language-java">public class WorkerService extends IntentService {    public WorkerService() {      super("WorkerThread");    }    @Override    protected void onHandleIntent(Intent intent) {      String action = intent.getAction();      if ("com.test.DO_JOB_1".equals(action)) {          // do job #1      }    }  }        startService(new Intent("com.test.DO_JOB_1"));</code></pre>    <h2><strong>UI线程性能总结</strong></h2>    <ul>     <li> <p>Activity or Fragment</p>      <ul>       <li> <p>AsyncTask</p> </li>       <li> <p>Handler,HandlerThread</p> </li>       <li> <p>AsyncTaskLoader</p> </li>      </ul> </li>     <li> <p>ContentProvider</p>      <ul>       <li> <p>AsyncQueryHandler</p> </li>       <li> <p>CursorLoader</p> </li>      </ul> </li>     <li> <p>Service</p>      <ul>       <li> <p>IntentService</p> </li>       <li> <p>Parcel.writeStrongBinder(IBinder binder)</p> </li>      </ul> </li>    </ul>    <h2><strong>View Hierarchy</strong></h2>    <ul>     <li> <p>Measure</p> </li>     <li> <p>Layout</p> </li>     <li> <p>Draw</p> </li>    </ul>    <ul>     <li> <p>Key Events</p> </li>     <li> <p>Trackball Events</p> </li>     <li> <p>Touch Evnets</p> </li>    </ul>    <p>Tips:</p>    <ul>     <li> <p>降低布局层次结构的复杂性</p> </li>     <li> <p>使用层次结构查看器来检查是否存在瓶颈</p> </li>     <li> <p>使用RelativeLayout或者GridLayout来简化复杂布局的层次嵌套</p> </li>     <li> <p>使用 <strong><merge /></strong> 标签来较少布局层次</p> </li>     <li> <p>使用 <strong><ViewStub /></strong> 标签来延迟该标签下的布局的渲染</p> <pre>  <code class="language-java"><ViewStub  android:id="@+id/stub_import"  android:inflatedId="@+id/panel_import"  android:layout="@layout/progress_overlay"  android:layout_width="fill_parent"  android:layout_height="wrap_content"  android:layout_gravity="bottom" /></code></pre> <pre>  <code class="language-java">((ViewStub)  findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);  // or  View importPanel = ((ViewStub)  findViewById(R.id.stub_import)).inflate();</code></pre> </li>     <li> <p>使用layoutopt检测常见问题</p> </li>    </ul>    <h2><strong>I/O性能优化</strong></h2>    <ul>     <li> <p>异步写SharedPreferences</p> <pre>  <code class="language-java">SharedPreferences.Editor.apply(); // 异步  SharedPreferences.Editor.commit(); // 同步</code></pre> </li>     <li> <p>数据库query查询语句中的 * 替换成具体的列值</p> </li>     <li> <p>使用TraceView配置您的数据库查询</p> </li>     <li> <p>使用LIMIT子句减少选择行</p> </li>     <li> <p>最小化完整窗口时间</p> </li>     <li> <p>使用索引优化数据库查询</p> </li>     <li> <p>预编译常用的SQL语句</p> </li>    </ul>    <pre>  <code class="language-java">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();    //或者使用 PreparaStatement</code></pre>    <ul>     <li>推迟ContentObserver.onChange()中的自动重新检查</li>    </ul>    <pre>  <code class="language-java">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;      }    }</code></pre>    <ul>     <li> <p>在事务中使用批量操作</p>      <ul>       <li>ContentProviderOperation!</li>       <li>ContentProviderOperation.Builder!</li>       <li>ContentResolver.applyBatch()</li>      </ul> </li>     <li> <p>在一个比较长的事务中允许偶尔的事务提前</p> <pre>  <code class="language-java">SQLiteDatabase.yieldIfContendedSafely()</code></pre> </li>     <li> <p>使用事件日志调试</p> adb logcat -b events content_query_sample:I <em> </em> <p><em>:S</em></p> <em> </em> :S <p>adb logcat -b events db_sample:I *:S</p> </li>    </ul>    <h2><strong>滑动性能优化(List)</strong></h2>    <ul>     <li> <p>ListView : 通过复用view来避免不必要的inflate操作</p> <pre>  <code class="language-java">@Override  public View getView(int position, View convertView, ViewGroup parent) {    if (convertView == null) {        convertView = mInflater.inflate(R.layout.main, parent, false);    }    // ....  }</code></pre> </li>     <li> <p>通过ViewHolder缓存v试图,而避免不必要的findViewByI'd</p> <pre>  <code class="language-java">@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;  }</code></pre> </li>     <li> <p>避免view的不必要绘制(例如背景的重复绘制)</p> <p>Android 中 会绘制每一个父view即使它被覆盖在一个不透明的子view之下</p> <p>当你有一个父view并且是永远不可见的,那么不要绘制它(包括他的背景)</p> </li>     <li> <p>大多数的情况下你不需要绘制window的背景</p> <pre>  <code class="language-java">//Activity中  getWindow().setBackgroundDrawable(null);    //style中  android:windowBackground="@null"</code></pre> </li>     <li> <p>避免在运行时进行图片缩放(特殊业务需求除外)</p> </li>     <li> <p>避免在视图(ListView等)滚动的时候进行动画,如果业务要求使用动画,那么请关闭绘制缓存</p> <pre>  <code class="language-java">ListView.setDrawableCacheEnabled(false)</code></pre> </li>     <li> <p>使用Allocation Tracker(内存分配追踪器)检测并避免频繁的垃圾回收</p> </li>     <li> <p>考虑使用Object Pool ,StringBuilder等封装类型</p> </li>     <li> <p>缓存的时候考虑使用SoftReference</p> </li>     <li> <p>在调试模式的时候启用StrictMode(可以检查大部分不规范,不安全操作)</p> <pre>  <code class="language-java">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();  }</code></pre> </li>     <li> <p>检查主线程Looper是否有不必要的活动</p> <pre>  <code class="language-java">Looper.setMessageLogging();</code></pre> </li>    </ul>    <h2><strong>Memory</strong></h2>    <p>在系统级别,Android使用低内存驱动程序运行修改过的OOM Killer</p>    <p>包括:</p>    <ul>     <li>Linux OOM killer</li>     <li>OOM_ADJ</li>     <li>Android Low Memory Killer</li>    </ul>    <p>Android中的低内存阈值(init.rc中)</p>    <pre>  <code class="language-java"># 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</code></pre>    <p>OOM_ADJ基于重要性级别(init.rc中)</p>    <pre>  <code class="language-java"># 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</code></pre>    <p>进程重要性级别</p>    <ul>     <li>Persistent(持续存在)      <ul>       <li>OOM_ADJ < 0</li>       <li>system_server (-16) , com.android.phone (-12)</li>      </ul> </li>     <li>Foreground(前台进程)      <ul>       <li>FOREGROUND_APP_ADJ = 0</li>       <li>运行前台Activity</li>       <li>运行一个Service,执行onCreate(),onStartCommand(),onDestroy()</li>       <li>托管由前台Activity或前台进程绑定的Service</li>       <li>运行一个BroadcastReceiver,执行onReceive()</li>       <li>托管由持续或前台进程使用的ContentProvider</li>      </ul> </li>     <li>Visible(可见进程)      <ul>       <li>VISIBLE_APP_ADJ = 1</li>       <li>运行可见的Activity(不在前台,也就是,不是正在和用户交互的)</li>       <li>运行由startService()启动的Service,Service使用startForeground()<br> 使自己处于前台状态</li>      </ul> </li>     <li>Service(服务进程)      <ul>       <li>SECONDARY_SERVER_ADJ = 4</li>       <li>运行由startService()启动Service,并且不是可见进程</li>      </ul> </li>     <li>Background(后台进程)      <ul>       <li>HIDDEN_APP_MIN_ADJ (7) .. EMPTY_APP_ADJ (15)</li>       <li>不包含任何活动应用程序组件的进程</li>      </ul> </li>    </ul>    <p>Low Memory 回调</p>    <pre>  <code class="language-java">Activity.onLowMemory()  Fragment.onLowMemory()  Activity.onSaveInstanceState(Bundle)  Service.onLowMemory()  ContentProvider.onLowMemory()</code></pre>    <p>在应用程序级别,Android限制了多少内存可以分配给每个应用程序。</p>    <p>Android为每个应用程序定义了一个堆限制,并指示何时抛出OutOfMemoryError</p>    <p>Android Studio 中 Heap窗口中的相关术语</p>    <table>     <thead>      <tr>       <th>术语</th>       <th>解释</th>      </tr>     </thead>     <tbody>      <tr>       <td>Heap limit</td>       <td>应用在Dalvik堆中的最大允许占用空间</td>      </tr>      <tr>       <td>Heap size</td>       <td>当前Dalvik堆的大小</td>      </tr>      <tr>       <td>Allocated</td>       <td>应用在Dalvik堆上分配的字节总数</td>      </tr>      <tr>       <td>Free</td>       <td>Heap size – Allocated</td>      </tr>      <tr>       <td>% Used</td>       <td>Allocated / Heap size * 100%</td>      </tr>      <tr>       <td>External allocation (3.0之前)</td>       <td>Bitmap byte[]</td>      </tr>     </tbody>    </table>    <p>ActivityManager.getMemoryClass() 可以查看当前应用Heap size limit</p>    <p>OOM 发生的情形</p>    <ul>     <li>3.0之前</li>    </ul>    <p>Heap size + external allocation + new allocation request >= Heap limit</p>    <ul>     <li> <p>3.0(包括)之后</p> <p>Heap size + new allocation request >= Heap limit</p> </li>    </ul>    <p>new allocation request : 新的内存开辟请求大小</p>    <p>不代表进程内存使用的情形</p>    <ul>     <li> <p>每个进程从zygote fork出来后,都会有2mb以上的开销</p> </li>     <li> <p>在使用native的时候会开辟更多的内存:</p>      <ul>       <li> <p>Android应用程序运行在Dalvik VM中,同时通过JNI加载本地库</p> </li>       <li> <p>由应用程序调用的Dalvik级API可以代表申请人使用本机库。</p> </li>      </ul> </li>     <li> <p>如果你启用了硬件加速(4.0中默认开启),那么会多有8mb的内存去使用OpenGL</p> </li>    </ul>    <p>查看内存使用情况</p>    <ul>     <li> <p>根据进程内存使用情况排序:</p> </li>    </ul>    <p>adb shell procrank -p</p>    <pre>  <code class="language-java">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</code></pre>    <p>VSS(Virtual Set Size):进程可以访问的页面总数</p>    <p>RSS(Resident Set Size): RAM中进程可以访问的页总数</p>    <p>PSS(Proportion Set Size):进程在RAM中使用的页面总数,其中每个页面的大小是页面总数除以共享它的进程数</p>    <p>USS(Unique Set Size):进程可以访问的非共享页面的数量</p>    <ul>     <li> <p>列出进程的虚拟内存区域</p> <p>adb shell procmem -p <pid></p> </li>    </ul>    <pre>  <code class="language-java">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__  .......</code></pre>    <p>adb shell dumpsys meminfo <pid></p>    <pre>  <code class="language-java">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!</code></pre>    <p>Private Dirty = USS</p>    <p>无法分页到磁盘并且不与任何其他进程共享的进程内部RAM量</p>    <p>当进程消失时,系统可以使用的RAM</p>    <ul>     <li> <p>一些重要的虚拟内存区域</p> </li>    </ul>    <p>/dev/ashmem/dalvik-heap : 在Dalvik级别为堆分配的匿名页面</p>    <p>[heap], [anonymous] : 由malloc()在本机级别分配的匿名页面</p>    <p>/system/framework/*.odex (release build)</p>    <p>/data/dalvik-cache/*.odex (debug build) : 文件支持的mmap页面</p>    <h3><strong>Garbage collection(垃圾收集)</strong></h3>    <ul>     <li>      <ol>       <li> <p>0之前的GC:</p> </li>      </ol> <p>收集垃圾的时候会停止其他所有的工作</p> <p>对整个堆进行收集</p> <p>造成的暂停时间一般都大于100ms</p> </li>     <li> <p>3.0及其之后</p> <p>不会暂停其他工作,而是与其他工作同时进行(绝大部分是这样的)</p> <p>一次垃圾收集只是对堆的一部分而已</p> <p>造成的暂停时间一般小于5ms</p> </li>    </ul>    <h3><strong>Memory leaks(内存泄露)</strong></h3>    <ul>     <li> <p>GC并不能避免内存泄露</p> </li>     <li> <p>有一个指向长期存在的且未使用的对象的应用,导致这个不被使用的对象不能被回收</p> </li>     <li> <p>在Android中,通常发生内存泄露的是对Context或者Activity的引用</p> </li>    </ul>    <p>常见的因为Context 或着 Activity造成的内存泄露</p>    <ol>     <li> <p>在Activity中存在长期存在的指向非静态内部类实例对象的引用</p> <pre>  <code class="language-java">public class TestActivity extends Activity{    static LeakyTest leaky = null;    class LeakyTest{      void doSoming(){        //doing      }    }      @Override    protected void onCreate(Bundle saveInstanceStates){      super.onCreate(saveInstanceStates);      if(leaky==null)        leaky = new LeakyTest();      //....    }    //.....  }</code></pre> </li>     <li> <p>在Activity中有超出Activity生命周期且长期存活的线程</p> <pre>  <code class="language-java">new Thread(new Runnable(){       @Override       public void run(){         //do long-live works       }     }).start();</code></pre> </li>    </ol>    <p>有用的方法</p>    <ul>     <li> <p>使用logcat检查是否有内存随着时间的推移而不断增加(尤其注意某些方法的执行步骤!)</p> </li>    </ul>    <p>例如得到的日志信息:</p>    <pre>  <code class="language-java">D/dalvikvm(9050):GC_CONCURRENT free 2049k, 65% free  3571k/9991k, external 4703k/5261k, paused 2ms+2ms</code></pre>    <p>D/dalvikvm(9050): <u>GC_CONCURRENT</u> free 2049k, 65% free 3571k/9991k, external 4703k/5261k, paused 2ms+2ms</p>    <p>下划线处GC的原因:</p>    <p>GC_CONCURRENT</p>    <p>GC_FOR_MALLOC</p>    <p>GC_EXTERNAL_ALLOC</p>    <p>GC_HPROF_DUMP_HEAP</p>    <p>GC_EXPLICIT</p>    <p>D/dalvikvm(9050): GC_CONCURRENT <u>free 2049k</u> , 65% free 3571k/9991k, external 4703k/5261k, paused 2ms+2ms</p>    <p>下划线处GC的原因:</p>    <p>内存释放</p>    <p>D/dalvikvm(9050): GC_CONCURRENT free 2049k, <u>65% free 3571k/9991k</u> , external 4703k/5261k, paused 2ms+2ms</p>    <p>下划线处GC的原因:</p>    <p>内存释放</p>    <p>堆进行信息统计</p>    <p>D/dalvikvm(9050): GC_CONCURRENT free 2049k, 65% free 3571k/9991k, <u>external 4703k/5261k,</u> paused 2ms+2ms</p>    <p>下划线处GC的原因:</p>    <p>内存释放</p>    <p>堆进行信息统计</p>    <p>内部内存进行信息统计</p>    <p>D/dalvikvm(9050): GC_CONCURRENT free 2049k, 65% free 3571k/9991k, external 4703k/5261k, <u>paused 2ms+2ms</u></p>    <p>下划线处GC的原因:</p>    <p>内存释放</p>    <p>堆进行信息统计</p>    <p>内部内存进行信息统计</p>    <p>时间暂停</p>    <ul>     <li> <p>使用分配跟踪器查看是否有随着时间分配未预料的对象(Android Studio中的logcat窗口中有对应的按钮)</p> </li>     <li> <p>使用(Histogram view)直方图视图查看活动实例的数量。 有多于一个Activity的一个实例,那么这是一个强烈的Activity / Context泄露的迹象。</p> </li>     <li> <p>按保留大小排序的Dominator Tree视图有助于识别保留了大量的内存且不能被释放的对象。 他们通常是找到内存泄漏的好起点。</p> </li>    </ul>    <p>强烈推荐郭神关于内存泄露分析的文章:</p>    <h2><strong>其他优化建议</strong></h2>    <ul>     <li> <p>造成OutOfMemoryError的原因通常是Bitmap或者对象进行了太多的内存分配</p> <p>加载图片的时候尽可能不要加载原尺寸的大图,可以使用缩略图</p> <p>回收已经不使用的Bitmap资源bitmap.recycle().</p> <p>2.3(包括)之前,Bitmap的引用是放在堆中的,而Bitmap的数据部分是放在栈中的,需要用户调用recycle方法手动进行内存回收 ,2.3之后,整个Bitmap,包括数据和引用,都放在了堆中,这样,整个Bitmap的回收就全部交给GC了,这个recycle方法就再也不需要使用了。</p> <p>列表中加载图片注意使用小图,以及做好缓存工作</p> <p>尽可能的避免碎片化</p> <p>减少Java在应用堆空间堆快满的时候再堆分配</p> <p>缓存中使用SoftReference</p> <p>使用WeakReference避免堆存泄露</p> </li>    </ul>    <pre>  <code class="language-java">//缩放图片  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);</code></pre>    <pre>  <code class="language-java">//对比之间的例子,这里改成了静态内部类  public class MainActivity extends Activity {    static Leaky leak = null;    static class Leaky {      void doSomething() {          //doing      }    }    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);      if (leak == null) {        leak = new Leaky();      }    }  }</code></pre>    <pre>  <code class="language-java">public class MainActivity extends Activity {  static Leaky leak = null;  static class Leaky {  private final Context mContext; //final修饰    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);    }  }  }</code></pre>    <pre>  <code class="language-java">public class MainActivity extends Activity {  static Leaky leak = null;  static class Leaky {  private final WeakReference<Context> mContext;//使用了弱引用  public Leaky(Context context) {    super();    mContext = new WeakReference<Context>(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);    }  }  }</code></pre>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/28f2bd6f32dc</p>    <p> </p>