关闭

Handler 使用学习总结

标签: androidandroid开发线程
267人阅读 评论(0) 收藏 举报
分类:

总结handler的使用,为小挑做知识储备。

为什么要使用handler

Handler的实现离不开Looper MessageQueue 三者紧密的联系在一个,Looper 相当于一个读取Message然后执行的角色,Handle者是一个发送小心的角色,他们内部维持着一个消息的队列。
在android开发中说有的UI更新都发生在main 线程中,当子线程更新消息的时候会发生异常。

ERROR/AndroidRuntime(421): FATAL EXCEPTION: Thread-8  
ERROR/AndroidRuntime(421): android.view.ViewRoot$CalledFromWrongThreadException:   
Only the original thread that created a view hierarchy can touch its views.  

因此我们在开发的使用的只能使用UI线程来更新UI,在android开发过程中有2种简单的方法可以达到这个目的,第一个就是 Handler
第二个就是AsyncTask
AsyncTask内部实现的机制还是handler。
在Activity的生命周期中就实现了一个UI 线程,在线程中有Looper 和MessageQueue ,因此我们不用去定义这两个了。
下面是一个Android 带有Handle looper原型的Thread

 //包含Looper的线程
    class LooperThread extends Thread{
        public Handler mhandler;

        @Override
        public void run() {
            //准备Looper
            Looper.prepare();
            mhandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                }
            };
            //消息循环
            Looper.loop();
        }
    }

Handler类

Handler类用来发送和处理消息(Message)以及和线程的消息队列(MessageQueue)关联的Runnable对象。
 每一个Handler对象都仅和一个线程及这个线程的消息队列关联。
  一个特定线程的所有Handler对象都会收到同样的方法。(这是一个“一对多”的关系)。
Handler主要有两种用途:
  1.合理调度安排消息和runnable对象,使它们在将来的某个点被执行。
  2.将一个动作入队安排在非当前线程执行。
  在handler中常用的函数:
  调度消息是通过一系列的post方法和sendMessage方法。
  post方法允许你向消息队列中入队一些Runnable对象,在它们被接收到的时候会被调用,(实际上post方法也就是将runnable对象包装在消息里,然后再通过sendMessage方法实现),post方法有:
  post(Runnable r)
  postAtFrontOfQueue(Runnable r)
  postAtTime(Runnable r, Object token, long uptimeMillis)
  postAtTime(Runnable r, long uptimeMillis)
  postDelayed(Runnable r, long delayMillis)
  sendMessage方法允许你入队一个消息对象(Message),包含一个bundle数据,之后将会被Handler的handleMessage(Message)方法所处理。
 
  sendEmptyMessage(int what)
  发送一个空的消息体的what到handler中
  sendEmptyMessageAtTime(int what, long uptimeMillis)
  sendEmptyMessageDelayed(int what, long delayMillis)
  sendMessage(Message msg)
  发送一个消息到message队列中
  sendMessageAtFrontOfQueue(Message msg)
  发送一个消息在消息队列的前面
  sendMessageAtTime(Message msg, long uptimeMillis)
  在确定的时间发送这个消息,这个时间通过这个参数指定这个时间由SystemClock.uptimeMillis()传递,uptimeMillis是相对系统开机时间的绝对时间
  sendMessageDelayed(Message msg, long delayMillis)
  延时发送一个消息到Message队列中
一个线程对应一个Looper,有一个消息队列,但是可以关联多个Handlers。
简单使用


public class MainActivity extends AppCompatActivity {


    TextView textView = null;
    ImageView imageView = null;
    Button btn = null;


    int index  = 0;
    private  Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    imageView.setImageResource(R.mipmap.ic_launcher);
                    textView.setText("i am fun" + index);
                    index++;
                    break;
            }

        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.imageView);
        textView = (TextView) findViewById(R.id.textView);
        btn = (Button) findViewById(R.id.button);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message msg = new Message();
                        msg.what = 1;
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                imageView.setImageResource(R.mipmap.ic_launcher);
                                textView.setText("i am fun" + index);
                            }
                        });
                        //handler.sendMessageAtTime(msg, SystemClock.uptimeMillis()+10000);
                        //handler.sendEmptyMessageAtTime(1, 1000);
                    }
                }).start();
            }
        });

    }
}

UI线程和非UI线程的通信

 当你的应用进程被创建的时候,应用进程的主线程(main thread)就建立一个消息队列,操纵top级别的应用对象(比如activities、broadcast receivers等)和它们创建的任何窗口。
  因为效率的考虑,所有的View和Widget都不是线程安全的,所以相关操作强制放在同一个线程,这样就可以避免多线程带来的问题。这个线程就是主线程,也即UI线程。
 你可以创建自己的线程,通过一个Handler对象和应用的主线程通信。

  如果你将一个Handler和你的UI线程连接,处理消息的代码就将会在UI线程中执行。

  新线程和UI线程的通信是通过从你的新线程调用和主线程相关的Handler对象的post或者sendMessage方法实现的,给定的Runnable或Message将会在Handler的消息队列中,并且在合适的时间被处理。

  总的来说,共有5种方式从非UI线程和UI线程通信:

Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Handle
AsyncTask
参考:Android UI线程和非UI线程

消息循环

消息处理机制中,消息存放在一个消息队列中,而线程围绕这个队列进入一个无限循环,直到程序退出。

  如果队列中有消息,线程就会把消息取出来,并分发给相应的Handler进行处理;

  如果队列中没有消息,线程就会进入空闲等待状态,等待下一个消息的到来。

