Android拾遗Android

(转)Android Handler 使用详解

2017-03-24  本文已影响8739人  空心散人

原文链接:https://segmentfault.com/a/1190000005926314


Handler:是一个消息分发对象,进行发送和处理消息,并且其 Runnable 对象与一个线程的 MessageQueue 关联。

作用:调度消息,将一个任务切换到某个指定的线程中去执行。

为什么需要 Handler?

子线程不允许访问 UI

假若子线程允许访问 UI,则在多线程并发访问情况下,会使得 UI 控件处于不可预期的状态。

传统解决办法:加锁,但会使得UI访问逻辑变的复杂,其次降低 UI 访问的效率。

引入 Handler

采用单线程模型处理 UI 操作,通过 Handler 切换到 UI 线程,解决子线程中无法访问 UI 的问题。


Handler 使用

方式一: post(Runnable)

new Thread(new Runnable() {
   @Override
   public void run() {
       /**
          耗时操作
        */
      handler.post(new Runnable() {
          @Override
          public void run() {
              /**
                更新UI
               */
          }
      });
   }
 }).start();

方式二: sendMessage(Message)


private Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {      //判断标志位
            case 1:
                /**
                 获取数据,更新UI
                */
                break;
        }
    }
};
   

public class WorkThread extends Thread {

    @Override
    public void run() {
        super.run();
       /**
         耗时操作
        */
  
        //从全局池中返回一个message实例,避免多次创建message(如new Message)
        Message msg =Message.obtain();  
        msg.obj = data;
        msg.what=1;   //标志消息的标志
        handler.sendMessage(msg);
    }
    
}
   
new WorkThread().start();


Handler 存在的问题

内存方面

Handler 被作为 Activity 引用,如果为非静态内部类,则会引用外部类对象。当 Activity finish 时,Handler可能并未执行完,从而引起 Activity 的内存泄漏。故而在所有调用 Handler 的地方,都用静态内部类。

异常方面

当 Activity finish 时,在 onDestroy 方法中释放了一些资源。此时 Handler 执行到 handlerMessage 方法,但相关资源已经被释放,从而引起空指针的异常。

避免


Handler 的改进


/**
 * 为避免handler造成的内存泄漏
 * 1、使用静态的handler,对外部类不保持对象的引用
 * 2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
 */
private static class MyHandler extends Handler {
    private final WeakReference<Activity> mActivityReference;    

    MyHandler(Activity activity) {
        this.mActivityReference = new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity = (MainActivity) mActivityReference.get();  //获取弱引用队列中的activity
        switch (msg.what) {    //获取消息,更新UI
            case 1:
                byte[] data = (byte[]) msg.obj;
                activity.threadIv.setImageBitmap(activity.getBitmap(data));
                break;
        }
    }
}

并在 onDesotry 中销毁:

@Override
protected void onDestroy() {
    super.onDestroy();
    //避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列
    handler.removeCallbacks(postRunnable);   //清除runnable对应的message
    //handler.removeMessage(what)  清除what对应的message
}


Handler 的使用实现

在 Activity 中创建 handler 对象,调用工作线程执行

public class MainActivity extends AppCompatActivity {

ImageView threadIv;
ImageView runnableIv;
SendThread sendThread;
PostRunnable postRunnable;
private final MyHandler handler = new MyHandler(this);

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    threadIv = (ImageView) findViewById(R.id.thread_iv);
    runnableIv = (ImageView) findViewById(R.id.runnable_iv);

    sendThread = new SendThread(handler);
    sendThread.start();

    postRunnable = new PostRunnable(handler);
    postRunnable.setRefreshUI(new PostRunnable.RefreshUI() {
        @Override
        public void setImage(byte[] data) {
            runnableIv.setImageBitmap(getBitmap(data));
        }
    });
    new Thread(postRunnable).start();
}

/**
  为避免handler造成的内存泄漏
  1、使用静态的handler,对外部类不保持对象的引用
  2、但Handler需要与Activity通信,所以需要增加一个对Activity的弱引用
 /
private static class MyHandler extends Handler {
    private final WeakReference<Activity> mActivityReference;

    MyHandler(Activity activity) {
        this.mActivityReference = new WeakReference<Activity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        MainActivity activity = (MainActivity) mActivityReference.get();  //获取弱引用队列中的activity
        switch (msg.what) {    //获取消息,更新UI
            case 1:
                byte[] data = (byte[]) msg.obj;
                activity.threadIv.setImageBitmap(activity.getBitmap(data));
                break;
        }
    }
}

private Bitmap getBitmap(byte[] data) {
    return BitmapFactory.decodeByteArray(data, 0, data.length);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    //避免activity销毁时,messageQueue中的消息未处理完;故此时应把对应的message给清除出队列
    handler.removeCallbacks(postRunnable);   //清除runnable对应的message
    //handler.removeMessage(what)  清除what对应的message
}
}

方式一:实现 runnable 接口,通过 post(Runnable)通信,并通过给定的回调接口通知 Activity 更新

public class PostRunnable implements Runnable {

    private Handler handler;
    private RefreshUI refreshUI;
    byte[] data = null;

    public PostRunnable(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void run() {
        /**
         * 耗时操作
         */
        final Bitmap bitmap = null;
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet("http://i3.17173cdn.com/2fhnvk/YWxqaGBf/cms3/FNsPLfbkmwgBgpl.jpg");
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpGet);
            if (httpResponse.getStatusLine().getStatusCode() == 200) {
                data = EntityUtils.toByteArray(httpResponse.getEntity());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //返回结果给UI线程
        handler.post(new Runnable() {
            @Override
            public void run() {
                refreshUI.setImage(data);
            }
        });
    }

    public interface RefreshUI {
        public void setImage(byte[] data);
    }

    public void setRefreshUI(RefreshUI refreshUI) {
        this.refreshUI = refreshUI;
    }
}

