Android的Handler原理,用法及常见问题!

一.Handler是什么?

handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它处理消息。


二.为什么要用Handler呢?

Android 在设计的时候,就封装了一套消息创建,传递,处理机制。如果不遵循这样的机制就没有办法更新UI信息的,就会抛出异常信息。


三.handler用法

首先我们进去官网看看Handler的开发文档

官网会有这样一段英文:
A Handler allows you to send and process Message and Runnable objects associated with a thread's 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.

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

Scheduling messages is accomplished with the post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler's handleMessage(Message) method (requiring that you implement a subclass of Handler).

When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.

When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler's message queue and processed when appropriate.

翻译:
处理程序允许您发送和处理与线程的MessageQueue相关联的消息和Runnable对象。每个处理程序实例都与一个线程和该线程的消息队列关联。当您创建一个新的处理程序时,它将绑定到正在创建的线程的线程/消息队列——从那时起,它将向该消息队列传递消息和runnables,并在它们从消息队列中释放时执行它们。

处理程序有两个主要用途:(1)将消息和runnables作为将来的某个点执行;以及(2)在不同的线程上执行要执行的操作。

调度消息是通过post(Runnable)、postAtTime(Runnable、long)、postDelayed(Runnable、long)、sendEmptyMessage(int)、sendMessage(消息)、sendMessageAtTime(消息、long)和sendMessageDelayed(消息、长)方法来完成的。post版本允许您在接收到的消息队列中对Runnable对象进行调用;sendMessage版本允许您对包含一组数据的消息对象进行队列,这些数据将由处理程序的handleMessage(Message)方法处理(要求您实现一个处理程序的子类)。

在发送或发送到处理程序时,您可以允许在消息队列准备就绪时立即处理该项目,或者在处理或绝对时间处理之前指定延迟。后两种方法允许您实现超时、计时和其他基于时间的行为。

当为应用程序创建一个进程时,它的主线程用于运行一个消息队列,该队列负责管理顶级应用程序对象(活动、广播接收器等)以及它们创建的任何窗口。您可以创建自己的线程,并通过一个处理程序与主应用程序线程进行通信。这是通过调用相同的post或sendMessage方法来完成的,但是从您的新线程中。然后,给定的Runnable或消息将被安排在处理程序的消息队列中,并在适当的时候进行处理。



