当前位置:首页 » 安卓系统 » androidhandlerui

androidhandlerui

发布时间: 2023-12-12 10:15:26

1. 能讲讲Android的Handler机制吗

Android的Handler机制是通俗讲为了互相发消息,一般是子线程给主线程发消息完成相应操作。

安卓中最常见的操作是子线程操作完事后得到数据想更新UI,安卓有规定不允许在子线程中刷新UI,所以Handler出现了。

使用和理解大致步骤。

  1. 创建全局Handler对象handler,然后在主线程中初始化它(一般在oncreate中),把它的handmessage里面的方法重写,这个方法是收到子线程发给它的消息后执行的逻辑。

  2. 在子线程中获取数据,调用handler.sendmessage,把要发的消息放在message中。message会添加到Messagequue(消息队列中,handler创建就带的)。

3.对象handler被创建和初始化的时候,系统自动会启动Handler.looper,也就是一个消息轮询器,它不断的去查看有没有消息进入到Messagequue(消息队列中),有就取出交给handler的handmessage去处理。//这段逻辑是系统自动执行,理解就行。*纯手打,不骗人~~~

2. [Android源码分析] - 异步通信Handler机制

一、问题:在Android启动后会在新进程里创建一个主线程,也叫UI线程( 非线程安全 )这个线程主要负责监听屏幕点击事件与界面绘制。当Application需要进行耗时操作如网络请求等,如直接在主线程进行容易发生ANR错误。所以会创建子线程来执行耗时任务,当子线程执行完毕需要通知UI线程并修改界面时,不可以直接在子线程修改UI,怎么办?

解决方法:Message Queue机制可以实现子线程与UI线程的通信。

该机制包括Handler、Message Queue、Looper。Handler可以把消息/ Runnable对象 发给Looper,由它把消息放入所属线程的消息队列中,然后Looper又会自动把消息队列里的消息/Runnable对象 广播 到所属线程里的Handler,由Handler处理接收到的消息或Runnable对象。

1、Handler

每次创建Handler对象时,它会自动绑定到创建它的线程上。如果是主线程则默认包含一个Message Queue,否则需要自己创建一个消息队列来存储

Handler是多个线程通信的信使。比如在线程A中创建AHandler,给它绑定一个ALooper,同时创建属于A的消息队列AMessageQueue。然后在线程B中使用AHandler发送消息给ALooper,ALooper会把消息存入到AMessageQueue,然后再把AMessageQueue广播给A线程里的AHandler,它接收到消息会进行处理。从而实现通信。

2、Message Queue

在主线程里默认包含了一个消息队列不需要手动创建。在子线程里,使用Looper.prepare()方法后,会先检查子线程是否已有一个looper对象,如果有则无法创建,因为每个线程只能拥有一个消息队列。没有的话就为子线程创建一个消息队列。

Handler类包含Looper指针和MessageQueue指针,而Looper里包含实际MessageQueue与当前线程指针。

下面分别就UI线程和worker线程讲解handler创建过程:

首先,创建handler时,会自动检查当前线程是否包含looper对象,如果包含,则将handler内的消息队列指向looper内部的消息队列,否则,抛出异常请求执行looper.prepare()方法。

 - 在 UI线程 中,系统自动创建了Looper 对象,所以,直接new一个handler即可使用该机制;

- 在 worker线程 中,如果直接创建handler会抛出运行时异常-即通过查‘线程-value’映射表发现当前线程无looper对象。所以需要先调用Looper.prepare()方法。在prepare方法里,利用ThreadLocal<Looper>对象为当前线程创建一个Looper(利用了一个Values类,即一个Map映射表,专为thread存储value,此处为当前thread存储一个looper对象)。然后继续创建handler, 让handler内部的消息队列指向该looper的消息队列(这个很重要,让handler指向looper里的消息队列,即二者共享同一个消息队列,然后handler向这个消息队列发送消息,looper从这个消息队列获取消息) 。然后looper循环消息队列即可。当获取到message消息,会找出message对象里的target,即原始发送handler,从而回调handler的handleMessage() 方法进行处理。

 - handler与looper共享消息队列 ,所以handler发送消息只要入列,looper直接取消息即可。

 - 线程与looper映射表 :一个线程最多可以映射一个looper对象。通过查表可知当前线程是否包含looper,如果已经包含则不再创建新looper。

