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

这篇具有很好参考价值的文章主要介绍了Android源码解析--享元设计模式,handler消息传递机制(基于Android API 33 SDK分析)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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()

跟进去

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    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();

分析下这段代码,

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    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()
        }
    }
 
}

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

分析: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 对象。文章来源地址https://www.toymoban.com/news/detail-674075.html

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了,有了自己的消息队列。

到了这里,关于Android源码解析--享元设计模式,handler消息传递机制(基于Android API 33 SDK分析)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【23种设计模式】享元模式【⭐】

    个人主页 :金鳞踏雨 个人简介 :大家好,我是 金鳞 ,一个初出茅庐的Java小白 目前状况 :22届普通本科毕业生,几经波折了,现在任职于一家国内大型知名日化公司,从事Java开发工作 我的博客 :这里是CSDN,是我学习技术,总结知识的地方。希望和各位大佬交流,共同进

    2024年02月09日
    浏览(35)
  • 设计模式-享元模式(Flyweight)

    享元模式(Flyweight Pattern)是一种结构型设计模式,它的主要目标是减少应用程序中对象的数量,以节省内存和提高性能。这一模式适用于对象数量庞大且相似的情况,通过共享内部状态来减少对象的创建。 在本篇博客中,我们将详细介绍享元模式的概念,并提供一个简单的

    2024年02月09日
    浏览(29)
  • 【前端设计模式】之享元模式

    享元模式是一种结构型设计模式,它通过共享对象来减少内存使用和提高性能。在前端开发中,享元模式可以用于优化大量相似对象的创建和管理,从而提高页面的加载速度和用户体验。 共享对象:享元模式通过共享相似对象来减少内存使用。相似对象可以共享一些不变的状

    2024年02月08日
    浏览(36)
  • 设计模式之享元模式

    本文看下一种结构型设计模式,享元模式。 当程序需要大量的重复对象,并且这些大量的重复对象只有部分属性不相同,其他都是相同的时候,就可以考虑使用享元设计模式设计模式来实现。典型的如围棋游戏中的棋子,除了颜色和位置不同外,其他都相同。射击游戏中的子

    2024年02月17日
    浏览(33)
  • Java设计模式-享元模式

    在Java领域的软件开发中,设计模式是提高代码可维护性和可扩展性的重要工具。其中,享元模式是一种被广泛使用的设计模式,它通过优化对象的重用来提升系统性能。 享元模式是一种结构型设计模式,旨在通过共享对象来减少系统中的对象数量,从而提升性能和减少内存

    2024年02月06日
    浏览(29)
  • js设计模式:享元模式

    当需要处理很多非常类似操作的时候,可以创建可以共享的对象,并暴露接口供其他对象调用 这个对象内包含这些操作的处理逻辑,可以优化性能  

    2024年02月22日
    浏览(31)
  • C#设计模式之---享元模式

    享元模式(Flyweight Pattern)是一种结构型模式,是运用共享技术有效的支持大量细粒度的对象。它使用共享对象,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似对象;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量对象。通常对象中的部分状态

    2024年02月16日
    浏览(25)
  • 设计模式结构型——享元模式

    目录 什么是享元模式 享元模式的实现 享元模式角色 享元模式类图 享元模式代码实现 享元模式的特点 优点 缺点 使用场景 注意事项         享元模式(Flyweight Pattern)是一种结构型设计模式,享元模式中的“享元”指被共享的单元,享元模式通过复用对象,以达到节省

    2024年02月16日
    浏览(36)
  • 设计模式之享元模式笔记

    记录下学习设计模式-享元模式的写法。JDK使用版本为1.8版本。 意图 :运用共享技术有效地支持大量细粒度的对象。 结构 : 其中: Flyweight描述一个接口,通过这个接口Flyweight可以接受并作用于外部状态。 ConcreteFlyweight实现Flyweight接口,并为内部状态(如果有)增加存储空间。C

    2024年02月11日
    浏览(34)
  • 【设计模式-08】Flyweight享元模式

    简要的理解:享元模式就是新建一个池(Pool),该池子(Pool)中有新建好的一堆对象,当需要使用时,从池子(Pool)中直接获取,不用重新新建一个对象。通俗的讲就是: 共享元数据 。 比如Java中的String就是使用享元模式,String的值存储在一个内存池中,不用每次分配一个内存存储

    2024年01月21日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包