目录
由于Handler是在主线程中创建的,因此handleMessage()方法里面的代码也会在主线程中运行。
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
和许多其他GUI库一样,Android的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线程来更新我们的UI控件。通过异步我们可以轻松的解决多线程之间的通信问题。
如果有多个任务执行的话,异步任务是按照顺序一个个执行的。
在Android中实现异步任务机制有两种方式,Handler和AsyncTask。
Handler Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,
完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,
在多个任务同时执行时,不易对线程进行精确的控制。
AsyncTask 使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。
5.解析异步消息处理机制
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper
5.1 Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。前面使用了what字段,除此之外,还可以使用arg1和arg2字段来携带一些整形数据;使用obj字段携带一个object对象。
5.2 Handler
Handler顾名思义就是处理者的意思,主要用于发送和处理消息。发送消息使用sendMessage()方法;发出的消息经过辗转处理后,会传递到handleMessage()方法中。
5.3 MessageQueue
MessageQueue就是消息队列的意思,主要用于存放所有通过Handler发送的消息,这部分消息会一直存在与消息队列中,等待被处理。每个线程只会有一个MessageQueue对象。
5.4 Looper
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