#安卓学习-Handler
##Handler
1.更新UI的一套机制(解决多线程更新ui造成混乱,如果加锁同步性能又降低的问题)
2.消息处理的机制(发送,处理)
##Handler中常用的方法
###1.post(Runnable r)
###2.postDelayed(Runnable r, long delayMillis)
###3.sendMessage(Message msg)
###4.sendMessageDelayed(Message msg, long delayMillis)
1.post(Runnable r)
直接在子线程中使用
new Thread(){
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
tv_tv.setText("handler");
}
});
};
}.start();
2.postDelayed(Runnable r, long delayMillis)(延迟执行)
用法与post类似
可用于隔一段时间无限执行某事
如下图,tv中的内容1秒变化一次
tv = ((TextView) findViewById(R.id.tv));
ra = new Runnable(){
@Override
public void run() {
i++;
tv.setText(i+"");
handler.postDelayed(ra, 1000);
}
};
handler.postDelayed(ra, 1000);
3.sendMessage(Message msg)
在子线程中发送消息给主线程
new Thread() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message m = new Message();
m.what = 0;
m.obj = new Date();
handler.sendMessage(m);
}
}.start();
主线程中handler重写handleMessage(Message msg)处理传过来的消息
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int a=msg.what;
}
};
获得消息对象三种方式的差异
一般使用bc两种方式
a.Message m = new Message();无消息池,无handler关联
b.Message.obtain();有消息池,无handler关联
c.handler.obtainMessage();有消息池,有handler关联
4.sendMessageDelayed(Message msg, long delayMillis)
用法与postDelayed类似
##主线程与子线程相互发送消息(以下会出现空指针异常)
实现一个与线程相关的handler
class MyRunble implements Runnable {
public Handler handlerThread;
public Looper looper;
@Override
public void run() {
//在其他线程中必须创建与该线程相关的Looper
** Looper.prepare();
looper = Looper.myLooper();
//子线程中的handler
handlerThread = new Handler() {
@Override
public void handleMessage(Message msg) {
//子线程接收到消息后发送消息给主线程
handler.sendEmptyMessage(0);
Log.i("test", Thread.currentThread().getName());
}
};
Looper.loop();**
}
}
//主线程中的handler
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//此时 myRunble可能为空,子线程中可能还没有创建对象
//looper也可能为空
//主线程接收到消息后发送消息给子线程
myRunble.handlerThread.sendEmptyMessage(0);
}
};
//开启子线程
myRunble = new MyRunble();
new Thread(myRunble).start();
//给主线程发送空消息
handler.sendEmptyMessage(0);
##主线程给子线程发送消息(多线程并发导致的空指针异常解决)
HandlerThread ht=new HandlerThread("bbbbb");
ht.start();
Handler handlerThread = new Handler(ht.getLooper()){
@Override
public void handleMessage(Message msg) {
Log.d("MainActivity", "子线程接收到消息了" + Thread.currentThread().getName());
}
};
给子线程发送消息,此时不会出现空指针异常
handlerThread.sendEmptyMessage(0);
原理:ht开启后,子线程就开始执行run方法得到Looper
ht.getLooper()中如果mLooper==null等待,直到不等于空时被唤醒。
##Handler中消息截获
要截取使用这个构造 Handler(Callback callback)
代码如下:
public class MainActivity extends AppCompatActivity {
Handler handler=new Handler(new Handler.Callback() {
@Override
//接收到消息后先走Callback里的这个方法,若果返回true则截断消息,false则由handler中的handleMessage(Message msg)处理消息。
public boolean handleMessage(Message msg) {
Toast.makeText(MainActivity.this, "Callback", Toast.LENGTH_SHORT).show();
return false;
}
}){
@Override
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this, "我收到消息了", Toast.LENGTH_SHORT).show();
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//发送消息
handler.sendEmptyMessage(0);
}
}
##总结
1.handler负责发送和处理消息。
2.Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己。
3.MessageQueue为消息列队,为储存消息的容器。
4.在应用创建的时候,就已经创建了与UI线程相关的Looper,所以主线程中创建的无参的Handler, 默认与主线程相关的Looper相关联。
5.调用方法的Handler和哪个线程的Looper相关联,其消息就在哪个线程中处理。
##常见异常
//在非主线程中更新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()
##在子线程中更新UI的几种方法(本质都是handler发送和处理消息(底层源码))
1.runOnUiThread(Runnable action)
2.handler.sendMessage(Message msg)
3.handler.post(Runnable r)
4.View.post(Runnable action)
##不能在非主线程中更新UI的底层原因(源代码)
更新ui的时候会调用View的invalidate()方法,
在这个方法中如果ViewParent不为null就检查当前线程是否为主线程,如果不为主线程就抛出异常,
而ViewParent为ViewRootImpl的实现类,
ViewRootImpl是在activity的onResume()中初始化的。
(所以在ViewRootImpl初始化之前,在子线程中可以直接更新ui而不会抛出异常,一般不建议使用)
##Handler
1.更新UI的一套机制(解决多线程更新ui造成混乱,如果加锁同步性能又降低的问题)
2.消息处理的机制(发送,处理)
##Handler中常用的方法
###1.post(Runnable r)
###2.postDelayed(Runnable r, long delayMillis)
###3.sendMessage(Message msg)
###4.sendMessageDelayed(Message msg, long delayMillis)
1.post(Runnable r)
直接在子线程中使用
new Thread(){
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
tv_tv.setText("handler");
}
});
};
}.start();
2.postDelayed(Runnable r, long delayMillis)(延迟执行)
用法与post类似
可用于隔一段时间无限执行某事
如下图,tv中的内容1秒变化一次
tv = ((TextView) findViewById(R.id.tv));
ra = new Runnable(){
@Override
public void run() {
i++;
tv.setText(i+"");
handler.postDelayed(ra, 1000);
}
};
handler.postDelayed(ra, 1000);
3.sendMessage(Message msg)
在子线程中发送消息给主线程
new Thread() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message m = new Message();
m.what = 0;
m.obj = new Date();
handler.sendMessage(m);
}
}.start();
主线程中handler重写handleMessage(Message msg)处理传过来的消息
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int a=msg.what;
}
};
获得消息对象三种方式的差异
一般使用bc两种方式
a.Message m = new Message();无消息池,无handler关联
b.Message.obtain();有消息池,无handler关联
c.handler.obtainMessage();有消息池,有handler关联
4.sendMessageDelayed(Message msg, long delayMillis)
用法与postDelayed类似
##主线程与子线程相互发送消息(以下会出现空指针异常)
实现一个与线程相关的handler
class MyRunble implements Runnable {
public Handler handlerThread;
public Looper looper;
@Override
public void run() {
//在其他线程中必须创建与该线程相关的Looper
** Looper.prepare();
looper = Looper.myLooper();
//子线程中的handler
handlerThread = new Handler() {
@Override
public void handleMessage(Message msg) {
//子线程接收到消息后发送消息给主线程
handler.sendEmptyMessage(0);
Log.i("test", Thread.currentThread().getName());
}
};
Looper.loop();**
}
}
//主线程中的handler
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
//此时 myRunble可能为空,子线程中可能还没有创建对象
//looper也可能为空
//主线程接收到消息后发送消息给子线程
myRunble.handlerThread.sendEmptyMessage(0);
}
};
//开启子线程
myRunble = new MyRunble();
new Thread(myRunble).start();
//给主线程发送空消息
handler.sendEmptyMessage(0);
##主线程给子线程发送消息(多线程并发导致的空指针异常解决)
HandlerThread ht=new HandlerThread("bbbbb");
ht.start();
Handler handlerThread = new Handler(ht.getLooper()){
@Override
public void handleMessage(Message msg) {
Log.d("MainActivity", "子线程接收到消息了" + Thread.currentThread().getName());
}
};
给子线程发送消息,此时不会出现空指针异常
handlerThread.sendEmptyMessage(0);
原理:ht开启后,子线程就开始执行run方法得到Looper
ht.getLooper()中如果mLooper==null等待,直到不等于空时被唤醒。
##Handler中消息截获
要截取使用这个构造 Handler(Callback callback)
代码如下:
public class MainActivity extends AppCompatActivity {
Handler handler=new Handler(new Handler.Callback() {
@Override
//接收到消息后先走Callback里的这个方法,若果返回true则截断消息,false则由handler中的handleMessage(Message msg)处理消息。
public boolean handleMessage(Message msg) {
Toast.makeText(MainActivity.this, "Callback", Toast.LENGTH_SHORT).show();
return false;
}
}){
@Override
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this, "我收到消息了", Toast.LENGTH_SHORT).show();
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//发送消息
handler.sendEmptyMessage(0);
}
}
##总结
1.handler负责发送和处理消息。
2.Looper负责接收Handler发送的消息,并直接把消息回传给Handler自己。
3.MessageQueue为消息列队,为储存消息的容器。
4.在应用创建的时候,就已经创建了与UI线程相关的Looper,所以主线程中创建的无参的Handler, 默认与主线程相关的Looper相关联。
5.调用方法的Handler和哪个线程的Looper相关联,其消息就在哪个线程中处理。
##常见异常
//在非主线程中更新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()
##在子线程中更新UI的几种方法(本质都是handler发送和处理消息(底层源码))
1.runOnUiThread(Runnable action)
2.handler.sendMessage(Message msg)
3.handler.post(Runnable r)
4.View.post(Runnable action)
##不能在非主线程中更新UI的底层原因(源代码)
更新ui的时候会调用View的invalidate()方法,
在这个方法中如果ViewParent不为null就检查当前线程是否为主线程,如果不为主线程就抛出异常,
而ViewParent为ViewRootImpl的实现类,
ViewRootImpl是在activity的onResume()中初始化的。
(所以在ViewRootImpl初始化之前,在子线程中可以直接更新ui而不会抛出异常,一般不建议使用)