浅谈Android中的缓存(一)——内存缓存

内存缓存一般通过Map、List、链表等集合实现。每次将新建的对象存入集合,下一次需要新建时直接从集合中去取,这样避免了每次新建对象带来的开销以及内存占用。

Map

我们通过lifecycle包创建ViewModel时会通过这样的方式:

1
ViewModelProviders.of(activity).get(MyViewModel.class);

看一下get方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);

if (modelClass.isInstance(viewModel)) {
return (T) viewModel;
} else {
if (viewModel != null) {
// TODO: log a warning.
}
}

viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}

可以看到,ViewModel是通过mViewModelStore.get(key)去获取的,key为DEFAULT_KEY + “:” + canonicalName。如果为空就新建一个,然后存入mViewModelStore。如果下一次需要新建相同的ViewModel类,直接从mViewModelStore取就好了。看一下mViewModelStore:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ViewModelStore {

private final HashMap<String, ViewModel> mMap = new HashMap<>();

final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}

final ViewModel get(String key) {
return mMap.get(key);
}

// 省略
}

ViewModelStore很简单,维护了一个HashMap,存储就是通过HashMap实现的,这也是我们常用的缓存策略。如果key为int,选用SparseArray更佳。之前写的库SimpleLineView中因为会频繁创建Point,用了HashMap缓存Point。

链表

我们通常在创建Message时不会通过new关键字来创建,而是通过Message.obtain()。
在看obtain方法之前我们先看一下Message中的recycleUnchecked方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;

void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;

synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}

我们知道每次Message处理完后,Looper会调用Message的recycleUnchecked方法来回收该Message对象。方法主要是将当前Message的参数重置,然后放入Message池——sPool(最多50个)。sPool为静态Message变量,将当前Message插到sPool所引用的Message对象前面,并且让sPool指向当前Message,如此就形成了一个缓存链。

再来看一下obtain方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

如果sPool不为空,就返回sPool所引用的Message对象,并且让sPool指向它的next,即当前Message的下一个Message对象,即obtain方法下一次调用时所返回的对象。最近写的持久化日志库PersistentLog因为会频繁创建日志对象LogBean,参照Message写了缓存池。

List

目前没想到例子,想起来了再补🐶