zoukankan      html  css  js  c++  java
  • 数据同步异步加载handler Looper

    MainActivity

    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Looper;
    import android.os.Message;
    import android.util.Log;
    import android.view.View;

    /**
    * 当点击按钮的时候,由子线程向主线程传递数据
    *
    * @author Administrator
    *
    */
    public class MainActivity extends Activity {
    private Handler handler = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    }

    // 点击按钮,创建子线程
    public void click_start(View v) {
    new MyThread().start();
    }

    // 点击该按钮,数据从主线程传递到子线程中
    public void click_send(View v) {
    // 由handler发送数据
    Message msg = Message.obtain();//
    msg.what = 1;
    msg.arg1 = 100;
    msg.obj = "我是从主线程中传递来的数据";
    handler.sendMessage(msg);// msg:target:handler
    }

    // 自定义类,继承Thread,表示用于子线程
    class MyThread extends Thread {
    @Override
    public void run() {
    // 子线程中要执行的代码:
    // 用于接收主线程传来的数据
    // 由handler接收处理数据就可以。
    /**
    * 报错信息: java.lang.RuntimeException: Can't create handler inside
    * thread that has not called Looper.prepare()
    */
    // 应该先执行:Looper.prepare();
    /**
    * 表示将当前的线程升级为:Looper线程
    *
    * 1.创建一个Looper对象。注意:一个线程中只能有一个Looper对象。用ThreadLocal<T>
    *
    * 2.一个Looper对象,负责维护一个消息队列:new MessageQueue
    */
    Looper.prepare();// 将子线程升级,成Looper线程。就可以操作handler对象。否则一个普通的子线程,不能操作handler。
    handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
    switch (msg.what) {
    case 1:
    int num = msg.arg1;
    String str = (String) msg.obj;
    Log.i("tag", "===子线程:" + Thread.currentThread().getId()
    + ",线程名字:" + Thread.currentThread().getName()
    + ",数据:" + num + ",str:" + str);
    break;

    default:
    break;
    }
    }
    };
    Looper.loop();// 用于循环处理消息。
    }
    }

    }

    源码分析:

    class MyThread extends Thread {
    @Override
    public void run() {
    // 子线程中要执行的代码:
    // 用于接收主线程传来的数据
    // 由handler接收处理数据就可以。
    /**
    * 报错信息: java.lang.RuntimeException: Can't create handler inside
    * thread that has not called Looper.prepare()
    */
    // 应该先执行:Looper.prepare();
    /**
    * 表示将当前的线程升级为:Looper线程
    *
    * 1.创建一个Looper对象。注意:一个线程中只能有一个Looper对象。用ThreadLocal<T>
    *
    * 2.一个Looper对象,负责维护一个消息队列:new MessageQueue
    */
    Looper.prepare();// 将子线程升级,成Looper线程。就可以操作handler对象。否则一个普通的子线程,不能操作handler。
    handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
    switch (msg.what) {
    case 1:
    int num = msg.arg1;
    String str = (String) msg.obj;
    Log.i("tag", "===子线程:" + Thread.currentThread().getId()
    + ",线程名字:" + Thread.currentThread().getName()
    + ",数据:" + num + ",str:" + str);
    break;

    default:
    break;
    }
    }
    };
    Looper.loop();// 用于循环处理消息。
    }
    }



    要想通过Hanlder对象来异步处理消息,必须要让handler所在的线程变为Looper线程。


    一。Looper:循环器
    public class Looper {
    //ThreadLocal线程本地变量,用于为该线程中只有一个Looper对象。
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    //Looper内部维护的消息队列
    final MessageQueue mQueue;
    //当前线程对象本身
    final Thread mThread;


    ...
    //用于初始化创建Looper
    public static void prepare() {
    prepare(true);
    }
    //创建Looper
    private static void prepare(boolean quitAllowed) {
    //从sThreadLocal线程本地变量中获取Looper。如果不为空,意味着已经有Looper对象了。
    if (sThreadLocal.get() != null) {
    //抛出异常
    throw new RuntimeException("Only one Looper may be created per thread");
    }
    //创建一个Looper对象,并且存入sThreadLocal线程本地变量中获取Looper
    sThreadLocal.set(new Looper(quitAllowed));
    }

    ....
    //Looper的构造方法
    private Looper(boolean quitAllowed) {
    //同时创建该Looper对象所维护的消息队列
    mQueue = new MessageQueue(quitAllowed);
    mRun = true;
    mThread = Thread.currentThread();
    }


    ...
    //循环工作
    public static void loop() {
    //获取当前的looper对象
    final Looper me = myLooper();
    //获取消息队列
    final MessageQueue queue = me.mQueue;
    //循环处理消息
    for (;;) {
    Message msg = queue.next(); // 从消息队列中获取消息

    msg.target.dispatchMessage(msg);//将msg交给对应的handler去分发。msg.target:就是对应的handler
    //handler.dispatchMessage(msg);

    msg.recycle();//回收消息到消息池,便于下次使用。
    }

    }
    //从线程的本地变量中获取Looper对象
    public static Looper myLooper() {
    return sThreadLocal.get();
    }
    }




    总结:Looper的注意事项
    1.每一个线程只能有最多一个Looper对象。
    2.当创建Looper的同时,MessageQueue一同被创建。
    3.调用loop()方法,循环从消息队列上获取消息,分发给对应的handler。




    -------------------------------------------------------
    二。Handler:异步处理者,用于发送和处理消息的。

    public class Handler {
    //与当前的handler对象关联的消息队列
    final MessageQueue mQueue;
    //与handler关联的Looper对象

    final Looper mLooper;

    final Callback mCallback;

    //创建Handler对象
    public Handler() {
    this(null, false);
    }

    public Handler(Callback callback, boolean async) {
    mLooper = Looper.myLooper();
    if (mLooper == null) {
    throw new RuntimeException(
    "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;//将Looper维护的消息队列,赋值给当前handler所关联的消息队列。
    mCallback = callback;
    }

    ..
    //由handler发送消息到关联的消息队列上。
    public final boolean sendMessage(Message msg)
    {
    return sendMessageDelayed(msg, 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) {
    //为当前的message贴标签:当前的handler对象
    msg.target = this;
    if (mAsynchronous) {
    msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
    }


    ...
    //分发消息:在Looper.loop()中调用。
    public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
    handleCallback(msg);
    } else {
    if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
    return;
    }
    }
    handleMessage(msg);
    }

    }
    //由接收数据的线程, 重写的方法,表示处理msg
    public void handleMessage(Message msg) {
    }

    handler.post();
    public final boolean post(Runnable r)
    {
    return sendMessageDelayed(getPostMessage(r), 0);
    }
    private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;//重写的new Runnalbe(){public void run(){}}
    return m;
    }

    private static void handleCallback(Message message) {
    message.callback.run();//执行自己重写new Runnalbe(){public void run(){}}
    }
    }


    Handler的属性和方法:
    属性:
    final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;


    方法:5个重要方法
    sendMessage(msg);
    handleMessage();
    post();
    sendEmptyMessage();
    obtainMessage();


    dispatchMessage();
    sendMessageDelayed();
    sendMessageAtTime();


    Handler的总结:
    注意事项:
    1.当创建Handler对象的时候,(应该先有Looper)。那么handler对象会和Looper相关联,以及和消息队列相关联。
    2.handler发送消息到消息队列,其实就是发送到关联的消息队列中。该消息队列是由对应的Looper维护。


    三。Message

    public final class Message implements Parcelable {
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    }
    what
    arg1
    arg2
    obj
    方法:
    obtain();
    setTarget(Handler target)
    setData()
    sendToTarget();

    总结:
    1.每一个线程最多只能有一个Looper
    2.一个looper负责维护一个MessageQueue
    3.Handler必须在Looper所在的线程创建。

  • 相关阅读:
    [原创]RTX使用printf输出后进入hardfault中断的处理方法
    [原创]单片机 HexToStr and HexToBcd BcdToStr
    [原创]单片机-HexToStr or HexToAsc
    再看 AspriseOCR
    [源创] STM32F103ZET6 基于XMODEM 通讯的 BOOTLOADER案列IAP
    单片机串口——如何判定接收一帧数据的完成
    [原创] 关于步科eview人机界面HMI的使用
    [原创] STM32 定时器TIMx 编码器应用 函数 TIM_EncoderInterfaceConfig 分析
    单片机的 HexToStr HexToBcd BcdToStr 几个转换函数
    [转载] 全局键盘钩子(WH_KEYBOARD)
  • 原文地址:https://www.cnblogs.com/achen0502/p/5098141.html
Copyright © 2011-2022 走看看