記憶體優化(三)Android物件池使用
文章目錄
概述
由記憶體優化(一)淺談記憶體優化中看出,記憶體優化不僅要從防止記憶體洩露入手,也要注意頻繁GC卡頓,記憶體抖動以及不必要的記憶體開銷造成的記憶體需求過大或者記憶體洩露。而避免記憶體無用開銷就必須理解Android開發中的一個重要原則——物件複用。
物件複用在我們開發中使用的案例很多,Adapter就是這個原則的著重體現。本文主要寫的是Android物件池的使用,在一些請求框架中可能會用到,頻繁建立Request Bean物件,這時,物件池就顯得尤為重要了,它能很好的複用物件,避免頻繁建立和銷燬。
Android Object Pools
Android物件池是由Android原始碼中提供的一個類:android.support.v4.util.Pools
,常規的使用物件池我們都可以通過它實現,它的原始碼也很簡單,如下:
Pools原始碼解析:
-
Pools中主要實現2個介面:acquire(從池中獲取物件),release(釋放物件,存入池中)
-
2個內部類,分別是SimplePool和SynchronizedPool,SynchronizedPool是繼承SimplePool,裡面通過Synchronized同步鎖實現一個安全的物件池
-
Pools通過維持一個物件陣列,存入複用物件,當陣列滿了便只能建立新的物件
-
Pools有個很好的特點:它並不需要預先建立物件到物件池,它需要在release方法中將回收物件新增到複用的物件池中
public final class Pools { public interface Pool<T> { /** * 獲取物件 */ @Nullable T acquire(); /** *釋放物件 */ boolean release(@NonNull T instance); } private Pools() { /* do nothing - hiding constructor */ } /** * 非同步物件池. */ public static class SimplePool<T> implements Pool<T> { private final Object[] mPool; private int mPoolSize; /** * 建立一個物件池,並指定最大存入物件數量 */ public SimplePool(int maxPoolSize) { if (maxPoolSize <= 0) { throw new IllegalArgumentException("The max pool size must be > 0"); } mPool = new Object[maxPoolSize]; } @Override @SuppressWarnings("unchecked") public T acquire() { if (mPoolSize > 0) { final int lastPooledIndex = mPoolSize - 1; T instance = (T) mPool[lastPooledIndex]; mPool[lastPooledIndex] = null; mPoolSize--; return instance; } return null; } @Override public boolean release(@NonNull T instance) { if (isInPool(instance)) { throw new IllegalStateException("Already in the pool!"); } if (mPoolSize < mPool.length) { mPool[mPoolSize] = instance; mPoolSize++; return true; } return false; } private boolean isInPool(@NonNull T instance) { for (int i = 0; i < mPoolSize; i++) { if (mPool[i] == instance) { return true; } } return false; } } /** * 同步物件池 */ public static class SynchronizedPool<T> extends SimplePool<T> { private final Object mLock = new Object(); public SynchronizedPool(int maxPoolSize) { super(maxPoolSize); } @Override public T acquire() { //通過synchronized實現同步 synchronized (mLock) { return super.acquire(); } } @Override public boolean release(@NonNull T element) { synchronized (mLock) { return super.release(element); } } } }
Pools結合Builder模式使用案例:
-
建立一個請求物件,當引數較多時結合Builder模式一起使用:
CtlRequestObj builder = new CtlRequestObj.Builder().setCmd(1).setState(0).setParam(2).builder();
-
CtlRequestObj :
/** * Created by Felix on 15/12/12. */ public class CtlRequestObj { private int cmd; private int param; private int state; public int getCmd() { return cmd; } public void setCmd(int cmd) { this.cmd = cmd; } public int getParam() { return param; } public void setParam(int param) { this.param = param; } public int getState() { return state; } public void setState(int state) { this.state = state; } private CtlRequestObj() { } /** * 初始化物件狀態 */ private void releaseConfig() { cmd = 0; param = 0; state = 0; } /** * 回收物件:初始化物件-->存入物件池 */ public void recycle() { // Clear state if needed. this.releaseConfig(); sPool.release(this); } //初始化執行緒池 private static final Pools.SimplePool<CtlRequestObj> sPool = new Pools.SimplePool<CtlRequestObj>(Constants.CTR_REQUEST_BEANS_SPOOL_SIZE); /** * 獲取(建立物件) * 預設從物件池中獲取,拿不到就new * * @return */ public static CtlRequestObj obtain() { CtlRequestObj instance = sPool.acquire(); return (instance != null) ? instance : new CtlRequestObj(); } /** * 通過Builder模式建立 */ public static class Builder { private int cmd; private int param; private int state; public Builder() { } public CtlRequestObj.Builder setCmd(int cmd) { this.cmd = cmd; return this; } public CtlRequestObj.Builder setState(int state) { this.state = state; return this; } public CtlRequestObj.Builder setParam(int param) { this.param = param; return this; } private void applyConfig(CtlRequestObj config) { config.cmd = this.cmd; config.param = this.param; config.state = this.state; } public CtlRequestObj builder() { CtlRequestObj obtain = CtlRequestObj.obtain(); applyConfig(obtain); return obtain; } } }
使用總結和注意事項
Android物件池的原始碼非常簡單,我們能夠自己封裝也能自己去實現,物件池的應用很廣泛,比如Message和Glide中都有用到。我們自己在使用中,大部分簡單的使用都可以通過Android提供的SimplePool和SynchronizedPool去實現,但是它也有弊端,物件池沒有最終銷燬機制,所以我們如果使用應該注意銷燬物件池。
應該需要的注意點:
-
recycle物件時注意清空物件的變數
-
當物件池滿時,獲取物件便只能通過new物件獲取,所以應該注意物件大小設定
-
當長時間不使用物件池時應該注意銷燬物件池
public void destoryPool() { if (sPool != null) { sPool = null; } }