文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android源码解析--享元设计模式,handler消息传递机制(基于Android API 33 SDK分析)

2023-08-31 05:10

关注

文章目录

Android源码解析–享元设计模式,handler消息传递机制(基于Android API 33 SDK分析)

一. 定义

使用共享对象可有效地支持大量的细粒度的对象

核心:对象复用。

1.1 享元模式Demo

火车票购票Demo

//火车票public class Ticket {    private String from;    private String to;    public Ticket(String from, String to) {        this.from = from;        this.to = to;    }    public int getPrice() {        return new Random().nextInt(100) + 20;    }}

缓存对象在一个Map中。下面我们还会分析

//火车票查询工厂public class TicketFactory {    public static Map<String, Ticket> sTicketMap = new HashMap<>();    public static Ticket getTicket(String from, String to) {        String key = from + "-" + to + "";        Ticket ticket = sTicketMap.get(key);        if (ticket != null) {            return ticket;        }        ticket = new Ticket(from, to);        sTicketMap.put(key, ticket);        return ticket;    }}

二. Android中源码实例分析Message

用法

val obtain = Message.obtain()

跟进去

        public static Message obtain() {        //防止多线程并发        synchronized (sPoolSync) {            //从线程池取对象            if (sPool != null) {                //链表取出每个Message对象                Message m = sPool;                sPool = m.next;                m.next = null;                m.flags = 0; // clear in-use flag                sPoolSize--;                return m;            }        }        return new Message();    }

这就是最明显的一个享元设计模式。

三. Message的关联者Handler

Android 开发一个知识点:UI 不能够在子线程中更新。

class DebugActivity : AppCompatActivity() {    private val TAG = javaClass.simpleName    private var handler: Handler = Handler(Looper.getMainLooper())    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)    }    fun doSomething(){        thread {            //耗时操作,得到结果,不能在这个线程更新 UI            // Handler 将结果传递到主线程中,更新UI            handler.post {                //更新UI            }        }    }}

我们跟进post函数

