Handler浅尝<笔记>


1、Handler是什么?
Hanlder是android给我们提供的用来更新UI的一种机制,也是一套消息处理机制,我们可以发送消息,也可以通过它处理消息。 查看FramWork源码就会发现所有Activity生命周期的回调方法都是通过handler发送消息给FramWork,然后通过不同的Message做相应的分支处理(Activity通过AMS(ActivityManagerService )进行交互)。
对于一个应用程序来说核心类-ActivityThread类,就是通过Handler机制接收到AMS发送的Activity生命周期的一些管理从而回调Activity生命周期的方法


2、为什么要用Handler?
Android在设计的时候就封装了一套消息创建、传递、处理机制,不遵循无法更新UI,会抛异常。


3、Handler怎么用?
API文档原文:

A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.

自译:与一个线程的MessageQueue关联的Handler允许发送、处理Message和Runnable对象。每个Handler实例与线程和线程的MessageQueue有关联。故创建一个Handler的时候,会绑定到创建好的线程或线程的MessageQueue当中 —>从这点可以看出,Handler会发送messages和runnables到messagequeue中,并当他们从messaqueue中出来的时候进行处理。
Handler的两大用途:1. 在未来某时安排message和runnable对象的执行;2. 将要执行的action加入队列并让不同的线程执行这个action。
四个方法:
(1)sendMessage( );
(2)sendMessageDelayed( );
(3)post(Runnable);
(4)postDelayed(Runnable, long);

(3)post(Runnable)用法:


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

(4)postDelayed(Runnable, long)用法:


class MyRunnable implements Runnable {
    @Override
    public void run() {
        index++;
        index = index % 3;
        imageView.setImageResource(images[index]);
        handler.postDelayed(myRunnable,1000);
    }
}
//接下来实例化MyRunable对象,并在OnCreate方法中调用handler.postDelayed(myRunnable,1000);

(1)sendMessage()方法


private Handler handler = new Handler() {
    public void handleMessage(Message msg) {
        mTextView.setText("" + msg.arg1);
    };
};
//OnCreate方法中创建新线程
new Thread() {
    public void run() {
        Message message = new Message();
        message.arg1 = 88;
        //也可以传递一个对象:messag.obj = (一个实例化的对象);
        handler.sendMessage(message);
}.start;

另一种方式: 
--------------------------------------------
new Thread() {
    public void run() {
        //返回一个其内部的target引用了Handler的message
        Message message = handler.obtainMessage();
        message.arg1 = 88;
        //本质还是调用了Handler的sendMessage方法
        message.sendToTarget();
}.start;

还可以用一个Callback接口的实例初始化Handler对象,并覆盖CallBack中的handleMessage()方法,在返回true的情况下可截获发送给Handler对象的handleMessage()的消息。

handler.removeCallbacks(Runnable runnable);//将runnable从handler中移除


4、Android为什么要设计只能通过Handler机制更新UI呢?
最根本的目的就是解决多线程并发问题
假设如果在一个Activity当中,有多个线程去更新ui,并且都没有加锁机制,会出现什么问题呢?–> 更新界面错乱

如果对UI进行加锁又会出现什么问题呢?–> 性能下降

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

5、Handler原理是什么?Looper MessageQueue
一、Handler封装了消息的发送(主要包括消息发送给了谁<默认发送给自己>)
Looper
1)、内部包含一个消息队列也就是MessageQueue,所有的Handler发送的消息都走向这个消息队列
2)、Looper.Loop方法,就是一个死循环,不断的从MessageQueue取消息,如果有消息就处理消息,没有消息就阻塞
二、MessageQueue,就是一个消息队列,可以添加消息,并处理消息
三、Handler也很简单,内部会跟Looper进行关联,也就是说在Handler内部会找到Looper,找到了Looper也就找到了MessageQueue,在Handler中发送消息,其实就是向Message队列中发送消息
总结:handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己,MessageQueue是一个存储消息的容器。

知识点:一个应用程序默认由ActivityThread创建Activity并回调其生命周期的方法。ActivityThread的main方法中会创建Looper,在Looper中会创建messagequeue
ActivityThread线程中的main方法里会调用Looper.prepareMainLooper();方法
而prepareMainLooper();方法中会有一个prepare方法

