1. 程式人生 > >隱式intent詳解

隱式intent詳解

 本篇文章,對隱式啟動Activity再做分析。

  有些人可能會說了,隱式啟動活動不是很簡單嗎?這有什麼不理解的?話先別說的這麼早,對於隱式啟動,還是具有很大的坑要爬的,當然,您如果是一個資深開發者就另當別論了。

  本篇文章,我們從最簡單的開始,一步步引入,相信這樣的方式,讀起來也會輕鬆一些。

  我們平時啟動一個活動,會通過兩種方式。1、顯示啟動;2、隱式啟動。
  (一)首先,我們來看兩個很簡單的小案例(實現打電話)。

我們再在佈局檔案提供一個TextView用於提示輸入電話號碼,在EditText裡面輸入號碼,點選按鈕的同時,獲取到使用者輸入的號碼,並且啟動打電話功能。

主活動程式碼很簡單:

MainActivity:

public class MainActivity extends Activity {

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

        //給按鈕設定點選偵聽
        //1.拿到按鈕物件
        Button bt = (Button) findViewById(R.id.bt_call);//Button類是View的子類,向下轉型要強轉。
        //2.設定偵聽
        bt.setOnClickListener(new MyListener());
    }

    class MyListener implements View.OnClickListener {

        //按鈕被點選時,此方法呼叫
        @Override
        public void onClick(View v) {
            //獲取使用者輸入的號碼
            EditText et = (EditText) findViewById(R.id.et_phone);
            String phone = et.getText().toString();

            //我們需要告訴系統,我們的動作:我要打電話
            //建立意圖物件
            Intent intent = new Intent();
            //把打電話的動作ACTION_CALL封裝至意圖物件當中
            intent.setAction(Intent.ACTION_CALL);
            //設定打給誰
            intent.setData(Uri.parse("tel:" + phone));//這個tel:必須要加上,表示我要打電話。否則不會有打電話功能,由於在打電話清單檔案裡設定了這個“協議”

            //把動作告訴系統,啟動系統打電話功能。
            startActivity(intent);
        }

    }

}

執行後如下:

 

這個簡直太簡單了,估計程式碼都能爛肚子裡了。

嗯,的確很簡單,那就緊跟腳步,我們繼續看一個簡單的自定義啟動活動的程式碼。
(二)自定義Activity,並隱式方式啟動

我們都知道,如果要想自定義隱式啟動別的activity,需要給該Activity新增“意圖過濾器”<intent-filter>。

通過在<activity>標籤下配置<intent-filter>的內容,可以指定當前活動能夠響應的 action
和 category,開啟 AndroidManifest.xml,新增如下程式碼:

<activity android:name=".NextActivity">
    <intent-filter>
        <action android:name="com.itydl"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>


在<action>標籤中我們指明瞭當前活動可以響應 com.itydl 這個 action,我們可以隨便寫裡面的內容,它的加入表示給我們的Activity新增一個動作,只有帶動作的Activity才能被隱式啟動。而<category>標籤則包含了一些附加資訊,更精確地指明瞭當前的活動能夠響應的 Intent 中還可能帶有的 category。意圖中設定的action必須跟"com.itydl"是完全匹配的,只有<action>和<category>中的內容同時能夠匹配上 Intent中指定的 action和 category時,這個活動才能響應該 Intent。我們也道,android.intent.category.DEFAULT 是一種預設的 category,在呼叫startActivity()方法的時候會自動將這category新增到 Intent中(檢視所有系統原始碼,也都帶上了這個category,我們也無需多去關心category它可能存在的坑了)。

對於Action的原理是:當StartActivity()執行的時候,該Activity會去系統所有清單檔案中找對應的Action("")裡面能匹配的Activity,找有沒有對應的action與我們所寫入的能匹配的,如果有(這裡是NextActivity),這樣就啟動了NextActivity。

既然,NextActivity有了動作,那麼我們的MainActivity再新增一個按鈕,使用隱式方式啟動它。如下:

mNext = (Button) findViewById(R.id.bt_next);
mNext.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent();
        intent.setAction("com.itydl");
        startActivity(intent);
    }
});


