Android Handler详解

Android面试常客Handler详解

链接: 视频课程.
有关handler面试的回答可借鉴:
Handler(面试详解).
Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

  主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在looper类的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒(nativeWake())主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
参考:Android 消息机制——你真的了解Handler?.

1. 什么是Handler?

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

2. Handler的用法

为什么要用Handler

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

Handler怎么用呢?

Handler使用机制及四个组成部分.

如果在子线程中更新UI会报如下错误:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

范例如下:

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(1000);
                    textView.setText("update thread");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

下面阐述handler的几种常用方法

(1) post(Runnable)的使用
 public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Handler handler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textview);
        Log.i("abc", "main id: " + String.valueOf(Thread.currentThread().getId()));
        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText("update thread");
                            Log.i("abc", "id: " + String.valueOf(Thread.currentThread().getId()));  // 查看当前运行的线程id,此id与主线程的id相同,表示运行在主线程中
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}
(2) postDelayed(Runnable, long)的使用
import android.media.Image;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private ImageView imageView;
    private Handler handler = new Handler();

    private int images[] = {R.mipmap.image1, R.mipmap.image2, R.mipmap.image3};
    private int index;
    private MyRunnable myRunnable = new MyRunnable();

    class MyRunnable implements Runnable{

        @Override
        public void run() {
            index++;
            index = index % 3;
            imageView.setImageResource(images[index]);
            handler.postDelayed(myRunnable, 1000);
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textview);
        imageView = (ImageView) findViewById(R.id.imageView);
        
        Log.i("abc", "main id: " + String.valueOf(Thread.currentThread().getId()));
        handler.postDelayed(myRunnable, 1000);
    }
}
(3) sendMessage的使用
 public class MainActivity extends AppCompatActivity {
    private TextView textView;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            Log.i("abc", "handlerMessage msg.arg1=" + msg.arg1);
            textView.setText("" + msg.arg1 + "-" + msg.arg2 + ", " + msg.obj);
        }
    };

    class Person {
        public int age;
        public String name;

        @Override
        public String toString() {
            return "name:" + name + ", age:" + age;
        }
    }
    @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);
                    Message msg = new Message();
                    msg.arg1 = 80;
                    msg.arg2 = 100;
                    Person person = new Person();
			        person.age = 20;
			        person.name = "james";
			        msg.obj = person;
                    handler.sendMessage(msg);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
        */
        
	    // 第二种使用
        //Message msg = new Message();  // 创建一个message对象
        Message msg = handler.obtainMessage();  // 获取一个message对象
        msg.arg1 = 80;
        msg.arg2 = 100;
        Person person = new Person();
        person.age = 20;
        person.name = "james";
        msg.obj = person;
        // 发送message的两种方式
        //handler.sendMessage(msg);  //方式一
        msg.sendToTarget(); //方式二
    }
}
(4) removecallback的使用
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private TextView textView;
    private ImageView imageView;
    private Button button;
    private Handler handler = new Handler();

    private int images[] = {R.mipmap.image1, R.mipmap.image2, R.mipmap.image3};
    private int index;
    private MyRunnable myRunnable = new MyRunnable();

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.button:
                handler.removeCallbacks(myRunnable);
        }
    }

    class MyRunnable implements Runnable{

        @Override
        public void run() {
            index++;
            index = index % 3;
            imageView.setImageResource(images[index]);
            handler.postDelayed(myRunnable, 1000);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textview);
        imageView = (ImageView) findViewById(R.id.imageView);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
        handler.postDelayed(myRunnable, 1000);
    }
}
(5) Callback的使用

  使用Handler.Callback接口中的handleMessage可以拦截handler发送的数据,如果handlerMessage方法中返回的值为true,则会拦截handler发送的数据,即不会执行Handler接口的handleMessage方法

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private Button button;

    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Toast.makeText(getApplicationContext(), "Callback.handleMessage",  Toast.LENGTH_SHORT).show();
            return false; //值为true表示拦截handler发送的消息,也就是无法执行到下面的handler的handleMessage方法
        }
    }){
        @Override
        public void handleMessage(Message msg) {
            Toast.makeText(getApplicationContext(), "Handler.handleMessage",  Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.button:
                handler.sendEmptyMessage(1);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
     }
}

3. Android为什么要设计只能通过Handler机制更新UI呢?

最根本的目的就是解决多线程并发问题。
  假如如果在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样子的问题呢?
更新界面错乱
  如果对更新UI的操作都进行加锁处理的话又会产生什么样子的问题?
性能下降

  处于对以上目的问题的考虑,android给我们提供了一套更新UI的机制,我们只需遵循这样的机制就可以了,根本不用去关心多线程问题,所以更新UI的操作,都是在主线程的消息队列中去轮询处理的。

4. Handler的原理是什么?

Handler的原理:
  当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件, 进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。 如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 “强制关闭”。 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的, 也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。 这个时候,Handler就出现了,来解决这个复杂的问题 ,由于Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传递)Message对象(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。

Handler分为五部分组成:

1.Message:message就是用来线程直接的交互数据,可以携带少量数据,它有四个常用的字段 1.what 2.age1 3.age2, 4.obj
what,age1,age2可以携带整行数据,obj呢就可以是任意类型。

2.Handler:handler呢就是用来发送消息和处理消息的,发送消息我们一般使用的都是Send和Post方法这个系列的方法,而这个Post一系列方法是通过Send一系列方法来实现的,而send的这些方法最后都是通过SendMessageAtTime()方法来实现的,Handler发送一条消息,就在消息队列中插一条消息。

3.Message Queue:消息队列的意思,它主要用于存放所有通过Handler发送的消息,这部分消息会一直存储在消息队列中,等待被处理,每个线程里面只能有一个Message Queue对象。
  MessageQueue它的底层是通过单链表的数据结构来维护消息列表的,它有两个方法 enqueueMessage()和next()
  enqueueMessage()主要根据时间的顺序向单链表中插入一条消息,next方法是一个无限循环的方法,如果有消息返回这条消息,并从链表中移除,如果没有消息的话,就一直阻塞在这里。

messageQueue消息处理机制:
① 如果消息队列不为空,当前时间还没到,会执行nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞等待,等待nextPollTimeoutMillis时间,时间到了会自己唤醒。
② 如果消息队列为空,当messageQueue执行enqueueMessage()流程的时候,会执行nativeWake(mPtr)进行唤醒。

4.Looper:每个线程通过Handler发送的消息都保存在MessageQueue中,Looper通过Loop() 的方法就去进入一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出来,并传递到Handler的handlermessage()方法中,每个线程只能有一个Looper对象。
  Looper在这个循环中不断的从messageQueue的next方法中获取消息,而next方法是一个阻塞操作,没有消息的时候一直处于阻塞状态,当有消息了通过dispatchMessage()发送消息给Handler对象

5.Threadlocal:messageQueue对象和Looper对象在每个线程中都只会有一个对象,怎么来保证它有一个对象,就通过Threadlocal来保存,Threadlocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后只能在指定线程中可以获取到存储的数据,对于其他的线程来说则无法获取到数据。ThreadLocal本身是不存储数据,是它内部类的ThreadLocalMap进行存储。每个线程里面都会对应一个ThreadLocalMap,ThreadLocalMap是weakReference弱引用键值对的形式进行存储,key为ThreadLocal,value为Looper。
如何保证唯一性呢?
Looper.java

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  // 表示sThreadLocal 是唯一的

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {  //sThreadLocal.get()获得的值就是Looper对象
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

  ThreadLocalMap是Map的存储结构,每次存的时候都会根据key(ThreadLocal)进行判断,当前的ThreadLocal是否有对应的Looper,这样就可以表示一个ThreadLocal对应唯一的一个Looper对象。
ThreadLocal.java

 public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

在主线程使用Handler的过程:
  首先在主线程创建一个Handler对象 ,并重写handleMessage()方法。然后当在子线程中需要进行更新UI的操作,我们就创建一个Message对象,并通过handler发送这条消息出去。之后这条消息被加入到MessageQueue队列中等待被处理,通过Looper对象会一直尝试从Message Queue中取出待处理的消息,最后分发会Handler的handler Message()方法中。

(1) Handler与Looper、MessageQueue的关系

一、Handler封装了消息的发送(主要包括消费发给给谁,发送会有个地址,这个地址就是msg.target也就是对应的handler)
Handler发送消息分为:同步(普通)、异步、Idle
  当主线程没有任务要做,这个就是主线程的空挡,于是,我们就可以利用这个空挡去做耗时任务,比如Idle消息。这个时候我们就可以利用这一点,来把耗时任务放到这个IdleHandler里面进行执行,从而提升了运行的速度。
二、Looper就是消息封装的载体,
1.内部包含了一个消息队列也就是MessageQueue, 所有的Handler发送的消息都走向这个消息队列。
2. Looper.Loop()方法,这是个死循环,不断的从MessageQueue取消息,如果有消息就处理消息,没有消息就阻塞。

三、MessageQueue 就是一个消息队列,可以添加消息,并处理消息 。
四、Handler很简单,内部会跟Looper进行关联,也就是说在Handler的内部可以找到Looper,找到Looper也就找到了MessageQueue,在Handler中发送消息,其实就是向MessageQueue队列中发送消息

总结:Handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己,MessageQueue就是一个存储消息的容器。
threadlocal在一个线程当中用于保存一些变量信息。

(2) Handler原理-图解原理与关系

在这里插入图片描述

5. Handler与子线程

(1)自定义与线程相关的Handler

public class SecondActivity extends Activity {
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            System.out.println("main Thread:" + Thread.currentThread());
        }
    };
    class MyThread extends Thread{

        public Handler handler;
        @Override
        public void run() {
            Looper.prepare();
            handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    System.out.println("MyThread:" + Thread.currentThread());
                }
            };
            Looper.loop();
        }
    }

    private MyThread myThread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView textView = new TextView(this);
        textView.setText("Hello world!!!");
        setContentView(textView);
        myThread = new MyThread();
        myThread.start();
        try {
            Thread.sleep(500);  //睡眠0.5秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread.handler.sendEmptyMessage(1);
        handler.sendEmptyMessage(1);
    }
}

  这个sleep语句必须要加,不然编译会报如下错误,这是因为 myThread.handler对象还没创建完成(其实是handler对应的Looper没有创建完成导致的),从而导致出现空指针现象。

