Android 開發學習筆記

Lockegogo發表於2024-07-17

Android 開發學習筆記

基本概念

Android 應用程式由一些零散的有聯絡的元件組成,透過一個工程 manifest 繫結在一起。在 manifest 中,描述了每一個元件以及元件的作用,其中有 6 個元件,它們是 Android 應用程式的基石。Android 有四大元件(也有說六大元件的,外加 Intent 和 Notification),分別是 Activity,Service,Content Provider 和 BroadcastReceiver。這四大元件一起組成了完整的 Android 程式。我們將分別簡要介紹。

介面顯示與邏輯處理:

利用 XML 標記描繪應用介面,使用 Java 程式碼書寫程式邏輯。

Activity

  1. Activity 指一個完整的佔了一個螢幕的頁面(上下滑動的內容也算這個介面內的內容,所以它的概念可以理解成類似網站的一個網頁一樣)。
  2. Activity 允許顯示一些控制元件、檢視,並可以監聽處理使用者的事件,做出響應等。Activity 之間透過 Intent 通訊(呼叫、跳轉等動作)。
  3. 一個 Activity 實際上是一個 XML 檔案,它可以被 Android 系統以視覺化的介面展現。每一個 Activity 都與一個 Java 後臺程式相聯絡,這個 Java 程式可以控制這個頁面的啟動、展示以及資料等資訊。頁面上展示的內容可以透過 Activity 本身的 xml 檔案配置,也可以由相聯絡的 Java 檔案來控制。

Service

  1. Service 是服務的意思。是 Android 程式中 “不可見” 的部分,但是它負責更新資料來源、觸發通知等。是一種沒有介面的長生命週期的適合監控或者後臺執行的程式。
  2. 最佳的例子是多媒體播放器,多媒體播放器程式可能含有一個或多個 Activity,使用者透過這些 Activity 選擇並播放音樂。然而,音樂回放並不需要一個 Activity 來處理,因為使用者可能會希望音樂一直播放下去,即使退出了播放器去執行其它程式。為了讓音樂一直播放,多媒體播放器 Activity 可能會啟動一個 Service 在後臺播放音樂。Android 系統會使音樂回放 Service 一直執行,即使在啟動這個 Service 的 Activity 退出之後。
  3. Android 服務有兩種:一是本地服務,另一種是遠端服務。前者只能由託管服務的應用程式訪問,後者是指由裝置上其他應用程式進行遠端訪問的服務。

Content Provider

  1. Content Provider 是指內容提供器。App 執行的時候需要很多外部資料作為支撐,這些資料一般由內容提供器儲存、共享。
  2. 比如,我們可以配置自己的 Content Provider 來存取其他應用程式,或者是透過其他應用程式給出的 Content Provider 來獲取他們的資料。
  3. 系統本身也提供了一些 Content Provider,如聯絡人資訊等。這些資料可以儲存在檔案系統、SQLite 資料庫或者其他一些媒介裡。

BroadcastReceiver

  1. 你的應用可以使用它對外部事件進行過濾,只對感興趣的外部事件(如當電話呼入時,或者資料網路可用時)進行接受並作出響應。
  2. 廣播接收器沒有使用者介面,然而,它們可以啟動一個 activity 或 serice 來響應它們收到的資訊,或者用 NotificationManager 來通知使用者。
  3. 通知可以用很多種方式來吸引使用者的注意力──閃動背燈、震動、播放聲音等。一般來說是在狀態列上放一個持久的圖示,使用者可以開啟它並獲取訊息。

Intent

  1. Intent(意圖)是各種元件之間通訊的橋樑,可以用來連線 Android 的應用元件,它提供了一種在不同應用之間進行任務執行繫結的工具,它最重要的應用是啟動活動 Activity。
  2. Intent 是非同步訊息,可以幫助一個應用元件從另一個元件中請求功能。
  3. Intent 是一個物件,即 android.content.Intent

Notification

  1. Notification 是通知元件,主要是和推送使用者資訊有關。

