核酸检测之Handler理解篇

关于Handler的理解

链接: NewOrin_CSDN.
链接:yxb_yingu_CSDN.
链接:songzi1228_CSDN.

这里强调一个问题,子线程其实也是可以更新UI操作的,只是不建议(不允许)日常使用(文章下面有解释)。

一、 对于handler的理解:

 对于Android界面内容的更新只能由主线程来完成,然而主线程做耗时操作轻则造成界面的卡顿,严重会导致ANR(字面意思就是应用无响应,主线程没有在规定的时间内完成相应的工作),所以通常我们把耗时操作放在子线程中去进行,当我们创建的子线程想要更新界面,就需要子线程和主线程存在某种联系,而这个存在它就是handler。

二、 基本使用
  1. 创建handler对象,重写handleMessge函数
  2. 创建线程实现Runnable接口
  3. 创建Message对象(3中创建方式)
  4. 在线程中用handler对象调用sendMessage函数把内容发送到MessageQueu(消息队列)中
  5. 主线程将消息取出,更新界面
public class MainActivity extends AppCompatActivity {
    private TextView tvAge;
    private TextView tvName;
    private A mMan;
    
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {//重写处理消息的方法
            super.handleMessage(msg);
            String name = Thread.currentThread().getName();
            Log.i("thread", "当前线程2: "+name);
            switch (msg.what) {
                case 100:
                    A man = (A) msg.obj;//强制转型
                    tvAge.setText(man.getAge()+"");
                    tvName.setText(man.getName());
                    break;
            }
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
    private void initView() {
        tvAge = (TextView) findViewById(R.id.tv_age);
        tvName = (TextView) findViewById(R.id.tv_name);
        new Thread(new Runnable() {
            @Override
            public void run() {
                mMan = new A(26, "牧区叔叔");
                String name = Thread.currentThread().getName();
                Log.i("thread", "当前线程1: "+name);
                Message msg = Message.obtain();
                msg.what = 100;
                msg.obj = mMan;
                mHandler.sendMessage(msg);
            }
        }).start();
    }
}

2020-10-18 19:02:20.127 11290-11356/com.example.myhelloword401 I/thread: 当前线程1: Thread-2
2020-10-18 19:02:20.188 11290-11290/com.example.myhelloword401 I/thread: 当前线程2: main

更新内容的传递路径:子线程→消息队列→主线程→界面

补充说明:

  • 系统在创建主线程时候会自动初始化Looper对象,同时会创建一个与其关联的MessageQueue(子线程也可以创建handler对象的,但是必须创建Looper才能使用,当前线程只能存在一个Looper)(下面会讲到)
  • Handler:的作用就是发送与处理消息(前提是当前线程必须有Looper)
  • Message(消息):Handler接收与处理的消息对象
  • MessageQueue(消息队列):先进先出,管理Message
  • Looper(消息循环者):每个线程中只能有一个Looper,管理着MessageQueue,不断的从其中取出Message分发给对应的Handler处理。
三、 Handler的作用
  • 更新UI
  • 发送消息
  • 处理消息
四、 原理分析

那么Handler是怎么工作的呢?首先我们要认识三个类,分别是Message、MessageQueue、Looper。

  1. Message:创建一个Message,包含必要的描述和属性数据,并且此对象可以发送给Handler处理(handler.sendMessage();)。

    Message对象有3种创建方式
    1、Message msg = new Message();//直接创建一个Message对象
    2、Message msg = Message.obtain();//从整个Messge池中返回一个新的Message实例,通过obtainMessage能避免重复Message创建对象。
    3、Message msg = handler.obtainMessage();//跟第二个一样
    这里推荐使用2或3种方式

    属性字段:
    arg1、arg2:用来存放整形数据;
    what:消息表示;
    obj:Object类型的任意对象;
    replyTo:消息管理器,关联到handler,handler处理其中的消息;

  2. MessageQueue:消息队列,内部存储了一组消息,以队列的形式对外提供插入和删除(先进先出)。虽然称之为消息队列,其实内部存储结构是采用单链表的数据结构来存储消息,仅仅是存储消息,而不能处理消息,所以Looper填补了这个功能。

