翻譯前的廢話:因為不熟悉intent-filter和intent,調bug時浪費了很多時間,這篇也就是在官方文檔的技術上,有個淺顯但是實用的解釋。
調用activity
intent與startActivity
從當前activity去調用其他activity的過程是,創建intent、作為參數傳入startActibity()來調用。intent分為Explicit intents(明確的intent)與 Implicit intents(隱性的intent),相關詳細內容請直接參考官方文檔。
隱性的intent,比如想打開指定的url的情況:(譯注:總之就是沒有指明本app中activity名的情況)
Uri uri = Uri.parse("http://www.bar.com/hoge");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
調用activity時的數據設定
startActivity()之前可以設定intent中的Extra。這次以明確的intent為例:
/**調用端*/
import android.content.Intent;
...
Intent intent = new Intent(this, SubActivity.class);
intent.putExtra("foo", someData);
startActivity(intent);
/**被調用端*/
import android.content.Intent;
...
Intent intent = getIntent();
String someData = intent.getStringExtra("foo"); // someData是String的情況
SomeData someData = (SomeData) intent.getSerializableExtra("foo"); // Serializable用implement封裝后,自定義格式的數據也可以傳遞
Bundle args = intent.getExtras(); // 傳遞的Extra通過Map形式全部取得
被其他actibity調用
intent-filter
通過前面說的startActivity()、隱性的intent形式被調用的場合,必須在AndroidManifest.xml里定義intent-filter:
<activity
android:name="com.example.android.hoge"
android:label="@string/foo">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https" android:host="www.bar.com" />
</intent-filter>
</activity>
想接受隱性的intent的場合一定要設定android.intent.category.DEFAULT的category。原因是把隱性的intent傳遞進startActivity()或者startActivityForResult()以調用其他activity時,默認作為CATEGORY_DEFAULT來處理,要是intent-filter里沒有對應的category系統就認為是不合適的intent而不進行處理。
Note: In order to receive implicit intents, you must include the CATEGORY_DEFAULT category in the intent filter. The methods startActivity() and startActivityForResult() treat all intents as if they declared the CATEGORY_DEFAULT category. If you do not declare this category in your intent filter, no implicit intents will resolve to your activity.
通過隱性的intent被調用的區別
通過隱性的intent被調用的場合,應該有指定intent里的URL,取出這個URL并判斷會比較好:
Intent intent = getIntent();
Uri uri = intent.getData();
if uri == null {
Log.d("test", "這是app里明確的Intent");
} else {
Log.d("test", uri.toString());
}
(譯注:p.s. Uri uri = intent.getData(); 注意根據activity的lauchMode,除了standard以外另外三個屬性會可能調用onNewIntent()而不是onCreate()來更新activity)
Try to override onNewIntent. The problem is that getIntent
returns the Intent that started the Activity, but not the most recent one. But you can override it using setIntent method.
因此需要重載onNewIntent():
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
限制調用activity
因為安全的原因要限制其他的activity的調用,或者需要確認那些啟動調用的activity。就會使用下面的兩種方式。
android:exported設定false
AndroidManifest.xml的activity的設定里,android:exported設定false的話,其他activity的component無法調用該activity:
AndroidManifest.xml
<activity
android:name="com.example.android.foo.SomeActivity"
android:label="@string/app_name"
android:exported="false">
API Guides的官方說明:
android:exported
Whether or not the activity can be launched by components of other applications — "true" if it can be, and "false" if not. If "false", the activity can be launched only by components of the same application or applications with the same user ID.
Activity.getCallingActibity()取得啟動調用的activity
使用Activity.getCallingActivity()的話,就可以知道startActivityForResult()的調用元。(startActivity()的話就還是不知道哦。) (譯注:getCallingActivity()返回依舊是null。寶寶就在這里調了好久~)