prepare方法中会获取一个ThreadLocal.get()判断是否为空,不为空会调用threadlocal的set(new Looper(quitAllowed))方法来创建一个Looper对象.

(threadlocal用于在一个线程当中保存一些变量信息)

源码:

private static void prepared (boolean quitAllowed) {
    if(sThraedLocal.get()!=null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

Looper会初始化一个MessageQueue对象
源码:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mRun = true;
        mThread = Thread.currentThread();
}

而Handler如何与Looper关联呢?

在Handler 类中:
会调用一个mLooper = Looper.myLooper( );方法
而在myLooper中就可以获得与主线程中关联的Looper对象。

myLooper源码:

public static Looper myLooper() {
    return sThreadLocal.get();
}

源码:

mLooper = Looper.myLooper();//这就是上文说的"Looper.myLooper()方法"
if(mLooper == null) {
    throw new RunTimeException(
    "Can't create handler inside thread that has not called Loopprepare()")
    mQueue = mLooper.mQueue;//获得Looper中的MessageQueue对象
    mCallback = null;
}

那么handler.sendEmptyMessage( );如何回调handleMessage方法呢

sendEmptyMessage中会调用sendEmptyMessageDelayed方法
sendEmptyMessageDelayed会返回sendMessageDelayed所返回的布尔值
sendMessageDelayed会返回sendMessageAtTime方法返回的布尔值

sendMessageAtTime源码:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;//取得MessageQueue对象
        if(queue != null) {
        msg.target = this;//定义消息发送的目标为自己(Handler)
        //将msg添加到消息队列中(通过Looper.Loop来轮循)
        sent = queue.enqueueMessage(msg,uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

Looper.Loop方法核心源码:

for (;;) {//死循环
            Message msg = queue.next(); //从消息队列中取消息
            if (msg == null) {//为空返回
                return;
            }
//...............................省略
            try {
            //不为空则调用Handler的dispatchMessage方法
                msg.target.dispatchMessage(msg);
            } finally {
//...............................省略             

Handler的dispatchMessage源码:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
        //mCallback就是初始化Handler传递的CallBack接口
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
//mCallback.handleMessage(msg)不为true会调用handleMessage(msg)方法
            handleMessage(msg);//这个想必大家很熟悉了
        }
    }

理一下:
1、ActivityThread的main方法会调用Looper中prepare方法(再调用prepare之前要调用其他方法,不再赘述,下同),通过ThreadLocal.set( )方法创建Looper,而Looper中会初始化一个MessageQueue。
2、在Handler中通过ThreadLocal.get()方法获得Looper
3、Handler的sendMessage将消息的发送目标设为自己(也就是Handler),并将消息发送到MessageQueue 中
4、通过Looper.Loop方法来轮循MessageQueue 中的消息,在每次循环中会调用Handler的dispatchMessage方法
5、dispatchMessage中就是熟悉的handleMessage方法。

也就是上面总结的那句话:
handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己,MessageQueue是一个存储消息的容器。

以上源码与当前的源码不太一样,但是原理是一样的,可能老师用的JDK是以前的。

自己觉得把总结的那句话记住就行了 ,源码看看加深理解。

下面是id为-霜花似雪-同学整理的,可以看看:
Message:消息,其中包含了消息ID,消息处理对象以及处理的数据,由MessageQueue同意列队,终由Handle处理
Handle:处理者,负责Message的接受和处理,使用Handle时,需要实现handleMessage(Message msg)方法对特定的Message进行处理,例如对UI更新等。
MessageQueue:消息队列,用来存放Handle发送过来的消息,并按照FIFO规则执行,当然,存放Message并非实际意义的保存,而是将Message以链表的方法串联起来的,等待Looper的抽取
Looper:消息泵。不断地从MessageQueue中抽取Message执行,因此,一个MessageQueue需要一个Looper。
Thread:线程,负责调整整个消息循环,及消息循环的执行场所


6、使用Handler时遇到的问题

//不能再非UI线程中去更新一个UI
(1).android.view.viewRootImpl$CalledFromWrongThreadException
Only the original thread that created a view hierarchy can touch its views

//在子线程中创建Handler时没有调用Looper.prepare( )方法
(2). Can’t create handler inside thread that has not called Looper.prepare( )


7、如何实现一个与线程相关的Handler?

自定义与线程相关的Handler对象:

public 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("currentThread:" + Thread.currentThread());
            }
    };
        Looper.loop();
    }
}

