Android事件处理之四 Handler消息传递机制

本文详细介绍了Android中UI线程安全的问题及解决方法,重点阐述了Handler的作用与工作原理,包括如何利用Handler实现线程间通信,Looper和MessageQueue的工作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
    }
}

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对象,可分如下两种情况处理:
  1.      主UI线程中,系统已经初始化了一个Looper对象
  2. 程序员自己启动子线程,必须自己创建一个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());
    }
然后调用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);
    }
    msg.recycle();
    }
在线程中使用Handler的步骤如下:
  1. 调用Looper的prepare()方法为当前线程创建Looper对象,此时,构造器会创建与之配套的MessageQueue
  2. 有了Looper之后,创建Handler子类实例,重写handleMessage()方法,该方法负责处理来自于其他线程的消息
  3. 调用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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值