Android_Handler

来源:互联网 发布:南航网络教学平台 编辑:程序博客网 时间:2024/06/06 04:28

Android_Handler

今天无意间翻出来从前有关Handler的笔记,突然感觉到有点懵逼,不过现在明白了,还是记录下来,防止以后再次遇见这种情况,同时跟大家分享一下,如果有什么错误的,请各位大神指正。以下的都是本人的理解,希望指正。谢谢。

我想在开始今天的学习之前先跟大家讲一下有关线程的知识。

线程

线程有主次之分,每个正在执行的程序都存在主线程,可能存在若干个分支线程(子线程)
当正在执行的程序存在多个线程时,表现为多个线程同时工作
当正在执行的程序存在多个线程时,这些线程会交替运行,交替的间隔时间和每次每个线程的执行时间都是不可以确定的
创建线程的方法:
1) 自定义类继承java.lang.Thread类,并重写public void run()方法,当需要启动线程时,初始化该线程类的对象,并调用start()方法开启线程
2) 自定义类实现java.lang.Runnable接口,并重写抽象的public void run()方法,当需要启动线程时,初始化Thread类的对象,并在创建对象时使用自定义的Runnable实现类对象作为其构造方法的参数,最后,调用Thread类对象的start()方法开启线程

线程控制

Thread.sleep()方法可以让线程休眠,即多少毫秒之内不会工作

ANDROID的UI线程模型

UI:User Interface,即:(软件与)用户(的)接口
在ANDROID系统中,主线程也叫作UI线程
所有的显示控件的初始化都是在主线程中运行的,ANDROID系统约定:只有创建控件的线程(主线程)才可以控制这些控件!即:子线程不允许控制各个控件

我们在编写Android应用程序的时候,有的Activity中不仅仅只有一个主线程在执行操作,像一些网路请求这种耗时操作,我们应该交给子线程去执行,如果不交给子线程去执行那么你就会遇见 ANR错误

ANR错误

ANR(Application Not Respoding)表示“应用程序没有响应”,出现该错误的原因在于执行时间过长,例如Activity的执行时间超过5s,或者广播接收者的执行时间超过10s
以上错误的出现原因针对主线程,子线程的执行时间不受限制

既然我们已经知道了在Android中需要用到子线程去执行一些耗时操作,那么问题来了,子线程只能去执行操作,但是不能更改UI,那么主线程和子线程是如何通信的呢?这就需要我们学习今天的知识 Handler机制。我们在学习Handler机制之前需要有以下知识做铺垫。

1、Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。通常我们会使用到的几个属性:message.what->用于标识是哪一个线程发送出来的message。what属性的值应该使用静态常量表示。message.arg1和arg2->用于携带 int值,message.obj携带的是一个Object对象。
2、Handler
Handler主要是用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列的辗转处理后,追中会传到Handler的handlerMessage()方法中。
3、MessageQueue
MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发动的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
4、Looper
Loop是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,饭后每当发现MessageQueue中存在一条消息,就会将它取出来,并传递到Handler的handlerMessage()方法中。每个线程中也只会有一个Looper对象。

为了让大家能够更好的理解Handler消息机制的工作原理,请看下图:

Handler消息机制

通过以上的知识了解,相信大家对Handler消息机制大概有了一定的了解,那么我们就来实践一下吧。以下的例子我是使用了一个Clock例子,该例子就是启动程序,主界面上实时显示当前的时间。

1、我的MainActivity所对应的activity_main.xml布局文件很简单就只有一个TextView控件,代码如下:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context="com.example.clockthreadtest.MainActivity">    <TextView        android:id="@+id/tv_Clock"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:text="Hello World!"        android:textSize="32sp"        /></RelativeLayout>

2、我在MainActivity中使用了大家平时都使用的Handler机制来发送消息和处理消息,代码很基础,我就不解释了。