05-03 08:54:15.610 4180-4180/com.android.performance.lsn.handlerdemo E/AndroidRuntime: FATAL EXCEPTION: main
                                                                                       Process: com.android.performance.lsn.handlerdemo, PID: 4180
                                                                                       java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.performance.lsn.handlerdemo/com.android.performance.lsn.handlerdemo.SecondActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.os.Handler.sendEmptyMessage(int)' on a null object reference
                                                                                           at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
                                                                                           at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
                                                                                           at android.app.ActivityThread.-wrap11(Unknown Source:0)
                                                                                           at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
                                                                                           at android.os.Handler.dispatchMessage(Handler.java:106)
                                                                                           at android.os.Looper.loop(Looper.java:164)
                                                                                           at android.app.ActivityThread.main(ActivityThread.java:6494)
                                                                                           at java.lang.reflect.Method.invoke(Native Method)
                                                                                           at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
                                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
                                                                                        Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.os.Handler.sendEmptyMessage(int)' on a null object reference
                                                                                           at com.android.performance.lsn.handlerdemo.SecondActivity.onCreate(SecondActivity.java:49)
                                                                                           at android.app.Activity.performCreate(Activity.java:6999)
                                                                                           at android.app.Activity.performCreate(Activity.java:6990)
                                                                                           at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
                                                                                           at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
                                                                                           at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856) 
                                                                                           at android.app.ActivityThread.-wrap11(Unknown Source:0) 
                                                                                           at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589) 
                                                                                           at android.os.Handler.dispatchMessage(Handler.java:106) 
                                                                                           at android.os.Looper.loop(Looper.java:164) 
                                                                                           at android.app.ActivityThread.main(ActivityThread.java:6494) 
                                                                                           at java.lang.reflect.Method.invoke(Native Method) 
                                                                                           at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
                                                                                           at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) 