5、基于这样的机制是怎样实现线程隔离的,即在线程中通信呢。 

核心在于 每一个线程拥有自己的handler、message queue、looper体系 。而 每个线程的Handler是公开 的。B线程可以调用A线程的handler发送消息到A的共享消息队列去,然后A的looper会自动从共享消息队列取出消息进行处理。反之一样。

二、上面是基于子线程中利用主线程提供的Handler发送消息出去,然后主线程的Looper从消息队列中获取并处理。那么还有另外两种情况:

1、主线程发送消息到子线程中;

采用的方法和前面类似。要在子线程中实例化AHandler并设定处理消息的方法,同时由于子线程没有消息队列和Looper的轮询,所以要加上Looper.prepare(),Looper.loop()分别创建消息队列和开启轮询。然后在主线程中使用该AHandler去发送消息即可。

2、子线程A与子线程B之间的通信。

1、 Handler为什么能够实现不同线程的通信?核心点在哪?

不同线程之间,每个线程拥有自己的Handler、消息队列和Looper。Handler是公共的,线程可以通过使用目标线程的Handler对象来发送消息,这个消息会自动发送到所属线程的消息队列中去,线程自带的Looper对象会不断循环从里面取出消息并把消息发送给Handler,回调自身Handler的handlerMessage方法,从而实现了消息的线程间传递。

2、 Handler的核心是一种事件激活式(类似传递一个中断)的还是主要是用于传递大量数据的?重点在Message的内容,偏向于数据传输还是事件传输。

目前的理解,它所依赖的是消息队列,发送的自然是消息,即类似事件中断。

0、 Android消息处理机制(Handler、Looper、MessageQueue与Message)

1、 Handler、Looper源码阅读

2、 Android异步消息处理机制完全解析,带你从源码的角度彻底理解

谢谢!

wingjay

![](https://avatars0.githubusercontent.com/u/9619875?v=3&s=460)

3. android怎么样在子线程实例化的handler去更新UI

之前用过android-async-http,虽然没认真看过源码,但也有简单的浏览过,心里一直有个疑问,因为android-async-http也是采用hanlder机制来执行回调的,也就是说handler是它实例化的,可我们知道handler的一个重要作用是将一个任务切换到handler所在的线程去执行的,我的疑问就是:如果我们在子线程调用android-async-http的网络请求,这时候如果handler是在子线程被实例化的呢(当然我没认真研究过源码,也不知道它是怎么实现),还能更新UI吗?
我们都正常以为handler在哪个线程实例化的,我们通过handler就可以把任务切换到该任务去执行,我们看如下代码:

private void initd() {

new Thread(new Runnable() {

@Override

public
void run() {

Looper.prepare();

Handler handler = new
Handler() {

@Override

public void handleMessage(Message msg) {

Toast.makeText(MainActivity.this, "测试子线程弹出toast",
Toast.LENGTH_LONG).show();

((TextView)findViewById(R.id.main_tv_text)).setText("测试子线程");

}

};

Looper.loop();

handler.sendEmptyMessage(0);

}

}).start();

}

经过我的测试上面这段方法是无法更新UI的,因为handler是在子线程实例化的,并非在UI线程,也证实了我们的想法。

如果我的疑问存在(我没尝试过在子线程使用android-async-http,不知道什么情况,这里只是做个假设),那么它使怎么实现的呢。

最近看了android开发艺术探索,特意去研究了一下android的消息机制,才弄明白了Handler的工作原理,其实起关键作用的是Looper并不是handler,我们先来看下Looper的构造方法:

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mThread = Thread.currentThread();

}