Android App 專案中的檔案簡介

  1. AndroidManifest.xml 檔案:在 mainfests 資料夾下面,叫做清單檔案,它描述了整個專案的資訊,包括專案名稱、SDK 版本等等。我們在應用程式中的每個活動必須在 AndroidManifest.xml 檔案中宣告,系統需要根據裡面的內容執行 APP 程式碼,顯示介面,注意這個檔名一個字都不能錯。

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
    
        <activity android:name=".SmsNewActivity">
            <!-- <intent-filter>-->
            <!-- <action android:name="android.intent.action.MAIN" />-->
            <!-- <category android:name="android.intent.category.LAUNCHER" />-->
            <!-- </intent-filter>-->
        </activity>
        <activity android:name=".NerEvalActivity">
            <!-- 下面兩行程式碼設定程式入口 -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    
  2. Java 資料夾:存放 java 源程式的地方。注意我們這裡有一些以 Activity 結尾的程式檔案,每一個檔案其實對應了一個 Activity 的頁面,也就是和下面資原始檔夾(res)中的 layout 裡面的內容繫結的。

  3. Res 資料夾:存放 java 資源的地方,包括圖片、佈局檔案、選單等等。

    1. drawable 資源:存放點陣圖的資料夾

    2. layout 資源:存放的是佈局資源,也就是指 Android 裡面的活動和檢視。在 Android 中,佔用一個螢幕的 UI 介面稱之為 Activity(活動),頁面中的按鈕、標籤、文字欄位等稱之為 View(檢視)。一個活動通常包含一個或者多個檢視(也就是一個頁面裡面有按鈕,文字之類的東西)。這裡的佈局檔案都是 XML 檔案,因為 Android 中檢視都是從 XML 檔案載入的,裡面描述了位置、大小等檢視資訊。佈局資源下每個檔案都將根據其檔名(不包含副檔名)生成一個唯一的常量 ID,可以透過某些手段與 java 原始檔繫結,或者被其他頁面呼叫。

    3. values 資源:是 Android 中存放陣列、顏色、尺寸、字串和樣式的資料夾。其實就是統一存放所有變數的地方,比如主題顏色、app 名稱、Logo 的樣式等,在 values 資源下統一定義可以使得我們在各個地方都呼叫同樣的資源,在修改的時候也只要更改一處即可。

    /res/values/strings.xml
    /res/values/colors.xml
    /res/values/dimens.xml
    /res/values/attrs.xml
    /res/values/styles.xml
    
    # 定義資源:尖括號定義資源型別,name 表示資源名稱,裡面表示內容
    <string name="app_name">樂購</string>
    <string name="edit_message">請輸入您想查詢的地點</string>
    
    1. minmap:存放程式啟動圖示的資料夾。一般只存放啟動圖示(就是桌面圖示)。
  4. Gradle Scripts:工程的編譯配置檔案

    1. build.gradle:分為專案級與模組級,用於描述 App 工程的編譯規則
    2. proguard-rules.pro:用於描述 java 程式碼的混淆規則,對編譯好的 class 檔案進行混淆處理,目的是防止 java 程式碼被反編譯
    3. gradle.properties:用於配置編譯工程的命令列引數,一般無須改動
    4. settings.gradle:配置了需要編譯哪些模組。初始內容為 include ':app' ,表示只編譯 app 模組
    5. local.properties:專案的本地配置檔案,在工程編譯時自動生成,用於描述開發者電腦的環境配置,包括 SDK 的本地路徑,NDK 的本地路徑等
    6. 注意每個版本的 Android Studio 都有對應的 Gradle 版本,只有二者的版本正確對應,App 工程才能成功編譯,比如 Android studio 4.1 對應的 Graddle 版本為 6.5。

Android 中的資源訪問(R 類 / R.java)

在 Android 開發中,所有的外部資源都透過其資源的 ID 來訪問,而所有的資源 ID 都在專案中 R 類中定義,而 R.java 這個類是由 aapt 工具自動生成的,使用者本身不用修改新增。只要在資源中申明瞭 ID,那麼 R 類會自動將該資源新增到其中。

