(1)所谓后台服务是指当我们程序在运行的时候,总有那么一些默默无闻的服务在后面跑着,提供着服务。程序是进程,进程中除了主线程之外可能有其他一些线程,那么这些线程在后台可能就提供者某类服务。本篇文章要研究的其实就是Android环境下多线程的用法。
(2)Java中多线程主要有三种方式:extends Thread类,implements Runable接口,通过匿名类的方式直接开始线程new Thread(new Runable(){}).start()
(3)在Android中只有主线程可以修改UI元素。比如界面上有个按钮,有一个文本,点击按钮来修改文本内容,如果点击事件中通过子线程去做这件事情,那么就会出问题。那么如何在一个子线程中修改用户界面呢?还是一个按钮改变文本的例子:
其界面和布局如下:
<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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:id="@+id/changeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="47dp"
android:text="子线程修改文本内容" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/changeText"
android:layout_centerHorizontal="true"
android:layout_marginTop="155dp"
android:text="@string/hello_world" />
</RelativeLayout>
其活动代码如下:
public class MainActivity extends Activity implements OnClickListener{
//布局控件
private Button changeText;
private TextView textView;
//标识码
public static final int UPDATE_TEXT = 1;
//处理器对象
private Handler handler = new Handler(){
//重写该方法
public void handleMessage(Message msg){
switch (msg.what) {
case UPDATE_TEXT:
//在此修改UI内容
textView.setText("内容已修改");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
changeText = (Button)findViewById(R.id.changeText);
textView = (TextView)findViewById(R.id.textView);
changeText.setOnClickListener(this);
}
//处理点击事件
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.changeText:
//启动一个线程
new Thread(){
//重写该方法
public void run(){
Message msg = new Message();
msg.what = UPDATE_TEXT;
handler.sendMessage(msg);
}
}.start();
break;
default:
break;
}
}
}
(4)其实上述实现是通过异步消息处理机制来做到的。在异步消息处理中,主要有四部分的内容:
Message: 线程之间传递的消息
Handler: 主要用于发送和处理消息
MessageQueue: 消息队列的意思,存放所有Handler发送的消息,每个线程中只会有一个MessageQueue
Looper: Looper是每个线程中MessageQueue的管家,用于不断循环,如果queue中有了消息,马上交给Handler处理。每个线程也只有一个。
上述方法的思路就是:
主线程创建一个Handler对象,重写handleMessage方法。子线程想改变UI的时候,创建一个Message对象,通过handler发送,消息进去了MessageQueue队列,Looper等到了队列,分发给Handler处理。因为Handler是主线程创建的,所以能够改变UI。
(5)为了更加方便的通过子线程操作UI,Android中提供了另外一个好用的工具,AsyncTask就是其中之一。其示例代码如下:
public class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
//编写子类继承AsyncTask,并实现里面的抽象方法
//包含三个参数:传入的参数 ; 如果前台显示后台的进度,第二个参数是进度单位;返回值类型
//这个方法会在后台任务开始执行前调用
protected void onPreExecute(){
progressDialog.show(...);//显示进度条
}
//这个方法中的所有代码都会在子线程中运行, 这里面的例子就是模仿下载速度
protected Boolean doInBackground(Void... arg0) {
try{
while(true){
int downloadPercent = doDownload(); //下载方法,虚构的方法
publishProgress(downloadPercent); //反馈当前的进度
if(downloadPercent >= 100){
break;
}
}
}catch(Exception e){
return false;
}
return true;
}
//当后台调用publishProgress方法时,这个方法很快被调用,方法中的参数是后台传递过来的 在这个方法中可以对UI进行操作
protected void onProgressUpdate(Integer... values){
//在这里更新下载速度
progressDialog.setMessage("下载 "+values+"%");
}
//当后台任务执行完毕,将要返回时调用这个方法
protected void onPostExecute(Boolean result){
progressDialog.dismiss(); //关闭进度对话框
//提示下载结果
if(result){
Toast.makeText(context, text, duration).show();
}else{
Toast.makeText(context, text, duration).show();
}
}
}