Android Intent基礎

Intent 是 Android 一個常用的用於組件間互相通信的信息對象,它不僅可以指明當前組件想要執行的動作,還可以在不同組件之間進行數據傳遞。

基本用例主要包括以下三個:

啓動 Activity

Activity 表示應用中的一個屏幕。通過將 Intent 傳遞給 startActivity(),您可以啓動新的 Activity 實例。Intent 用於描述要啓動的 Activity,並攜帶任何必要的數據。如果您希望在 Activity 完成後收到結果,請調用 startActivityForResult()。在 Activity 的onActivityResult() 回調中,您的 Activity 將結果作爲單獨的 Intent 對象接收。

啓動服務

Service 是一個不使用用戶界面而在後臺執行操作的組件。使用 Android 5.0(API 級別 21)及更高版本,您可以啓動包含 JobScheduler 的服務。

對於 Android 5.0(API 級別 21)之前的版本,您可以使用 Service 類的方法來啓動服務。通過將 Intent 傳遞給 startService(),您可以啓動服務執行一次性操作(例如,下載文件)。Intent 用於描述要啓動的服務,並攜帶任何必要的數據。

如果服務旨在使用客戶端-服務器接口,則通過將 Intent 傳遞給 bindService(),您可以從其他組件綁定到此服務。

傳遞廣播

廣播是任何應用均可接收的消息。系統將針對系統事件(例如:系統啓動或設備開始充電時)傳遞各種廣播。通過將 Intent 傳遞給 sendBroadcast() 或 sendOrderedBroadcast(),您可以將廣播傳遞給其他應用。

Intent大致可以分爲兩種:顯式Intent和隱式Intent。

那麼如何構建一個Intent呢?我們常見的代碼中經常使用Intent以下的構造函數:

Intent(Context, Class) 
Context:應用和組件提供 Context 
Class:Class 對象

Intent 對象攜帶 Android 系統用來確定要啓動哪個組件的信息(例如,準確的組件名稱或應當接收該 Intent 的組件類別),以及收件人組件爲了正確執行操作而使用的信息(例如,要採取的操作以及要處理的數據)。

Intent 中包含的主要信息如下:

組件名稱:要啓動的組件名稱。 這是可選項,但也是構建顯式 Intent 的一項重要信息,這意味着 Intent應當僅傳遞給由組件名稱定義的應用組件。如果沒有組件名稱,則 Intent 則爲隱式,且系統將根據其他 Intent信息(例如,以下所述的操作、數據和類別)決定哪個組件應當接收 Intent

操作:指定要執行的通用操作(例如,查看或選取)的字符串。
對於廣播 Intent,這是指已發生且正在報告的操作。操作會在很大程度上決定其餘 Intent 的構成,特別是數據和 extra 中包含的內容。

數據: 引用待操作數據和/或該數據 MIME 類型的 URI(Uri 對象)。提供的數據類型通常由 Intent 的操作決定。例如,如果操作是ACTION_EDIT,則數據應包含待編輯文檔的 URI。

類別:一個包含應處理 Intent 組件類型的附加信息的字符串。您可以將任意數量的類別描述放入一個 Intent 中,但大多數 Intent 均不需要類別。

Extra:攜帶完成請求操作所需的附加信息的鍵值對。正如某些操作使用特定類型的數據 URI 一樣,有些操作也使用特定的 extra。
您可以使用各種 putExtra() 方法添加 extra 數據,每種方法均接受兩個參數:鍵名和值。您還可以創建一個包含所有 extra 數據的 Bundle 對象,然後使用 putExtras() 將 Bundle 插入 Intent 中。

標誌 :標誌在 Intent 類中定義,充當 Intent 的元數據。標誌可以指示 Android 系統如何啓動 Activity(例如,Activity 應屬於哪個任務),以及啓動之後如何處理

顯式Intent:通過提供目標應用的軟件包名稱或完全限定的組件類名來指定可處理 Intent 的應用。通常,我們會在自己的應用中使用顯式 Intent 來啓動組件,這是因爲我們知道要啓動的 Activity 或服務的類名。

在我們上一篇的基礎上,我們稍作修改並且添加第二個Activity(QIntentOne)用來展示Intent的使用。

修改第一個Activity的顯示文字:
在這裏插入圖片描述
QIntentOne佈局文件:
在這裏插入圖片描述

QFirstActivity是我們的啓動頁面,所以創建Intent的過程應該在這個頁面中,然後在啓動QIntentOne這個Activity.