編譯應用時,aapt 會生成 R 類,其中包含您的 res/ 目錄中所有資源的資源 ID。 每個資源型別都有對應的 R 子類(例如,R.drawable 對應於所有可繪製物件資源),而該型別的每個資源都有對應的靜態整型數(例如,R.drawable.icon)。這個整型數就是可用來檢索資源的資源 ID。

定義資源 ID 主要包括兩個部分,一個是資源型別如 string、drawable 和 layout 等。另一個是資源名稱,不包括其副檔名(當然也可以是 xml 中 android:name 屬性中的值)。

訪問這些資源有兩種方式,一種是在 Java 程式中,一種是在 XML 檔案中。

// Java 程式中訪問資源,如下面的程式設定內容顯示為某個 activity
// 可以使用 R.layout.activity 名稱的方式。其中 layout 是資源型別,後面的是資源名稱
setContentView(R.layout.activity_display_message);
// 這種方式是在 XML 檔案中訪問資源,使用 @ 開頭表示什麼型別,然後斜槓後面寫上資源名稱
@string/hello

NDK 相關

  1. NDK 是 Native Development Kit 的縮寫,是 Android 的工具開發包。
  2. 作用是快速開發 C/C++ 的動態庫,並自動將動態庫與應用一起打包到 apk 。

參考:https://blog.csdn.net/afei__/article/details/80897404

開發步驟

建立新的 App 頁面

完整的頁面建立過程包括三個步驟:

  1. 在 layout 目錄下建立 XML 檔案
  2. 建立與 XML 檔案對應的 Java 程式碼
  3. 在 AndroidManifest.xml 中註冊頁面配置

右鍵之後可以一鍵建立 Activity,系統同時幫我們做好上述三件事情。

實現介面的跳轉

點選事件和長按事件:

  1. 監聽器:意思是專門監聽控制元件的動作行為,只有控制元件發生了指定的動作,監聽器才會觸發開關去執行對應的程式碼邏輯。
  2. 常用的兩種監聽器:
    1. 點選監聽器:透過 setOnClickListener 方法設定
    2. 長按監聽器:透過 setOnLongClickListener 方法設定
public class LKdemoActivity extends AppCompatActivity {
    // 注意 onCreate 方法有兩個,我們需要選擇引數是 @Nullable Bundle savedInstanceState 的這一個
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lk);
        // 跳轉到 MobileRecActivity
        Button button = findViewById(R.id.button2);
        // 點選監聽器
        button.setOnClickListener((new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                // 設定跳轉的 Activity
                intent.setClass(LKdemoActivity.this, MobileRecActivity.class);
                startActivity(intent);
            }
        }));

        TextView tv_hello = findViewById(R.id.tv_hello);
        tv_hello.setText(R.string.welcome);
        tv_hello.setTextSize(30);
    }
}

還有一種設定監聽的方式:button.setOnClickListener(this)

// 如果監聽很多,不要建立太多類,可以公用這個類
public class LKdemoActivity extends AppCompatActivity implements View.OnClickListener {
    // 注意 onCreate 方法有兩個,我們需要選擇引數是 @Nullable Bundle savedInstanceState 的這一個
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lk);
        // 跳轉到 MobileRecActivity
        Button button = findViewById(R.id.button2);
        // 點選監聽器
        button.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        // 有很多監聽事件,我們需要判斷是哪個按鈕被點選了
        if (view.getId() == R.id.button2) {
            Intent intent = new Intent();
            // 設定跳轉的 Activity
            intent.setClass(LKdemoActivity.this, MobileRecActivity.class);
            startActivity(intent);
        }
}

Button 點選之後顯示時間

直接在 layout 中的 XML 檔案中設定:需要知道呼叫的方法名稱(高耦合)

<Button
    android:layout_width="0dp"
    android:layout_height="40dp"
    android:layout_marginBottom="20dp"
    android:layout_weight="1"
    // 在此處進行設定,點選之後執行 doClick 函式
    android:onClick="doClick" />