public class MainActivity extends Activity {    private TextView tvClock;    private Handler handler;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        requestWindowFeature(Window.FEATURE_NO_TITLE);        setContentView(R.layout.activity_main);        tvClock = (TextView) findViewById(R.id.tv_Clock);        handler =new InnerHandler();       new InnerThread().start();    }    class InnerHandler extends Handler {        @Override        public void handleMessage(Message msg) {            String data= (String) msg.obj;            tvClock.setText(data);        }    }    class InnerThread extends Thread {        @Override        public void run() {            for (int i = 0; i < 1200; i++) {                Message message =new Message();                message.obj =new Date().toString();                handler.sendMessage(message);                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }}

其实对于Handler机制,要是按照上面的写法完全没有什么错误,但是Handler其实还有其他的写法,就让我们来了解一下,开阔一下视野吧。

使用Message.obtain创建message

我新建了一个Activity名叫ClockActivityTest,它的布局文件的代码和activity_main代码相同,让我们来看一下ClockActivityTest的代码吧:
对了,要记着在AndroidManifest.xml文件中将ClockActivityTest设置为启动界面。

public class ClockActivity extends Activity {    private TextView tvClock;    private static final int UPDATA_MESSAGE_TIME = 1000;    private Handler handler;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        requestWindowFeature(Window.FEATURE_NO_TITLE);        setContentView(R.layout.activity_clock);        tvClock = (TextView) findViewById(R.id.ClockActivity_tv_clock);        handler = new Handler();        new InnerThread().start();    }    //自定义类继承Runnable,这个类的主要做用是处理消息,下面的    //Message.obtion(handler,Runnable)中的Runnable就是该InnerRunner    public class InnerRunner implements Runnable {        @Override        public void run() {            tvClock.setText(new Date().toString());        }    }    public class InnerThread extends Thread {        @Override        public void run() {            while (true) {                //Message类有公有的无参数的构造方法,开发者可以随                              意创建Message对象,但是,                // Message对象大多是被处理后就没有存在的意义了,所以反复创建Message对象会无谓的消耗内存                //推荐使用Message.obtain() 方法获取消息的对象,Message类自身使用了“Message池”对消息对象进行管理,                //从而可以控制产生的Message对象的数量.如果使用的Message.obtain() 方法中,                // 使用到了Handler做为参数,则需要使用Message对象的sendToTarget() 发送消息,                //而不再使用Handler的sendMessage() 系列方法                //Message的what属性用于标识消息的类型(因为子线程可能向主线程发送若干种不同的消息),                // 不应该用于封装有具体数值含义的数据,通常,what属性的值应该使用静态常量表示,                // 例如msg.what = MESSAGE_UPDATE_CLOCK;                //通过Handler对象的obtainMessage() 系列方法也可以获取Message对象,                // 其实现效果与Message类的obtain() 方法是等效的                Message message = Message.obtain(handler, new InnerRunner());                message.what = UPDATA_MESSAGE_TIME;                message.sendToTarget();                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }}

使用CallBack处理消息

我自己又新建了一个Activity名叫ClockActivityTestTwo,它的布局文件的代码和activity_main代码相同,让我们来看一下ClockActivityTestTwo的代码吧:
对了,要记着在AndroidManifest.xml文件中将ClockActivityTestTwo设置为启动界面。

public class ClockActivityTwo extends AppCompatActivity {    private TextView tvClock;    private Handler handler;    private String dataString;    private Date date =new Date();    private SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        requestWindowFeature(Window.FEATURE_NO_TITLE);        setContentView(R.layout.activity_clock_activity_two);        tvClock = (TextView) findViewById(R.id.ClockActivityTwo_tv_clock);        //使用Handler.Callback接口的实现类对象,做为Handler构造方法的参数,        // 也可以实现对消息的处理【推荐】        handler =new InnerHandler( new InnerCallBack());        new InnerThread().start();    }    //Handler.Callback定义了public boolean handleMessage(Message msg)方法,    // 其中,返回值(boolean类型)表示“是否已经完全处理了消息”,    // 当返回值为false时,如果存在其它的消息处理方法,则对应的代码也会被执行,    // 当返回值为true时,表示“已经完全处理了消息”,则其它的处理消息的代码不会运行    class InnerCallBack implements Handler.Callback {        @Override        public boolean handleMessage(Message msg) {            tvClock.setText(dataString);            Log.i("ClockActivityTwo", "HandlerCallBack");            return true;        }    }    class InnerHandler extends Handler {        public InnerHandler(Callback callback) {            super(callback);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            tvClock.setText(dataString);            Log.i("ClockActivityTwo", "HandlerMessage");        }    }    class InnerThread extends Thread {        @Override        public void run() {            for (int i = 0; i < 1200; i++) {                Message message =Message.obtain();                date.setTime(System.currentTimeMillis());                dataString =sdf.format(date);                handler.sendMessage(message);                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }}

这里我分别使用了callBack和handlerMessage()方法处理消息,当我在public boolean handleMessage(Message msg) 这是返回值为true时,那么handlerMessage就不会处理该消息了。可以看一下log日志:

06-05 23:19:04.489 24761-24761/com.example.clockthreadtest I/ClockActivityTwo: HandlerCallBack06-05 23:19:05.489 24761-24761/com.example.clockthreadtest I/ClockActivityTwo: HandlerCallBack06-05 23:19:06.489 24761-24761/com.example.clockthreadtest I/ClockActivityTwo: HandlerCallBack

当我将callBack中的public boolean handleMessage(Message msg) 这是返回值为false时,handlerMessage也会执行。让我们来看一下log日志:

06-05 23:19:55.489 25109-25109/com.example.clockthreadtest I/ClockActivityTwo: HandlerCallBack06-05 23:19:55.489 25109-25109/com.example.clockthreadtest I/ClockActivityTwo: HandlerMessage06-05 23:19:56.489 25109-25109/com.example.clockthreadtest I/ClockActivityTwo: HandlerCallBack06-05 23:19:56.489 25109-25109/com.example.clockthreadtest I/ClockActivityTwo: HandlerMessage

本篇博客我给大家列举了三种不同的Handler的处理方法,至于你要使用哪一种方式,那就要看你自己的习惯了。下面我来总结一下这三种方式:

关于处理消息的方式

1) 使用Message.obtain(Handler h, Runnable callback)中的Runnable对象处理消息
2) 使用Handler.Callback处理消息
3) 重写Handler类的handleMessage()处理消息
以上3种方式可以并存,优先级为1 -> 2 -> 3
当存在第1种方式时,消息将直接被处理,并且不会向后传递
当第2种方式存在时,其public boolean handleMessage(Message msg)方法的返回值决定消息是否向后传递

0 0
原创粉丝点击