  3. Looper:在MessageQueue存了消息之后Looper就会以无限循环的形式去查是否有新消息,如果有的话就去处理消息,否则就是一直等待着。Looper中还有一个特殊的概念就是ThreadLocal,ThreadLocal并不是线程,它是用来在每个线程中存储数据。Handler创建的时候就会采用ThreadLocal获取当前线程的Looper构造消息循环系统。还有一点非常要注意的是,工作线程是默认是没有Looper的,若要在线程中使用就需要创建Looper,否则就会抛出Can’t create handler inside thread that has not called Looper.prepare(),意思是不能创建Handler因为Looper没有执行Looper.prepare()方法,尚未初始化。而UI线程即主线程中,是默认初始化了Looper的,所以不需要再UI线程再次执行Looper.prepare()方法了。
    Looper.loop():这个方法就是一个死循环,不断地从MessageQueue取消息,如有消息就处理,没有就阻塞;

五、 工作流程

Handler的主要工作包含消息的发送与接收过程。消息的发送通过post和send的一系列方法来实现,其实post的一系列方法最终还是通过send来实现的。当Handler发送了消息之后,MessageQueue里就插入了一条消息,然后MessageQueue就会返回这条消息给Looper,Looper接收到消息之后就开始处理了,最终消息由Looper交给Handler处理,即Handler的dispatchMessage方法会被调用,这时Handler就进入了处理消息的阶段,最后经过一系列的判断之后,就会调用handleMessage()方法了。

handler在哪里创建是没有关系的,重要的是handler发送的message到达了那个looper的消息队列,不管通过什么方法,只要让handler发送的消息到达UI线程Looper的消息队列,就能实现对UI线程的修改!

前方高能!:
  1. 一个普通Thread不能创建handler的,需要在该线程的run()方法中执行Looper.papare(),将该线程变为looperThread。
    那么我们会问为什么在UI线程创建Handler时候没有执行Looper.papare()呢?
     其实UI线程自动执行了looper.papare和looper.loop()方法,所以UI线程本身就是looper线程
  2. 一个looperThread只能有一个looper,一个looper绑定一个messageQeue;
  3. 一个looperThread可以用多个handler;

  Handler跟Thread没有关系,它跟looper绑定,具体绑定那个looper,就要看用的那个构造方法,如果用的是new Handler()这个构造方法,会默认绑定它所在线程的looper和消息队列,如果用的是new Handler(Looper looper)的构造方法,就会与传进去的那个looper绑定。

六、 Android为什么要设计只能通过Handler机制更新UI呢?

最根本的目的是解决多线程迸发问题。
假如在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样子的问题?
答案是:更新界面错乱。
如果对更新UI的操作都进行加锁处理的话又会产生什么样子的问题?
答案是:性能下降。
出于对以上问题的考虑,android给我们提供了一套更新UI的机制,我们只需要遵循这样的机制就行了,而根本不用去关心多线程问题,所有的更新UI的操作,都是在主线程的消息队列当中去轮询处理的。

===== ** 子线程操作UI就一定会报错崩溃吗?** ====

不!首先我们要了解系统为什么会崩毁并报错,是系统本身有一个判断机制,当执行到layoutRequset 时,只有判断到parentLayout != null系统才会判断并抛出异常,导致错误;
其实,在oncreate()方法中执行对UI控件值的修改是可以的,因为此时activity的生命周期才刚开始,parentLayout还没创建和绘制,其值是null,所以“监控失效”,所以不会抛错和崩溃,并且控件的属性值会被正常修改。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Autosar Handler是指根据Autosar标准设计和实现的处理程序。Autosar(Automotive Open System Architecture)是一个针对汽车软件架构的开放标准,旨在提供一种标准化的方法来开发汽车电子控制单元(ECU)软件。Autosar Handler是Autosar架构中的一部分,负责处理与ECU相关的各种任务和功能。 Autosar Handler的主要功能包括:任务调度和优先级管理、资源管理、通信管理和错误处理。任务调度和优先级管理负责根据预设的优先级规则,合理地分配和调度各个任务的执行顺序,确保系统的高效稳定运行。资源管理负责对系统中的资源进行管理和分配,例如内存、处理器、通信总线等。通信管理负责处理ECU之间的通信,确保信息的可靠传输和处理。错误处理模块负责监测系统中的错误和异常情况,并进行相应的处理和纠正,以确保系统的可靠性和稳定性。 在Autosar Handler中,各个模块之间通过标准化的接口进行交互和通信,确保各个模块的兼容性和可替换性。Autosar Handler的设计和实现遵循一系列的Autosar标准和规范,确保了系统的可靠性、可扩展性和可维护性。 总而言之,Autosar Handler是Autosar架构中的一部分,用于对ECU软件的任务调度、资源管理、通信管理和错误处理进行统一的管理和处理。通过Autosar Handler的使用,可以有效提高汽车电子控制系统的稳定性、可靠性和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值