public class LKdemoActivity extends AppCompatActivity {
    // bt_time 在兩個函式中都要用,所以要設定為類變數
    private TextView bt_time;

    // 注意 onCreate 方法有兩個,我們需要選擇引數是 @Nullable Bundle savedInstanceState 的這一個
    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lk);
        bt_time = findViewById(R.id.tv_result6);
    }

    public void doClick(View view) {
        String desc = String.format("%s 您好,歡迎使用按鈕: %s", Common.getNowTime(), ((Button) view).getText());
        bt_time.setText(desc);
    }
}

// 在 utils 資料夾 Common 類增加獲取當前時間的函式
public static String getNowTime() {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    return sdf.format(new Date());
}

在 activity 檔案對應的 xml 檔案中增加一個 button 和一個點選之後顯示內容的文字框:

<Button
    android:id="@+id/button6"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="顯示時間"
    android:onClick="doClick"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.489"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.408" />

<TextView
    android:id="@+id/tv_result6"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="這裡檢視按鈕的點選結果"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.498"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.335" />

Activity

啟停活動頁面

Activity 的啟動和結束

  1. 從當前頁面跳到新頁面:
// Intent 的建構函式需要傳入兩個引數:上下文和目標元件的 Class 物件。
startActivity(new Intent(源頁面.this, 目標頁面.class))
  1. 從當前頁面回到上一個頁面,相當於關閉當前頁面
finish() // 結束當前的活動頁面

Activity 的生命週期

img

private static final String TAG ="ning";

protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "onCreate: LKdemoActivity");
}

@Override
protected void onStart() {
    super.onStart();
    Log.d(TAG, "onStart: LKdemoActivity");
}

// 在 Logcat 控制檯輸入 tag:ning 進行過濾篩選
  1. onCreate():建立活動,把頁面佈局載入進記憶體,進入初始狀態
  2. onStart():開始活動,把活動頁面顯示在螢幕上,進入就緒狀態
  3. onResume():修復活動,活動頁面進入活躍狀態,能夠與使用者正常互動,例如允許響應使用者的點選動作、允許輸入文字等
  4. onPause():暫停活動,無法與使用者正常互動,Activity 處於部分可見狀態。這發生在 Activity 失去焦點但仍部分可見的時候,例如有一個對話方塊或透明的 Activity 遮蓋在其上。如果有動畫,在這裡暫停
  5. onStop():停止活動,Activity 處於不可見狀態。Activity 將停止顯示在螢幕上,並且它失去了使用者焦點,但仍然保留在 Activity 堆疊中
  6. onDestroy():銷燬活動,Activity 將被銷燬。這發生在使用者按下 "Back" 鍵或呼叫 finish() 方法關閉 Activity 時。一旦 Activity 被銷燬,它就會從 Activity 堆疊中移除
  7. onNewIntent:重用已有的活動例項

Activity 的啟動模式

我們可以在配置檔案中指定啟動模式:

<activity android:name=".LKdemoActivity" android:launchMode="standard" />

在 Android 中,Activity 的啟動模式(Launch Mode)定義了 Activity 如何在任務棧中啟動和管理。不同的啟動模式可以影響 Activity 的例項化和任務棧的行為。以下是常見的 Activity 啟動模式:

  1. Standard(標準模式):這是預設的啟動模式。每次啟動一個 Activity,都會建立一個新的例項,並將其放入任務棧的頂部。無論是否已經存在相同的例項,都會建立新的例項。
  2. SingleTop(單頂模式):在 SingleTop 模式下,如果要啟動的 Activity 已經位於任務棧的頂部(即棧頂有一個相同的例項),則不會建立新的例項,而是呼叫現有例項的 onNewIntent() 方法來傳遞新的 Intent 資料。如果 Activity 不在棧頂,仍然會建立新的例項。
  3. SingleTask(單任務模式):在 SingleTask 模式下,系統會確保一個特定的 Activity 只存在於一個任務棧中的一個例項。如果要啟動的 Activity 已經在其他任務棧中存在,系統會將該任務棧移到前臺,並呼叫現有例項的 onNewIntent() 方法傳遞新的 Intent 資料。如果要啟動的 Activity 在當前任務棧中已經存在,則不會建立新的例項,而是將任務棧中其他 Activity 移除,使該 Activity 變為棧頂。

