上一篇 OkHttp设计模式剖析(三)策略模式
下一篇 OkHttp设计模式剖析(五)观察者模式
OKHTTP:
由大名鼎鼎的Square公司开发的网络通信库。
设计模式:
软件开发中问题的解决套路。
享元模式简介
定义:使用共享对象可有效地支持大量的细粒度的对象。
享元模式(Flyweight)是对象池的一种实现,可以节约内存,适合可能存在大量重复对象的场景。享元对象中的部分状态可以共享,可以共享的状态为内部状态,不可以共享的状态为外部状态。
许多某某池都使用了享元模式,比如线程池,连接池,数据池,缓存池,消息池等。享元模式会在池中先找,若没有可以复用的对象,才新建一个。
ConnectionPool(连接池)中的享元模式
OkHttp3将客户端与服务器之间的连接定义为接口Connection,通过RealConnection实现,在ConnectionPool中,将连接储存在一个双端队列中。
public final class ConnectionPool {
// 双端队列储存RealConnection
private final Deque connections = new ArrayDeque<>();
// 构造函数
public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) { ...... }
}
跳转到StreamAllocation类中findConnection函数,源码如下:
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;
}
}
// 连接池中没有,则新建
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;
}
这就是通过享元模式实现连接的复用,从而节省内存。
基于享元模式构建的其他代码
1、OkHttp中的Dispatcher的线程池也用到了享元模式
public final class Dispatcher {
private int maxRequests = 64;
private int maxRequestsPerHost = 5;
private Runnable idleCallback;
private ExecutorService executorService; //懒加载的无边界限制的线程池
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(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
}
通过维护ExecutorService线程池实现复用。
2、Android源码中的消息队列
家用工具箱1-10号螺丝刀
public class Screwdriver {
private int size;
public Screwdriver(int size) {
this.size = size;
}
public int getSize() {
return size;
}
}
public class TestScrewdriver {
static HashMap s = new HashMap();
public static void main(String[] args) {
// 假设螺丝刀只有1-10号
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("已经有"+i+"号螺丝刀,不用买了 "+s.get(i));
}else {
System.out.println("需要购买一把"+i+"号螺丝刀");
}
}
}
}
/*输出结果:
已经有1号螺丝刀,不用买了 Screwdriver@15db9742
已经有2号螺丝刀,不用买了 Screwdriver@6d06d69c
需要购买一把3号螺丝刀
需要购买一把4号螺丝刀
已经有5号螺丝刀,不用买了 Screwdriver@7852e922
需要购买一把6号螺丝刀
需要购买一把7号螺丝刀
需要购买一把8号螺丝刀
需要购买一把9号螺丝刀
已经有10号螺丝刀,不用买了 Screwdriver@4e25154f
*/
所以,享元设计模式的核心就是:池中复用。
参考文献
1、设计模式|菜鸟教程:https://www.runoob.com/design-pattern/design-pattern-tutorial.html
2、《Android源码设计模式解析与实战》何红辉,关爱民著
3、隔壁老李头:https://www.jianshu.com/p/82f74db14a18