Android中的UI是线程不安全的,也就是说,如果要更新应用程序里的UI 元素,则必须在主线程中进行,否则就会出现异常。在这里介绍两个方法来解决这个问题
Android中的异步消息处理主要分为四个部分,Message、Handler、MessageQueue、Looper。
1.Message 是在线程之间传递的消息,它可以在内部携带少量的消息,用于在不同线程之间交换数据。
2.Handler 顾名思义就是处理者的意思,它主要是用来发送和处理消息的。
3.MessageQueue 是消息队列的意思,它主要是用于存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程只有一个消息队列对象。
4.Looper 是每个线程中消息队列的管家,调用其loop()方法后,就会进入到一个无限循环中,然后每发现消息队列中有一条消息,就会将它取出,并传递到Handler的handleMessage()方法中,每个线程也只会有一个Looper对象。
定义一个点击事件,使得点击时,服务可以改变UI中的内容
package com.example.administrator.myhandlerapplication;
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
public static final int UPDATE_TIME=1;
private int count=60;
private Button mButton;
private Button mButtonMainsend;
private TextView mTextView;
//定义一个内部类Handle
private Handler handler=new Handler(){
public void handleMessage(Message msg){
switch (msg.what){
case UPDATE_TIME
//在这里进行UI操作
String time= (String) msg.obj;
mButton.setText(time);
if (count>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//这里还可以简单的使用,对UI的操作在这里写,点击事件只需要发送一个空消息,然后在这里类似递归调用发送空消息,然后在这里对UI一次又一次的操作。即把count--放在handler中。
//handler.sendEmptyMessage(UPDATE_TIME);
}
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton= (Button) findViewById(R.id.button_time);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new MyThread().start();
new Thread(new Runnable() {
@Override
public void run() {
while (count>0){
count--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message=handler.obtainMessage(); //new Message();
message.obj=count+"秒";
message.what=UPDATE_TIME;
//发送Message对象
handler.sendMessage(message);
}
}
}).start();
}
});
}
}
先定义一个整型常量用来表示更新UI 中的内容的一个操作,然后新建一个Handler对象,并重写父类的handleMessage方法,然后对Message进行处理
这里是由子线程发送消息,由主线程来作相应处理,还可以通过主线程发送消息,然后子线程做相应处理,但是不常用。这里只是举例说明。
package com.example.administrator.myhandlerapplication;
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
public static final int UPDATE_TIME=1;
private int count=60;
private Button mButton;
private Button mButtonMainsend;
private TextView mTextView;
//private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton= (Button) findViewById(R.id.button_time);
mButtonMainsend= (Button) findViewById(R.id.button_mainsend);
mButtonMainsend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//这个点击事件用来表示主线程发送消息,只是发送的是空消息,表示不进行任何操作
handler.sendEmptyMessage(UPDATE_TIME);
}
});
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//这个按钮点击事件用来启动子线程。
new MyThread().start();
}
//这是子线程,等待接受主线程发送的消息,使用log打印来显示。
class MyThread extends Thread{
@Override
public void run() {
//super.run();
Looper.prepare();
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d("11111111111111", "接收到主线程发来的消息");
}
};
Looper.loop();
}
}
}
在主线程中已经将Looper全部封装好所以不需要定义。而在子线程中需要将接收消息写在Looper的prepare()方法和loop()之间。
Android还提供了AsyncTask抽象类来实现子类对UI进行操作。其实这个类实现原理也是基于消息处理机制,只是做了封装而已,通过几个方法来实现。
AsyncTask是一个抽象类,所以使用时需要创建一个子类去继承,继承时需要制定三个泛性值参数。
第一个参数Params ,在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
第二个参数Progress ,在后台执行任务时,如果需要在界面上显示当前的进度,使用这里制定的泛型作为进度单位
第三个参数Result ,当任务执行完毕后如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
继承自AsyncTask的类还需要重写其方法,其中经常需要重写的有四个方法
1.onPreExecute() 在后台任务开始执行之前调用,用于进行一些界面上的初始化操作。
2.doInBackground这个方法中的代码都会在子线程中运行,需要在这里处理所有的耗时操作任务。任务完后之后通过return语句来讲任务的执行结果返回,如果AsyncTask的第三个泛型制定的是Void,可以不返回任务执行结果。需要注意的是,在这个方法中是不可以进行UI操作的,如果需要更新UI 元素,可以调用publishProgress()方法来完成
3.onProgressUpdate(Integer… values)当后台任务调用了publishProgress()方法后,这个方法就会很快被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4.onPostExecute(String s)当后台任务执行完毕并通过return语句进行返回时调用这个方法,返回的数据作为参数传递到这个方法中,可以利用返回的数据进行UI操作。
首先,定义一个按钮和一个进度条,通过按钮的点击事件启动后台服务,后台服务将信息发送回来显示到进度条上
"@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"/>
在MainActivity中声明一个类继承自AsyncTask并重写其方法,然后在按钮的点击事件中得到该类的一个对象,并可传参数给后台服务。
package com.example.administrator.masynctask;
import android.app.Activity;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
public class MainActivity extends Activity {
private int count=0;
private Button mButtonStart;
private ProgressBar mProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButtonStart= (Button) findViewById(R.id.button_sync);
mProgressBar= (ProgressBar) findViewById(R.id.progressBar);
mButtonStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//得到一个AsyncTask的对象,这里需要传参数,所以为空
MyAsyncTask myAsyncTask=new MyAsyncTask();
myAsyncTask.execute();
}
});
}
class MyAsyncTask extends AsyncTask{
@Override
protected String doInBackground(Void... params) {
//进行后台操作
while (count<101){
count++;
publishProgress(count);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "下载完成";
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
//利用doInBackground(Void... params)完成之后返回的值对UI 进行操作
mButtonStart.setText(s);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//利用doInBackground(Void... params)中publishProgress(count)传过来的值对UI中的进度条进行操作
mProgressBar.setProgress(values[0]);
}
}
}