一.Handler是什么?

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


二.为什么要用Handler呢?

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


三.handler用法

首先我们进去官网看看Handler的开发文档
https://developer.android.google.cn/reference/android/os/Handler.html

官网会有这样一段英文: 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对象。

更多相关文章

  1. 浅析Android中的消息机制-解决:Only the original thread that cr
  2. Android异步消息机制之Handler
  3. Android的Handler机制详解3_Looper.looper()不会卡死主线程
  4. Andorid Dialog 示例【慢慢更新】
  5. Android(安卓)SDK Manager无法更新的解决
  6. Android热更新框架Tinker无法更新?
  7. Android之Handler用法总结
  8. Android开发之消息处理机制(一)——Handler
  9. Android,一个思路实现APP版本更新

随机推荐

  1. Android获取存储设备挂载路径
  2. Android中EditText 设置 imeOptions 无效
  3. Ubuntu Touch 只是另一个 Android 皮肤?
  4. unity-与Android交互(unity5、android st
  5. Android消息机制Handler源码分析
  6. Android自动化测试之环境搭建(二)
  7. Android 4.1源码编译找不到资源文件解决
  8. Android基本组件__文本框和编辑框
  9. Android中RelativeLayout中各个属性的用
  10. Android使用真机无法调试,抱错INSTALL_FAI