單任務模型的應用場景:

  • 程式主介面:我們肯定不希望主介面被建立多次,而且在主介面退出的時候退出整個 app 是最好的效果
  • 耗費系統資源的 activity:設定為單任務模式可以減少資源耗費
  1. SingleInstance(單例項模式):SingleInstance 是最特殊的啟動模式。在 SingleInstance 模式下,系統會為該 Activity 建立一個新的任務棧,並且該任務棧中只會存在一個例項。其他應用程式的 Activity 無法與其共享任務棧。這種模式適用於需要獨立存在且與其他應用程式隔離的 Activity。

  2. 在 JAVA 程式碼中動態設定啟動模式

    1. 在兩個頁面之間跳來跳去:設定 setFlags 以避免重複跳轉
    public void onClick(View view) {
        // 有很多監聽事件,我們需要判斷是哪個按鈕被點選了
        if (view.getId() == R.id.button2) {
            Intent intent = new Intent();
            // 設定跳轉的 Activity
            intent.setClass(LKdemoActivity.this, MobileRecActivity.class);
            // 棧中存在待跳轉的活動例項時,則重新建立該活動的例項,並清除原例項上方的所有例項
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);
        }
    
    1. 登入成功後不再返回登入介面:設定啟動標誌 FLAG_ACTIVITY_CLEAR_TASK,該標誌會清空當前活動棧裡的所有例項,不過全部清空之後,意味著當前棧沒法用了,必須另外找個活動棧才行,也就是同時設定啟動標誌 FLAG_ACTIVITY_NEW_TASK,該標誌用於開闢新任務的活動棧。這種操作可以實現從登入介面登入成功之後,無法再次返回到登入介面。
    Intent intent = new Intent();
    // 設定跳轉的 Activity
    intent.setClass(LKdemoActivity.this, MobileRecActivity.class);
    // 設定啟動標誌:跳轉到新頁面時,棧中的原有例項都被清空,同時開闢新任務的活動棧
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
                    Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);  // 跳轉到意圖指定的活動頁面
    

在活動之間傳遞訊息

元素名稱 設定方法 說明與用途
componentName setComponent 指定意圖的來源與目標
action(動作) setAction 指定意圖的動作行為
category(類別) addCategory 指定意圖的操作類別
data(資料) setData 指定動作要操縱的資料路徑
type(資料型別) setType 指定訊息的資料型別
extras(擴充套件資訊) putExtras 指定裝載的包裹資訊
Flags(標誌位) setFlags 指定活動的啟動標誌

顯式 Intent 和隱式 Intent

  1. Intent 是各元件之間資訊溝通的橋樑,用於 Android 各元件之間的通訊,主要完成下列工作:

    1. 標明本次通訊請求從哪裡來、到哪裡去、要怎麼走;
    2. 發起方攜帶本次通訊需要的資料內容,接受方從收到的意圖中解析資料;
    3. 發起方若想判斷接受方的處理結果,意圖就要負責讓接受方傳回應答的資料內容。
  2. 顯式 Intent:直接指定來源活動與目標活動,屬於精確匹配,有三種構建方式:

    1. 在 Intent 的建構函式中指定
    // 建立一個目標明確的意圖
    Intent intent = new Intent(CurrentActivity.this, NextActivity.class)
    
    1. 呼叫意圖物件的 setClass 方法指定
    // 建立一個新意圖
    Intent intent = new Intent();
    // 設定跳轉的 Activity
    intent.setClass(CurrentActivity.this, NextActivity.class);
    
    1. 呼叫意圖物件的 setComponent 方法指定
    // 建立一個新意圖
    Intent intent = new Intent();
    // 建立包含目標活動在內的元件名稱物件
    ComponentName component = new ComponentName(CurrentActivity.this, NextActivity.class);
    // 設定意圖攜帶的元件資訊
    intent.setComponent(component);
    
  3. 隱式 Intent:沒有明確指定要跳轉的目標活動,只給出一個動作字串讓系統自動匹配,屬於模糊匹配

    1. App 不希望向外部暴露活動名稱,只給出一個事先定義好的標記串,約定俗成就好
    Intent intent = new Intent();
    // 設定意圖動作為準備撥號
    intent.setAction(Intent.ACTION_DIAL);
    