QFirstActivity

kotlin代碼

import kotlinx.android.synthetic.main.qfirst_layout.*;

class QFirstActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState);
        //加載佈局文件
        setContentView(R.layout.qfirst_layout);

        firstBtn.setOnClickListener {
            val intent = Intent(this,QIntentOne::class.java);
            startActivity(intent);
        }
    }
}

這裏有些東西需要坐下解釋,避免有些朋友不太明白。

kotlin-android-extensions是kotlin爲Android專門提供的擴展插件,在本例中用來替代findViewById功能。

要想使用此功能,需要在build.gradle文件中進行相應的配置,不過Android Studio已經爲我們添加好了,當然了這是因爲選擇的是基於kotlin語言。
在這裏插入圖片描述

配置完成我們需要導入對應的佈局文件,然後只需要使用控件的id就可以操作控件。

 import kotlinx.android.synthetic.main.qfirst_layout.*;

格式:kotlinx.android.synthetic.main.佈局名稱.*

當然了,也可以進行局部導入,需要哪個控件就導入哪一個。比如:

 import kotlinx.android.synthetic.main.qfirst_layout.firstBtn;

Java代碼

public class QFirstActivity extends AppCompatActivity {

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

        Button firstBtn = (Button) findViewById(R.id.firstBtn);

        firstBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(QFirstActivity.this, QIntentOne.class);
                startActivity(intent);
            }
        });
    }
}

希望大家可以留意兩者寫法差異。具體的我們就不在仔細研究了。首先我們知道能夠怎麼寫,怎麼用,其他的有興趣可以自己仔細的查閱相關資料。

運行效果:
在這裏插入圖片描述

在這裏插入圖片描述

隱式 Intent :不指定特定的組件,但必須聲明要執行的常規操作,從而允許其他應用中的組件來處理。例如,如需在地圖上向用戶顯示位置,則可以使用隱式 Intent,請求另一具有此功能的應用在地圖上顯示指定的位置。

隱式 Intent 指定能夠在可以執行相應操作的設備上調用任何應用的操作。如果您的應用無法執行該操作而其他應用可以,且您希望用戶選取要使用的應用,則使用隱式 Intent 非常有用。

在我們使用Intent的過程中,推薦使用resolveActivity來驗證 Activity 是否會接收 Intent。如果結果爲非空,則至少有一個應用能夠處理該 Intent,並且可以安全調用startActivity()。如果結果爲空,不要使用該 Intent。

我們想一下,QIntentOne這個Activity目前能夠響應什麼樣的Intent呢?好像沒有吧?

要想讓我們的Activity響應隱式Intent,那麼第一步我們需要在清單文件AndroidManifest.xml中使用 元素爲每個應用組件聲明一個或多個 Intent 過濾器。每個 Intent 過濾器均根據 Intent 的操作、數據和類別指定自身接受的 Intent 類型。僅當隱式 Intent 可以通過 Intent 過濾器之一傳遞時,系統纔會將該 Intent 傳遞給應用組件。

intent-filter 內部,可以使用以下三個元素中的一個或多個指定要接受的 Intent 類型:

action在 name 屬性中,聲明接受的 Intent 操作。該值必須是操作的文本字符串值,而不是類常量。
data使用一個或多個指定數據 URI(scheme、host、port、path)各個方面和 MIME 類型的屬性,聲明接受的數據類型。
category在 name 屬性中,聲明接受的 Intent 類別。該值必須是操作的文本字符串值,而不是類常量。要接收隱式 Intent,必須將 CATEGORY_DEFAULT 類別包括在 Intent 過濾器中,否則隱式 Intent 不會解析該 Activity.

定義自己的操作,請確保加入應用的軟件包名稱作爲前綴,如下action內容區域所示。
在這裏插入圖片描述

修改QFirstActivity,以隱式Intent方式啓動QIntentOne.

kotlin代碼
class QFirstActivity : AppCompatActivity() {

    private val ACTION_INTENT_START = "com.qiushangge.android_kotlin.INTENT_START";

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.qfirst_layout);

        firstBtn.setOnClickListener {
            // val intent = Intent(this,QIntentOne:: class.java);
            val intent = Intent().apply {
                action = ACTION_INTENT_START;
            }
            if (intent.resolveActivity(packageManager) != null) {
                startActivity(intent);
            }
        }
    }
}

