1. 程式人生 > >Android Intent隱式啟動通過scheme開啟應用

Android Intent隱式啟動通過scheme開啟應用

本文轉載自tickTick的BLOG,原始出處
本文通過完成一個實戰任務,來掌握Android開發中隱式Intent的用法。
任務:假設我們已經實現了一個視訊播放器(PlayerActivity),我們希望能把它註冊到系統中,當用戶點選本地視訊或者線上視訊時,能啟動這個視訊播放器。
(假設該類的全路徑為:com.jhuster.videoplayer.PlayerActivity)
1. 什麼是隱式Intent?
Intent是Android中比較重要的元件,常用來啟動一個新的Activity或者Service、廣播某個事件,以及在Android元件之間傳遞資料。通過Intent來啟動新的Activity或者Service通常有兩種方法,一種是顯示啟動,另一種是隱式啟動。
顯示啟動就是在明確指出要啟動的Activity或者Service的類或者包名。例如:

Intent intent = new Intent(this, PlayerActivity.class);
startActivity(intent);
Intent intent =new Intent();
intent.setClass(this,PlayerActivity.class);
startActivity(intent);
Intent intent =new Intent();
intent.setClassName(“com.jhuster.videoplayer”,“com.jhuster.videoplayer.PlayerActivity”);
startActivity(intent);

隱式啟動則是不明確指定啟動哪個Activity或者Service,而是通過設定Action、Data、Category,讓系統來篩選出合適的目標。
例如撥打電話:

Intent intent =new Intent(Intent.ACTION_DIAL,Uri.parse(“tel:021-80961111”));
startActivity(intent);

系統接收到隱式啟動請求後,會根據系統中各個Activity在AndroidManifest.xml檔案中宣告的<intent-filter>來比較和判斷是否匹配當前的Intent請求的。
因此,如果我們希望PlayerActivity能夠被系統隱式啟動,則首先需要在AndroidManifest.xml檔案中為該Activity新增<intent-filter>.
2. 為PlayerActivity新增<intent-filter>

<intent-filter>的標籤有很多,這裡只介紹和新增最基本且最常用的三個標籤,分別是<action>,<category>和<data>。
2.1 新增<action>
這個標籤是必須新增的,可以自己定義,也可以使用系統預定義的變數,Android系統預設定義了很多action,具體可以檢視SDK文件,或者Google一下“android.intent.action.”。
這裡,因為我們的類是用來“播放視訊”的,因此可以使用系統預定義的:android.intent.action.VIEW,它表示需要啟動某個Activity顯示指定的資料(包括圖片、視訊、文件等)。
添加了後的如下所示:

<activity android:name="com.jhuster.videoplayer.PlayerActivity">
  <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
  </intent-filter>
</activity>

2.2 新增<category>
category代表類別,定義了Activity的類別,Activity可以設定一個或者多個category標籤。常用的一般有3個:DEFAULT,HOME,LAUNCHER

DEFAULT  預設動作
HOME     設定為本地桌面應用
LAUNCHER 本APP的啟動Activity

本應用中我們使用DEFAULT類別即可,DEFAULT也是category最常用的選項。
添加了category後的如下所示:

<activity android:name="com.jhuster.videoplayer.PlayerActivity">
  <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <categoryandroid:name="android.intent.category.DEFAULT"/>
  </intent-filter>
</activity>

2.3 新增<data>
data 代表資料來源,是<intent-filter>中最複雜的標籤,因為不同的Activity支援的資料來源和型別多種多樣,所以需要通過詳細的data標籤資訊來指明。

