安卓学习笔记之Handler
来源:互联网 发布:无间道知乎细节 编辑:程序博客网 时间:2024/06/10 22:24
UI线程
当系统启动的时候,就会创建一个主线程(Main Thread),然后这个主线程向UI组件分发事件,主线程和UI的组件进行交互,故称UI线程。
线程安全
Android的UI线程是不安全的。引用一下,百度百科的解释
线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
from 百度百科
既然这样,Google给我们提供了更新ui界面的Handler类。
Handler
一个处理异步消息的类,创建一个子线程发送消息至主线程,在主线程更新相应的UI界面。比如在子线程进行长时间的网络操作,然后更新UI界面上的TextView。
使用的方法有两种,
1.post(runnable)&2.sendMessage(message)
先演示一下,所谓不能直接在UI线程更新TextView的操作。在XML布局文件里创建一个TextView。
package com.example.myapplication;import android.app.Activity;import android.os.Bundle;import android.support.annotation.Nullable;import android.widget.Button;import android.widget.TextView;/** * Created by Does on 2017/7/20. */public class HandlerExample extends Activity { private TextView tv_content; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_content= (TextView) findViewById(R.id.tv_content); new Thread(){ @Override public void run() { try { Thread.sleep(5000); tv_content.setText("更新UI"); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }}
写好之后,运行之后,
Exception:只有创建了视图层次结构的原始线程才能触及它的视图
为了解决这种问题,我们使用Handler。
post(runnable)?
这里模拟一个简单的下载功能。XML文件里面添加两个组件,一个Button和一个Textview
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private TextView tv_content; private Button btn_onclick; private Handler handler=new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_content= (TextView) findViewById(R.id.tv_content); btn_onclick= (Button) findViewById(R.id.btn_onclick); btn_onclick.setOnClickListener(this); } @Override public void onClick(View view) { DownLoadThread downLoadThread=new DownLoadThread(); downLoadThread.start(); } class DownLoadThread extends Thread{ @Override public void run() { try { System.out.println("文件正在下载..."); Thread.sleep(2000); System.out.println("文件下载成功"); Runnable runnable=new Runnable() { @Override public void run() { System.out.println("当前线程的id是: "+Thread.currentThread().getId()); MainActivity.this.tv_content.setText("textview已经改变"); } }; handler.post(runnable); } catch (InterruptedException e) { e.printStackTrace(); } } }}
运行效果如下,点击Button之前
点击Button之后
sendMessage(message)
public class HandlerExample extends Activity { private TextView tv_content; private Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1: tv_content.setText("UI变化了"); break; default: break; } } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_content= (TextView) findViewById(R.id.tv_content); new Thread(){ @Override public void run() { try { Thread.sleep(5000); Message message=new Message(); message.what=1; handler.sendMessage(message); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); }}
效果和上面的是一样的。
这个明显和上一个有些不同,多了几样的东西。Message(),sendMessage(),handleMessage()
首先要子线程中,创建一个Handler的一个对象handler,然后执行handler.sendMessage(message),
handler携带message的一个对象,message.what=1,给它一个标识,随便取。在handleMessage()方法里,就会接受到传过去的值,进而在handleMessage里进行UI的更新操作。
Handler运行机制
接下来我们说说内部机制的运行。主要有用到Looper,Handler,MessageQueu(消息队列).从源码入手,
当我们手机一启动的时候,系统默认主线程会先调用prepareMainLooper()方法。先执行prepare(false)
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { ... sMainLooper = myLooper(); } }
接着创建一个Looper对象,将ThreadLocal设置为线程安全的对象。
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)); }
接着sThreadLocal向Looper类里传过去quitAllowed,并在Looper()构造器里创建了一个MessageQueue的对象mQueue。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
最后,再调用prepareMainLooper()中的myLooper(),取出线程安全的Looper对象。
public static Looper myLooper() { return sThreadLocal.get(); }
这是系统帮我们做的事情!!
接着我们需要做一些事情更新UI的操作,我们在MainActivity方法中创建了一个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; mCallback = callback; mAsynchronous = async; }
handler取出了系统为我们创建的Looper对象,并取出系统创建的mQueue对象。此时我们创建的handler就持有了系统刚创建的Looper对象和MessageQueue对象。
**
发送消息
**
当我们发送消息的时候,sendMessage
sendMessage(Message msg) sendMessageDelayed(Message msg, long delayMillis) public final boolean sendMessageAtFrontOfQueue(Message msg) { 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, 0); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
首先是取出Looper对象中MessageQueue在enqueueMessage()方法中,handler携带我们要发送的Message,然后放入消息队列MessageQueue中
boolean enqueueMessage(Message msg, long when) { ... boolean needWake; synchronized (this) { ... msg.when = when; Message p = mMessages; if (p == null || when == 0 || when < p.when) { 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; prev.next = msg; } } ... return true; }
循环取出
接着Looper调用loop()方法
public static void loop() { final Looper me = myLooper(); ... final MessageQueue queue = me.mQueue; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); ... // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; msg.target.dispatchMessage(msg); ... final long newIdent = Binder.clearCallingIdentity(); ... msg.recycle(); } }
myLooper()取出当前Looper对象
me.mQueue拿到当前的MessageQueue对象
queue.next();取出下一个消息
如果消息存在 则调用消息的msg.target.dispatchMessage(msg);
处理消息
这就是我们重写的方法,
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
- 安卓学习笔记之Handler
- 安卓handler学习
- 安卓学习-Handler
- 安卓学习-Handler
- 安卓学习笔记之Handler更新UI的几种方法总结
- 安卓学习之路之Handler的简单实用
- xamarin学习笔记A13(安卓Handler异步消息处理)
- 安卓源码学习之Handler执行流程
- 安卓开发-Handler学习
- 安卓Handler入门学习
- 安卓之handler机制
- 安卓学习之控件学习笔记
- android学习笔记之Handler
- Android学习笔记之Handler
- 【安卓笔记】Handler+Thread使用浅析
- 【安卓面试笔记】Handler(三)
- 《安卓笔记》Handler消息机制
- 安卓之路-学习笔记1
- 有手机直播源码,不会搭看这里
- HaProxy安装/启动/简单配置
- json格式转换list
- Java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{*****Activity}: java.lang.C
- 洛谷P1594 护卫队
- 安卓学习笔记之Handler
- 覆盖父类方法的new和override关键字
- 引用作为函数与指针及值传递的差别
- SpringMVC 通过ajax从controller传值给js出现中文乱码
- spring学习之---依赖注入
- poj 3280 Cheapest Palindrome
- jquery源码解析之遍历后代
- 测试工具
- 【Python】求一个数组中的奇数的立方的和