亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

全部開發者教程

Android 入門教程

菜單類控件
菜單:Menu
并發編程
多線程

多線程

多線程可以讓你同時異步執行多種任務,是各種編程語言里很重要的一個概念。合理的采用多線程可以讓你的 App 擁有更好的運行性能,但是如果使用不當可能會讓你的程序非?;靵y,出現很多令人費解且難以定位的問題。

1. 多線程初探

當用戶打開一個 App 時,Android 系統會創建一個 Linux 進程,同時在進程中創建一個執行線程,我們稱之為“主線程”,因為 Andfoid 規定只能在主線程更新 UI,所以又叫“UI線程”。
系統在創建主線程的時候幫我們創建好了一套消息處理機制,包含了第 38 節提到的 Handler、Looper、MessageQueue 等模塊,主線程就利用這一套消息機制來實現 Actvity、Fragment 的生命周期回調以及和其他 App 之間的通信。所有需要在 UI 線程執行的任務都要首先被 push 到任務隊列中,然后等待主線程 Looper 來輪詢。如果我們將所有的任務都放到主線程的任務隊列,那么可能需要等很久才能執行到,所以一個比較好的選擇就是將耗時任務單獨放到一個子線程中,這樣就可以獨享一個 MessageQuene,并且不再占用主線程的資源。

2. 多線程注意事項

  • Android 規定刷新 UI 的操作必須在主線程執行;
  • 網絡請求、數據庫或者文件 I / O 等都屬于耗時操作,非常容易導致主線程的阻塞造成 App 卡頓;
  • 由于主線程的 Looper 是按順序輪詢 MessageQueue 的,所以主線程的所有任務都是同步執行。這樣如果有耗時操作那么會阻塞主線程,后面的任務都需要等待耗時操作的執行;
  • 除了 I / O 操作外,開發人員需要自行評估任務的耗時情況,合理采用多線程避免主線程的阻塞;
  • Android 提供了多種創建和管理線程的方法,當然如果有高并發的場景還有一些第三方庫可以使用,但是系統的線程、線程池可以應對大部分常見場景。

接下來我們來看看具體怎么使用 Android 多線程。

3. 線程的使用方法

Java 虛擬機支持多線程并發編程,并發意味著同時執行多個任務。在 Android 中常見的多線程常見就是在子線程執行耗時操作,然后將結果通過線程間通信傳遞給主線程,主線程僅僅拿到結果進行 UI 的刷新。

3.1 線程的創建

我們有兩種方式進行線程的創建

  1. 繼承Thread實現一個線程類:
class TestThread extends Thread {
    @Override
    public void run() {
        Log.d("Threading", "繼承 Thread 的線程:"+Thread.currentThread().getName());
    }
}
  1. 實現Runnable接口
class TestRunnable implements Runnable {
    @Override
    public void run() {
        Log.d("Runable", "實現 Runable 的線程>"+Thread.currentThread().getName());
    }
}

無論是哪種方式,都需要在類中實現一個無參的run()方法,然后將線程的實際執行任務放在run()方法中,在要用多線程的類中需要創建出一個Runnable接口實例。

3.2 啟動進程

對于第一種創建方式,直接創建 TestThread 實例調用start()即可:

new TestThread().start()

而對于第二種方式,在創建 Thread 的同時傳入 Runnable 接口實例,然后調用start()

new Thread(new TestRunnable()).start()

調用了 Thread 對象的 start()之后,run()方法就會在我們的子線程中執行了。

3.3 線程生命周期

和 Activity 一樣,Thread 在執行過程中也有自己的生命周期,一共有 5 種狀態:

  • **New:**剛創建好,還未執行
  • **Runnable:**已經調用了start(),等待 CPU 分配時間片
  • **Running:**正在運行
  • **Blocked:**由于某些原因(等待、睡眠、CPU暫時回收資源等)線程進入阻塞
  • **Dead:**線程任務執行結束,或者主動關閉

各個生命周期的切換如下圖:

thread

4. 多線程示例

本節創建兩個耗時子線程,在線程的開始和結束分別打上日志,然后觀察兩個線程任務是同時執行,還是需要等待其中一個線程的耗時任務執行結束才能執行第二個。
MainActivity 代碼很簡單,在里面創建兩個線程,為了方便演示我們用“繼承自 Thread”和“實現 Runnable”兩種方式來創建兩個線程:


package com.emercy.myapplication;

import android.app.Activity;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.annotation.Nullable;

import java.io.IOException;
import java.util.Random;

import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;

public class MainActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        new Thread() {
            @Override
            public void run() {
                Log.d("ThreadTest", "Thread1 start");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d("ThreadTest", "Thread1 end");

            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("ThreadTest", "Thread2 start");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d("ThreadTest", "Thread2 end");
            }
        }).start();
    }

    private void task() {
        for (int i = 0; i < 10; i++) {
            Log.d("Thread", Thread.currentThread().getName() + " 當前i = " + i);
        }
    }
}

在兩個線程中通過sleep()來模擬 500 毫秒的耗時任務,在任務的開始和結束都打上日志,觀察結果如下:

圖片描述

可以看到首先會同時開啟兩個子線程,然后分別同時執行 500 毫秒的任務,在執行結束打上結束的 Log,可以證明兩個 Thread 是同時執行的。

5. 小結

本節學習了一個能讓你的 App 并發高效執行任務的方式,多線程可以幫助你提升 App 的整體性能,但用之不當可能會造成一定的資源浪費,所以一定要謹記本節所提到的注意事項。然后按照步驟去創建、運行子線程,了解線程執行的生命周期,讓程序更好的為用戶服務。