Looper会把所在的当前的线程保存起来,而handler的工作需要Looper,于是我尝试了一下如下代码:

private void init() {

new Thread(new Runnable() {

@Override

public
void run() {

Handler handler = new
Handler(Looper.getMainLooper()) {

@Override

public void handleMessage(Message msg) {

Toast.makeText(MainActivity.this, "测试子线程弹出toast",
Toast.LENGTH_LONG).show();

((TextView)findViewById(R.id.main_tv_text)).setText("测试子线程");

}

};

handler.sendEmptyMessage(0);

}

}).start();

}

handler实例化的时候,我传入的是UI线程的Looper,确实是可以更新UI。

总结:

1、handler执行任务不是在它实例化所在的线程决定的,而是关键在于Looper。

2、我们可以在子线程实例化handler并且可以用它来更新UI了。

4. Android:在一个非主线程内直接调用UI线程的Handler实例,这样没问题吗

我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的。

下面说下有关Handler相关的知识。
多线程一些基础知识回顾:
在介绍Handler类相关知识之前,我们先看看在Java中是如何创建多线程的
方法有两种:
通过继承Thread类,重写Run方法来实现
通过继承接口Runnable实现多线程

接下来让我们看看Handler是什么?如何来用

Handler是这么定义的:
主要接受子线程发送的数据, 并用此数据配合主线程更新UI.

Handler的主要作用:主要用于异步消息的处理

Handler的运行过程:
当(子线程)发出一个消息之后,首先进入一个(主线程的)消息队列,发送消息的函数即刻返回,而在主线程中的Handler逐个的在消息队列中将消息取出,然后对消息进行处理。这样就实现了跨线程的UI更新(实际上还是在主线程中完成的)。
这种机制通常用来处理相对耗时比较长的操作,如访问网络比较耗时的操作,读取文大文件,比较耗时的操作处理等。

在大白话一点的介绍它的运行过程:
启动应用时Android开启一个主线程 (也就是UI线程) , 如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象(这也就是你在主线程中直接访问网络时会提示你异常的原因,如我们上篇文章所述Android主线程不能访问网络异常解决办法
)。
这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,更新UI只能在主线程中更新.。
这个时候,Handler就出现了,来解决这个复杂的问题,由于Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。

接下来我们看看关于Handler都有哪些方法(它都能干什么):
其中Handler对象的一些主要方法,如下:

post(Runnable) postAtTime(Runnable,long)

postDelayed(Runnable long)

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

正如方法名字中看到的,有两类方法:

(1)在某个主线程中执行Runnable
(2)在子线程中发送一个消息,在主线程中检测该消息处理

线程间传递Message对象的sendMessage方法和发送Runnable多线程对象的post方法。正对应着上面所说的两个特性1)、2)

下面开发个Handler实例做说明:
用post的方法执行一个Runnable对象,在该对象中随机产生一个10-100之间的随机数,赋值到UI主线程中的TextView中线程,执行5次,每次相隔5秒, 拼接每次的数字, 最后执行结果应该如:10 22 33 44 61

主要代码如下:
int i = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler.post(run);
}

Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
String s = String.valueOf(msg.what);
TextView tv = (TextView)findViewById(R.id.textView);
tv.setText(tv.getText() + ” ” + s);
}
};

Runnable run = new Runnable(){
@Override
public void run(){
Random r = new Random();
int rnum = r.nextInt((100 – 10) + 1) + 10;
handler.sendEmptyMessage(rnum);
handler.postDelayed(run, 5000);
i++;
if (i==5){
handler.removeCallbacks(run);
}
}
};

5. Android-Handle(线程间通信)详解

线程间通信是在Android开发中比较经常遇到的,我们刷新UI界面一般是通过子线程做完某些事情后,要改变主页面就要通过数据的通信,让主线程接收到信息后自己改变UI界面。

1. Handle 先进先出原则;
2. Looper 类用来管理特定线程内对象之间的消息交换(MessageExchange);
3. Message 类用来保存数据。

1.Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列);
2.Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息;