那如果在Android中不在主线程更新UI会有什么问题出现呢?大家看下面的代码:
在非主线程中更新UI的代码演示:
public class MainActivity extends AppCompatActivity {
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView=(TextView) findViewById(R.id.textview);
        new Thread(){
            public void run(){
                try {
                    Thread.sleep(1000);
                    textView.setText("更新线程");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

}

这时候会出现这样的异常:

意思就是说只有原始线程当中更新UI。



那我们再看看handler更新UI的方法:
public class MainActivity extends AppCompatActivity {
    private Handler handler =new Handler();
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView=(TextView) findViewById(R.id.textview);
        Log.e("aaa", "1所在进程:"+ Process.myPid()+" 所在线程:"+Process.myTid());
        new Thread(){
            public void run(){
                try {
                    Thread.sleep(1000);
                    Log.e("aaa", "2所在进程:"+ Process.myPid()+" 所在线程:"+Process.myTid());
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            Log.e("aaa", "3所在进程:"+ Process.myPid()+" 所在线程:"+Process.myTid());
                            textView.setText("更新线程");
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

这样就可以更新UI了,我们打印出三个地方的进程线程ID看看,发现在handler方法中更新UI的方法所在线程ID
是与主线程一样的;
注:Process.myPid()--获得当前进程ID;Process.myTid()--获得当前线程ID。


那如果是想定时间更新UI的话,代码如下:
public class MainActivity extends AppCompatActivity {
    private Handler handler =new Handler();
    private TextView textView;
    private int a[]={1,2,3,4,5,6};
    private int index;
    private MyRunnable myRunnable=new MyRunnable();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView=(TextView) findViewById(R.id.textview);
        handler.postDelayed(myRunnable,1000);
    }
    class MyRunnable implements Runnable{
        @Override
        public void run() {
            index=index%6;
            textView.setText(""+a[index]);
            index++;
            handler.postDelayed(myRunnable,1000);
        }
    }
}
handler.postDelayed()方法中myRunnable是一个Runnable对象,1000是代表1000毫秒,数量可以自己设置;
这样的话界面的文字就会自动的循环更新了,你也可以换成是图片试试。


也可以通过Handler发送消息的方法更新UI:
handler.sendMessage()方法的使用:
public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Handler handler =new Handler(){
        public void handleMessage(Message message){
            textView.setText(""+message.arg1);
        };
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView=(TextView) findViewById(R.id.textview);
        new Thread(){
            public void run(){
                try {
                    Thread.sleep(1000);
                    Message message=new Message();
                    message.arg1=99;
                    handler.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();
    }
}


四.handler原理是什么?

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

handler原理是什么?

一.
Handler封装了消息的发送,(主要包括消息发送给谁);
Looper:
1.内部包括一个消息队列也就是MessageQueue,所有的Handler发送消息都走向这个消息队列;
2.Looper.loop()方法,就是一个死循环,不断地从MassageQueue取消息,如有消息就处理消息,没有消息就阻塞;
二.MessageQueue就是一个消息队列,可以添加消息,并处理消息;
三.Handler也很简单,内部会跟Looper进行关联,也就是说在Handler的内部可以找到Looper,找到了Looper也就是找到了
MessageQueue,在Handler中发送消息,其实就是向MessageQueue队列中发送消息;
总结:handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己,MessageQueue
就是一个存储消息的容器。


与Looper,MessageQueue的关系:


五.HandlerThread是什么:

public class MainActivity extends AppCompatActivity {
    private HandlerThread thread;
    private TextView textView;
    private Handler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView=(TextView) findViewById(R.id.textview);
        thread=new HandlerThread("handlerthread");
        thread.start();
        handler=new Handler(thread.getLooper()){
            public void handleMessage(Message message){
                Log.e("AAA",Thread.currentThread()+"");
            };
        };
        handler.sendEmptyMessage(1);
    }
}

这里我们可以看到log打印了名为“handlerthread”的线程
简单的说HandlerThread就是:
new出一个HandlerThread,解决多线程并发的问题,在子线程中处理一些事务,handlerThread.getLooper()方法拿到一个Looper对象,与默认的handler关联,handleMessage()方法就会在子线程中操作。


六.如何在主线程给子线程发送消息?交互;

public class MainActivity extends AppCompatActivity {
    private Button button1,button2;
    private Handler handlerThread;
    //主线程的Handler
    private Handler handler=new Handler(){
        public void handleMessage(Message msg){
            Message message=new Message();
            Log.e("aaa", "mainHandler");
            handlerThread.sendMessageDelayed(message,1000);
        };
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
	//创建子线程
        HandlerThread thread=new HandlerThread("handlerthread");
        thread.start();
	//子线程的Handler
        handlerThread=new Handler(thread.getLooper()){
            public void handleMessage(Message msg){
                Message message=new Message();
                Log.e("aaa","threadHandler");
                handler.sendMessageDelayed(message,1000);
            };
        };
	//发送按钮点击事件
        button1=(Button) findViewById(R.id.botton1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.e("aaa","button1");
                handler.sendEmptyMessage(1);
            }
        });
	//暂停按钮点击事件
        button2=(Button) findViewById(R.id.botton2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.e("aaa","button2");
                handler.removeCallbacksAndMessages(null);
                handlerThread.removeCallbacksAndMessages(null);
            }
        });
    }
}
这里我们打印一下log看看

可以看到主线程和子线程相互发送消息。


七.Android中更新UI的几种方式:

android中可以更新UI的方法有:
runOnUiThread();
handler.post();
handler.sendMessage();
view.post();
我们代码演示一下:
public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            textView.setText("handler.sendMessage");
        };
    };
    private void handler1(){
        handler.post(new Runnable() {
            @Override
            public void run() {
                textView.setText("handler.post");
            }
        });
    }
    private void handler2(){
        handler.sendEmptyMessage(1);
    }
    private void updateUI(){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText("runOnUiThread()");
            }
        });
    }
    private void viewUI(){
        textView.post(new Runnable() {
            @Override
            public void run() {
                textView.setText("view.post()");
            }
        });
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView=(TextView) findViewById(R.id.textview);
        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    //handler1();//handler.post()更新UI。
                    handler2();//handler.sendMessage()更新UI。
                    //updateUI();//runOnUiThread()更新UI。
                    //viewUI();//view.post()更新UI。
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

八.非UI线程真的不能更新UI吗?

程序员有没有想过非UI线程真的不能更新UI吗?
我们来看看下面的两个例子:
public class MainActivity extends AppCompatActivity {
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView=(TextView) findViewById(R.id.textview);
        new Thread(){
            @Override
            public void run() {
                textView.setText("Thread");
            }
        }.start();
    }
}
上面这个是可以更新UI的


public class MainActivity extends AppCompatActivity {
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView=(TextView) findViewById(R.id.textview);
        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    textView.setText("Thread");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}
上面这个是会出现错误的:也就是非UI线程不可以更新UI。


为什么会出现这两种情况呢?
第一个例子中,子线程没有休眠,在Avtivity的生命周期的OnCreate()方法完成了,子线程中更新UI的操作。
第二个例子中,子线程发生了休眠,在Activity的生命周期的OnResume()方法完成了,checkThread()这个方法。
void checkThread(){
        if(mThread != Thread.currentThread()){
            throw new CalledFromWrongThreadException(" Only the original thread that created a view hierarchy can touch its views.")
        }
    }
因为 :在Activity的Resume()方法会跑一个ViewRootImpl(),而这个ViewRootImpl()里会有一个checkThread()来判断更新UI的线程是不是主线程。如果不是就会抛出异常。



九.使用handler时候遇到的问题:

除了前面所讲到的非UI线程不可以更新UI,会报错的问题;
使用handler时还会遇到这样一个问题:
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(){
            @Override
            public void run() {
                try {
                    Handler handler=new Handler();
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

意思就是说创建handler时,需要有一个与之关联的Looper对象。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值