和其他的UI库一样,Android的UI线程也是不安全的。即如果想要更新应用程序里面的UI元素,则必须在主线程中进行。如果不在主线程中进行,就会出现异常。废话不多说,直接上例子。
<span style="font-size:18px;">public class MainActivity extends Activity
{
private TextView tvText;
private Button changeText;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.tv_text);
changeText = (Button) findViewById(R.id.change_text);
changeText.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.change_text:
new Thread(new Runnable()
{
@Override
public void run()
{
tvText.setText("Hi,world");
}
}).start();
break;
default:
break;
}
}
});
}
}</span>
<span style="font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.hanlertest.MainActivity" >
<TextView
android:id="@+id/tv_text"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="22sp"
android:textColor="#f00"
android:text="@string/hello_world" />
<Button
android:id="@+id/change_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:text="Button" />
</RelativeLayout></span>
我只能说,想想很美好,现实很残酷。当然,由此也可以证明了子线程中是不可以进行更新UI操作的。但是在某些情况下,我们需要在子线程中进行UI操作。怎么办呢?虽然我们第一次程序错误了,但是我们也不能就此认输,现在我们将代码进行一点改进:
public class MainActivity extends Activity
{
private TextView tvText;
private Button changeText;
private static final int MESSAGE_TYPE = 1;
<strong><span style="color:#ff0000;">private Handler handler = new Handler()
{
public void handleMessage(Message msg)
{
//在这里我们可以进行相应的UI操作
switch (msg.what)
{
case MESSAGE_TYPE:
tvText.setText("Hi, world");
break;
default:
break;
}
};
};</span></strong>
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = (TextView) findViewById(R.id.tv_text);
changeText = (Button) findViewById(R.id.change_text);
changeText.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.change_text:
new Thread(new Runnable()
{
@Override
public void run()
{
<strong><span style="color:#ff0000;">Message message = new Message();
message.what = MESSAGE_TYPE;
handler.sendMessage(message);//将消息发送出去</span></strong>
}
}).start();
break;
default:
break;
}
}
});
}
}
Android中的主线程是不允许进行延时操作的, Android中的异步处理消息主要由四个部分组成,Message, Handler , MessageQueue , Looper。下面简要介绍下这四个组成部分。
1、Message:Message是在线程之间传递的消息,它可以在内部携带少量的消息。主要用于在不同的线程中传递消息。
2、Handler: Handler是处理者的意思,主要用于发送消息和处理消息的。发送消息使用的是Handler.sendMessage(),发出的消息经过一系列的操作后,又会调用Handler.handlerMessage()方法对消息进行处理。
3、MessageQueue: 消息队列。Handler通过sendMessage方法发送消息后,该消息会保存在MessageQueue中,Handler发送的消息会一直存在于消息队列中,等待被处理。每一个线程中只有一个MessageQueue。
4、Looper:MessageQueue是一个消息队列,Looper就是维护这个消息队列的“保安”,它内部有一个维护着一个Loop方法,当调用了Looper的Loop方法后,会进入到一个无线循环中,然后每当在MessageQueue中发现一条消息,就取出,交给Handler去处理。每个线程只有一个Looper对象。
了解了MessageQueue,Message, Handler, Looper对象后,我们在对整个流程进行梳理一遍,首先在主线程中创建一个Message对象,重写父类的handlerMessage方法,然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。发出去的这条消息会被添加到MessageQueue中,也就是在消息队列中队尾等待被处理,而Looper则会一直尝试从MessageQueue中取出等待被处理的消息,最后分发回Handler的handleMessage方法中进行处理。由于Handler是在主线程中创建的,所以此时handleMessage方法中的代码也是在主线程中运行,所以我们在这里可以进行UI操作。
整个异步消息处理机制如下图所示:图是百度找的,将就着看。整个消息处理机制就是这样。