在阅读 Framework 源码时常会看到一些简洁优雅的编码写法,今天周末,轻松一点,分享三个非常简单的 "小技巧"。
更灵活的单例
在获取 AMS、WMS 等远程 binder 服务时,Framework 中会通过一个 Singleton 缓存它的单例,该类十分简单,如下:
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
缓存 WMS binder 服务:
private static Singleton<IWindowManager> sWindowManager =
new Singleton<IWindowManager>() {
protected IWindowManager create() {
return IWindowManager.Stub
.asInterface(ServiceManager.getService("window"));
}
};
这种泛型单例相比一般的饿汉、DCL 方式有什么优点呢?它可以更灵活的定义单例的范围,可以在某模块中实现单例,而不再是整个进程。
如果让你讲一下 Framework 中涉及的设计模式,是不是又多了一个答案呢?
监听 GC
如何在代码中监听程序发生 GC 呢?
你可能会想到创建一个弱引用,判断其被回收就代表发生了 GC,那要何时判断呢?无限循环判断岂不是增加了 CPU 的消耗?
别忘了存在感很低的 finalize 方法,Framework 中是这样实现的:
public class GcWatchDog {
private static WeakReference<GcWatcher> sGcWatcher
= new WeakReference<>(new GcWatcher());
private static final ArrayList<Runnable> sGcWatchers
= new ArrayList<>();
private static Runnable[] sTmpWatchers = new Runnable[1];
private static long sLastGcTime;
static final class GcWatcher {
@Override
protected void finalize() throws Throwable {
sLastGcTime = SystemClock.uptimeMillis();
synchronized (sGcWatchers) { //锁的粒度尽量小
sTmpWatchers = sGcWatchers.toArray(sTmpWatchers);
}
for (Runnable sTmpWatcher : sTmpWatchers) {
if (sTmpWatcher != null) {
sTmpWatcher.run();
}
}
//重建实现继续监听
sGcWatcher = new WeakReference<>(new GcWatcher());
}
}
//添加 GC 监听
public static void addGcWatcher(Runnable watcher) {
synchronized (sGcWatchers) {
sGcWatchers.add(watcher);
}
}
}
打印方法耗时
你是怎么记录程序执行耗时的呢?除了 System.currentTimeMillis() - startTimeMs 外还有什么方式呢?在 Framework 中可以看到大量的 trace 语句,比如 SystemServiceManager 中的 startService 方法:
public <T extends SystemService> T startService(Class<T> serviceClass) {
try {
final String name = serviceClass.getName();
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
"StartService " + name);
...省略
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
}
}
traceBegin 方法为 @hide,我们无法直接使用,但是可以通过 beginSection、endSection 方法实现:
Trace beginSection 方式:
Trace.beginSection("tagName");
doWork();
Trace.endSection();
如上使用 Trace beginSection 方式,可以通过 systrace 生成一个 trace.html,然后就能查看指定 "tagName" 相关的执行信息。
另外还可以通过 Debug startMethodTracing 方式:
Debug.startMethodTracing();
doWork();
Debug.stopMethodTracing();
当使用 Debug startMethodTracing 方式时,会生成一个 trace 文件,然后用 DDMS 工具打开 trace 文件,可以查看具体的方法耗时。
这两种方式不仅是代码层面,而是需要结合 systrace 等工具一同使用。
推荐阅读
你亲手写的代码,正在出卖你
深夜,聊聊架构设计
深夜,分享一个Git小技巧
编程·思维·职场
欢迎扫码关注