Android的主线程循环创建

在Activity的源码中有一个变量

    private ComponentName mComponent;
    /*package*/ ActivityInfo mActivityInfo;
    /*package*/ ActivityThread mMainThread;
    Activity mParent;
    boolean mCalled;

在ActivityThread 中又能找到

static Handler sMainThreadHandler;  // set once in main()
 final ApplicationThread mAppThread = new ApplicationThread();
    final Looper mLooper = Looper.myLooper();
public static void main(String[] args) {
        // other codes...

        // 创建主线程循环
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        // 进入当前线程(此时是主线程)消息循环
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

在这个ActivityThread中维持着handle looper MessageQueue
在Main中有一个 Looper.prepareMainLooper();

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

 上面这个方法是专门为创建应用程序的主线程调用的,其他线程都不应该调用这个方法,而应该调用prepare()方法。

主线程的Looper对象创建好之后会存在Looper类的成员变量mMainLooper里,通过一个get方法可以获取到:


 public synchronized static final Looper getMainLooper() {
        return mMainLooper;
    }

这样之后,程序中其他线程就可以获取主线程的消息循环对象,从而和主线程通信。

线程创建消息循环:Looper.prepare()

  非主线程创建消息循环时,调用的是Looper类的prepare()方法,其实创建主线程的方法实质也调用了prepare方法:
  这个方法会调用Looper类的私有构造方法,创建Looper类对象。


   /** Initialize the current thread as a looper.
      * This gives you a chance to create handlers that then reference
      * this looper, before actually starting the loop. Be sure to call
      * {@link #loop()} after calling this method, and end it by calling
      * {@link #quit()}.
      */
    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));
    }
    private Looper() {
        // 私有构造方法,在prepare()方法里面调用
        // 创建消息队列
        mQueue = new MessageQueue();
        mRun = true;
        // 当前线程
        mThread = Thread.currentThread();
    }

进入消息循环:Looper.loop()

  不管是不是主线程,prepare之后需要调用Looper类的loop()方法,可以看作是进入消息循环:

  /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    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();
        }
    }

消息分发和处理——Handler

  前面创建了消息循环,并且进入了这个循环,但是消息队列中的消息是如何加入和处理的呢?是通过Handler。

Handler构造:

  Handler有几个构造重载,如果构造时不提供Looper类对象参数,会获取当前线程的Looper对象,即将当前线程的消息循环作为Handler关联的消息循环。

  前面说过,不是所有线程都有一个消息循环,所以如果当前线程没有消息循环,而构造Handler对象时又没有指定Looper对象,则会抛出一个运行时异常:

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

  如果没有抛出异常,Handler对象构造好之后,它就关联了相应的Looper实例和消息队列实例,即完成绑定。

消息发送:

  Handler对象的post方法和sendMessage方法本质上都是发送消息的方法(post类方法实质上是调用了sendMessage方法)。

  所谓发送消息就是把消息放入消息队列中的合适位置,并且把消息的target设置为本Handler对象。

  (这里将消息加入队列,也有一些什么线程唤醒的事儿咱们不深入讨论了)。

  可以添加,也就相应地有一些移除方法。

消息处理:

  在上面的Looper.loop()方法中,调用了消息对象target(即发送这个消息的Handler对象)的dispatchMessage()方法。

/**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {

        // 首先,处理Message自己的callback,调用其run方法
        if (msg.callback != null) {
            handleCallback(msg);
        }
        else {
            // 其次,调用Handler自留的接口对象
            // 这个成员变量声明时的注释如下:
            /**
             * Callback interface you can use when instantiating a Handler to
             * avoid having to implement your own subclass of Handler.
             */
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }

            // 最后,调用handleMessage方法处理消息,Handler类中这个方法为空,子类可以重写这个方法
            handleMessage(msg);
        }
    }

 Handler类的handleMessage()方法默认实现为空:
 

  /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }

  上面的代码中也解释了为什么一个消息队列可以关联很多个Handler对象,因为虽然队列只有一个,但是消息的target是当时把它加入的Handler对象。
  所以当队列中的消息处理的时候,也会找到当时送它来的Handler对象,调用其相应的dispatchMessage()方法,进而调用其中的handleMessage()方法或者mCallback成员的handleMessage()方法来进行处理。

参考资料
  Handler:http://developer.android.com/reference/android/os/Handler.html
  Looper:http://developer.android.com/reference/android/os/Looper.html
  
比较好的几个博文:
  Android应用程序线程消息循环模型分析
  Android应用程序消息处理机制(Looper、Handler)分析
  Android的消息队列模型
  [Android中的Handler, Looper, MessageQueue和Thread](http://www.cnblogs.com/xirihanlin/archive/2011/04/11/2012746.html
)
 Android中的UI线程与非UI线程
 Android的消息循环机制 Looper Handler类分析

0
0

猜你在找
【直播】计算机视觉原理及实战——屈教授
【套餐】深度学习入门视频课程——唐宇迪
【套餐】Hadoop生态系统零基础入门
【套餐】嵌入式Linux C编程基础
【套餐】2017软考系统集成项目——任铄
【套餐】Android 5.x顶级视频课程——李宁
【直播】广义线性模型及其应用——李科
【直播】从0到1 区块链的概念到实践
【直播】机器学习之凸优化——马博士
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:96868次
    • 积分:1864
    • 等级:
    • 排名:千里之外
    • 原创:79篇
    • 转载:77篇
    • 译文:0篇
    • 评论:12条
    博客专栏
    最新评论