Android HandlerThread FD 裁剪优化

2,700 阅读15分钟

核心内容

通过共享HandlerThread的方式,减少MessageQueue的创建,从而减少FD的创建,减少OOM的发生。

前言

我们常说,面向对象的开发语言中一切皆对象,任何对象都有他的属性和成员,因此后来拓展出了“类”这个概念,类是对象的抽象,一切都能抽象成为类,但类也是对象,在抽象的过程中先有对象才有类。作为操作系统,linux也有自己的规则——一切皆文件。

Android 作为基于unix-like的操作系统,本自带了操作系统最核心的基因——一切皆文件,这个核心思想是通过I/O的思想简化组件通信模式,一个组件必须以Input、Output或者同时兼备的模式去适配,这也统一后续的系统层通信发展路线,避免了碎片化。

FD在unix-like系统中用于描述组件(如进程)、资源(如文件)、节点(如驱动)等,在Android中,我们使用的进程、文件、MessageQueue、Socket、Binder、Pipe、共享内存、sqlite等均与其有关。

现状

FD释放

在很多情况下,对文件I/O读写必须及时close,当然为了避免被忽略,java官方开发了新的回收方式

 try (FileInputStream fis = new FileInputStream("a.txt")) {
      byte[] b = new byte[1024];
      fis.read(b);
  }catch (IOException e) {
     System.out.println(e.getMessage());
 }

同样,只要是Closeable的子类,均可以如此释放,如Socket等。 但问题是,I/O操作并不一定是单一方法种处理,比如我们录音过程,如果要添加音效,势必会转移到其他线程处理,这个时候,频繁的开启和关闭i/O显然不明智。其次,阻塞可能导致杂音问题,这个时候显然这个方法是不合适的。再比如SocketServer,监听到Socket,一般也要开启新的线程处理处理请求数据。

基于上述情况的不足,官方做法是利用CloseGuard做法是对I/O对象进行监视,在对象finalize发出warning信息,严格模式也是利用这种实现。

  class Foo {

      private final CloseGuard guard = new CloseGuard();
     public Foo() {
          ...;
          guard.open("cleanup");
      }
  public void cleanup() {
         guard.close();
         ...;
         if (Build.VERSION.SDK_INT >= 28) {
             Reference.reachabilityFence(this);
         }
         // For full correctness in the absence of a close() call, other methods may also need
         // reachabilityFence() calls.
      }
      protected void finalize() throws Throwable {
          try {
              // Note that guard could be null if the constructor threw.
              if (guard != null) {
                  guard.warnIfOpen();
              }
              cleanup();
          } finally {
              super.finalize();
          }
      }
  }
}

那么,除了常见的一些监控和编程规范外,还有什么优化方式呢?

fd 共享

fd共享本质尽可能让同一个资源被多次利用,而不是一个资源打开多次,典型的做法就是Sqlite的操作放到ContentProvider中,以及Okhttp 中的连接池也起到相同的作用,再后来,Http 2.0的多路复用实现。说到这里,作为协程的老祖宗,epoll也是多路复用的实现,通过epoll可以做到监视多种fd,其实也可以做到fd共享。

fd 监控

在Android系统中,没有相应的api来获取应用的fd信息,更早期的Android是可以通过下面方式创建Shell(uid是相同的)进程去获取信息,类似CMD命令行一样,后来就不行了。

fd查询

命令方式

adb shell ls -l /proc/{pid}/fd

app中调用的话使用如下方法

public Process exec(String[] cmdarray, String[] envp, File dir)
    throws IOException {
    return new ProcessBuilder(cmdarray)
        .environment(envp)
        .directory(dir)
        .start();
}

在实际的线上环境中,很难通过Process去监控fd的数量变化,不过读取fd的方法还是有的,如何做到监控呢,实际上可以定时也可以在关闭特定页面时触发检测逻辑,对比前后变化的差异。

public static String[] getFds() {
    String FD_DIR = "/proc/" + Process.myPid() + "/fd";
    File fdFileDir = new File(FD_DIR);
    String[] fds = fdFileDir.list();// 列出当前目录下所有的文件
    return fds;
}

然后对FD进行识别

