目录
1.概述
1.1 定义
1.2 创建
1.2.1 方式一
1.2.2 方式二
1.2 在子线程中更新UI
2.案例
2.1 说明
2.2 创建布局
2.3 使用异步消息处理机制解决子线程更新UI
2.4 分析
由于Handler是在主线程中创建的,因此handleMessage()方法里面的代码也会在主线程中运行。
3.同步与异步
3.1 同步
3.2 异步
4.解析异步消息处理机制
4.1 Message
4.2 Handler
4.3 MessageQueue
4.4 Looper
4.5 异步消息流程
4.5 思想
5.使用AsyncTask
1.概述
1.1 定义
Android多线程跟Java多线程类似
1.2 创建
1.2.1 方式一
新建一个类继承自Thread,然后重写其父类的run()方法,并在里面编写逻辑代码即可。如下所示:
public class MainActivity_5 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_4);
//创建MyThread对象
MyThread myThread = new MyThread();
//开启线程
myThread.start();
}
class MyThread extends Thread{
@Override
public void run() {
//处理逻辑代码
}
}
}
1.2.2 方式二
耦合性比较高,更多的时候都会选择使用实现Runnable接口的方式来定义一个线程。如下所示:
public class MainActivity_5 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_4);
//创建MyThread对象
MyThread myThread = new MyThread();
//开启线程:传入一个Runnable对象参数
new Thread(myThread).start();
}
class MyThread implements Runnable{
@Override
public void run() {
//处理逻辑代码
}
}
}
匿名内部类的方式,如下所示:
new Thread(new Runnable() { @Override public void run() { //处理逻辑代码 } }).start();
1.2 在子线程中更新UI
线程不安全的。也就是说想要更新应用程序里面的UI元素,就必须在主线程中进行,否则就会出现异常。
2.案例
2.1 说明
比较在子线程与主线程中更新UI
2.2 创建布局
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/change_text_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="Change Text"/>
<TextView
android:id="@+id/texts"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Hello World"
android:textSize="20sp"
android:textColor="#ed0404"/>
</RelativeLayout>
MainActivity.java:
public class MainActivity extends AppCompatActivity {
private Button button;
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
button = findViewById(R.id.change_text_btn);
text =findViewById(R.id.texts);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//开启子线程
new Thread(new Runnable() {
@Override
public void run() {
//在子线程中更新控件
text.setText("改变内容!");
}
}).start();
}
});
}
}
效果:点击Change Text按钮,TextView控件值更新后,程序崩溃。
程序报错信息如下所示:
E/AndroidRuntime: FATAL EXCEPTION: Thread-1669
Process: com.luckyliuqs.materialdesign, PID: 16751
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6357)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:874)
2.3 使用异步消息处理机制解决子线程更新UI
在MainActivity.java中修改代码如下所示:
public class MainActivity extends AppCompatActivity {
private static final int UPDATE_TEXT = 1;
private Button button;
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
button = findViewById(R.id.change_text_btn);
text =findViewById(R.id.texts);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//开启子线程
new Thread(new Runnable() {
@Override
public void run() {
//创建Message对象
Message message = new Message();
//设置Message的what字段
message.what = UPDATE_TEXT;
//将Message对象发送出去
handler.sendMessage(message);
}
}).start();
}
});
}
private Handler handler = new Handler(){
//处理收到的Message对象方法:里面的代码就是在主线程中运行
public void handleMessage(Message msg){
//匹配Message的what字段
switch (msg.what){
case UPDATE_TEXT:
//在这里进行UI操作
text.setText("内容改变!");
break;
default:
break;
}
}
};
}
2.4 分析
原理是:在子线程里面,Handler发送一个Message对象,然后再Handler接收到Message对象后在主线程里面更新UI。
由于Handler是在主线程中创建的,因此handleMessage()方法里面的代码也会在主线程中运行。
3.阻塞与非阻塞
4.同步与异步
4.1 同步
4.2 异步
将耗时的操作放在异步任务当中来执行,并随时将任务执行的结果返回给我们的UI线程来更新我们的UI控件。通过异步我们可以轻松的解决多线程之间的通信问题。
如果有多个任务执行的话,异步任务是按照顺序一个个执行的。
在Android中实现异步任务机制有两种方式,Handler和AsyncTask。
Handler
Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,
完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,
在多个任务同时执行时,不易对线程进行精确的控制。
AsyncTask使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。
5.解析异步消息处理机制
Message、Handler、MessageQueue和Looper
5.1 Message
传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。前面使用了what字段,除此之外,还可以使用arg1和arg2字段来携带一些整形数据;使用obj字段携带一个object对象。
5.2 Handler
处理者的意思,主要用于发送和处理消息。发送消息使用sendMessage()方法;发出的消息经过辗转处理后,会传递到handleMessage()方法中。
5.3 MessageQueue
消息队列的意思,主要用于存放所有通过Handler发送的消息,这部分消息会一直存在与消息队列中,等待被处理。每个线程只会有一个MessageQueue对象。
5.4 Looper
MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环中,每当发现MessageQueue中存在一条消息,就会将消息取出,并传递到Handler的handleMessage()方法中。同样,每个线程中,只有一个Looper对象。
5.5 异步消息流程
流程如下所示:
1.在主线程中创建一个Handler对象,并重写其handleMessage()方法。2.当子线程需要进行UI操作时,就创建一个Message对象,并通过Handler的sendMessage(Message msg)将消息发送出去。3.发送出去的消息会被添加到MessageQueue的队列中等待被处理。4.Looper在期间会一直尝试从MessageQueue中取出待处理的消息,然后分发回Handler的hanleMessage()方法中5.由于Handler是在主线程中创建的,因此handleMessage()方法里面的代码也会在主线程中运行。
图示如下:
5.5 思想
一个Message经过上面图示的流程辗转调用之后,就从子线程进入到了主线程,从不能更新的UI变成了可更新的UI。
6.使用AsyncTask