向下一個 Activity 傳送資料

  1. Intent 使用 Bundle 物件存放待傳遞的資料資訊
  2. 在程式碼中傳送訊息包裹,呼叫意圖物件的 putExtras 方法,即可存入訊息包裹
  3. 在程式碼中接收訊息包裹,呼叫意圖物件的 getExtras 方法,即可取出訊息包裹
Intent intent = new Intent(CurrentActivity.this, NextActivity.class);
// 1. 可以直接傳入一個字串
intent.putExtra("key", value);

// 2. 也可以建立一個包裹
Bundle bundle = new Bundle();
bundle.putString("request_time", DateUtil.getNowTime());
bundle.putString("request_content", tv_send.getText().toString());
intent.putExtras(bundle);

startActivity(intent);

// 1. 在目標 Activity 中接收資料:
Intent intent = getIntent();
String name = intent.getStringExtra("key");

// 2. 在目標 Activity 中接收包裹
Bundle bundle = getIntent().getExtras();
String request_time = bundle.getString("request_time");
String request_content = bundle.getString("request_content");

向上一個 Activity 傳送資料

在 Android 中,Activity 之間的資料傳遞是單向的,即從一個 Activity 向另一個 Activity 傳送資料。如果你想要從目標 Activity 返回資料給上一個 Activity,可以透過以下步驟實現:

  1. 在目標 Activity 中,建立一個新的 Intent 並使用 putExtra() 方法來新增要返回的資料到 Intent 中。
Intent intent = new Intent();
intent.putExtra("result", "這是返回的資料");
  1. 在目標 Activity 中呼叫 setResult() 方法來設定結果碼和返回的 Intent。
setResult(Activity.RESULT_OK, intent);
  1. 在目標 Activity 中呼叫 finish() 方法來關閉當前 Activity,並返回到上一個 Activity。
finish();
  1. 在上一個 Activity 中,可以在 onActivityResult() 方法中接收返回的資料。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        if (data != null && data.hasExtra("result")) {
            String result = data.getStringExtra("result");
            // 在這裡處理返回的資料
        }
    }
}

上述步驟中的 REQUEST_CODE 是一個自定義的請求碼,用於標識請求的來源。在呼叫 startActivityForResult() 方法啟動目標 Activity 時,可以傳遞這個請求碼。

示例程式碼如下:

  1. 在當前 Activity 中啟動目標 Activity:
private static final int REQUEST_CODE = 1;

// 啟動目標
ActivityIntent intent = new Intent(CurrentActivity.this, TargetActivity.class);
startActivityForResult(intent, REQUEST_CODE);
  1. 在目標 Activity 中返回資料:
// 在目標 Activity 中設定返回資料
Intent intent = new Intent();
intent.putExtra("result", "這是返回的資料");
setResult(Activity.RESULT_OK, intent);
finish();
  1. 在上一個 Activity 中接收返回的資料:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        if (data != null && data.hasExtra("result")) {
            String result = data.getStringExtra("result");
            // 在這裡處理返回的資料
        }
    }
}

透過這種方式,你可以在目標 Activity 中返回資料給上一個 Activity,並在上一個 Activity 中獲取並處理這些返回的資料。

Android AsyncTask 非同步任務

