上一篇 OkHttp設(shè)計(jì)模式剖析(三)策略模式
下一篇 OkHttp設(shè)計(jì)模式剖析(五)觀察者模式
OKHTTP:
由大名鼎鼎的Square公司開發(fā)的網(wǎng)絡(luò)通信庫(kù)吕世。
設(shè)計(jì)模式:
軟件開發(fā)中問(wèn)題的解決套路捍靠。
享元模式簡(jiǎn)介
定義:使用共享對(duì)象可有效地支持大量的細(xì)粒度的對(duì)象膊毁。
享元模式(Flyweight)是對(duì)象池的一種實(shí)現(xiàn)断序,可以節(jié)約內(nèi)存废睦,適合可能存在大量重復(fù)對(duì)象的場(chǎng)景骚秦。享元對(duì)象中的部分狀態(tài)可以共享碍舍,可以共享的狀態(tài)為內(nèi)部狀態(tài)既忆,不可以共享的狀態(tài)為外部狀態(tài)驱负。
許多某某池都使用了享元模式嗦玖,比如線程池,連接池跃脊,數(shù)據(jù)池宇挫,緩存池,消息池等酪术。享元模式會(huì)在池中先找器瘪,若沒(méi)有可以復(fù)用的對(duì)象,才新建一個(gè)绘雁。
ConnectionPool(連接池)中的享元模式
OkHttp3將客戶端與服務(wù)器之間的連接定義為接口Connection橡疼,通過(guò)RealConnection實(shí)現(xiàn),在ConnectionPool中庐舟,將連接儲(chǔ)存在一個(gè)雙端隊(duì)列中欣除。
public final class ConnectionPool {
// 雙端隊(duì)列儲(chǔ)存RealConnection
private final Deque<RealConnection> connections = new ArrayDeque<>();
// 構(gòu)造函數(shù)
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) { ...... }
}
跳轉(zhuǎn)到StreamAllocation類中findConnection函數(shù),源碼如下:
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) throws IOException {
Route selectedRoute;
synchronized (connectionPool) {
if (released) throw new IllegalStateException("released");
if (codec != null) throw new IllegalStateException("codec != null");
if (canceled) throw new IOException("Canceled");
RealConnection allocatedConnection = this.connection;
if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
return allocatedConnection;
}
// 從連接池中找符合條件的連接挪略,若有則返回
RealConnection pooledConnection = Internal.instance.get(connectionPool, address, this);
if (pooledConnection != null) {
this.connection = pooledConnection;
return pooledConnection;
}
selectedRoute = route;
}
if (selectedRoute == null) {
selectedRoute = routeSelector.next();
synchronized (connectionPool) {
route = selectedRoute;
refusedStreamCount = 0;
}
}
// 連接池中沒(méi)有历帚,則新建
RealConnection newConnection = new RealConnection(selectedRoute);
synchronized (connectionPool) {
acquire(newConnection);
Internal.instance.put(connectionPool, newConnection);
this.connection = newConnection;
if (canceled) throw new IOException("Canceled");
}
newConnection.connect(connectTimeout, readTimeout, writeTimeout, address.connectionSpecs(),
connectionRetryEnabled);
routeDatabase().connected(newConnection.route());
return newConnection;
}
這就是通過(guò)享元模式實(shí)現(xiàn)連接的復(fù)用,從而節(jié)省內(nèi)存杠娱。
基于享元模式構(gòu)建的其他代碼
1挽牢、OkHttp中的Dispatcher的線程池也用到了享元模式
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Runnable idleCallback;
private ExecutorService executorService; //懶加載的無(wú)邊界限制的線程池
public Dispatcher(ExecutorService executorService) {
this.executorService = executorService;
}
public Dispatcher() {
}
public synchronized ExecutorService executorService() {
if (executorService == null) {
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
}
通過(guò)維護(hù)ExecutorService線程池實(shí)現(xiàn)復(fù)用。
2摊求、Android源碼中的消息隊(duì)列
家用工具箱1-10號(hào)螺絲刀
public class Screwdriver {
private int size;
public Screwdriver(int size) {
this.size = size;
}
public int getSize() {
return size;
}
}
public class TestScrewdriver {
static HashMap<Integer, Screwdriver> s = new HashMap<Integer, Screwdriver>();
public static void main(String[] args) {
// 假設(shè)螺絲刀只有1-10號(hào)
s.put(1, new Screwdriver(1));
s.put(2, new Screwdriver(2));
s.put(5, new Screwdriver(5));
s.put(10, new Screwdriver(10));
for(int i=1; i<=10;i++) {
if(s.containsKey(i)) {
System.out.println("已經(jīng)有"+i+"號(hào)螺絲刀,不用買了 "+s.get(i));
}else {
System.out.println("需要購(gòu)買一把"+i+"號(hào)螺絲刀");
}
}
}
}
/*輸出結(jié)果:
已經(jīng)有1號(hào)螺絲刀,不用買了 Screwdriver@15db9742
已經(jīng)有2號(hào)螺絲刀,不用買了 Screwdriver@6d06d69c
需要購(gòu)買一把3號(hào)螺絲刀
需要購(gòu)買一把4號(hào)螺絲刀
已經(jīng)有5號(hào)螺絲刀,不用買了 Screwdriver@7852e922
需要購(gòu)買一把6號(hào)螺絲刀
需要購(gòu)買一把7號(hào)螺絲刀
需要購(gòu)買一把8號(hào)螺絲刀
需要購(gòu)買一把9號(hào)螺絲刀
已經(jīng)有10號(hào)螺絲刀,不用買了 Screwdriver@4e25154f
*/
所以禽拔,享元設(shè)計(jì)模式的核心就是:池中復(fù)用。
參考文獻(xiàn)
1睹簇、設(shè)計(jì)模式|菜鳥教程:https://www.runoob.com/design-pattern/design-pattern-tutorial.html
2奏赘、《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》何紅輝,關(guān)愛民著
3太惠、隔壁老李頭:http://www.reibang.com/p/82f74db14a18