此時執行程式,發現能夠正常啟動。我們發現第一個例子啟動打電話功能時候,還添加了一個setData()。那麼,我們自定義的也可以同樣新增data。修改清單檔案NextActivity配置程式碼如下:

<intent-filter>
    <action android:name="com.itydl"/>
    <data android:scheme="ydl"/>
    <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

這個時候,我們再執行程式,發現程式崩潰。這是因為我們隱式啟動程式碼中,並沒有完全跟清單檔案中<intent-filter>下面的內容匹配。要想匹配成功,我們也要去程式碼中設定data。再回到小案例一中,我們也就知道了為什麼啟動打電話功能要設定setData了,還不是因為電話清單檔案原始碼中有這個data標籤嗎。

在這裡淺顯介紹一下系統新增data下面的標籤都有哪些:

<data>標籤中主要可以配置以下內容。
1.  android:scheme
用於指定資料的協議部分。
2.  android:host
用於指定資料的主機名部分。
3.  android:port
用於指定資料的埠部分,一般緊隨在主機名之後。
4.  android:path
用於指定主機名和埠之後的部分,如一段網址中跟在域名之後的內容。
5.  android:mimeType
用於指定可以處理的資料型別,允許使用萬用字元的方式進行指定。只有<data>標籤中指定的內容和 Intent 中攜帶的 Data 完全一致時,當前活動才能夠響應該 Intent。不過一般在<data>標籤中都不會指定過多的內容,常見的是mimeType和scheme。

給我們的隱式啟動按鈕加入如下程式碼:

intent.setData(Uri.parse("ydl:qwe"));

執行程式不再報錯。

上邊的內容,對於一個初學者來說是必須掌握的內容。那麼再來看看一些細節問題,這些細節問題不知道,可能還真會讓你不知道如何去隱式啟動別的Activity!
(三)深入分析隱式啟動的細節

首先先從自定義隱式啟動開始討論。上邊程式碼Uri.parse()引數內容"ydl:qwe"我們看到qwe好像很彆扭,其實setData的英文名稱就告訴我們設定資料的了。基於上面對系統<data>裡面配置介紹,我們清單檔案中是android:scheme,這是一個協議,因而我們設定資料必須要以scheme後邊內容開頭。這裡是"ydl"作為了協議。而後面"qwe"內容可以隨便寫。例如我改成如下程式碼:

intent.setData(Uri.parse("ydl:234"));

仍然可以啟動下一個活動。

那麼我們再回到最初的小案例,有如下程式碼:

intent.setData(Uri.parse("tel:" + phone));


這裡不就是一個協議嗎?在清單檔案中對應的Activity肯定有這個協議,我們就去上層原始碼看一看,找到如下程式碼:

    <activity android:name="OutgoingCallBroadcaster"
                    android:permission="android.permission.CALL_PHONE"
                    android:theme="@android:style/Theme.NoDisplay"
                    android:configChanges="orientation|keyboardHidden">
                <!-- CALL action intent filters, for the various ways
                     of initiating an outgoing call. -->
                <intent-filter>
                    <action android:name="android.intent.action.CALL" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:scheme="tel" />
                </intent-filter>
                <intent-filter android:icon="@drawable/ic_launcher_sip_call">
                    <action android:name="android.intent.action.CALL" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:scheme="sip" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.CALL" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:scheme="voicemail" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.CALL" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:mimeType="vnd.android.cursor.item/phone" />
                    <data android:mimeType="vnd.android.cursor.item/phone_v2" />
                    <data android:mimeType="vnd.android.cursor.item/person" />
                </intent-filter>
            </activity>

我們看到android:permission就是我們打電話要指定的許可權。而往下看第一個intent-filter

action android:name="android.intent.action.CALL" />不就是針對我們程式碼中要設定的Action(intent.setAction(Intent.ACTION_CALL);)嗎?

<data android:scheme="tel"不就是我們在程式碼中要指定的協議嗎(intent.setData(Uri.parse("tel:110"));)?只不過我們所以帶過去一些資料phone,使用者輸入的號碼嗎?

到了這裡,相信這個細節大家都能掌握了。那麼接著往下繼續爬坑~

