Android Intent/Context Flags 使用
文章目錄
- Android Intent/Context Flags 使用
-
- 一、基本使用
- 二、Flags介紹
-
- 2.1. 系統定義的Flags
-
- 2.1.1 Intent
- 2.1.2 Context
- 2.2 Actvity Flags
-
- 測試工具
- FLAG_ACTIVITY_NEW_TASK
- FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- FLAG_ACTIVITY_FORWARD_RESULT
- FLAG_ACTIVITY_NEW_DOCUMENT
- FLAG_ACTIVITY_NO_ANIMATION
- FLAG_ACTIVITY_NO_HISTORY
- FLAG_ACTIVITY_NO_USER_ACTION
- FLAG_ACTIVITY_LAUNCH_ADJACENT
- FLAG_ACTIVITY_REORDER_TO_FRONT
- FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- FLAG_ACTIVITY_RETAIN_IN_RECENTS
- FLAG_ACTIVITY_SINGLE_TOP
- FLAG_DEBUG_LOG_RESOLUTION
- FLAG_EXCLUDE_STOPPED_PACKAGES / FLAG_INCLUDE_STOPPED_PACKAGES
- FLAG_FROM_BACKGROUND
- FLAG_ACTIVITY_TASK_ON_HOME
- 2.3 Uri Flags
-
- FLAG_GRANT_READ_URI_PERMISSION / FLAG_GRANT_WRITE_URI_PERMISSION
- FLAG_GRANT_PERSISTABLE_URI_PERMISSION
- FLAG_GRANT_PREFIX_URI_PERMISSION
- 使用案例
- 2.4 Receiver Flags
-
- FLAG_RECEIVER_REGISTERED_ONLY
- FLAG_RECEIVER_REPLACE_PENDING
- FLAG_RECEIVER_FOREGROUND
- FLAG_RECEIVER_NO_ABORT
- FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
- 2.5 Service Flags
-
- BIND_AUTO_CREATE
- BIND_DEBUG_UNBIND
- BIND_NOT_FOREGROUND
- BIND_ABOVE_CLIENT
- BIND_ALLOW_OOM_MANAGEMENT
- BIND_WAIVE_PRIORITY
- BIND_IMPORTANT
- BIND_ADJUST_WITH_ACTIVITY
一、基本使用
setFlags
, 直接設定flags, 之前設定的flags會被覆寫
内部實作如下:
public @NonNull Intent setFlags(@Flags int flags) {
mFlags = flags;
return this;
}
addFlags
, 追加flags, 在之前設定的flags的基礎上添加
内部實作如下:
public @NonNull Intent addFlags(@Flags int flags) {
mFlags |= flags;
return this;
}
二、Flags介紹
2.1. 系統定義的Flags
2.1.1 Intent
- FLAG_GRANT_READ_URI_PERMISSION
- FLAG_GRANT_WRITE_URI_PERMISSION
- FLAG_GRANT_PERSISTABLE_URI_PERMISSION
- FLAG_GRANT_PREFIX_URI_PERMISSION
- FLAG_FROM_BACKGROUND
- FLAG_DEBUG_LOG_RESOLUTION
- FLAG_EXCLUDE_STOPPED_PACKAGES
- FLAG_INCLUDE_STOPPED_PACKAGES
- FLAG_DEBUG_TRIAGED_MISSING
- FLAG_IGNORE_EPHEMERAL
- FLAG_ACTIVITY_MATCH_EXTERNAL
- FLAG_ACTIVITY_NO_HISTORY
- FLAG_ACTIVITY_SINGLE_TOP
- FLAG_ACTIVITY_NEW_TASK
- FLAG_ACTIVITY_MULTIPLE_TASK
- FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_FORWARD_RESULT
- FLAG_ACTIVITY_PREVIOUS_IS_TOP
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- FLAG_ACTIVITY_BROUGHT_TO_FRONT
- FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
- FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
- FLAG_ACTIVITY_NEW_DOCUMENT
- FLAG_ACTIVITY_NO_USER_ACTION
- FLAG_ACTIVITY_REORDER_TO_FRONT
- FLAG_ACTIVITY_NO_ANIMATION
- FLAG_ACTIVITY_CLEAR_TASK
- FLAG_ACTIVITY_TASK_ON_HOME
- FLAG_ACTIVITY_RETAIN_IN_RECENTS
- FLAG_ACTIVITY_LAUNCH_ADJACENT
- FLAG_RECEIVER_REGISTERED_ONLY
- FLAG_RECEIVER_REPLACE_PENDING
- FLAG_RECEIVER_FOREGROUND
- FLAG_RECEIVER_NO_ABORT
- FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
- FLAG_RECEIVER_BOOT_UPGRADE
- FLAG_RECEIVER_INCLUDE_BACKGROUND
- FLAG_RECEIVER_EXCLUDE_BACKGROUND
- FLAG_RECEIVER_FROM_SHELL
- FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
2.1.2 Context
- BIND_AUTO_CREATE
- BIND_DEBUG_UNBIND
- BIND_NOT_FOREGROUND
- BIND_ABOVE_CLIENT
- BIND_ALLOW_OOM_MANAGEMENT
- BIND_WAIVE_PRIORITY
- BIND_IMPORTANT
- BIND_ADJUST_WITH_ACTIVITY
2.2 Actvity Flags
測試工具
主要利用IntentTestApp進行測試驗證
FLAG_ACTIVITY_NEW_TASK
要點
- 很重要的一個辨別
- 啟動Activity時, 如果符合Activity TaskAffinity的棧沒有建立, 就會建立一個, 然後再啟動Activity放置其中
- 當App從Launcher啟動時, Intent會添加這個标志位
- 如果使用Application啟動Activity, 必須添加這個标志位
- 通常配合的其他标志位有:
-
Intent.FLAG_ACTIVITY_CLEAR_TASK
-
Intent.FLAG_ACTIVITY_CLEAR_TOP
-
Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-
Intent.FLAG_ACTIVITY_NEW_DOCUMENT
-
FLAG_ACTIVITY_CLEAR_TOP
要點
- 沒有指定Activity TaskAffinity棧的情況下
- 如果目标Activity還未啟動, 直接在預設棧建立一個新的執行個體
- 如果目标Activity已經啟動, 并且已處于棧頂, 則會先啟動一個新的執行個體, 再Destroy掉之前的執行個體
- 如果目标Activity已經啟動, 并且不處于棧頂, 則會先Destroy掉之前的執行個體, 再啟動一個新的執行個體, 最後Destroy掉之前執行個體之上的其他Activity執行個體
- 已經指定Activity TaskAffinity棧的情況下
- 如果目标Activity還未啟動, 不管是否有棧名稱指定的Affinity比對, 直接建立一個新棧和新的執行個體
- 如果目标Activity已經啟動, 直接将所在棧拉到前台
- 如果隻指定
這一個标志位, 啟動時系統自動添加FLAG_ACTIVITY_CLEAR_TOP
标志位, 效果等同于SingleInstanceFLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
要點
- 設定後Activity不會展示到最近任務頁面中, 建議加上TaskAffinity配合使用
- 例子: A啟動B時, 添加
标志, 按Home回到桌面後再按最近任務頁面, 顯示的為A, 而不是BFLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- 例子: A啟動B時, 添加
- 通過點選最近頁面或者圖示再次進入時, 會進入其他Activity頁面
- 嘗試配合
和FLAG_ACTIVITY_NEW_TASK
使用時, 效果很奇怪, 整個App都不在最近頁面了FLAG_ACTIVITY_MULTIPLE_TASK
FLAG_ACTIVITY_FORWARD_RESULT
要點
- 委托請求結果
- 例子: A通過
啟動B, B不知如何startActivityForResult
, B就啟動C, 由C設定setResult
setResult
- 當C傳回結果後, B設定
并沒有任何效果, A收到的值是C的結果setResult
- 再由B啟動C時需要調用
, 使用startActivity
會抛出startActivityForResult
異常android.util.AndroidRuntimeException: FORWARD_RESULT_FLAG used while also requesting a result
- 例子: A通過
FLAG_ACTIVITY_NEW_DOCUMENT
要點
- 在最近任務頁面裡面添加同一個App的不同頁面記錄
- 如果之前沒有啟動過Activity, 會建立一個新的任務棧, 并啟動一個新的Activity放入, 否則直接将之前的任務棧拉到前台
- 系統會自動添加
标志位FLAG_ACTIVITY_NEW_TASK
- 如果單獨使用的話, 效果果同在Manifest設定
documentLaunchMode="intoExisting"
- 可以配合
使用, 這樣就總會建立新的任務棧和Activity執行個體, 效果同在Manifest設定FLAG_ACTIVITY_MULTIPLE_TASK
documentLaunchMode="always"
- 在手機上測試時發現, 不指定這個标志位, 隻設定TaskAffinity, 也能達到這種效果, 但隻有一個執行個體, 設定
也沒有效果FLAG_ACTIVITY_MULTIPLE_TASK
FLAG_ACTIVITY_NO_ANIMATION
要點
- 去除動畫效果
- 僅這一次有效, 如果A啟動B, B再啟動C, 需要再次設定這個标志位
FLAG_ACTIVITY_NO_HISTORY
要點
- 通過設定
啟動的Activity在失去前台後會被銷毀掉FLAG_ACTIVITY_NO_HISTORY
- 并不是立即銷毀掉, 實際是這個Activity上面沒有其他Activity時才會開始銷毀
- 感覺就是: 先失去前台, 再擷取到前台, 發現設定了
标志位, 就直接銷毀了FLAG_ACTIVITY_NO_HISTORY
FLAG_ACTIVITY_NO_USER_ACTION
要點
- Intent設定
後, 調用startActivity所在的Activity, 不會調用FLAG_ACTIVITY_NO_USER_ACTION
, 不是被調用者onUserLeaveHint
- 不設定
情況下FLAG_ACTIVITY_NO_USER_ACTION
-
在失去前台時會被調用, 但在Activity銷毀時不會被調用onUserLeaveHint
- 不知道是不是手機問題, 通過最近任務頁面跳轉到其他App時,
也不會被調用onUserLeaveHint
-
FLAG_ACTIVITY_LAUNCH_ADJACENT
要點
- 僅在分屏多視窗模式下起作用
- 新Activity将顯示在啟動它的Activity的旁邊
- 需要與
搭配使用, 如果需要建立現有Activity的新執行個體, 可以添加FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_MULTIPLE_TASK
- 這個沒有在手機上實際驗證
FLAG_ACTIVITY_REORDER_TO_FRONT
要點
- 效果為在不銷毀Activity的前提下, 更改目前棧Activity的順序
- 例子:
- 假設目前棧有Activity A、B、C、D, D在棧頂
- D 啟動Activity B, 并添加
标志位FLAG_ACTIVITY_REORDER_TO_FRONT
- 目前棧的順序改為: A、C、D、B
- B 會調用
onNewIntent
- 如果設定了
, 忽略本标志位, 走FLAG_ACTIVITY_CLEAR_TOP
這一套FLAG_ACTIVITY_CLEAR_TOP
- 與
的差別就在于, 是否會銷毀上面的ActivityFLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
要點
- 轉移Activity到其他棧, 一般用于不同App之間
- 需要在清單檔案配置如下兩個屬性:
-
允許其他App啟動android:export="true"
-
允許Activity所在棧可以更換android:allowTaskReparenting=true
-
- 參考網絡資源, 主要工作流程如下:
- A 應用有主Activity A 和Activity A_2, A_2 設定
和android:export="true"
android:allowTaskReparenting=true
- B 應用有主Activity B
- Activity B 啟動 Activity A_2, 目前Activity A_2 在應用B的棧内
- 回到桌面啟動Activity A, 啟動時, 系統會自動添加
和FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
标志位FLAG_ACTIVITY_NEW_TASK
- 此時系統會從所有App棧記錄中查找和 Activity A taskAffinity相同的Activity, 并将找到的Activity放入這個A應用的新棧中
- A 應用有主Activity A 和Activity A_2, A_2 設定
FLAG_ACTIVITY_RETAIN_IN_RECENTS
要點
- 需要配合
使用, 系統會自動添加FLAG_ACTIVITY_NEW_DOCUMENT
标志位FLAG_ACTIVITY_NEW_TASK
- 使用
時, 加不加FLAG_ACTIVITY_NEW_DOCUMENT
并沒明顯作用FLAG_ACTIVITY_RETAIN_IN_RECENTS
FLAG_ACTIVITY_SINGLE_TOP
要點
- 如果Activity已經在棧頂部, 則不會啟動新執行個體, 僅調用
onNewIntent
- 可以搭配
使用, 如果隻使用FLAG_ACTIVITY_CLEAR_TOP
, 會銷毀本Activity和上面的其他Activity; 如果加上FLAG_ACTIVITY_CLEAR_TOP
, 就隻銷毀本Activity上面的其他Activity, 本Activity會保留, 并調用FLAG_ACTIVITY_SINGLE_TOP
onNewIntent
FLAG_DEBUG_LOG_RESOLUTION
要點
- 啟動Debug的标志, 在解析本Intent時列印日志消息
- 可以在logcat中過濾
關鍵字, 主要是IntentResolver
方法queryIntent
FLAG_EXCLUDE_STOPPED_PACKAGES / FLAG_INCLUDE_STOPPED_PACKAGES
要點
-
FLAG_EXCLUDE_STOPPED_PACKAGES
- 此Intent不去比對沒有運作的App, 防止喚醒
-
FLAG_INCLUDE_STOPPED_PACKAGES
- 此Intent去比對所有App, 包括沒有運作的App
- 這兩個Flag都不設定或者都設定, 效果同
FLAG_EXCLUDE_STOPPED_PACKAGES
FLAG_FROM_BACKGROUND
要點
- 表示Intent來自背景操作, 而不是直接來自使用者操作
- 手機測試時, 并沒有發現什麼特殊效果
FLAG_ACTIVITY_TASK_ON_HOME
要點
- 當Actvity傳回時, 直接回到桌面, 需要搭配
使用FLAG_ACTIVITY_NEW_TASK
- 需要讓Activity在棧底, 可選下列其一
- 指定taskAffinity
- 配合
FLAG_ACTIVITY_NEW_DOCUMENT
2.3 Uri Flags
FLAG_GRANT_READ_URI_PERMISSION / FLAG_GRANT_WRITE_URI_PERMISSION
要點
- 臨時對目标授予Uri的讀/寫權限
- 隻使用這兩個的話, 裝置重新開機權限就消失了, 需要重新授予
FLAG_GRANT_PERSISTABLE_URI_PERMISSION
要點
- 授予目标的權限一直保持, 重新開機裝置後依然存在, 這個隻是提供可能持久授權
- 需要
實作ContentResolver.takePersistableUriPermission(Uri, modeFlag)
FLAG_GRANT_PREFIX_URI_PERMISSION
要點
- 添加
标志位後, 隻要Uri的字首相比對就有相應的權限FLAG_GRANT_PREFIX_URI_PERMISSION
- 如果沒有這個标志位, 需要Uri完全比對才有相應的權限
使用案例
授予目标權限
Intent intent = new Intent();
// 這裡指定讀和寫權限
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
);
// 指定賦予權限的Uri
intent.setData = Uri.parse("content://com.jeasoon.learnintent/hello/world");
intent.setComponent = new ComponentName("com.jeasoon.learnintent_1", "com.jeasoon.learnintent.MainActivity");
// 啟動目标, 啟動後的目标就有對指定Uri的讀寫權限了
startActivity(intent);
回收權限
// 回收權限後, 目标就不能對Uri的讀寫做操作了
Context.revokeUriPermission(
Uri.parse("content://com.jeasoon.learnintent/hello/world"),
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
2.4 Receiver Flags
FLAG_RECEIVER_REGISTERED_ONLY
要點
- 隻有動态注冊的BroadcastReceiver可以收到消息
- 在AndroidManifest.xml注冊的BroadcastReceiver無法收到
FLAG_RECEIVER_REPLACE_PENDING
要點
- 如果你的廣播發送速度過快, 系統還沒來得及處理之前的廣播, 這些廣播就會被最新的廣播替換掉
- 確定讓程式處理的廣播是最新的
FLAG_RECEIVER_FOREGROUND
要點
- 以前台優先級運作廣播接受者
- 優先運作, 運作間隔會變小
FLAG_RECEIVER_NO_ABORT
要點
- 發送有序廣播時, 不允許中斷廣播
- 後續廣播接受者可以收到廣播并繼續修改傳遞結果
FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
要點
- 廣播可被Instant App接收
- 預設Instant App不接收任何廣播
2.5 Service Flags
BIND_AUTO_CREATE
要點
- 如果目标Service沒有啟動, 自動啟動目标Service, 但不調用
方法onStartCommand
- 如果目标Service已經啟動, 則隻調用目标Service的
方法onBind
- 如果目标Service沒有啟動, 并且沒有指定
, 則不會啟動目标Service, 但傳回值為BIND_AUTO_CREATE
, 當其他地方使用true
啟動Service時, 才會自動進行綁定startService
- 在4.0之前, Service設定
标志位後, Service的優先級就會和調用Service的程序優先級一緻, 不指定就是背景任務BIND_AUTO_CREATE
- 在4.0之後, Service設定
标志位後, Service才會與調用Service的程序優先級一緻BIND_ADJUST_WITH_ACTIVITY
- 在4.0之後, 為了保持相容, 在不指定
标志位時保持和4.0版本之前的行為一緻, 系統會自動添加BIND_AUTO_CREATE
和BIND_WAIVE_PRIORITY
标志位BIND_ADJUST_WITH_ACTIVITY
BIND_DEBUG_UNBIND
要點
- 因為使用時有記憶體洩漏問題, 建議僅用于debug模式
- 記錄解綁操作
, 當再次調用解綁操作unbindService()
時app會崩潰, 會把上次解綁的位置列印出來unbindService()
Caused by: java.lang.IllegalArgumentException: Originally unbound here:
at android.app.LoadedApk.forgetServiceDispatcher(LoadedApk.java:1277)
at android.app.ContextImpl.unbindService(ContextImpl.java:1521)
at android.content.ContextWrapper.unbindService(ContextWrapper.java:658)
at com.jeasoon.flags.MainActivity$onCreate$2.run(MainActivity.kt:44)
at android.os.Handler.handleCallback(Handler.java:754)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:163)
at android.app.ActivityThread.main(ActivityThread.java:6337)
BIND_NOT_FOREGROUND
要點
- 不允許綁定的Service優先級提升至前台
- 隻針對CPU排程, 不管用戶端的CPU排程優先級在哪裡, Service的CPU排程優先級都為背景
- 記憶體優先級依然和用戶端保持一緻, 不受影響
BIND_ABOVE_CLIENT
要點
- 提升Service的優先級比用戶端程序更重要
- 在記憶體不足時先殺用戶端(理論是這樣, 實際操作常常不是)
BIND_ALLOW_OOM_MANAGEMENT
要點
- 在OOM時, 作為殺死的目标
BIND_WAIVE_PRIORITY
要點
- 放棄優先級提升, 作為正常的背景标準程式, 允許系統利用LRU機制管理
BIND_IMPORTANT
要點
- Service對用戶端來說比較重要, 需要将Service提升到與用戶端同一等級
- 優先級最高也隻能提升到可見程序, 即使用戶端已經在前台了
BIND_ADJUST_WITH_ACTIVITY
要點
- 隻有綁定Service的是Actvitiy才會生效
- Service的優先級會受到Actvity的優先級影響, 就是說Acticity優先級提升, Service優先級也提升, 但是提升到什麼程度, 并沒有給出說明
- 會忽略其他降低優先級的标志位