精华内容
下载资源
问答
  • HANDLE
    千次阅读
    2022-02-07 12:12:31

    一、前言

    Handle原理是一个老生常谈的事情,这里对其整个流程简单记录一下。这份理解是基于SDK31版本

    二、示例代码

    ​ 对于Handle来说,主要有两个功能,一个是延迟处理消息,一个是线程间切换(参考链接:https://developer.android.google.cn/reference/android/os/Handler?hl=en)。这里简单定义个Handle来进行说明:

    Handle有两种定义场景。一种是UI线程,一种是子线程。

    UI线程

    在主线程创建的话可以在子线程发送消息到主线程

    class MainActivity : AppCompatActivity() {
        private var handle: Handler = object : Handler(Looper.myLooper()!!){
            override fun handleMessage(msg: Message) { //接受消息
                super.handleMessage(msg)
                Log.e("YM","-->what:${msg.what}")
            }
        }
      
         override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(binding.root)
            Thread{
                handle.sendEmptyMessage(10) //发送消息
            }.start()
        }
    }
    

    子线程

    以下的例子是建立一个两个子线程直接相互通信的示例

    class MainActivity : AppCompatActivity() {
      private var handle: Handler ?= null
      override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(binding.root)
         		ThreadA().start()
            ThreadB().start()
      }
      inner class ThreadA : Thread(){
            override fun run() {
                super.run()
                Looper.prepare()
                handle = object : Handler(Looper.myLooper()!!){
                    override fun handleMessage(msg: Message) {
                        super.handleMessage(msg)
                        Log.e("YM--->ThreadA","--->接受的参数:${msg.what}")
                    }
                }
                Log.e("YM--->ThreadA","--->循环开始")
                Looper.loop()
                Log.e("YM--->ThreadA","--->循环结束")
            }
        }
        inner class ThreadB : Thread(){
            override fun run() {
                super.run()
                Log.e("YM--->ThreadA","--->发送消息")
                handle?.sendEmptyMessageDelayed(12,100L)
            }
        }
    }
    

    三、问题

    ​ 看这篇文章的前提,是希望大家对MessageMessageQueueLoopHandle有个基本的概念。大致的意思如下(纯属个人理解,跟其他人理解可能不太一样)

    • Message: 一个消息实体,Handle进行发送消息就是发送的这种格式的消息
    • MessageQueue:消息队列,用来存储消息的(说个题外话,其实消息队列没存这里面,而是采用链表方式存在Message里面,叫消息队列是,其实解释来说是使用链表创建的队列,来保持先进先出的方式,这个稍后再说。)。
    • Loop:用来轮训消息的,并分发给Handle
    • Handle: 这个就是用来发消息,对消息做些简单的加工,然后将拿到的消息回传给业务代码。相当于管理器的作用。

    我们先看第一个问题,就是Handle是如何将Message传递过去的

    1、消息是如何传递的?

    ​ 当我们调用Handle::sendEmptyMessage()函数时候,该消息会进入Handle中进行发送消息,该消息在Handle中会调用MessageQueue::enqueueMessage(Message msg, long when)将消息添加进去。在MessageQueue::enqueueMessage(Message msg, long when)函数中会发现,最终是调用Message.next字段进行赋值到下一个。所以MessageQueue类只是对消息存储的一个管理类,最终存储是在Message中的。通过查看Message源码可以知道,该方式是采用链表的方式进行数据存储,并不是集合的方式进行存储的。这里会延伸出几个问题,首先因为有延迟消息,也有立刻执行的消息,那么链表的排序规则是什么?

    2、Message消息的排列规则是什么?

    ​ 上个问题解释了在Handle机制中是使用链表这个数据结构进行消息存储的,那么消息排列的规则是什么呢?是如何处理延迟消息和立刻发送的消息的?在上一个问题中可以知道不管哪一类消息最终都会调用MessageQueue::enqueueMessage(Message msg, long when)进行存储消息的,该函数中有一个变量when。其值是用来在Loop::loop()函数取值时候判断是否满足条件,满足条件即取出,不满足跳过。其时间计算方式为SystemClock.uptimeMillis() + delayMillis。默认为0。到此为止就可以知道Handle如何将消息进行存储的。(SystemClock.uptimeMillis()为开机到现在的时间,单位为毫秒)

    3、消息是怎么发送给Handle的?

    ​ 上个问题解释了消息是如何存储的及其存储规则,那么消息该如何发送给Handle?通过官方文档或者源码说明或者网上其余的参考资料可知,是通过Loop::loop()函数进行循环取出消息链表中的消息的。该函数最终是调用Loop.loopOnce(final Looper me,final long ident, final int thresholdOverride)进行遍历循环的----通常来说,我们是在重写的Handle::handleMessage(msg: Message)函数中接受到消息的,在源码中,该函数为空函数,最终由Handle::dispatchMessage(@NonNull Message msg)函数调用。而在Loop.loopOnce(final Looper me,final long ident, final int thresholdOverride)函数中通过Message msg = me.mQueue.next();获取目标的消息,最终调用了msg.target.dispatchMessage(msg);。其中Message.target参数为Handle。这样就将消息传递给了业务代码。

    4、Loop和MessageQueue和Handle的关系是什么?

    ​ 上述的几个问题解释了消息的传递流程,但是有一些地方没有解释清楚,比如Loop是如何将Handle和线程以及MessageQueue绑定在一起的。通过查看源码可知Loop类中有一个变量static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();。该变量是由静态的ThreadLocal存储了Loop对象。所以各个地方都可以使用同一个变量,而不会出现数据不一致的情况。由于ThreadLocal特性的问题,可以解决每个线程对数据的访问隔离,举个例子就是A线程对ThreadLocal进行赋值,B线程是取不出来的。这样就可以在线程中通过判断有没有值来判断是不是绑定了,这样就将Loop和线程绑定在一起了。在通过Loop::prepare()函数进行创建Loop时候最终调用了私有的构造函数,如下:

      private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    可以看到这时候创建了MessageQueue对象,因此这时候是将LoopMessageQueue进行了绑定。由于创建Handle的时候需要将Loop传入进去(无参构造函数被废弃了,因为有时候会出现Loop为空的情况,所以官方建议传入Loop),这样就完成了HandleLoop的绑定。

    这样可以在Handle里面取到Loop及其MessageQueue

    5、延迟消息是什么时候发送的?

    ​ 上述问题已经解释了每个Message里面都有一个when参数,当该参数的时间不到时候,循环空转,直至时间满足条件才会进行发送消息。另外注意到是插入消息只是按照先后顺序添加了,并没有按照时间顺序进行重新排列

    6、如果创建一个新的Message也会有Handle吗?

    ​ 上文说到了最终回调用Message.targe.dispatchMessage(msg)将消息传递给Handle,那么如果new Message()也会有Handle吗?这个是的,因为最终会传递给Handle。只要在里面发送消息之前赋值即可。参考代码如下:

     public class Handler { 
       ...
    	private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                long uptimeMillis) {
            msg.target = this; //赋值Handle
            msg.workSourceUid = ThreadLocalWorkSource.getUid();
    
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
       ...
     }
    

    7、没有消息的时候会不会停掉Loop

    ​ 这个问题问法有很多种,这里解释下过程。首先轮询是需要通过Loop.loop()进行开启,这个Loop.loop()是一个死循环for(;;),所以没有数据就就会空跑起来,一直到有数据为止。除非Handle结束了或者退出才会终止循环。Message msg = me.mQueue.next();该代码中的next()函数也是有一个死循环for(;;)。这样就不会出现取不到数据返回null的情况。

    8、死循环不会导致ANR吗?

    ​ ANR的情况只会出现在生命周期函数里面出现大量耗时操作才会出现ANR,但是Loop.loop()没有在生命周期里面。还有一个需要注意的地方就是UI线程也是一个线程,如果任务结束了,那么线程就退出了,如果UI线程退出了,那么程序就会结束了,换而言之,也是Loop.loop()是程序一直在运行。而且生命周期触发其实也是通过Handle进行触发的。在程序启动时候最终会通过ActivityThread启动一个UI线程,其入口为main函数,Loop就是就是在这里进行初始化以及开始轮询的。

    9、Handle是如何进行线程间切换

    上面的问题已经说了会通过Handle::dispatchMessage(@NonNull Message msg)。这个函数是定义在Handle定义的线程里面的,通过Message进行调用,而Message是通过Loop进行轮询,Loop又是通过ThreadLocal进行保存的,由于线程隔离,所以最终会回调到其创建时候的线程里面。同时还需要理解另外两部分,一部分是存message,一方面是取message。存是子线程存的,取是通过绑定线程的Loop取的。就好像是放了一份共同的数据,一个线程存,一个线程取一样。这份共同的数据就是MessageQueue。因为Loop不知道什么时候有数据,所以在需要不停的轮询,因为轮询操作是在UI线程,所以取到数据后面执行的代码也是在UI线程。所以其实没有存在线程切换的问题,只是外在感知为切换而已。

    这里面可以尝试以下简化版的线程切换代码:

    class ThreadLocalTest {
    
        @Test
        fun test(){
            val runnableA = ThreadA()
            Thread.sleep(50)
            val runnableB = ThreadB()
            Thread(runnableA).start()
            Thread.sleep(50)
            Thread(runnableB).start()
        	Thread.sleep(5000)
        }
        private var handle: MyHandle ?= null
        inner class ThreadA : Runnable{
            override fun run() {
                handle = object : MyHandle(){
                    override fun dispatchMessage(msg: String){ //用于分发消息
                        println("YM------>收到的消息:$msg, threadId: ${Thread.currentThread().id}")
                    }
                }
                println("YM--->ThreadA-->创建时候的线程ID:${Thread.currentThread().id}")
    
                handle?.loop()
            }
        }
    
        inner class ThreadB() : Runnable{
            override fun run() {
                println("YM--->ThreadB-->创建时候的线程ID:${Thread.currentThread().id}")
                handle?.sendMessage("=======")
            }
        }
    }
    open class MyHandle{
        private val messageList = Vector<String>() //存储消息集合,这里使用线程安全的集合,为了防止多线程出现问题,在Handle是使用synchronized处理
        open fun dispatchMessage(msg: String){ //用于分发消息
    
        }
    
        fun sendMessage(msg: String){
            messageList.add(msg)
        }
    
        //开始循环
        fun loop(){
            while (true){
                Thread.sleep(15)//这里是为了测试,所以不让循环次数太多
                for (index in 0 until messageList.size){
                    val value = messageList[index]
                    dispatchMessage(value)
                    messageList.remove(value)
                }
            }
        }
    
    }
    

    执行结果

    YM--->ThreadA-->创建时候的线程ID:24
    YM--->ThreadB-->创建时候的线程ID:25
    YM------>收到的消息:=======, threadId: 24
    

    10、Handle::sendMessage(Message m)和Handle::post(Runnable r)

    这两个函数都可以在主线程刷新任务,但是有什么区别?首先我们知道Handle::sendMessage(Message m)可以在重写的方法handleMessage(Message msg)中收到消息,但是需要注意的Handle::post(Runnable r)这个需要传入Runnable。然后最终处理的逻辑也是在Runnable中触发,并不会发送到handleMessage(Message msg1)函数中,这是结论。那么看到结论你会怎么想呢?为什么会出现这个情况,会不会认为是直接启动了一个线程去做的?
    首先来说即使是Handle::post(Runnable r)函数也是会给MessageQueue添加一个Message的。所以也是会执行之前的轮询Message的那一套流程。可以参考如下代码:

    public class Handler{
    	...
    	 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;
        }
    	...
    }
    

    可以看到Runnable是赋值给Message的一个参数了。那么什么时候执行这个Runnable呢?之前知道消息轮询完后最终会调用Handle::dispatchMessage(@NonNull Message msg)进行消息分发,这里看下代码

    public class Handler{
    	...
    	   public void dispatchMessage(@NonNull Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
         private static void handleCallback(Message message) {
            message.callback.run();
        }
    	...
    }
    

    可以看到当callback,也就是之前传入的Runabble不为空时候,执行Runnable而不是分发给Handler::handleMessage(Message msg)函数。这里需要注意的是这里没有执行异步任务,Runnable并没有在Thread中执行。这里可以尝试以下简单的测试代码:

    class MainActivity: AppCompatActivity(){
     override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
     		Log.e("YM--->","---->UI线程ID:${Thread.currentThread().id}")
            val runnable = MyRunnable()
            runnable.run()
            Thread{
              	Thread.sleep(100)//延迟下保证顺序
                val runnable1 = MyRunnable()
                runnable1.run()
            }.start()
     }
    
      inner class MyRunnable: Runnable{
            override fun run() {
                Log.e("YM","---->异步任务MyRunnable的线程Id:${Thread.currentThread().id}")
            }
        }
    
    }
    

    执行结果如下:

    E/YM--->: ---->UI线程ID:2
    E/YM: ---->异步任务MyRunnable的线程Id:2
    E/YM: ---->异步任务MyRunnable的线程Id:451
    

    可以看到实例化的Runnable不会单独启动一个线程,只会运行在所处的线程。假如该环境是UI线程,那么Runnable运行的环境是主线程,这里的话就需要注意一个地方,如果Handle::post(Runnable r)在异步任务中执行的话那么,这个环境是主线程还是子线程?如下代码:

        Thread{
                Log.e("YM--->","---->异步任务Thread:线程ID:${Thread.currentThread().id}")
                handle?.post {
                    Log.e("YM--->","---->异步任务post:线程ID:${Thread.currentThread().id}")
                }  
            }.start()
    

    执行结果如下:

    E/YM--->: ---->UI线程ID:2
    E/YM--->: ---->异步任务Thread:线程ID:436
    E/YM--->: ---->异步任务post:线程ID:2
    

    这又是为什么呢?答案就是。上文已经解释了消息传递过程,所以知道Loop::loop()是在UI线程执行的,所以在UI线程中取出Meesage,然后执行Runnable当然还是在UI线程中了!

    11、View::post()

    ​ 有时候会通过View::post()函数进行线程切换,其实内部也是使用Handle进行切换的。这里可以看下官方是怎么使用Handle

    ​ 首先View中的Handle函数是定义在内部静态类AttachInfo中,该值是构造函数的必传参数。该参数在View中的全局变量为mAttachInfo,其赋值函数为View::dispatchAttachedToWindow(AttachInfo info, int visibility)。该函数是通过ViewRootImpl进行进行调用的。通过代码可知只有第一次加载进窗口的时候才会初始化。通过源码顶部的注释可知,该类是整个视图顶部的层次结构,所有View加载都会通过该类进行处理。所以当View第一次加载进窗口的时候就已经有Handle了。关于View先到此结束,继续Handle的话题。View内部的Handle是在ViewRootImpl中进行定义的。然后其为一个继承于Handle的子类ViewRootHandle。除此之外暂时没有发现有需要特别注意的地方。有一个需要注意的就是post()添加的任务会放在绘制任务之后(该源码需要进行确认)

    更多相关内容
  • JSON-handle_0.6.1.rar

    2021-07-12 17:10:42
    JSON-handle_0.6.1.crx
  • Json-Handle 查看json文件

    2018-08-24 14:29:12
    浏览器插件,适用于chrome浏览器内核。直接拖到浏览器里,提示安装即可。显示的json可以折叠。很好用
  • JSON-handle_0.4.19.crx

    2017-09-10 09:36:21
    JSON-Handle 支持google-chrome. JSON-Handle 支持google-chrome. JSON-Handle 支持google-chrome. JSON-Handle 支持google-chrome.
  • JSON-handle_0.6.1

    2021-04-10 17:42:46
    JSON-handle_0.6.1
  • JSON-handle

    2019-02-19 13:20:16
    JSON-handle 谷歌浏览器插件,格式化json字符串,亲测可用,无效请联系我。
  • vc编程的基础知识,提供用来大家交流学习
  • chrome-json-handle

    2018-05-11 11:31:23
    chrome-json-handle
  • json-handle谷歌浏览器json数据解析工具

    千次下载 热门讨论 2014-10-24 08:26:23
    json-handle谷歌浏览器json数据解析工具,装了此插件,在访问json数据时,自动解析成清晰的json格式.不用再为json而看得头晕眼花.此插件的使用:打开谷歌浏览器,到设置里面的拓展程序,然后勾选最上面的开发者模式,最后...
  • JSON-handle Firefox插件

    2017-03-27 15:31:16
    JSON-handle Firefox插件
  • handle有处理;对待;操作;触;买卖;把手;柄等意思,那么你知道handle的用法吗?下面跟着学习啦小编一起来学习handle的英语知识吧,希望对大家的学习有所帮助!handle的用法handle的用法1:handle主要用作及物动词,宾语...

    handle有处理;对待;操作;触;买卖;把手;柄等意思,那么你知道handle的用法吗?下面跟着学习啦小编一起来学习handle的英语知识吧,希望对大家的学习有所帮助!

    871c6f4d7656b6062e8aad4318373d8f.png

    handle的用法

    handle的用法1:handle主要用作及物动词,宾语可以是人或事物;

    handle的用法2:偶尔用作不及物动词,其主动形式往往表示被动意义。

    handle的常用短语

    用作名词 (n.)

    give〔put〕 a handle to

    给人以姓名

    handle相关词汇辨析

    deal with,cope with,dispose of,manage,handle

    这些动词或短语动词都含有“处理”,“对付”之意。

    deal with 既可指处理具体事情,也可指处理或解决具有抽象意义的问题。

    cope with 指成功地处理或对付更为重大,更为严重的问题或事物。

    dispose of 与deal with同义,普通用法。

    manage 指处理日常事务与工作,也可指经营管理。

    handle 从原义“手柄”,转引申为作“处理”解时,其内涵是管理和操纵。

    handle的用法例句

    1. The public never had faith in his ability to handle the job.

    公众从来不相信他有能力胜任这一职位。

    2. Our plan is to allocate one member of staff to handle appoint-ments.

    我们的计划是分派一位职员处理预约事宜。

    3. Don't take on more responsibilities than you can handle.

    不要承担过多的责任。

    4. He began informally to handle Ted's tax affairs for him.

    他开始非正式地为特德打理税务。

    5. Some preteens are able to handle a good deal of responsibility.

    有些青春期前的儿童能够承担很多责任。

    6. The more powerful the car the more difficult it is to handle.

    汽车的功率越大,就越难驾驭。

    7. The device is manually operated, using a simple handle.

    一只普通的手柄就可以手动操作这个装置。

    8. I think I would handle a meeting with Mr. Siegel very badly.

    我觉得和西格尔先生会谈我可能会搞得一团糟。

    9. I knew how to load and handle a gun.

    我知道怎么上子弹,也知道枪怎么用。

    10. Tents have been set up next to hospitals to handle the overflow.

    医院旁搭起了帐篷以安置容纳不下的人员。

    11. The handle of a cricket bat protruded from under his arm.

    一个板球拍柄从他的胳肢窝下面伸了出来。

    12. I turned the handle and found the door was open.

    我转了一下门把手,发现门开着。

    13. He flew off the handle at the slightest thing.

    他为一点小事就大发脾气。

    14. Using a fabric conditioner will make clothes easier to handle and iron.

    使用织物柔顺剂会使衣服打理、熨烫起来更为容易。

    15. Some students have emotional problems that teachers feel ill equipped tohandle.

    一些学生的情感问题令老师们束手无策。

    猜你喜欢:

    1.handle的过去式和用法例句

    2.walk的用法和短语例句

    3.dress的复数形式和用法例句

    4.代词语法讲解及练习题

    5.belong的用法和短语例句

    6.inspire的短语有哪些

    7.easy的用法和短语例句

    思考:

    思考1:解释一下Handle,学这么久了,都没弄明白它啥意思!

    提示:可以看成是安全指针。像指针一样,可以通过它操作某个对象;但是handle提供了比pointer更加安全的保护;pointer意味着内存地址,意味着可以直接操作对象内部的任何信息handle的实现很简单,你可以简单想象成是在进程内部的一个数组数组的每个元...

    思考2:C语言中handle类型是什么意思?

    提示:1、handle是句柄类型,来源于Handle-C,Handle-C是硬件描述语言。windows在创建一个系统对象的同时,把一个句柄赋值给这个实体 ,可以用这个句柄来识别或者修改这个对象, 这就是handle句柄的作用。 2、句柄(HANDLE)是资源的标识。操作系统要管...

    思考3:handle什么意

    展开全文
  • handle

    千次阅读 2018-05-27 18:48:57
    本文是我学习C++沉思录第6章的笔记本文主要讲述了Handle类的概念,定义方法以及写时复制技术。 在前文(Surrogate代理类)的讲解中我们了解到了代理的实现方法.代理类有很多好处,但是麻烦的是每次都得进行复制.如果...

    本文是我学习C++沉思录第6章的笔记

    本文主要讲述了Handle类的概念,定义方法以及写时复制技术。

     

    在前文(Surrogate代理类)的讲解中我们了解到了代理的实现方法.

    代理类有很多好处,但是麻烦的是每次都得进行复制.如果该类是经常使用并且member很多的话,这样复制的消耗是十分客观的.

    因此这里就要介绍另外一种代理类,Handle,也就是句柄类.

     

    为何使用句柄类?

    首先就是复制问题.前面有谈到,有些类内部数据很多,采用复制消耗非常大,这种情况下就必须采用句柄类来进行操作.

    其次是由于函数的参数和返回值都是采用了复制进行自动传递.虽然c++中引用可以避免,但是很多情况下返回值采用引用并不明智.

    对于采用指针的方式,可以解决问题,但是又会引入调用者对于动态管理内存的麻烦.而这往往是很多错误的根源.

     

    何为句柄类呢?

    句柄类可以理解为采用了引用计数的代理类.

    其多个句柄共享了同一个被代理的类.通过引用计数的方式来减少复制以及内存管理.

    其行为类似指针,因此也有智能指针之称,但其实差别很大.后面会有讲述.

     

    句柄类例子:

    先有一个简单的类Point

    复制代码
     1 class Point
    2 {/*{{{*/
    3 public:
    4 Point():_x(0),_y(0){}
    5 Point(int x,int y):_x(x),_y(y){}
    6 int x()const {return _x;}
    7 void x(int xv) { _x = xv;}
    8 int y()const { return _y;}
    9 void y(int yv) { _y = yv;}
    10 private:
    11 int _x;
    12 int _y;
    13 };/*}}}*/
    复制代码

    接下来我们要定义其的Handle类.

    我们的Handle类:

    复制代码
     1 class Handle
    2 {
    3 public:
    4 Handle():up(new UPoint){}
    5 Handle(int x,int y):up(new UPoint(x,y)){}
    6 Handle(const Point&p):up(new UPoint(p)){}
    7 Handle(const Handle &h);
    8 ~Handle();
    9 Handle& operator=(const Handle &h);
    10 int x() const{ return up->p.x(); }
    11 int y() const{ return up->p.y(); }
    12 Handle& x(int);
    13 Handle& y(int);
    14
    15
    16 private:
    17 UPoint *up;
    18 void allocup();
    19 };
    复制代码

    这里说明我们的Handle和指针的不同之处.

    也许有读者会对Handle有疑问,为什么不采用operator->来直接操作point呢?

    其实顾虑就是operator->返回的是point的地址.也就是使用者可以轻易的获得point的地址进行操作,这并不是我们想要的.这也就是Handle也pointer不想同的地方.

    UPoint是为了采用引用计数定义的数据结构

    复制代码
     1 //all member is private..only assess by Handle
    2 class UPoint
    3 {/*{{{*/
    4 friend class Handle;
    5
    6 Point p;
    7 int u;//count
    8
    9 UPoint():u(0){}
    10 UPoint(const Point&pv):p(pv){}
    11 UPoint(int x,int y):p(x,y),u(1){}
    12 UPoint(const UPoint &up):p(up.p),u(1){}
    13 };/*}}}*/
    复制代码

     

    对于Handle类的操作,我们要在Handle类进行复制的时候,累加Handle指向的UPoint的计数值

    即复制构造函数以及赋值函数

    复制代码
     1 Handle::Handle(const Handle &h)
    2 :up(h.up)
    3 {
    4 ++up->u;
    5 }
    6
    7 Handle& Handle::operator=(const Handle &h)
    8 {
    9 ++h.up->u;
    10 if (--up->u == 0)
    11 delete up;
    12 up = h.up;
    13 return *this;
    14 }
    复制代码

    而对于析构函数,则是减小引用计数,如果减到0了,就说明没有其他的Handle指向了UPoint,因此我们要删除掉.

    1 Handle::~Handle()
    2 {
    3 if (--up->u == 0)
    4 delete up;
    5 }

    剩下的就是定义Handle对于Point的操作了.即Handle::x(int xv)和Handle::(int yv)了.

    这里有2种写法.

    一种是像指针一样,对于赋值,就直接修改指向的Point里面的值.这种方法有一个问题,即所以都指向这个Point的Handle类获取的x值都会变化.

    代码:

    复制代码
     1 //point like
    2 Handle& Handle::x(int xv)
    3 {
    4 up->p.x(xv);
    5 return *this;
    6 }
    7 //point like
    8 Handle& Handle::y(int yv)
    9 {
    10 up->p.y(yv);
    11 return *this;
    12 }
    复制代码

     

    还有一种是写时复制技术,即每次对于共享的Point进行修改的时候都复制一份新的Point,然后进行修改.

    这种技术在Handle中大量采用.在stl中,string也采用了同样的方法.

    其额外开销很小,而效率也不差.

    代码:

    复制代码
     1 void Handle::allocup()
    2 {
    3 if (up->u != 1)
    4 {
    5 --up->u;
    6 up = new UPoint(up->p);
    7 }
    8 }
    9
    10 Handle& Handle::x(int xv)
    11 {
    12 allocup();
    13 up->p.x(xv);
    14 return *this;
    15 }
    16
    17 Handle& Handle::y(int yv)
    18 {
    19 allocup();
    20 up->p.y(yv);
    21 return *this;
    22 }
    复制代码


    至此,Handle类的第一部分就讲完了.

    之后会有第二部分的讲解.解决了多出了一个UPoint的麻烦.

    展开全文
  • Handle_ClildWind.zip,Handle_ClildWind,resource.h,Debug,vc60.pdb,vc60.idb,StdAfx.obj,Handle_ClildWind.pdb,Handle_ClildWind.pch,Handle_ClildWind.ilk,Handle_ClildWindDlg.obj,Handle_ClildWind.obj,Handle_...
  • Handle初解,看完你就懂了handle

    千次阅读 2020-12-03 19:55:42
    文章目录前言一、Handle是什么?二、Handle有啥用呢?1.传递消息到ui线程2.代码示例3注意点最后 前言 提示:看完本篇,你可以了解到Handle的相关知识与常见的误区提醒 一、Handle是什么? 我们查看一下谷歌官方对...


    前言

    提示:看完本篇,你可以了解到Handle的相关知识与常见的误区提醒


    一、Handle是什么?

    我们查看一下谷歌官方对其的定义:
    A Handler allows you to send and process {@link Message} and Runnable
    objects associated with a thread’s {@link MessageQueue}. Each Handler
    instance is associated with a single thread and that thread’s message
    queue. When you create a new Handler, it is bound to the thread /
    message queue of the thread that is creating it – from that point on,
    it will deliver messages and runnables to that message queue and execute
    them as they come out of the message queue.

    Handler它可以通过消息队列发送消息或者runable对象,也可以处理消息或者runable对象。每一个handler实例,都持有一个线程以及此线程的消息队列。
    那么handle相当于什么呢?其实就相当于我们现实世界的搬运工,可以发送消息,也可以处理消息。

    二、Handle有啥用呢?

    1.传递消息到ui线程

    在我们安卓开发的时候,我们都知道ui线程是不可以进行耗时操作的,如果在这里进行的话,就会导致ANR,因此开辟一个子线程,在子线程中去处理相关的耗时任务,这是必然的结果。那么处理完的结果需要改变ui,即我们的控件显示的时候怎么办呢?在子线程中处理完直接改变ui是安卓中不允许的行为,因为ui线程不是线程安全的,那么我们有必要需要一个工具将子线程处理的结果带回去给ui线程去显示,而handle正是完成了这个任务。

    2.代码示例

    主线程定义一个handle(mHandle),构造方法里面new一个callback,复写handleMessage方法,这
    就表明当有信息来的时候,由handleMessage方法去执行
          mHandler = new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(@NonNull Message msg) {
                    //处理消息
                    if(msg.what == 1) {
                        Log.d("hanldeTest","object is -- > " + msg.obj);
                    }
                    return false;
                }
            });
    
    子线程中,假设处理完了一些耗时操作,比如读写数据库,随后即可执行一下代码
    Message message = mHandler.obtainMessage();
            message.what = 1;
            message.obj = "我是新消息!";
            mHandler.sendMessage(message);
    

    这样子一个发送,一个接受,就完成了就基本的handle使用,值得注意的是,上诉方法已经过期了
    在这里插入图片描述
    下面是最新的使用方法:

            boolean test=new Handler(getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                  //更新ui处理,在耗时任务完成后
                }
            });
    

    这里细心观察的小伙伴就会发现,参数里面有一个getMainLooper(),字面意思就是获得主looper对吧,那么looper是个啥?拿了有啥用?我们先以过期的方法去解释更方便
    handle源码:

        public Handler(@Nullable Callback callback, boolean async) {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
    
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread " + Thread.currentThread()
                            + " that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    

    这里有个值得的两个对象,mLooper,mQueue。当系统创建一个ActivityThread的时候,也创建了一个Looper,在looper里面有一个消息队列(其实是单链表结构的东西),Looper负责不断循环保持我们的程序不会结束,queue则负责接受消息,然后有handle去处理。我们一个线程只有一个looper,负责当前线程的不中断,但是handle可以有多个,所有的handle都依附于某个消息队列(没有队列你要搬运工干嘛对吧),而队列由looper创建,这也相当于handle间接依赖于looper了嘛~(看源码mQueue = mLooper.mQueue;)
    因为系统一开始就创建好ui线程的时候也自动帮我们创建好了looper,也所以我们在主线程创建的时候可以不用关心handle具体依附于哪个looper。
    但是如果在子线程中呢?子线程则没有这样好的待遇,它没有自动生成looper,所以我们在普通子线程中创建handle不指定好特定的looper就会报错,因为handle要好好依赖于一个消息队列,而消息队列由我们的looper产生的嘛~因此就有了new Handler((放一个looper过来!))这个方法,如
    new Handler(getMainLooper())。

    3注意点

    下面说一下几个注意点,我在面试的时候看到面试官也会问错的问题。handle是如何实现异步的?
    可能有的同学就会说,异步?就创建一个子线程啊!其实handle,runnable之类的并不会创建一个新线程!

    1、handle可以在主线程中创建,而正在使用发送信息是在子线程,而不是handle自己new了一个子线程
    2、handle创建的时候依赖于传入的looper,如果传入的looper是ui线程的looper,那么当在子线程调用的时候,handle发送的消息也是发送到ui线程的消息队列里面,由消息队列取出来再处理,所以才会有这样一个异步的操作。线程的looper一直在调用.loop方法,去查询消息队列有没有东西,有的话就取出来。
    源码如下

    public static void loop() {
            final Looper me = myLooper();
    		
    		......
    		
    		//开始轮询,这个轮询可能阻塞
            for (;;) {
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    			
    			......
    			
                long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
                try {
                    msg.target.dispatchMessage(msg);
                    if (observer != null) {
                        observer.messageDispatched(token, msg);
                    }
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } catch (Exception exception) {
                    if (observer != null) {
                        observer.dispatchingThrewException(token, msg, exception);
                    }
                    throw exception;
                } finally {
                    ThreadLocalWorkSource.restore(origWorkSource);
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
    			
    			......
    			
                msg.recycleUnchecked();
            }
        }
    

    最后

    看到这里你应该对handle处理消息是如何进行的吧,感谢各位看官~

    展开全文
  • handle的用法总结大全

    千次阅读 2021-05-24 10:18:45
    handle的意思n. (织物、毛皮等的)手感,手柄,举动,柄状物vi. 操作,操控,容易搬运vt. 用双手触摸、举起或握住,用手操作,操纵,处理或负责,管理,〈美〉买卖,经营变形:过去式: handled; 现在分词:handling; 过去...
  • Java8 CompletableFuture handle一、handle的作用和特点1. handle的特点2. handle和thenApply的区别3. handle和whenComplete的区别二、测试案例 一、handle的作用和特点 1. handle的特点 在写代码时,我们常用try…...
  • Json-Handle插件下载安装使用

    千次阅读 2022-05-06 21:08:20
    插件功能 美化整个JSON字串,使JSON结构一目了然,还能对JSON中的对象进行展开及收起,可以大大提升开发效率 。...用JSON-Handle对JSON文件格式的内容进行浏览和编辑,以树形图样式打开展现JSON文档
  • typedef void *HANDLE

    千次阅读 2022-01-19 10:39:05
    那是,HANDLE相当于void ,们能够叫它披着句柄皮的指针(PS:指针和句柄是有差别的,在这说句废话);,它会自动将你写得HANDLE理解为void。 总结: 1)void*类型的指针其实本质就是一个过渡型的指针状态,必须要赋予...
  • 初识handle

    千次阅读 2019-09-05 09:44:30
    handle 是用于分配,管理互联网中数字对象,其它标识符的综合系统。他包含一套开源的自有协议,标识占位以及协议的实现。handle 协议为在分布式计算系统中,存储数字资源,解析访问数据资源的必要信息。这些信息可以...
  • HANDLE hConsole = GetStdHandle((STD_OUTPUT_HANDLE))在C语言中表示:从一个特定的标准设备(标准输出)中取得一个句柄(用来标识不同设备的数值)。其中,STD_OUTPUT_HANDLE表示标准输出的句柄。GetStdHandle是Windows...
  • 句柄Handle的含义及使用

    千次阅读 多人点赞 2019-05-30 17:08:30
    #define DECLARE_HANDLE(name) typedef HANDLE name #endif DECLARE_HANDLE(HMODULE); DECLARE_HANDLE(HINSTANCE); DECLARE_HANDLE(HLOCAL); DECLARE_HANDLE(HGLOBAL); DECLARE_HANDLE(HDC); DECLARE_HANDLE(HRGN)...
  • FigureHandle

    2013-01-28 12:40:29
    FigureHandle
  • 句柄(handle)是什么?

    千次阅读 2021-07-24 12:25:21
    句柄(Handle)是什么?句柄(Handle)是什么?句柄(Handle)是什么? 句柄是一个用来标识对象或者项目的标识符,可以用来描述窗体、文件等,还有注意句柄(Handle)不能是常量!
  • 30.全局句柄表(_HANDLE_TABLE)

    千次阅读 2021-11-21 20:06:51
    Windows全局句柄表/_HANDLE_TABLE/_HANDLE_TABLE_ENTRY/定位全局句柄表,解析全局句柄表.
  • HANDLE”未定义问题解决方法

    千次阅读 2021-11-10 16:02:08
    考虑是否对“HANDLE”的用法有误,是否需要在使用前对其进行定义,再次进行尝试,发现在定义了“HANDLE”后会产生新的问题,显然不需要在自己的代码中对其定义。 最后考虑到windows.h是系统文件,那是不是SDK的问题...
  • Invalid query handle: xxxxxxxxxxxx

    千次阅读 2021-08-09 19:43:34
    在impala执行sql时,报错“Caused by: org.apache.hive.service.cli.HiveSQLException: Invalid query handle: ba49fe5374bba89c:bf8f6dee00000000”,开始一直以为是sql编写错误的问题 解决 打断点排查后发现是...
  • C++HANDLE的理解

    千次阅读 2020-06-30 21:17:52
    HANDLE:句柄,是WINDOWS用来表示对象的,是一个通用句柄表示。 在WINDOWS程序中,有各种各样的资源(窗口、图标、光标等),系统在创建这些资源时为他们分配内存,并返回标示这些资源的标示号,即句柄。 但是如果...
  • handle与handler的区别与内涵意思

    千次阅读 多人点赞 2019-08-29 16:33:30
    虽然handle和handler只有一个字符之差,但在计算机的世界里,含义却大相径庭。 1. 先说说handle 北京话说"一边儿玩儿去,玩勺子把儿去","勺子把儿"说的就是handle。而将handle译成"句柄"绝对是一个相当文雅相当...
  • Android中的Handle的使用与理解

    千次阅读 2020-10-13 23:37:31
    本文主要记录Android开发中Handle的作用 一个应用程序打开时,linux都会为其创建一个进程process,包含了主线程(UI线程),handle的作用主要是 当需要费时间的操作时,可以使界面ui同主线程一起运行,费时间的操作...
  • spy++获取handle

    千次阅读 2021-11-30 22:50:45
    最近在复现一个游戏脚本,为了固定游戏界面窗口使用了spy++工具获取窗口的句柄handle。 网上有很多关于spy++的下载和使用教程,我在这里就不重复说辣。 def full_screenshot(): hwnd = win32gui.FindWindow(None,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 851,266
精华内容 340,506
关键字:

HANDLE