Android的UI操作并不是线程安全的。Android消息传递机制是另一种形式的“事件处理”,Android只允许UI线程修改Activity里的UI组件,这样就会导致新启动的线程无法改变界面组件的属性值。这就需要Handler来实现
Handler的主要作用:
- 在新线程中发送消息
- 在主线程中获取、处理消息
为让主线程适时处理新线程所发送的消息,只能通过回调的方式来实现——开发者需要重写Handler类中处理消息的方法,当新线程发送消息时,消息会发送到与之关联的MessageQueue,而Handler会不断地从MessageQueue中获取并处理消息
Handler包含如下方法用于发送、处理消息:
void handleMessage(Message msg)
|
处理消息的方法
|
final boolean hasMessage(int what)
|
检查消息队列中是否包含what属性为指定值的消息
|
final boolean hasMessage(int what ,Object object)
|
what属性且object属性为制定对象的消息
|
多个重载的Message obtainMessage()
|
获取消息
|
sendEmptyMessage(int what)
|
发送空消息
|
final boolean sendMessageDelayed(int what, long delayMillis)
|
指定多少毫秒之后发送空消息
|
final boolean sendMessage(Message msg)
|
立即发送消息
|
final boolean sendMessageDelayed(Message msg, long delayMillis)
|
指定多少毫秒之后发送消息
|
示例代码:
public
class
MainActivity
extends
ActionBarActivity {
int [] imageIds = new int []{
R.drawable. java ,
R.drawable. ee ,
R.drawable. ajax ,
R.drawable. xml ,
R.drawable. classic
};
int currentImageId = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. activity_main );
final ImageView show = (ImageView) findViewById(R.id. show );
final Handler myHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg. what == 0x123)
{
show.setImageResource( imageIds [ currentImageId ++
% imageIds . length ]);
}
}
};
new Timer().schedule( new TimerTask()
{
@Override
public void run()
{
myHandler.sendEmptyMessage(0x123);
}
},0,1200);
int [] imageIds = new int []{
R.drawable. java ,
R.drawable. ee ,
R.drawable. ajax ,
R.drawable. xml ,
R.drawable. classic
};
int currentImageId = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. activity_main );
final ImageView show = (ImageView) findViewById(R.id. show );
final Handler myHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg. what == 0x123)
{
show.setImageResource( imageIds [ currentImageId ++
% imageIds . length ]);
}
}
};
new Timer().schedule( new TimerTask()
{
@Override
public void run()
{
myHandler.sendEmptyMessage(0x123);
}
},0,1200);
}
}
3.5.2 Handler、Loop、MessageQueue的工作原理
Message
|
Handler接收和处理的消息对象
|
Looper
|
每个线程只能拥有一个Looper。负责读取MessageQueue中的消息
|
MessageQueue
|
消息队列,它采用先进先出的方式来管理Message。程序创建Looper对象时会在它的构造器中创建Looper对象
|
Looper提供的构造器源代码:
private Looper()
{
mQueue = new MessageQueue();
mRun = true;
mThread = Thread.currentThread();
}
程序在初始化Looper时会创建一个与之失联的MessageQueue。
- Handler: 它的作用有两个——发送和处理消息。如果希望Handler工作,必须在当前线程中有一个Looper对象。为保证当前线程中有Looper对象,可分如下两种情况处理:
- 主UI线程中,系统已经初始化了一个Looper对象
- 程序员自己启动子线程,必须自己创建一个Looper对象,并启动它。创建Looper对象,调用它的prepare()方法即可
prepare()方法代码:
public static final void prepare()
{
if (sThreadLocal.get() != null )
{
throw new RuntimeException( "Only one Looper may be created per thread" );
}
sThreadLocal.set( new Looper());
public static final void prepare()
{
if (sThreadLocal.get() != null )
{
throw new RuntimeException( "Only one Looper may be created per thread" );
}
sThreadLocal.set( new Looper());
}
然后调用Looper的静态方法loop()方法来启动它。loop()方法使用一个死循环不断取MessageQueue中的消息,并将取出的消息对应的Handler进行处理。
loop()方法源代码:
for
(;;)
{
Message msg = queue.next();
if (msg == null )
{
return ;
}
Printer logging = me.mLogging;
if (logging != null )
{
logging.println( ">>>>>Dispatchging to " + msg.target + ""
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != newIdent)
{
Log.wtf(TAG, "Thread identity changed from ox"
+Long.toHexString(ident)+ "to 0x"
+Long.toHexString(newIdent)+ "while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + "what=" + msg.what);
{
Message msg = queue.next();
if (msg == null )
{
return ;
}
Printer logging = me.mLogging;
if (logging != null )
{
logging.println( ">>>>>Dispatchging to " + msg.target + ""
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != newIdent)
{
Log.wtf(TAG, "Thread identity changed from ox"
+Long.toHexString(ident)+ "to 0x"
+Long.toHexString(newIdent)+ "while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + "what=" + msg.what);
}
msg.recycle();
}
在线程中使用Handler的步骤如下:
- 调用Looper的prepare()方法为当前线程创建Looper对象,此时,构造器会创建与之配套的MessageQueue
- 有了Looper之后,创建Handler子类实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息
- 调用Looper的loop()方法启动Looper
尽量避免在UI线程中执行耗时操作,因为这样可能导致一个著名的异常:ANR异常,只要在UI线程中执行需要消耗大量时间的操作,都会引发ANR异常,因为这会导致Android应用程序无法响应输入事件和Broadcast
示例代码:
public
class
MainActivity
extends
ActionBarActivity {
static final String UPPER_NUM = "upper" ;
EditText etNum ;
CalThread calThread ;
class CalThread extends Thread
{
public Handler mHandler ;
public void run()
{
Looper.prepare();
mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg. what == 0x123)
{
int upper = msg.getData().getInt( UPPER_NUM );
List<Integer> nums = new ArrayList<Integer>();
outer:
for ( int i = 2; i <= upper; i++)
{
for ( int j = 2; j < Math.sqrt(i); j++)
{
if (i != 2 && i % j == 0)
{
continue outer;
}
}
nums.add(i);
}
Toast.makeText(MainActivity. this , nums.toString(), Toast. LENGTH_LONG ).show();
}
}
};
Looper.loop();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. activity_main );
etNum = (EditText) findViewById(R.id. etNum );
calThread = new CalThread();
calThread .start();
}
public void cal(View source)
{
Message msg = new Message();
msg. what = 0x123;
Bundle bundle = new Bundle();
bundle.putInt( UPPER_NUM , Integer.parseInt( etNum .getText().toString()));
msg.setData(bundle);
calThread . mHandler .sendMessage(msg);
static final String UPPER_NUM = "upper" ;
EditText etNum ;
CalThread calThread ;
class CalThread extends Thread
{
public Handler mHandler ;
public void run()
{
Looper.prepare();
mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg. what == 0x123)
{
int upper = msg.getData().getInt( UPPER_NUM );
List<Integer> nums = new ArrayList<Integer>();
outer:
for ( int i = 2; i <= upper; i++)
{
for ( int j = 2; j < Math.sqrt(i); j++)
{
if (i != 2 && i % j == 0)
{
continue outer;
}
}
nums.add(i);
}
Toast.makeText(MainActivity. this , nums.toString(), Toast. LENGTH_LONG ).show();
}
}
};
Looper.loop();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout. activity_main );
etNum = (EditText) findViewById(R.id. etNum );
calThread = new CalThread();
calThread .start();
}
public void cal(View source)
{
Message msg = new Message();
msg. what = 0x123;
Bundle bundle = new Bundle();
bundle.putInt( UPPER_NUM , Integer.parseInt( etNum .getText().toString()));
msg.setData(bundle);
calThread . mHandler .sendMessage(msg);
}
}