AsyncTask 是 Android 中用於在後臺執行緒執行非同步任務並在主執行緒更新 UI 的類。它提供了一種簡單的方法來執行後臺計算、網路請求或其他耗時操作,然後將結果返回到主執行緒,以便更新使用者介面。AsyncTask 可以幫助開發者避免在主執行緒執行耗時操作而導致的介面卡頓和 ANR(Application Not Responding)問題。

AsyncTask 包含了四個關鍵的回撥方法,它們分別是:

  1. onPreExecute(): 在後臺任務開始執行之前,在主執行緒中呼叫。通常用於初始化 UI 或顯示進度對話方塊。
  2. doInBackground(Params...): 在後臺執行緒中執行耗時操作的方法。在該方法內部進行計算、網路請求或其他耗時任務,但不要更新 UI。doInBackground 結束後會返回一個 String 型別的資料到 onPostExecute 中,接下來 onPostExecute 就可以使用這個返回的資料來做 UI 更新的邏輯了。
  3. onProgressUpdate(Progress...): 當呼叫 publishProgress(Progress...) 方法時,在主執行緒中呼叫。通常用於更新 UI 進度。
  4. onPostExecute(Result): 在後臺任務執行完成後,在主執行緒中呼叫。通常用於處理後臺任務的結果,並更新 UI。

使用 AsyncTask 的步驟如下:

  1. 建立一個繼承自 AsyncTask 的子類,並指定泛型引數:Params(傳遞給 doInBackground 的引數型別)、Progress(傳遞給 onProgressUpdate 的引數型別)、Result(傳遞給 onPostExecute 的引數型別)。
  2. 實現 doInBackground 方法,執行後臺任務,並在需要時呼叫 publishProgress 方法來更新進度。
  3. 實現 onPostExecute 方法,處理後臺任務的結果,並更新 UI。
  4. 在主執行緒中例項化並執行 AsyncTask,呼叫 execute() 方法。

下面是一個簡單的示例,演示了使用 AsyncTask 進行後臺計算,並在完成後更新 UI。

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private ProgressBar progressBar;
    private TextView resultTextView;
    private Button calculateButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        progressBar = findViewById(R.id.progressBar);
        resultTextView = findViewById(R.id.resultTextView);
        calculateButton = findViewById(R.id.calculateButton);

        calculateButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 執行 AsyncTask
                MyAsyncTask myAsyncTask = new MyAsyncTask();
                myAsyncTask.execute(10); // 將計算的引數傳遞給 AsyncTask
            }
        });
    }

    private class MyAsyncTask extends AsyncTask<Integer, Integer, Integer> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // 初始化 UI 或顯示進度對話方塊
            progressBar.setVisibility(View.VISIBLE);
            calculateButton.setEnabled(false);
            resultTextView.setText("");
        }

        @Override
        protected Integer doInBackground(Integer... params) {
            // 後臺計算任務
            int num = params[0];
            int result = 0;
            for (int i = 1; i <= num; i++) {
                // 假設這是一個耗時的計算任務
                result += i;
                // 更新進度
                // publishProgress 方法被呼叫後,會觸發 onProgressUpdate 方法的執行
                // 這裡的引數會自動傳遞給 onProgressUpdate
                // 注意:不能寫成 onProgressUpdate(i * 10),因為這樣會導致在錯誤的執行緒中執行
                publishProgress(i * 10);
            }
            return result;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // 更新 UI 進度
            progressBar.setProgress(values[0]);
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);
            // 處理計算結果,並更新 UI
            progressBar.setVisibility(View.GONE);
            calculateButton.setEnabled(true);
            resultTextView.setText("計算結果:" + result);
        }
    }
}

在這個示例中,當使用者點選按鈕時,會觸發 AsyncTask 的執行。doInBackground 方法會在後臺執行緒中執行計算任務,並使用 publishProgress 方法來更新進度。在 onProgressUpdate 中更新 UI 進度,最終在 onPostExecute 中更新計算結果。這樣,整個計算過程在後臺進行,不會阻塞主執行緒,並且在計算完成後更新了 UI。

參考資料

  1. Android 開發入門基礎

相關文章