Android_Handler

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: HandlerCallBack
06-05 23:19:05.489 24761-24761/com.example.clockthreadtest I/ClockActivityTwo: HandlerCallBack
06-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: HandlerCallBack
06-05 23:19:55.489 25109-25109/com.example.clockthreadtest I/ClockActivityTwo: HandlerMessage
06-05 23:19:56.489 25109-25109/com.example.clockthreadtest I/ClockActivityTwo: HandlerCallBack
06-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)方法的返回值决定消息是否向后传递

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值