我們發現,打電話OutgoingCallBroadcaster的清單檔案中有太多的intent-filter,看花了眼,我們到底怎麼知道匹配哪個intent-filter才能啟動打電話這個活動呢?其實,原理很簡單,在清單檔案中的intent-filter代表我們可以有好幾種啟動方式好幾種寫法去啟動這個打電話這個活動。我們使用哪一種方式都無所謂的,都可以。那麼我們模仿這種情況,來對自定義啟動活動也這麼新增幾個intent-filter,看完下面的內容,這些坑也就爬完了。
(四)模仿原始碼為自己的活動配置不同的intent-filter

首先我們在清單檔案中繼續新增intent-filter

<intent-filter>
    <action android:name="com.itydl2"/>
    <data android:scheme="ydl2"/>
    <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

此時兩個intent-filter,我們為了驗證剛才的說法,把啟動活動的程式碼修改一下:

Intent intent = new Intent();
intent.setData(Uri.parse("ydl2:234"));
intent.setAction("com.itydl2");
startActivity(intent);

我們發現都改成了ydl2,這樣啟動時匹配的是上邊這個intent-filter。能正常啟動活動。

注意,啟動互動程式碼必須與某一個intent-filter相匹配才能正常啟動活動,否則會報錯。這是為什麼,相信我們很簡單就能理解原因了。

繼續跟進:

我們在清單檔案中除了可以配置多個intent-filter,還可以配置多個data,以及多個action嗎?(很多原始碼中也是這樣的)。我們再把清單檔案中程式碼作如下修改來試試:

<intent-filter>
    <action android:name="com.itydl2"/>
    <action android:name="com.itydl3"/>
    <action android:name="com.itydl4"/>
    <action android:name="com.itydl5"/>
    <data android:scheme="ydl2"/>
    <data android:scheme="ydl3"/>
    <data android:scheme="ydl4"/>
    <data android:scheme="ydl5"/>
    <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

這裡添加了多個data多個action,其實,在我們在啟動活動的程式碼位置,只需要配置任意一條data以及任意一條action就可以成功啟動這個activity了,不需要全部匹配。活動程式碼即使這麼寫,也是沒有任何問題:

Intent intent = new Intent();
intent.setAction("com.itydl2");
intent.setData(Uri.parse("ydl4:234"));
startActivity(intent);

啟動活動成功。

最後,還有一點點就完結了。即常見的android:mimeType這個屬性

我們知道了,data是用來傳遞資料的,聽過setData把資料放進裡面,會把資料傳遞給目標Activity,在目標activity可以通過getIntent()(獲取啟動這個activity的意圖物件)方式來獲取裡面的資料。而android:mimeType是用來定義你setData用來傳遞什麼資料型別的。例如,一般我們傳遞文字資料,我們就可以這麼寫:

<intent-filter>
    <action android:name="com.itydl2"/>
    
    <data android:mimeType="text/username"/>
    <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

我們首先測試只有android:mimeType的時候。

Intent intent = new Intent();
intent.setAction("com.itydl2");
intent.setType("text/username");
startActivity(intent);

這個時候,程式碼和清單檔案也是完全匹配的。我們執行程式,也是沒有任何問題。

當android:mimeType和android:scheme同時存在的時候,我們需要注意

如果程式碼先設定setData();後setType()後者會把前者清理掉,反之亦然,這裡就跟你女神問你“你媽和我掉水裡你先救誰”原理是一樣的。我們需要通過setDataAndType()方法,把scheme和mimeType同時設定進去才能匹配對應的清單檔案中的intent-filter。例如我清單檔案中程式碼是:

<intent-filter>
    <action android:name="com.itydl2"/>
    <data android:scheme="ydl2"/>
    <data android:mimeType="text/username"/>
    <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>

那麼我們啟動的時候,需要通過

intent.setDataAndType(Uri.parse("ydl2:qwe"),"text/username");

這樣就能完好匹配清單檔案內容,可以正常啟動actiivity了。


終於寫完了,相信看完此篇文章你對隱式啟動活動的原理更加清晰了。以後啟動任何互動,只需要看看那個活動的清單檔案內容,就能輕鬆通過隱式啟動的方式去開啟它了!
---------------------
作者:楊道龍
來源:CSDN
原文:https://blog.csdn.net/qq_32059827/article/details/53819775
版權宣告:本文為博主原創文章,轉載請附上博文連結!