handler是什么?
handler是android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它处理消息。在android的framework中Activity的生命周期中的处理函数都是系统通过handler消息处理回调的。
为什么要用handler?
android在设计的时候,就封装了一套消息创建、传递、处理机制,如果不遵循这样的机制就没有办法更新UI信息,就会抛出异常。android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
handler怎么用呢?
1、利用handler.post(Runnable)方法实现在异步线程更新UI,本身这个函数是在主线程被调用
2、 //利用handler实现定时刷新
3、//利用Callback类实现对发来的信息的拦截
4、//利用handler的sendMessage(Message)来从子线程发送消息给主线程
5、//移除handler中的Runnable
demo代码:
package com.example.handler;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.os.Message;
public class MainActivity extends Activity implements OnClickListener{
private TextView textView;
private ImageView imageView;
private Button button;
private int[] image =
{R.drawable.img1, R.drawable.img2, R.drawable.img3};
private int index=0;
//利用handler实现定时刷新
Handler handler = new Handler();
MyRunnable myRunnable = new MyRunnable();
class MyRunnable implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
imageView.setImageResource(image[index]);
index++;
index %= image.length;
//实现定时刷新(每隔2秒刷新一次)
handler.postDelayed(myRunnable, 2000);
}
}
//利用Handler处理子线程发来的消息来更新UI
Handler handler1 = new Handler() {
@Override
public void handleMessage(Message msg) {
textView.setText("" + msg.arg1 + "-" + msg.arg2 + " " +
msg.obj.toString());
}
};
class Person {
public String name;
public int age;
@Override
public String toString() {
// TODO Auto-generated method stub
return "name=" + name + " " + "age=" + age;
}
}
//利用Callback类实现对发来的信息的拦截
private Handler handler2 = new Handler(new Callback() {
@Override
public boolean handleMessage(Message arg0) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "信息拦截", 3000).show();
//当返回值为false时不对信息进行拦截,为true时拦截Message信息,
//handler的handleMessage就接受不到信息了
return true;
}
}){
//@Override
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(), "信息未被拦截", 3000).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView1);
imageView = (ImageView) findViewById(R.id.imageView1);
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
// 利用 handler.post(Runnable)方法实现在异步线程更新UI,本身这个函数是在主线程被调用
// new Thread() {
// public void run() {
// try {
// Thread.sleep(1000);
// handler.post(new Runnable() {
// public void run() {
// textView.setText("你好。。。");
// }
// });
//
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
// }
// }.start();
// 利用handler的postDelayed(Runnable,time)实现每个time时间对图片的刷新
new Thread() {
public void run() {
//实现定时刷新(每隔2秒刷新一次)
handler.postDelayed(myRunnable, 2000);
}
}.start();
//利用handler的sendMessage(Message)来从子线程发送消息给主线程
new Thread() {
public void run() {
Message msg = new Message();
//msg 对象也可以利用以下语句创建
//Message msg = handler1.obtainMessage();
//利用msg.sndToTarget();发送该消息给对应的handler
msg.arg1 = 8;
msg.arg2 = 10;
Person person = new Person();
person.name = "tom";
person.age = 19;
msg.obj = person;
handler1.sendMessage(msg);
}
}.start();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
//移除handler中的Runnable(本利用停止图片定时刷新)
//handler.removeCallbacks(myRunnable);
//发送一个空消息
handler2.sendEmptyMessage(1);
}
}
android为什么要设计只能通过Handler机制更新UI?
最根本的目的就是解决对线程并发问题,
假如在一个Activity当中,有多个线程去更新UI,并且都没有加锁机制,那么会产生什么样的问题呢?
更新界面错乱
如果对更新UI的操作都进行加锁处理的话又会产生什么样子的问题?
性能下降
出于对以上目的问题的考虑,android给我们提供了一套更新UI的机制(Handler处理机制),我们只需要知道遵循这样的机制就可以了。
根本不用去关心多线程问题,所以更新UI的操作,都是在主线程的消息队列当中去轮询处理的。
在线程中使用Handler
demo代码
package com.example.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.TextView;
public class SecondActivity extends Activity {
private Handler handler1 = new Handler() {
public void handleMessage(Message msg) {
System.out.print("UI----->" + Thread.currentThread());
}
};
//实现一个与线程相关的Handler
class MyThread extends Thread {
public Handler handler;
public Looper looper;
@Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();//创建Looper对象
looper = Looper.myLooper();
handler = new Handler() {
public void handleMessage(Message msg) {
System.out.println("currentThread" + Thread.currentThread());
}
};
//死循环,不断取出消息队列中的消息,并交给Handler处理
Looper.loop();
}
}
private MyThread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
TextView textView = new TextView(this);
textView.setText("你好。。");
setContentView(textView);
thread = new MyThread();
thread.start();
// try {
// Thread.sleep(500);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//thread.handler.sendEmptyMessage(1);
//handler1.sendEmptyMessage(1);
//把handler和相应的线程绑定(这样会因为线程同步问题导致空指针异常)
//可以通过HandlerThread来实现外部Handler和线程的绑定(详见ThreadActivity)
Handler handler = new Handler(thread.looper) {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
System.out.println("Thread---->" + Thread.currentThread());
}
};
handler.sendEmptyMessage(1);
}
}
利用HandlerThread实现Handler通过Looper实现和子线程的关联
demo代码:
package com.example.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.widget.Button;
import android.widget.TextView;
public class ThreeActivity extends Activity {
/**
* 利用HandlerThread实现Handler通过Looper实现
* 和子线程的关联
*/
private HandlerThread thread;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView textView = new TextView(this);
textView.setText("你好。。");
setContentView(textView);
thread = new HandlerThread("handler thread");
thread.start();
handler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
System.out.println("Thread--->" + Thread.currentThread());
}
};
handler.sendEmptyMessage(1);
}
}
子线程和主线程互相发送消息
demo代码
package com.example.handler;
import android.app.Activity;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class CommThreadActivity extends Activity implements OnClickListener {
private Button button1;
private Button button2;
//定义一个子线程的Handler
private Handler threadHandler;
//创建主线程的Handler
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
System.out.println(msg.arg1);
Message message = new Message();
message.arg1 = 2;
//向子线程发送消息
threadHandler.sendMessageDelayed(message, 1000);
}
};
@Override
protected void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
button1 = (Button) findViewById(R.id.button1);
button2 = (Button) findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
//利用HandlerThread创建子线程
HandlerThread thread = new HandlerThread("thread handler");
thread.start();
//子线程的Handler
threadHandler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
System.out.println(msg.arg1);
Message message = new Message();
message.arg1 = 1;
//向主线程发送消息
handler.sendMessageDelayed(message, 1000);
};
};
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()) {
case R.id.button1:
//向主线程发送一个消息
handler.sendEmptyMessage(1);
break;
case R.id.button2:
//移除一个消息
handler.removeMessages(1);
break;
}
}
}
子线程中更新UI的几种方式
1、利用Handler的post方法
2、利用Handler的sendMessage方法
3、利用Activity的runOnUiThread方法
4、利用View的post方法
demo 代码:
package com.example.handler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
public class UpdateUIActivity extends Activity {
private TextView textView;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
textView.setText("你好。。");
};
};
//利用handler的post()函数更新UI
public void update1() {
handler.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
textView.setText("你好。。");
}
});
}
//利用handler的sengMessage更新UI
public void update2() {
handler.sendEmptyMessage(1);
}
//Activity自己提供的在子线程中更新UI
public void update3() {
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
textView.setText("你好。。");
}
});
}
//利用View提供的子线程更新UI的方法
public void update4() {
textView.post(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
textView.setText("你好。。");
}
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.updateui);
textView = (TextView) findViewById(R.id.textView1);
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//更新UI
update1();
}
}.start();
};
}
子线程中也可以更新UI,不过得在ViewRootImpl对象初始化完成之后。ViewRootImpl对象初始化是在Activity的onResume方法中,当一个线程在onCreate方法中创建后马上更新UI则系统不会报出错误,若ViewRootImpl对象已经被初始化了在去更新UI则会抛出android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.异常。
Handler常出现的异常有
1、android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.ViewRootImp对象初始化后在子线程中更新UI。
2、在子线程中handler传入Looper对象时(线程同步导致的)