//在OnCreate方法中:
Mythread thread = new Mythread();
thread.start;
thread.handler.sendEmptyMessage(1);

8、HandlerThead又是什么呢?

首先介绍多线程导致的空指针问题:

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

//在OnCreate方法中:
Mythread thread = new Mythread();
thread.start;

Handler handler = new Handler(thread.looper) {
    public void handleMessage(Message msg){
        System.out.println("-----");
    }
};

handler.sendEmptyMessage(1);

运行后程序报空指针异常,是因为主线程执行到了要为Handler传递自定义looper对象的时候子线程并没有完成对looper对象的创建,导致异常。

如果用HandlerThread对象就不会出现这种问题:

//在OnCreate方法中
HandlerThread thread = new HandlerThread("handler thread");
thread.start;

Handler handler = new Handler(thread.getLooper()) {
    public void handleMessage(Message msg){
        System.out.println("current thread---->"+Thread.currentThread());
    }
};
handler.sendEmptyMessage(1);

控制台打印的消息:current thread—->Thread[handler thread,5,main]
说明handlerMessage方法是在子线程中处理的

那么HandlerThread是怎么做到不同线程间的同步呢?

HandlerThread的getLooper()方法源码:

public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created. 
        //mLooper对象为空,则线程等待
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

HandlerThread的run方法源码:

public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();//此时mLooper不为空
            notifyAll();//唤醒本线程
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

在HandlerThread中执行耗时任务,相当于异步任务机制。


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

//主线程的handler
Handler handler = new Handler(){
    public void handleMessage(android.os.Message msg) {
        Message message = new Message();
        //向子线程发送消息
        threadHandler.sendMessageDelayed(message, 1000);
    };
};

//Oncreate方法中:
HandlerThread handlerThread = new HandlerThread("handler thread");
handlerThread.start();
//子线程的handler
Handler threadHandler = new Handler(handlerThread.getLooper()){
    @Override
    public void handleMessage(Message msg) {
        Message message = new Message();
        //向主线程发送消息
        handler.sendMessageDelayed(message, 1000);
    }
};

以上就完成了主线程与子线程之间的交互

需要注意的是想得到一个子线程的Handler只需要用子线程的getLooper方法初始化此Handler对象即可。


10、Android中更新UI的几种方式

更新UI的4种方式
(1)、实现Handler.post(Runnable runnable)Runnable接口的run方法更新:

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

(2)、覆写Handler中的HandlerMessage方法更新:

private Handler handler = new Handler() {
    public void handleMessage(Message msg) {
        mTextView.setText("----");
    };
};
//OnCreate方法
handler.sendMessage(message);

(3)、实现runOnUIThread(Runnable runnable)Runnable接口的run方法更新:
注意runOnUIThread用final修饰过的只能继承不能重写

runOnUiThread (new Runnable() {
    @Override
    public void run() {
        mTextView.setText("----");
        }
    });

(4)、实现View.post(Runnable runnable)Runnable接口的run方法更新:

TextView tv = (TextView )findViewById(R.id.tv);
tv.post(new Runnable(){
    @Override
    public void run() {
         mTextView.setText("----");
    }
});

这4种方法其实都是通过handler来实现更新的


11、非UI线程真的不能更新UI吗?

例如setText
setText会调用checkForRelayout
checkForRelayout会调用invalidate
invalidate会初始化ViewParent
ViewParent向下调用invalidateChildParent方法
invalidateChildParent的checkThread方法会检查当前线程是否为主线程,又因ViewParent为viewRootImpl的实例,而viewRootImpl会在Activity回调onResume方法时创建。

故Activity没有回调onResume之前可以在子线程中更新UI


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值