Java代碼
package com.qiushangge.android_java;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class QFirstActivity extends AppCompatActivity {

    private static final String ACTION_INTENT_START = "com.qiushangge.android_kotlin.INTENT_START";

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

        Button firstBtn = (Button) findViewById(R.id.firstBtn);

        firstBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Intent intent = new Intent(QFirstActivity.this, QIntentOne.class);
                Intent intent = new Intent(ACTION_INTENT_START);
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(intent);
                }
            }
        });
    }
}

注意:每個Intent只能指定一個action,但是能指定多個category

如果有多個應用響應隱式 Intent,則用戶可以選擇要使用的應用,並將其設置爲該操作的默認選項。如果用戶可能希望每次使用相同的應用執行某項操作(例如,打開網頁時,用戶往往傾向於僅使用一種網絡瀏覽器),則選擇默認選項的功能十分有用。

但是,如果多個應用可以響應 Intent,且用戶可能希望每次使用不同的應用,則應採用顯式方式顯示選擇器對話框。選擇器對話框會要求用戶選擇用於操作的應用(用戶無法爲該操作選擇默認應用)

要顯示選擇器,請使用 createChooser() 創建 Intent,並將其傳遞給 startActivity()。

kotlin代碼

           val intent = Intent().apply {
                action = Intent.ACTION_SEND
                putExtra(Intent.EXTRA_TEXT, "測試數據發送")
                type = "text/plain"
            }
            val chooser: Intent = Intent.createChooser(intent, "測試");
            if (intent.resolveActivity(packageManager) != null) {
                startActivity(chooser);
            }

java代碼

     			Intent intent = new Intent(Intent.ACTION_SEND);
                intent.putExtra(Intent.EXTRA_TEXT, "測試數據發送");
                intent.setType("text/plain");
                Intent chooser = Intent.createChooser(intent, "測試");
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(chooser);
                }

順便看一個調用系統撥號界面的小例子。

kotlin代碼
 firstBtn.setOnClickListener {
            val intent = Intent(Intent.ACTION_DIAL).apply {
                data = Uri.parse("tel:10000");
            }
            if (intent.resolveActivity(packageManager) != null) {
                startActivity(intent);
            }
Java代碼
        firstBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_DIAL);
                intent.setData(Uri.parse("tel:10000"));
                if (intent.resolveActivity(getPackageManager()) != null) {
                    startActivity(intent);
                }
            }
        });

其中Intent.ACTION_DIAL爲Android內置動作,使用setData方法設置協議和數據,更多內容請自行查閱文檔。

在這裏插入圖片描述

Intent向下一個ACtivity數據傳遞

上面我們使用Intent進行Activity的啓動操作,實際過程中,啓動新的Activity的同時,我們在有些情況下需要將一些特定的數據傳遞至該Activity中。

Intent傳遞數據可以使用下面的方式:

Intent.putExtra(鍵名,鍵值)

假設一個場景,我們在QFirstActivity頁面中得到了一個學生的年齡,姓名等信息,在QIntentOne頁面中我們需要對這些信息進行編輯,那麼我們就需要進行數據的傳遞。

修改QFirstActivity頁面佈局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="姓名">

        </TextView>

        <EditText
            android:id="@+id/nameText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="請輸入姓名:">

        </EditText>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="年齡"
            >

        </TextView>

        <EditText
            android:id="@+id/ageText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="請輸入年齡:">

        </EditText>
    </LinearLayout>
    <Button
        android:id="@+id/firstBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="傳遞數據">

    </Button>
</LinearLayout>

在這裏插入圖片描述
QIntentOne頁面佈局類似,就不再貼出來。。。下面的代碼就不放kotlin實現了,這裏就算是一個小小的對比,如果開發android使用kotlin在搭配Anko 確實寫起來方便很多。不過學習也是需要代價的。。

QFirstActivity中我們需要使用putExtra方法傳遞數據,實現如下:

	    String name = nameEdit.getText().toString();
        String age = ageEdit.getText().toString();
        Intent intent = new Intent(QFirstActivity.this,QIntentOne.class);
        intent.putExtra("Name",name);
        intent.putExtra("Age",age);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }

然後在QIntentOne中接收數據並顯示出來:

package com.qiushangge.android_java;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class QIntentOne extends AppCompatActivity {
    private EditText nameEdit;
    private EditText ageEdit;
    private Button backBtn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qintent_one);

        nameEdit = findViewById(R.id.nameText);
        ageEdit = findViewById(R.id.ageText);
        backBtn = findViewById(R.id.intentOneBtn);

        Intent intent = getIntent();
        nameEdit.setText(intent.getStringExtra("Name"));
        ageEdit.setText(intent.getStringExtra("Age"));
    }
}

在這裏插入圖片描述

在這裏插入圖片描述

ok,我們已經成功的得到了數據。前面已經說過常見的有兩種方式傳遞數據,接下來看看另外一種方式。

那麼Bundle是何許人也呢?

Bundle經常使用在Activity之間或者線程間傳遞數據,傳遞的數據可以是boolean、byte、int、long、float、double、string等基本類型或它們對應的數組,也可以是對象或對象數組。當Bundle傳遞的是對象或對象數組時,必須實現Serializable或Parcelable接口。更多內容請參考android開發文檔。

 String name = nameEdit.getText().toString();
        String age = ageEdit.getText().toString();
        Intent intent = new Intent(QFirstActivity.this, QIntentOne.class);

        /**
         * 使用Bundle進行數據傳遞
         */
        Bundle bundle = new Bundle();
        bundle.putString("Name", name);
        bundle.putString("Age", age);
        intent.putExtras(bundle);

        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }

同理我們貼出QIntentOne接收參數的代碼


        nameEdit.setText(intent.getExtras().getString("Name"));
        ageEdit.setText(intent.getExtras().getString("Age"));
返回ACtivity數據

上面介紹瞭如何向新調用起的Activity傳遞數據,但是在某些情況下我們希望得到新Activity給我們返回的一些數據。

還是上面的例子,QIntentOne頁面接收數據並顯示在當前頁面中,同時該頁面也可以修改姓名和年齡信息,我們希望在信息修改完成後,QFirstActivity能夠得到修改後的信息。

這裏我們需要使用startActivityForResult函數進行Activity的啓動。

先來看看startActivityForResult定義

public void startActivityForResult(android.content.Intent intent,
                                   int requestCode)

intent:將要啓動的intent
requestCode:請求碼,如果該值大於0,那麼在被調用的Acitivity銷燬時會將該值返回到onActivityResult中。requestCode主要用來區分是哪一個Activity返回。
注意:requestCode必須是一個唯一值,並且requestCode <= 0xffff,一般是65535.

那麼修改QFirstActivity中關於啓動Activity的代碼:

  if (intent.resolveActivity(getPackageManager()) != null) {
//            startActivity(intent);
            startActivityForResult(intent,1);
        }

上面提到了,接收Activity返回結果我們需要使用onActivityResult,首先我們的實現onActivityResult方法,並且根據requestCode和返回結果再做對應的處理。

函數定義:

protected void onActivityResult(int requestCode,
                                int resultCode,
                                @Nullable Intent data)

參數說明:

requestCode: 啓動Activity時傳入的請求碼
resultCode:返回數據時傳入的處理結果
data:攜帶返回數據的Intent

QFirstActivity中重寫onActivityResult的實現:

  @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

        switch (requestCode) {
            case 1:
                if (resultCode == RESULT_OK) {
                    //接收返回數據,做相應的處理
                    nameEdit.setText(data.getExtras().getString("Name"));
                    ageEdit.setText(data.getExtras().getString("Age"));
                }
                break;
            default:
        }

一般的邏輯就是這樣子的。針對我們上面的例子來說,就是獲取到修改後的姓名和年齡,並且更改當前頁面顯示。

在需要返回數據的Activity也就是QIntentOne中,在Activity銷燬的時候返回數據。

這裏當返回數據按鈕點擊的時候,銷燬Activity並返回修改後的姓名和年齡。

添加Button的點擊事件:

 backBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendBackData();
            }
        });

sendBackData方法用來實現數據返回,實現如下:

private void sendBackData() {
        String name = nameEdit.getText().toString();
        String age = ageEdit.getText().toString();

        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString("Name", name);
        bundle.putString("Age", age);
        intent.putExtras(bundle);

        setResult(RESULT_OK, intent);
        finish();
    }

瞭解下setResult方法:

void setResult (int resultCode,
Intent data)

resultCode
int:結果返回碼,通常爲RESULT_CANCELED  RESULT_OK,可以自定義
data
Intent: 攜帶返回數據的Intent

運行效果:
在這裏插入圖片描述
在這裏插入圖片描述
修改數據並點擊返回數據按鈕i:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章