(2) HandlerThread是什么?

  HandlerThread是Thread的子类,就是一个线程,只是它在自己的线程里面帮忙创建娥Looper。
HandlerThread存在的意义:
1)方便使用:① 方便初始化,② 方便获取线程looper
2) 保证了线程安全

   为了解决上述空指针问题,可以通过HandlerThread来进行解决。

public class ThirdActivity extends Activity {
    private TextView text;
    private Handler handler;
    private HandlerThread handlerThread;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        text = new TextView(this);
        text.setText("Handler Thread");
        setContentView(text);
        handlerThread = new HandlerThread("handler thread");
        handlerThread.start();
        handler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                System.out.println("current thread:" + Thread.currentThread());
            }
        };
        handler.sendEmptyMessage(1);
    }
}

调用handlerThread.getLooper()会执行下面的语句。

 public Looper getLooper() {
        if (!isAlive()) {  // 判断线程是否活着
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

  如果线程是活着的,并且当前Looper为null,则会执行wait()等待操作,执行run()方法进行唤醒。

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();  //创建Looper对象
        notifyAll();  //唤醒当前等待的对象
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

  HandlerThread的作用:在HandlerThread中创建一个Looper,创建Looper对象之后跟一个默认的Handler进行关联,这些所有的Handler、handleMessage方法都是在子线程中去调用;这样的情况下,就可以用HandlerThread去模拟一个异步任务的操作,只需要在主线程当中给子线程发送消息,让子线程去处理一些比较耗时的操作,比如下载网络图片、更新数据库信息等。

6. Handler-主线程与子线程之间的信息交互

如何在主线程中给子线程发送消息呢?

public class FourActivity extends Activity implements View.OnClickListener{
    private Button button1, button2;
    // 创建主线程的handler
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Message message = new Message();
            Log.d("abc", "Main message:" + message);
            // 向子线程发送消息
            threadHandler.sendMessageDelayed(message, 1000);
        }
    };

    private Handler threadHandler;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.four);
        button1 = (Button) findViewById(R.id.button1);
        button2 = (Button) findViewById(R.id.button2);
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);

        HandlerThread thread = new HandlerThread("handler thread");
        thread.start();
        // 创建子线程的handler
        threadHandler = new Handler(thread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                Message message = new Message();
                Log.d("abc", "threadHandler message:" + message);
                // 向主线程发送消息
                handler.sendMessageDelayed(message, 1000);
            }
        };
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.button1:
                handler.sendEmptyMessage(1);
                break;
            case R.id.button2:
                handler.removeMessages(1);
                break;
            default:
                break;
        }
    }
}

