Android的多线程和java类似,基本都是使用相同的语法。
class MyThread extends Thread{
@Override
public void run() {
//处理具体的逻辑
}
}
启动:
new MyThread().start();//来启动线程。
使用继承的方式,耦合性比较高,所以更多的使用实现Runnable接口的方式来定义一个线程。
class MyThread implements Runnable{
@Override
public void run() {
//处理具体的逻辑
}
}
启动:
MyThread myThread = new MyThread();
new Thread(myThread).start();
如果不想专门再定义一个类去实现Runnable接口,也可以使用匿名类的方式,这种写法更常见。
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
在子线程内更新U
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/change_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="change text"/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World"
android:textSize="30sp"/>
</LinearLayout>
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private TextView mText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView)findViewById(R.id.text);
Button changeText = (Button)findViewById(R.id.change_text);
changeText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
mText.setText("OK");
}
}).start();
}
});
}
}
会发现程序崩溃。
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
这里可以证明Android不允许在子线程中进行UI更新。但是有时候我们必须在子线程内进行一些耗时的操作,对于这种情况Android提供了一套异步消息处理机制,完美的解决了在子线程内更新UI操作的问题。
修改MainActivity.java:
public class MainActivity extends AppCompatActivity {
public static final int UPDATE_TEXT = 1;
private TextView mText;
private Handler mHandler = new Handler(){
public void handleMessage(Message message){
switch (message.what){
case UPDATE_TEXT:
//这里进行UI操作
mText.setText("OK");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView)findViewById(R.id.text);
Button changeText = (Button)findViewById(R.id.change_text);
changeText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
mHandler.sendMessage(message);
}
}).start();
}
});
}
}
新增了一个Handler对象,并重写父类的handleMessage()方法,在这里对具体的Message进行处理,如果发现Message.what的值等于UPDATE_TEXT,就更新UI。
点击按钮的事件:
在里面创建了一个Messge的对象,将它的what字段赋值,然后调用sendMessage的方法,发生该Message,然后在Handler内执行,意味在主线程内执行。
效果图:
解析异步消息处理机制
异步消息处理主要由四个部分组成:Message,Handler,MessageQueue和Loop。
1.Message:
Message是在线程之间传递的信息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。
2.Handler:
处理的意思,主要用于发送和处理消息,发送消息一般是使用Handler的sendMessage()的方法,而发出的消息会最终进入到Handler的handleMessage()内。
3.MessageQueue:
MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息,这部分消息会一直存在于消息队列中,等待被处理,每个线程内只会又一个MessageQueue对象。
4.Looper:
Looper是每个线程内的MessageQueue的管家,调用Looper的loop()方法,就会进入到一个无限循环当中,然后每当发现MessageQueue内存在一条消息,就将它取出,并传递到Handler的handlerMessage()内,每个线程内也只有一个Looper的对象。
使用AsyncTask
AsynccTask的基本用法:
它是一个抽象类,意味着我们要使用继承的方式来使用它,在继承时,要使用三个泛型参数。、
这三个参数的用途如下:
Params 在执行AsyncTask时候需要传入的参数,可用于在后台任务中执行。
Progress 后台任务执行时候,如果需要在界面上显示当前的进度,则使用这里的指定的泛型作为进度单位。、
Resust 当任务执行完毕后如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
重写AsnyTask的几个方法:
onPreExecute() 这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框。
2.doInBackground(Params…)
这个方法的所以代码都会在子线程内运行,所以要在这里进行任务的耗时操作,任务一旦完成就可以通过return语句来将结果返回,如果AsnyTask的第三个泛型的参数指定的为Void,就可以不返回任务执行的结果。注意这个方法里是不可以进行UI操作的,如果需要更新UI元素,比如反馈当前任务的进度,可以调用publishProgress(Progress…)方法来完成。3.onProgressUpdate(Progress…)
当在后台任务内调用publishProgress(Progress…)方法后,onProgressUpdate(Progress…)方法就很快被调用,该方法内的参数就是在后台任务中传递过来的在这个方法内可以进行对UI操作,利用参数的值来对界面上的UI进行操作。- 4.onPostExecute(Result) 当后台任务的执行完毕,这个方法很快就被调用,返回的数据会作为参数传递到这个方法内,可以利用返回的数据来进行UI操作,比如关闭对话框,提醒任务的结果。
最后启动任务的写法:
class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
}
new DownloadTask().execute();