android.os.Message的主要功能是进行消息的封装,同时可以指定消息的操作形式,Message类定义的变量和常用方法如下:

在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,但是有这么几点需要注意:

在使用Handler处理Message时,需要Looper(通道)来完成。在一个Activity中,系统会自动帮用户启动Looper对象,而在一个用户自定义的类中,则需要用户手工调用Looper类中的方法,然后才可以正常启动Looper对象。Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。使用Looper类创建Looper线程很简单:

这是在子线程中创建Handler的情况,如果在主线程中创建Handler是不需要调用 Looper.prepare(); 和 Looper.loop(); 方法。

Handler是更新UI界面的机制,也是消息处理的机制。我们可以通过Handle发送消息,也可以处理消息。

Android在设计的时候,封装了一套消息创建、传递、处理机制,如果不遵循这样的机制就没有办法更新UI信息,就会抛出异常。
创建Handler实例化对象时,可以重写的回调方法:

6. Android-Handler同步屏障

消息机制的同步屏障,其实就是阻碍同步消息,只让异步消息通过。而开启同步屏障的方法就是调用下面的方法:

源码如下:

在这里可以看到,Message对象初始化的时候并没有给target赋值,因此target==null的来源就找得到了。这样就可以插入一条target==null的消息,这个消息就是一个同步屏障。
那么开启消息屏障后,所谓的异步消息又是如何处理的呢?
消息的最终处理其实都是在消息轮询器Looper#loop()中,而loop()循环中会调用MessageQueue#next()从消息队列中进行取消息。

从上面的MessageQueue.next方法可以看出,当消息队列开启同步屏障的时候(即标识为msg.target==null),消息机制在处理消息的时候,会优先处理异步消息。这样,同步屏障就起到了一种过滤和优先级的作用。

如果上图所示,在消息队列中有同步消息和异步消息(黄色部分)以及一道墙(同步屏障--红色部分)。有了同步屏障的存在,msg_2和msg_M这两个异步消息可以被优先处理,而后面的msg_3等同步消息则不会被处理。那么这些同步消息什么时候可以被处理呢?就需要先移除这个同步屏障,即调用MessageQueue#removeSyncBarrier()

同步屏障一般在日常开发中比较少用,而在系统源码中就有使用。Android系统中的UI更新相关的消息即为异步消息,需要优先处理。
16ms左右刷新UI,而是60hz的屏幕,即1s刷新60次。
在Android中什么是异步消息?即给:

比如,在View更新时,draw、requestLayout、invalidate等很多地方都调用了。ViewRootImpl#scheleTraversals()。

在这里,mChoreographer.postCallback最终会执行到了Choreographer#postCallbackDelayedInternal()

可以看到,这里就开启了同步屏障,并且发送了异步消息。由于UI相关的消息是优先级最高的,这样系统就会优先处理这些异步消息。
最后,当然要移除同步屏障的时候,调用ViewRootImpl#unscheleTraversals

在ViewRootImpl中的doTraversal()方法中也会移除同步屏障,这里移除是因为requestLayout或者invalidate的时候,刷新之后,在doTraversal()中就会移除同步屏障,因为此时消息已经发送并且处理了。

热点内容
国图数据库 发布:2024-11-29 10:34:15 浏览:540
vpn免流服务器搭建 发布:2024-11-29 10:26:12 浏览:244
c源文件编译后的扩展名为 发布:2024-11-29 10:08:40 浏览:923
脚本自动登录 发布:2024-11-29 09:55:27 浏览:62
安卓的无线网络在哪里 发布:2024-11-29 09:54:13 浏览:652
谷歌浏览器打开ftp 发布:2024-11-29 09:46:30 浏览:668
热线女孩ftp 发布:2024-11-29 09:46:29 浏览:71
安卓作曲哪个软件好用 发布:2024-11-29 09:29:30 浏览:903
怎么看拨号服务器ip 发布:2024-11-29 09:28:17 浏览:931
王者配置怎么样 发布:2024-11-29 09:22:58 浏览:858