7. Android中更新UI的几种方式

(1) runOnUiThread

public class FiveActivity extends Activity {
    private TextView textView;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.five);
        textView = (TextView) findViewById(R.id.textView);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                textView.setText("runOnUiThread");
            }
        });
    }
}

runOnUiThread方式其实是调用了Handler.post()方法进行实现

@Override
public final void runOnUiThread(Runnable action) {
     if (Thread.currentThread() != mUiThread) {
         mHandler.post(action);
     } else {
         action.run();
     }
 }

(2) handler post

public class FiveActivity extends Activity {
    private TextView textView;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //textView.setText("sendMessage");
        }
    };


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.five);
        textView = (TextView) findViewById(R.id.textView);

        new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(500);
                   handler.post(new Runnable() {
                       @Override
                       public void run() {
                           textView.setText("handler post");
                       }
                   });
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }).start();
    }
}

post源代码流程:

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

(3) handler sendMessage

public class FiveActivity extends Activity {
    private TextView textView;
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            textView.setText("sendMessage");
        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.five);
        textView = (TextView) findViewById(R.id.textView);
        
       	new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(500);
                   handler.sendEmptyMessage(1);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }).start();
    }
}

  sendMessage 和 sendEmptyMessage其实后面的调用流程是一样的,源代码流程:
sendMessage方法实现:

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

sendEmptyMessage方法实现:

public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

(4) view post

public class FiveActivity extends Activity {
    private TextView textView;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.five);
        textView = (TextView) findViewById(R.id.textView);
        textView.post(new Runnable() {
            @Override
            public void run() {
                textView.setText("view post");
            }
        });
    }
}

view post方式源代码调用其实也是调用了handler.post()接口实现。

public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

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

答案是否定的。如下方式就可以在子线程中进行更新UI操作。但是不推荐这样使用,有可能会出现意想不到的错误,最好是使用Handler方式进行更新UI。