public static String readlink(String name) {
    String FD_DIR = "/proc/" + Process.myPid() + "/fd";
    String link = FD_DIR + File.separator + name;

    Object realpath = BlockGuardHookerOs.invokeOsMethod("realpath", link);
    if (realpath instanceof String) {
        return (String) realpath;
    }

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        try {
            return Os.readlink(link);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return null;
}

fd 差异比较

差异比较是比较之前的fd列表与最新的fd列表

FDMonitor.diff(oldFdList,newFdList)

fd 监控缺陷

但是,这里仍然是有缺陷的,因为读取的到的fd有些 :node ,很难去识别,也很难定位到准确的线程资源上去,比如MessageQueue、进程和socket的。这些fd申请之后,如何对应到对象上去显然有一些难度。

fd表示符号资源类型
socket:[1992]网络请求相关
anon_inode:[eventpoll]MessageQueue epoll
anon_inode:[eventfd]MessageQueue相关
anon_inode:[timerfd]系统定时器
anon_inode:[dmabuf]InputChannel WMS通信
/vendor/一般是系统操作使用
/dev/ashmem内存相关
pipe管道通信相关
/sys/一般是系统操作使用
/data/data/打开文件相关/data/app/私有目录文件fd
/storage/emulate/0/com.sample.abcsdcard打开文件fd

FD OOM

fd是OOM的元凶之一,fd触发的OOM基本上分为三类,一类是FD超过了阈值数量(如线程创建、too open many file),另一类是过多的fd资源申请,导致其他操作无法申请到内存,最后一类是fd申请的资源(如内存)超过了系统所能提供的内存。

FD 最大限制梳理

这里说一下,默认情况下,fd的数量最多为1024,当然一些高端机会超过这个数值限制,查询命令如下

adb shell ulimit -n

当然,如果要在app中读取,上面的命令是不可以的,因此需要换种方式,那就是读取自己进程目录下的limits文件

File file = new File("/proc/" + android.os.Process.myPid() + "/limits");

读取到文件,并进行解析

Soft Limit=1024,Hard Limit=4096,Units=files

以上是现状了,下面我们进入本篇的主题

FD 常用优化方法

FD优化的工具其实并不多,常用的的方法有

  • 及时关闭fd
  • 避免短时间内申请大量的fd资源 (如mmap、socket、thread、进程),按优先级事项申请
  • 控制fd数量,如线程的申请不宜过多。过多的HandlerThread并不一定能发挥cpu的优势,反而线程占用的内存比较高,更容易造成oom
  • 监控fd,防止fd泄露

下面,我们实现一种特殊的方法

HandlerThread FD 优化

本篇的重点是通过共享HandlerThread实现FD数量优化。

在 Android 系统中,创建一个 Looper 通常会创建 2 个 FD,一个是eventfd,另一个是epollfd,基本都是MessageQueue创建,而线程是没有FD的。在 Android 5.x上甚至会创建三个 FD,pipe_in、pipe_out 和 epollfd。

那么,在这里,我们的优化点显然不Looper,因为很难去更改系统底层实现,除非利用bhook这样的工具。如果想要通过纯java方式实现,那么,我们只能把视线放到HandlerThread上。

我们共享HandlerThread,这样每次只创建Thread,MessageQueue就可以避免创建了。

共享HandlerThread

实现自己的ShareHandlerThread和LightHandler

我们让HandlerThread 共享一个即可,思路就是模仿epoll多路复用机制,epoll多路复用和协程一样,一个监视线程,另一个是处理线程,我们这里让同一个RealHandlerThread (真实的HandlerThread)监视自定义的ShareHandlerThread创建的所有的Handler发送过来的Message,一旦到了执行时间,就发送Message到指定的线程执行,执行时到指定的Handler。

不过要这么实现的话,Handler我们也用不了,只能使用LightHandler了,但是为了保证执行的先后顺序,我们需要引入执行队列。

企业微信20231217-124109@2x.png

为什么这样可以实现,首先Looper机制本身具备先后顺序,因此我们把MessageQueue的消息发送到执行队列,其实影响并不大。

定义ShareHandlerThread

  • 必须也能进行MessageQueue类似的操作,如删除和查询消息,因为其中需要定义队列
  • 需要拿到消息后转发到自己的队列
  • 必须在自己的线程执行
  • 需要找到自己的LightHandler执行,因此这里得用Map影射一下
  • 必须可以quit或者quitySafely
  • 必须Callback消息的正确执行
  • 防止消息执行之前LightHandler被回收

我们这里主要核心是使用Handler.Callback去拦截消息,看过handler.dispatchMessage源码就能知道,其可以优先拦截消息,但其中Callback消息是很难拦截的,使用Handler.Callback也是无法拦截到,因为他在Handler.callback之前执行,因此我们只能在LightHandler中做特殊处理。

LightHandler映射也是很简单,我们使用name记录下来

private final Map<String, WeakReference<LightHandler>> lightHandlerMaps = new ConcurrentHashMap<String, WeakReference<LightHandler>>();

但LightHandler被回收是一个潜在风险,因此我们可以利用WeakHashMap监视原始的Handler,只有原始的Handler被回收LightHandler才能被回收。

Looper 问题解决

在Android系统中,Looper本身提供了很多静态方法,但是如何更好的兼容原有逻辑呢?这里提供一种新的方法,定义一个LightLooper,使其实现如下逻辑。

public abstract class LightLooper {
    //获取Looper
    public abstract LightLooper getLooper();
    static final ThreadLocal<LightLooper> sThreadLocal = new ThreadLocal<LightLooper>();
    //静态获取Looper
    public static LightLooper myLooper() {
        return sThreadLocal.get();
    }

    void onPrepare() {
        sThreadLocal.set(this);
    }

    void onEnd() {
        sThreadLocal.set(null);
    }
}

注意看,LightLooper是抽象类,如何让LightLooper 优雅的兼容进去呢?

这里我们让ShareHandlerThread继承LightLooper即可,当然还有其他方法,这里我们怎么简单怎么来。

ShareHandlerThread具体源码

下面是核心类ShareHandlerThread的源码实现

public class ShareHandlerThread extends LightLooper implements Runnable, InternalHandler.SimpleCallback {
    final static HandlerThread realThreadHandler = new HandlerThread("realThreadHandler");

    static {
        realThreadHandler.start();
    }
    private final Map<String, WeakReference<LightHandler>> lightHandlerMaps = new ConcurrentHashMap<String, WeakReference<LightHandler>>();
    // 防止消息执行之前 LightHandler 被提前回收
    private final WeakHashMap<Handler, LightHandler> recycleMonitor = new WeakHashMap<>();
    private final String threadName;
    Thread thread = null;
    boolean isQuited = false;
    boolean isStarted = false;

    private static final int MSG_QUIT = Integer.MAX_VALUE;
    private AbstractQueue<Message> queue = null;
    //带名称的锁
    private final Object nameLock = "queueLock";
    private int tid;
    private boolean isPrepared = false;

    public ShareHandlerThread(String name) {
        this.threadName = "ShareThread#" + name;
    }
    public static HandlerThread real() {
        return realThreadHandler;
    }

    public void start() {
        if (isStarted) {
            throw new IllegalThreadStateException();
        }
        if (this.queue == null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                this.queue = new LinkedTransferQueue<>();
            } else {
                this.queue = new LinkedBlockingQueue<>();
            }
        }
        try {
            thread = new Thread(this, this.threadName);
            thread.start();
            isStarted = true;
        } catch (Throwable e) {
            e.printStackTrace();
            throw e;
        }

    }

    public LightHandler createHandlerFromClass(Class<? extends LightHandler> KlassLightHandler) {
        return createHandlerFromClass(KlassLightHandler, null);
    }

    public LightHandler createHandlerFromClass(Class<? extends LightHandler> KlassLightHandler, Handler.Callback callback) {
        try {
            Constructor<? extends LightHandler> constructor = KlassLightHandler.getDeclaredConstructor(Handler.class, Handler.Callback.class, ShareHandlerThread.class, String.class);
            Handler handler = new InternalHandler(realThreadHandler.getLooper(), this);
            return constructor.newInstance(handler, callback, this, generateHandlerName());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    public static LightHandler newHandler(LightLooper looper) {
        if (!(looper instanceof ShareHandlerThread)) {
            return null;
        }
        ShareHandlerThread shareHandlerThread = (ShareHandlerThread) looper;
        LightHandler lightHandler = new LightHandler(new InternalHandler(realThreadHandler.getLooper(), shareHandlerThread), null, shareHandlerThread, shareHandlerThread.generateHandlerName());
        return lightHandler;
    }

    public LightHandler createHandler() {
        return createHandler(null);
    }

    public LightHandler createHandler(Handler.Callback callback) {
        LightHandler lightHandler = new LightHandler(new InternalHandler(realThreadHandler.getLooper(), this), callback, this, generateHandlerName());
        return lightHandler;
    }

    private String generateHandlerName() {
        return threadName + "#" + SystemClock.uptimeMillis();
    }

    public LightHandler createAsyncHandler() {
        return createAsyncHandler(null);
    }

    public LightHandler createAsyncHandler(Handler.Callback callback) {
        LightHandler lightHandler = new LightHandler(new InternalHandler(realThreadHandler.getLooper(), this, true), callback, this, generateHandlerName());
        return lightHandler;
    }

    @Override
    public void run() {
        isPrepared = true;
        onPrepare();
        tid = Process.myTid();
        while (!isQuited) {
            Message msg = null;
            synchronized (nameLock) {
                msg = queue.poll();
            }
            if (msg == null) {
                continue;
            }
            if (msg.what == MSG_QUIT) {
                msg.recycle();
                break;
            }
            doHandleMessage(msg);
            msg.recycle();
        }
        onEnd();
    }

    private void doHandleMessage(Message msg) {
        LightHandler lightHandler = getTarget(msg);
        if (lightHandler == null) {
            return;
        }
        lightHandler.dispatchMessage(msg);
    }

    @Override
    public void dispatchMessage(Message msg) {
        Message copyMessage = Message.obtain(msg);
        synchronized (nameLock) {
            queue.offer(copyMessage);
        }
        //这里返回true,不要和共享looper关联
    }

    public void addLightHandler(String handlerName, LightHandler lightHandler) {
        lightHandlerMaps.put(handlerName, new WeakReference<>(lightHandler));
    }

    //不安全退出
    public void quit() {
        Message message = Message.obtain();
        message.what = MSG_QUIT;
        synchronized (nameLock) {
            queue.clear();
            queue.add(message);
        }
    }

    //安全退出
    public void quitSafely() {
        Message message = Message.obtain();
        message.what = MSG_QUIT;
        synchronized (nameLock) {
            queue.offer(message);
        }
    }

    public LightHandler getTarget(Message msg) {
        Bundle data = msg.peekData();
        if (data == null) {
            return null;
        }
        if (data.isEmpty()) {
            return null;
        }
        String keyName = (String) data.get(LightHandler.KEY);
        if (TextUtils.isEmpty(keyName)) {
            return null;
        }
        WeakReference<LightHandler> reference = lightHandlerMaps.get(keyName);
        if (reference == null) {
            return null;
        }
        return reference.get();
    }

    //判断是否有消息
    public boolean hasMessage(LightHandler lightHandler, int what, Object object) {
        synchronized (nameLock) {
            Iterator<Message> iterator = queue.iterator();
            while (iterator.hasNext()) {
                Message next = iterator.next();
                if (next.what != what || next.obj != object) {
                    continue;
                }
                LightHandler target = getTarget(next);
                if (target != lightHandler) {
                    continue;
                }
                return true;
            }
        }
        return false;
    }

    //移除消息
    public void removeCallbacksAndMessages(LightHandler lightHandler, Object token) {
        synchronized (nameLock) {
            Iterator<Message> iterator = queue.iterator();
            while (iterator.hasNext()) {
                Message next = iterator.next();
                if ((next.obj == token || token == null) && getTarget(next) == lightHandler) {
                    iterator.remove();
                }
            }
        }
    }

    // 移除消息
    public void removeMessages(LightHandler lightHandler, int what, Object object) {
        synchronized (nameLock) {
            Iterator<Message> iterator = queue.iterator();
            while (iterator.hasNext()) {
                Message next = iterator.next();
                if (next.what == what && (next.obj == object || object == null)&& getTarget(next) == lightHandler) {
                    iterator.remove();
                }
            }
        }
    }

    //删除callback,要删除的callback无需做保护, == 比较的是hashcode
    public void removeCallbacks(LightHandler lightHandler, Runnable callback, Object token) {

        synchronized (nameLock) {
            Iterator<Message> iterator = queue.iterator();
            while (iterator.hasNext()) {
                Message next = iterator.next();
                if (next.getCallback() == callback && (next.obj == token || token == null) && getTarget(next) == lightHandler) {
                    iterator.remove();
                }
            }
        }
    }

    //查询callback
    public boolean hasCallback(LightHandler lightHandler, Runnable callback, Object o) {
        synchronized (nameLock) {
            Iterator<Message> iterator = queue.iterator();
            while (iterator.hasNext()) {
                Message next = iterator.next();
                if (next.getCallback() == callback && (next.obj == o || o == null) && getTarget(next) == lightHandler) {
                    return true;
                }
            }
        }
        return false;
    }

    public int getTid() {
        return tid;
    }

    public long getThreadId() {
        return thread == null ? -1 : thread.getId();
    }

    public Thread getThread() {
        return thread;
    }

    //移除LightHandler
    public void removeLightHandler(String key) {
        lightHandlerMaps.remove(key);
    }

    public void addMonitor(Handler handler, LightHandler lightHandler) {
        recycleMonitor.put(handler, lightHandler);
    }

    @Override
    public LightLooper getLooper() {
        if (isStarted) {
            return this;
        }
        return null;
    }
}

这里容易出问题的地方就是recycle的调用和消息复制,复制使用Message#obtain(Message),不要使用Message#copyFrom(Message),而recycle要避免多次调用

InternalHandler实现

不过,在上面的代码中我们可以看到,我们还需要一个辅助HandlerThread的InternaHandler,主要负责消息传递和消息转发,充当接收和转发,意味着它本身不会执行任何耗时任务。

public class InternalHandler extends Handler {
    private SimpleCallback callback;

    public InternalHandler(Looper looper, SimpleCallback callback) {
        this(looper, callback, false);
    }

    public InternalHandler(Looper looper, SimpleCallback callback, boolean isAsync) {
        super(looper);
        this.callback = callback;
        setAsynchronous(isAsync);
    }

    private void setAsynchronous(boolean isAsync) {
        try {
            Field mAsynchronousField = Handler.class.getDeclaredField("mAsynchronous");
            changeFinalValue(this, mAsynchronousField, isAsync, true);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

   //支持修改final字段
    public static void changeFinalValue(Object object, Field field, Object newValue, boolean isSafeThread) {

        try {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            // 如果field为private,则需要使用该方法使其可被访问
            Field modifersField = null;
            try {
                modifersField = Field.class.getDeclaredField("modifiers");
            } catch (Throwable e) {
                e.printStackTrace();
            }
            try {
                modifersField = Field.class.getDeclaredField("accessFlags");
            } catch (Throwable e) {
                e.printStackTrace();
            }
            modifersField.setAccessible(true);

            int modifiers = field.getModifiers();
            if (Modifier.isFinal(modifiers)) {
                // 把指定的field中的final修饰符去掉
                modifersField.setInt(field, modifiers & ~Modifier.FINAL);
            }

            field.set(object, newValue); // 为指定field设置新值
            if (isSafeThread) {  //如果要考虑线程安全,建议还原
                if (Modifier.isFinal(modifiers)) {
                    modifersField.setInt(field, modifiers | Modifier.FINAL);
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }


    @Override
    public void dispatchMessage(Message msg) {
     //将消息收敛到 ShareHandlerThread中
        callback.dispatchMessage(msg); 
    }

    public interface SimpleCallback {
         void dispatchMessage(Message msg);
    }
}

上面是InternalHandler的主要实现,很简单,但是难度主要在final字段修改这里,因为异步Hander的mAsynchronous字段是final修饰的且不公开的,因此还需要需要反射。

LightHandler实现

映射

这里有些难点,难点主要是如何让realHandlerThread中的消息执行时找到当前的LightHandler,为此我们这里做了个映射关系,在上面ShareHandlerThread中我们创建LightHandler给name的目的就是为了生成映射关系,但是Callback消息就有一定的难度,我们先处理一般消息。

不过,这里有个细节要注意,我们不能通过msg.getData()去获取Bundle,不然默认的new Bundle()会产生占内存4+4个数组元素的对象,因此,这里我们用peekData拿数据。

private void attachLightHandler(Message msg) {
   Bundle data = msg.peekData(); 
   if(data == null){ 
       data = new Bundle(1); 
   } 
   data.putString(KEY, handlerName); 
   msg.setData(data);
}

最终映射关系会知道ShareHandlerThread总的LightHandler

    private final Map<String, LightHandler> lightHandlerMaps = new ConcurrentHashMap<>();

callback 问题

由于我们可以在Handler#dispatchMessage方法中修改回调,那事实上这个callback就不成问题

这里,只需要模拟正式的Handler处理流程

public void dispatchMessage(Message msg) {
    try {
        cleanKeyData(msg);
        Runnable msgCallback = msg.getCallback();
        if(msgCallback != null){
            msgCallback.run();
            return;
        }
        Handler.Callback callback = getCallback();

        if (callback != null) {
            if (callback.handleMessage(msg)) {
                return;
            }
        }
        msg.setTarget(null);
        handleMessage(msg, this);
    } catch (Throwable e) {
        e.printStackTrace();
        throw e;
    } finally {
        msg.recycle();
    }
}

getLooper问题

我们自定义的ShareHandlerThread显然是没有Looper的,因此我们最简单的办法就是把ShareHandlerThread对象返回出去即可。

public Object getLooper(){
    return fakeLooper;
}

跨进程问题

如果你对Handler熟悉的话,就知道Handler是可以借助IMessenger间接跨进程的,但LightHandler实现跨进程显然要修改Message或者在外面封装一下,考虑到Handler跨进程使用的机会并不多,因此我们暂时不做太多的处理。

LightHandler完整代码

下面是完整的实现代码,主要实现了

  • Message消息标记
  • 消息监控
  • callback 代理
  • callback 消息保护
  • finalize消息回收

InternalHandler负责接收消息到RealHandlerThread,InternalHandler分发消息到指定的LightHandler,而LightHandler负责真正的消息处理。

public class LightHandler {

    private final Handler handler;
    private final Handler.Callback callback;
    private final String handlerName;
    final static String KEY = "android.os.lightHandler.handlerName";

    private ShareHandlerThread fakeLooper;
    private Field mMessageQueueField;
    private Method hasMessagesMethod;

    public LightHandler(Handler handler, Handler.Callback callback, ShareHandlerThread fakeLooper, String name) {
        this.handler = handler;
        this.callback = callback;
        this.fakeLooper = fakeLooper;
        this.handlerName = name;
        fakeLooper.addLightHandler(handlerName, this);
        fakeLooper.addMonitor(handler, this);
    }

    public final boolean sendMessage(Message msg) {
        attachLightHandler(msg);
        return this.handler.sendMessage(msg);
    }

    private void attachLightHandler(Message msg) {

        Bundle data = msg.peekData();
        if (data == null) {
            data = new Bundle(1);
        }
        data.putString(KEY, handlerName);
        msg.setData(data);

    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        attachLightHandler(msg);
        return this.handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        attachLightHandler(msg);
        return this.handler.sendMessageAtFrontOfQueue(msg);
    }

    public final boolean sendEmptyMessage(int what) {
        return sendEmptyMessageDelayed(what, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        attachLightHandler(msg);
        msg.what = what;
        return this.handler.sendMessageAtTime(msg, uptimeMillis);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        attachLightHandler(msg);
        return this.handler.sendMessageAtTime(msg, uptimeMillis);
    }

    // obtain方法

    public final Message obtainMessage() {
        Message msg = Message.obtain(this.handler);
        attachLightHandler(msg);
        return msg;
    }

    public final Message obtainMessage(int what) {
        Message msg = Message.obtain(this.handler, what);
        attachLightHandler(msg);
        return msg;
    }

    public final Message obtainMessage(int what, Object obj) {
        Message msg = Message.obtain(this.handler, what, obj);
        attachLightHandler(msg);
        return msg;
    }

    public final Message obtainMessage(int what, int arg1, int arg2) {
        Message msg = Message.obtain(this.handler, what, arg1, arg2);
        attachLightHandler(msg);
        return msg;
    }

    public final Message obtainMessage(int what, int arg1, int arg2, Object obj) {
        Message msg = Message.obtain(this.handler, what, arg1, arg2, obj);
        attachLightHandler(msg);
        return msg;
    }

    //runable方法
    private Message getPostMessage(Runnable r) {
        return getPostMessage(r, null);
    }

    private Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain(this.handler, r);
        m.setTarget(null);
        m.obj = token;
        return m;
    }

    public final boolean post(Runnable r) {
        return sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean postAtFrontOfQueue(Runnable r) {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

    public final boolean postAtTime(Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

    public final boolean postAtTime(
            Runnable r, Object token, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
    }

    public final boolean postDelayed(Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

    public final boolean postDelayed(
            Runnable r, Object token, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r, token), delayMillis);
    }

    //remove类方法

    /**
     * Remove any pending posts of messages with code 'what' that are in the
     * message queue.
     */
    public final void removeMessages(int what) {
        removeMessages(what, null);
    }

    /**
     * Remove any pending posts of messages with code 'what' and whose obj is
     * 'object' that are in the message queue.  If <var>object</var> is null,
     * all messages will be removed.
     */
    public final void removeMessages(int what, Object object) {
        this.handler.removeMessages(what, object);
        this.fakeLooper.removeMessages(this, what, object);
    }


    public final void removeCallbacksAndMessages(Object token) {
        this.handler.removeCallbacksAndMessages(token);
        this.fakeLooper.removeCallbacksAndMessages(this, token);
    }

    public final boolean hasMessages(int what) {
        return hasMessages(what, null);
    }

    public final boolean hasMessages(int what, Object object) {
        return this.handler.hasMessages(what, object) || fakeLooper.hasMessage(this, what, object);
    }

    public final boolean hasCallbacks(Runnable r) {
        if (this.fakeLooper.hasCallback(this, r, null)) {
            return true;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            return this.handler.hasCallbacks(r);
        }
        MessageQueue queue = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            queue = this.handler.getLooper().getQueue();
        } else {
            try {
                if (mMessageQueueField == null) {
                    mMessageQueueField = Handler.class.getDeclaredField("mQueue");
                }
                mMessageQueueField.setAccessible(true);
                queue = (MessageQueue) mMessageQueueField.get(this.handler);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        if (queue == null) {
            return false;
        }
        try {
            if (hasMessagesMethod == null) {
                hasMessagesMethod = MessageQueue.class.getDeclaredMethod("hasMessages", Handler.class, Runnable.class, Object.class);
            }
            hasMessagesMethod.setAccessible(true);
            hasMessagesMethod.invoke(this.handler, r, null);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * Remove any pending posts of Runnable r that are in the message queue.
     */
    public final void removeCallbacks(Runnable r) {
        removeCallbacks(r, null);
    }

    public final void removeCallbacks(Runnable r, Object token) {
        this.handler.removeCallbacks(r, token);
        this.fakeLooper.removeCallbacks(this, r, token);
    }

    public LightLooper getLooper() {
        return fakeLooper;
    }

    public void dispatchMessage(Message msg) {
        try {
            Runnable msgCallback = msg.getCallback();
            if(msgCallback != null){
                msgCallback.run();
                return;
            }
            Handler.Callback callback = getCallback();

            if (callback != null) {
                if (callback.handleMessage(msg)) {
                    return;
                }
            }
            msg.setTarget(null);
            handleMessage(msg, this);
        } catch (Throwable e) {
            e.printStackTrace();
            throw e;
        }
    }

    public void handleMessage(Message msg, LightHandler lightHandler) {

    }

    public Handler.Callback getCallback() {
        return callback;
    }
    @Override
    protected void finalize() throws Throwable {
        fakeLooper.removeLightHandler(this.handlerName);
        super.finalize();
    }
}

上面的风险点是如果Message的attachLightHandler的信息附带不上,那么消息处理可能存在风险。

完整代码就是核心逻辑。

使用

用法基本和HandlerThread类似,但要注意Handler的创建

this.shareHandlerThread = new ShareHandlerThread("SurfaceHandler");
this.shareHandlerThread.start();
this.surfaceHandler = shareHandlerThread.createHandler(this);

或者静态方式创建LightHandler,这里调用静态方法ShareHandlerThread#newHandler即可

this.shareHandlerThread = new ShareHandlerThread("SurfaceHandler");
this.shareHandlerThread.start();
this.surfaceHandler = ShareHandlerThread.newHandler(shareHandlerThread.getLooper());

另外消息的如下

surfaceHandler.obtainMessage(MSG_DRAW,0,0,surface).sendToTarget();

总结

通过上述实现,我们就能实现共享Looper的的情况下,实现了消息的发送和到指定线程的处理,从而减少了MessageQueue的创建,开头说过,也避免了FD开销。

当然,这个是个简单的逻辑,实际过程中还需要进一步打磨优化,目前可以正常运行和小规模使用。