三个编码小技巧

在阅读 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小技巧

编程·思维·职场
欢迎扫码关注

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值