public class FiveActivity extends Activity {
    private TextView textView;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
        }
    };


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.five);
        textView = (TextView) findViewById(R.id.textView);
        
        new Thread(new Runnable() {
           @Override
           public void run() {
               textView.setText("updata UI");
           }
       }).start();
    }
}

  上述代码能够更新成功,那是因为ViewRootImpl还是没创建完成,ViewRootImpl是在onResume()方法中进行创建的,ViewRootImpl创建完成之后会有一个CheckThread()方法用于比较当前线程是否为主线程。
比如通过sleep一段时间再更新UI就会报错误。

public class FiveActivity extends Activity {
    private TextView textView;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //textView.setText("sendMessage");
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.five);
        textView = (TextView) findViewById(R.id.textView);

       new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Thread.sleep(500);
                   textView.setText("updata UI");
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }).start();
    }
}

上面的代码会报如下错误:

05-03 12:03:09.559 10367-10390/com.android.performance.lsn.handlerdemo E/AndroidRuntime: FATAL EXCEPTION: Thread-2
                                                                                         Process: com.android.performance.lsn.handlerdemo, PID: 10367
                                                                                         android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
                                                                                             at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7313)
                                                                                             at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1161)
                                                                                             at android.view.View.requestLayout(View.java:21995)
                                                                                             at android.view.View.requestLayout(View.java:21995)
                                                                                             at android.view.View.requestLayout(View.java:21995)
                                                                                             at android.view.View.requestLayout(View.java:21995)
                                                                                             at android.view.View.requestLayout(View.java:21995)
                                                                                             at android.widget.TextView.checkForRelayout(TextView.java:8531)
                                                                                             at android.widget.TextView.setText(TextView.java:5394)
                                                                                             at android.widget.TextView.setText(TextView.java:5250)
                                                                                             at android.widget.TextView.setText(TextView.java:5207)
                                                                                             at com.android.performance.lsn.handlerdemo.FiveActivity$2.run(FiveActivity.java:58)
                                                                                             at java.lang.Thread.run(Thread.java:764)

9. 使用Handler时候遇到的问题

第一种:在非UI线程中更新UI操作导致的异常

 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

第二种:对创建的Handler没有指定Looper进行关联导致的异常

05-03 12:12:13.124 11020-11038/com.android.performance.lsn.handlerdemo E/AndroidRuntime: FATAL EXCEPTION: Thread-2
                                                                                         Process: com.android.performance.lsn.handlerdemo, PID: 11020
                                                                                         java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
                                                                                             at android.os.Handler.<init>(Handler.java:204)
                                                                                             at android.os.Handler.<init>(Handler.java:118)
                                                                                             at com.android.performance.lsn.handlerdemo.FiveActivity$2.run(FiveActivity.java:55)
                                                                                             at java.lang.Thread.run(Thread.java:764)

异常代码如下:

public class FiveActivity extends Activity {
    private TextView textView;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //textView.setText("sendMessage");
        }
    };


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.five);
        textView = (TextView) findViewById(R.id.textView);

       new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Handler handler = new Handler();
                   Thread.sleep(500);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }).start();
    }
}

将上面的代码修改为如下即可:

public class FiveActivity extends Activity {
    private TextView textView;

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //textView.setText("sendMessage");
        }
    };


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.five);
        textView = (TextView) findViewById(R.id.textView);
        
        new Thread(new Runnable() {
           @Override
           public void run() {
               try {
                   Looper.prepare();
                   Handler handler = new Handler();
                   Thread.sleep(500);
                   textView.setText("updata UI");
                   Looper.loop();
                   //handler.sendEmptyMessage(1);  // sendMessage方式
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }).start();
    }
}

10. Android中Handler源码部分接口介绍

1)Looper.prepare

  • 创建了Looper对象,在构造函数中创建了MessageQueue
  • 然后将Looper对象通过ThreadLocal跟当前的线程绑定

2)new Handler()

  • 通过Looper类中的ThreadLocal从线程中获取到了Looper对象
  • 然后通过Looper对象获取到了MessageQueue的引用

3)handler.sendMessage()

  • 找到MessageQueue对象
  • 然后msg.target = this, 将handler对象成为msg的一个成员变量
  • 最后把msg添加到MessageQueue中

4) Looper.loop()

  • 找到MessageQueue
  • 然后开启死循环遍历MessageQueue池
  • 当获取到msg的时候,通过msg.target找到发送这个msg的handler,然后调用handler的dispatchMessage()方法
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值