方式二:继承Thread,通过handler的sendMessage通信

public class SendThread extends Thread {

    private Handler handler;

    public SendThread(Handler handler) {
        this.handler = handler;
    }

    @Override
    public void run() {
        super.run();
        /**
         * 耗时操作
         */
        byte[]data=null;
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet("https://d36lyudx79hk0a.cloudfront.net/p0/descr/pc27/3095587d8c4560d8.png");
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpGet);
            if(httpResponse.getStatusLine().getStatusCode()==200){
                data= EntityUtils.toByteArray(httpResponse.getEntity());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //返回结果给UI线程
        doTask(data);
    }

    /**
     * 通过handler返回消息
     * @param data
     */
    private void doTask(byte[] data) {
        Message msg =Message.obtain();  //从全局池中返回一个message实例,避免多次创建message(如new Message)
        msg.obj = data;
        msg.what=1;   //标志消息的标志
        handler.sendMessage(msg);
    }
}

Handler 通信机制

imageimage

Handler 如何关联 Looper、MessageQueue

imageimage

1、Handler 发送消息

上一段很熟悉的代码:

Message msg =Message.obtain(); 
msg.obj = data;
msg.what=1;   //标志消息的标志
handler.sendMessage(msg);

从sendMessageQueue开始追踪,函数调用关系:sendMessage -> sendMessageDelayed ->sendMessageAtTime,在sendMessageAtTime中,携带者传来的message与Handler的mQueue一起通过enqueueMessage进入队列了。

对于postRunnable而言,通过post投递该runnable,调用getPostMessage,通过该runnable构造一个message,再通过 sendMessageDelayed投递,接下来和sendMessage的流程一样了。

2、消息入队列

在enqueueMessage中,通过MessageQueue入队列,并为该message的target赋值为当前的handler对象,记住msg.target很重要,之后Looper取出该消息时,还需要由msg.target.dispatchMessage回调到该handler中处理消息。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

在MessageQueue中,由Message的消息链表进行入队列

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

3、Looper 处理消息

再说处理消息之前,先看Looper是如何构建与获取的:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
public static void prepare() {
    prepare(true);
}
 
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

它是一个保存Looper的TheadLocal实例,而ThreadLocal是线程私有的数据存储类,可以来保存线程的Looper对象,这样Handler就可以通过ThreadLocal来保存于获取Looper对象了。

public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeValues(currentThread);
    }
    values.put(this, value);
}

public T get() {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values != null) {
        Object[] table = values.table;
        int index = hash & values.mask;
        if (this.reference == table[index]) {
            return (T) table[index + 1];
        }
    } else {
       values = initializeValues(currentThread);
    }

    return (T) values.getAfterMiss(this);
}

在 set 中都是通过 values.put 保存当前线程的 Looper 实例,通过 values.getAfterMiss(this)获取,其中put和getAfterMiss都有key和value,都是由Value对象的table数组保存的,那么在table数组里怎么存的呢?

table[index] = key.reference;
table[index + 1] = value;

很显然在数组中,前一个保存着ThreadLocal对象引用的索引,后一个存储传入的Looper实例。

在loop中,一个循环,通过next取出MessageQueue中的消息

若取出的消息为null,则结束循环,返回。

设置消息为空,可以通过MessageQueue的quit和quitSafely方法通知消息队列退出。

若取出的消息不为空,则通过msg.target.dispatchMessage回调到handler中去。

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
            Long.toHexString(ident) + " to 0x"
            Long.toHexString(newIdent) + " while dispatching to "
            msg.target.getClass().getName() + " "
            msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}

4、handler处理消息

Looper把消息回调到handler的dispatchMessage中进行消息处理:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

总结

以一个时序图来总结handler的消息机制,包含上述如何关联Looper和MessageQueue的过程。

imageimage
上一篇 下一篇

猜你喜欢

热点阅读