 public final boolean post(@NonNull Runnable r) {           return  sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) {        Message m = Message.obtain();        m.callback = r;        return m;  } 

Handler 传递了一个 Runnable给UI线程,装到一个 Message 对象中。

跟进sendMessageDelayed函数

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {        if (delayMillis < 0) {            delayMillis = 0;        }        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {     //当前 Handler 所在的消息队列        MessageQueue queue = mQueue;        if (queue == null) {            RuntimeException e = new RuntimeException(                    this + " sendMessageAtTime() called with no mQueue");            Log.w("Looper", e.getMessage(), e);            return false;        }     //将消息添加到消息队列中        return enqueueMessage(queue, msg, uptimeMillis); }

sendMessageDelayed 函数调用了 sendMessageAtTime函数,不手动传递 Looper 那么 Handler 持有的 Looper 就是当前线程的 Looper,也就是说在哪个线程创建的 Handler,就是哪个线程的 Looper。

在 getPostMessage 中的 Message 对象是Message.obtain()函数

 Message m = Message.obtain();

分析下这段代码,

        public static Message obtain() {        //防止多线程并发        synchronized (sPoolSync) {            //从线程池取对象            if (sPool != null) {                //链表取出每个Message对象                Message m = sPool;                sPool = m.next;                m.next = null;                m.flags = 0; // clear in-use flag                sPoolSize--;                return m;            }        }        return new Message();    }

Message消息池没有使用 map 这样的容器,使用的是链表。

在这里插入图片描述

如何放到这个消息池里面呢?

我们看

Message 对象回收到消息池中

public void recycle() {    //该消息还在使用        if (isInUse()) {            if (gCheckRecycle) {                throw new IllegalStateException("This message cannot be recycled because it "                        + "is still in use.");            }            return;        }    //消息添加到消息池中        recycleUnchecked();    }

跟进recycleUnchecked()

 void recycleUnchecked() {        // Mark the message as in use while it remains in the recycled object pool.        // Clear out all other details.     //清空消息状态     flags = FLAG_IN_USE;        what = 0;        arg1 = 0;        arg2 = 0;        obj = null;        replyTo = null;        sendingUid = UID_NONE;        workSourceUid = UID_NONE;        when = 0;        target = null;        callback = null;        data = null;//回收消息到消息池中        synchronized (sPoolSync) {            if (sPoolSize < MAX_POOL_SIZE) {                next = sPool;                sPool = this;                sPoolSize++;            }        }    }

这里用链表当作了一个缓存池,存消息对象。每生成一条消息就会加入到链表在。

四. Android 的消息机制

Android应用程序的入口实际上是ActivityThread,跟进去

public static void main(String[] args) {        ......//创建Looper,UI线程的消息队列        Looper.prepareMainLooper();......    //启动应用程序        ActivityThread thread = new ActivityThread();        thread.attach(false, startSeq);//循环消息        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

Looper 从消息队列中取消息,处理消息。Handler不断地往消息队列中添加消息,消息不断地被处理。

那么Handler是如何关联消息队列

Handler 的构造函数

public Handler(@Nullable Callback callback, boolean async) {      ......        mLooper = Looper.myLooper();//获取 Looper  ......        mQueue = mLooper.mQueue;//获取消息队列        mCallback = callback;        mAsynchronous = async;    }

Handler 通过myLooper()来获取 Looper 对象,

跟进myLooper()

public static @Nullable Looper myLooper() {    //myLooper通过sThreadLocal.get()获取        return sThreadLocal.get();    }

Looper对象存储在sThreadLocal中的,

@Deprecated    public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }//prepare()方法中创建了一个 Looper 对象private static void prepare(boolean quitAllowed) {        if (sThreadLocal.get() != null) {            throw new RuntimeException("Only one Looper may be created per thread");        }        //将该对象设置给了sThreadLocal,这样线程和队列就关联上了        sThreadLocal.set(new Looper(quitAllowed));    }

Handler和线程、线程的消息队列关联,Handler 发送的消息就会被执行在这个线程上。

调用 Looper 的 loop 函数,不断地从消息队列中取出、处理消息

 public static void loop() {      ......          //死循环        for (;;) {            //取消息            if (!loopOnce(me, ident, thresholdOverride)) {                return;            }        }    }

跟进loopOnce

private static boolean loopOnce(final Looper me,            final long ident, final int thresholdOverride) {    //获取消息 (might block )        Message msg = me.mQueue.next(); // might block       ......        try {            //处理消息            msg.target.dispatchMessage(msg);            if (observer != null) {                observer.messageDispatched(token, msg);            }            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;        }     ......            //回收消息,也就是我们分析享元模式时提到的将 Message 添加到消息池的操作        msg.recycleUnchecked();        return true;    }

看看next()核心代码

Message next() {       ......//native层的事件            nativePollOnce(ptr, nextPollTimeoutMillis);......                if (msg != null) {                    //消息延迟,                    if (now < msg.when) {                        // Next message is not ready.  Set a timeout to wake up when it is ready.                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                    } else {                        // Got a message.                        mBlocked = false;                        if (prevMsg != null) {prevMsg.next = msg.next;                        } else {mMessages = msg.next;                        }                        msg.next = null;                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);                        msg.markInUse();                        return msg;                    }                } else {                    // No more messages.                    nextPollTimeoutMillis = -1;                }              ......        }    }

next 函数从消息队列中依次取出消息,如果这个消息到了执行时间,那么就将这条消息返回给 Looper,队列链表的指针后移。

五. 子线程中创建Handler抛出异常

class DebugActivity : AppCompatActivity() {    private val TAG = javaClass.simpleName    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        thread {            val handler = Handler()        }    } }

在这里插入图片描述

分析:Looper 对象是 ThreadLocal,每个线程都有自己的Looper,要在子线程中创建 Handler 对象时,如果 Looper 为空,那么就会抛出异常。跟进Handler的构造方法看看

public Handler(@Nullable Callback callback, boolean async) {        ......        //获取looper        mLooper = Looper.myLooper();        if (mLooper == null) {            //抛出异常            throw new RuntimeException(                "Can't create handler inside thread " + Thread.currentThread()                        + " that has not called Looper.prepare()");        }     ......    }

mLooper 对象为空,抛出异常。该线程中的Looper 对象还没有创建,在子线程中没有手动调用 Looper.prepare之前该线程的 Looper为空,解决方法就是在构造 Handler 之前为当前线程设置 Looper 对象。

class DebugActivity : AppCompatActivity() {    private val TAG = javaClass.simpleName    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        thread {            //绑定到 ThreadLocal中            Looper.prepare()            val handler = Handler()            //启动消息循环            Looper.loop()        }    }}
这样子线程的Looper对象就不会为null了,有了自己的消息队列。

来源地址:https://blog.csdn.net/weixin_46039528/article/details/132391529

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