data 標籤有很多屬性,包括:
android:host: 指定主機名,例如:google.com
android:port:  制定主機埠,例如: 80
android:path:  指定URL的有效路徑值,例如: /index/examples
android:mimeType: 指定元件可以執行的資料型別,例如:image/jpeg,video/*
android:scheme: 指定特定的模式,例如:content,http

這裡,假設我們的視訊播放器支援多種資料來源,包括:本地視訊檔案,本地媒體URL,網路視訊流(HTTP、RTMP、RTSP協議),另外,假設我們的視訊播放器只支援mp4和3gpp兩種檔案格式。
那麼,下面我們來新增兩種最常用<data>的標籤,scheme和mimeType,並且解釋每條標籤對應的是怎樣的一種資料來源或者資料格式。
(1)<data android:scheme="xxx"/>
這裡的xxx可以是:file,content,網路協議(HTTP,RTMP、RTSP等)
本應用中我們給PlayerActivity的中<Intent-filter>新增:

<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:scheme="http"/>
<data android:scheme="rtsp"/>

添加了這樣幾條data標籤項之後,如果隱式Intent中的資料來源URL是以“file://”、“content://”、“http://”、“rtsp://”開頭的URL資源,都會隱式地啟動我們的PlayerActivity。
例如,其他的Activity可以通過下面的方法來隱式啟動我們的PlayerActivity.

Intent intent =new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.fromFile(new File("/sdcard/test.3gp")));
startActivity(intent);

Uri.fromFile這條語句會把指定的檔案位置轉換為以“file://”開頭的Uri物件,如上述例子最終得到的URL為:“file:///sdcard/test.3gp”
同理,可以通過Uri.parse來轉換我們常見的網路地址字串為Uri物件,例如:

Intent intent =new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://ticktick.blog.51cto.com/test.mp4"));
startActivity(intent);

(2)<data android:mimeType="xxx"/>
mimeType用來設定資料型別,例如影象資料(image/png或者image/),視訊資料(video/mp4或者video/),如果使用*代表匹配所有的子型別。
MIME TYPE是網際網路的一種標記資料型別的標準,現在已經支援非常多的型別了,這裡我不一一列舉,大家可以在Google上搜索一下。
本應用中我們假設需要支援的是mp4和3gpp兩種型別,那麼,我們可以新增這樣兩條 mimeType :

<data android:mimeType="video/3gpp"/>
<data android:mimeType="video/mp4"/>

那麼,其他的Activity就可以通過下面的方法來隱式啟動我們的PlayerActivity. 注意,當<intent-filter>已經添加了mimeType之後,隱式Intent必須設定Type引數才能匹配到該Activity,所以建議使用setDataAndType方法,而不是單一的setData方法。

Intent intent =new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File("/sdcard/test.3gp")),"video/3gpp");
startActivity(intent);

當然,這裡的"video/3gpp"也可以寫成:"video/*",但這樣可能會匹配到一些不支援3gpp的播放器。

(3) 小結
添加了<data>標籤後的如下所示:

<activity android:name="com.jhuster.videoplayer.PlayerActivity">
  <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:scheme="file"/>
    <data android:scheme="content"/>
    <data android:scheme="http"/>
    <data android:scheme="rtsp"/>
    <data android:scheme="rtmp"/>
    <data android:mimeType="video/3gpp"/>
    <data android:mimeType="video/mp4"/>
  </intent-filter>
</activity>

3. 在PlayerActivity中獲取引數
通過上面的介紹,我們已經知道了怎樣新增<intent-filter>以及怎樣通過隱式Intent來呼叫我們的PlayerActivity,那麼,下面我們還要了解如何在PlayerActivity中解析來自隱式Intent的引數。
其實,Intent提供了很多方法可以Get相關的引數資訊,例如:

public String getAction();
public Uri    getData();
public String getScheme();
public String getType();

上述方法分別可以獲取Intent的Action,Data Uri,Scheme和MimeType值。
對於“file://”開頭的Uri物件,我們可以通過Uri.getPath方法得到去除了“file://”字首的具體檔案地址。例如: “file:///sdcard/test.mp4”則可以轉換為實際的“/sdcard/test.mp4”。
對於網路碼流,例如:“http://”、“rtsp://”等開頭的Uri,則可以直接通過toString()方法轉換為實際地址的字串。
而對於“content://”開頭的URI物件,一般是從系統的媒體資料庫中檢索出來的結果,因此需要反向查詢得到實際的檔案地址,這裡提供一個函式進行轉換。

public static String getVideoPath(Context context, Uri uri) {
  Uri videopathURI = uri;
  if(uri.getScheme().toString().compareTo("content") ==0) {
    Cursor cursor = context.getContentResolver().query(uri,null,null,null,null);
    if(cursor.moveToFirst()) {
      intcolumn_index = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
      videopathURI = Uri.parse(cursor.getString(column_index));
      return videopathURI.getPath();
    }
  }else if(uri.getScheme().compareTo("file") ==0) {
    return videopathURI.getPath();
  }
  return videopathURI.toString();
}