天天看點

Android Intent/Context Flags 使用Android Intent/Context Flags 使用

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

      這一個标志位, 啟動時系統自動添加

      FLAG_ACTIVITY_NEW_TASK

      标志位, 效果等同于SingleInstance

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

要點
  • 設定後Activity不會展示到最近任務頁面中, 建議加上TaskAffinity配合使用
    • 例子: A啟動B時, 添加

      FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

      标志, 按Home回到桌面後再按最近任務頁面, 顯示的為A, 而不是B
  • 通過點選最近頁面或者圖示再次進入時, 會進入其他Activity頁面
  • 嘗試配合

    FLAG_ACTIVITY_NEW_TASK

    FLAG_ACTIVITY_MULTIPLE_TASK

    使用時, 效果很奇怪, 整個App都不在最近頁面了

FLAG_ACTIVITY_FORWARD_RESULT

要點
  • 委托請求結果
    • 例子: A通過

      startActivityForResult

      啟動B, B不知如何

      setResult

      , B就啟動C, 由C設定

      setResult

    • 當C傳回結果後, B設定

      setResult

      并沒有任何效果, A收到的值是C的結果
    • 再由B啟動C時需要調用

      startActivity

      , 使用

      startActivityForResult

      會抛出

      android.util.AndroidRuntimeException: FORWARD_RESULT_FLAG used while also requesting a result

      異常

FLAG_ACTIVITY_NEW_DOCUMENT

要點
  • 在最近任務頁面裡面添加同一個App的不同頁面記錄
  • 如果之前沒有啟動過Activity, 會建立一個新的任務棧, 并啟動一個新的Activity放入, 否則直接将之前的任務棧拉到前台
  • 系統會自動添加

    FLAG_ACTIVITY_NEW_TASK

    标志位
  • 如果單獨使用的話, 效果果同在Manifest設定

    documentLaunchMode="intoExisting"

  • 可以配合

    FLAG_ACTIVITY_MULTIPLE_TASK

    使用, 這樣就總會建立新的任務棧和Activity執行個體, 效果同在Manifest設定

    documentLaunchMode="always"

  • 在手機上測試時發現, 不指定這個标志位, 隻設定TaskAffinity, 也能達到這種效果, 但隻有一個執行個體, 設定

    FLAG_ACTIVITY_MULTIPLE_TASK

    也沒有效果

FLAG_ACTIVITY_NO_ANIMATION

要點
  • 去除動畫效果
  • 僅這一次有效, 如果A啟動B, B再啟動C, 需要再次設定這個标志位

FLAG_ACTIVITY_NO_HISTORY

要點
  • 通過設定

    FLAG_ACTIVITY_NO_HISTORY

    啟動的Activity在失去前台後會被銷毀掉
  • 并不是立即銷毀掉, 實際是這個Activity上面沒有其他Activity時才會開始銷毀
  • 感覺就是: 先失去前台, 再擷取到前台, 發現設定了

    FLAG_ACTIVITY_NO_HISTORY

    标志位, 就直接銷毀了

FLAG_ACTIVITY_NO_USER_ACTION

要點
  • Intent設定

    FLAG_ACTIVITY_NO_USER_ACTION

    後, 調用startActivity所在的Activity, 不會調用

    onUserLeaveHint

    , 不是被調用者
  • 不設定

    FLAG_ACTIVITY_NO_USER_ACTION

    情況下
    • onUserLeaveHint

      在失去前台時會被調用, 但在Activity銷毀時不會被調用
    • 不知道是不是手機問題, 通過最近任務頁面跳轉到其他App時,

      onUserLeaveHint

      也不會被調用

FLAG_ACTIVITY_LAUNCH_ADJACENT

要點
  • 僅在分屏多視窗模式下起作用
  • 新Activity将顯示在啟動它的Activity的旁邊
  • 需要與

    FLAG_ACTIVITY_NEW_TASK

    搭配使用, 如果需要建立現有Activity的新執行個體, 可以添加

    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

    這一套
  • FLAG_ACTIVITY_CLEAR_TOP

    的差別就在于, 是否會銷毀上面的Activity

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

要點
  • 轉移Activity到其他棧, 一般用于不同App之間
  • 需要在清單檔案配置如下兩個屬性:
    • android:export="true"

      允許其他App啟動
    • android:allowTaskReparenting=true

      允許Activity所在棧可以更換
  • 參考網絡資源, 主要工作流程如下:
    • 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應用的新棧中

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

    使用, 如果隻使用

    FLAG_ACTIVITY_CLEAR_TOP

    , 會銷毀本Activity和上面的其他Activity; 如果加上

    FLAG_ACTIVITY_SINGLE_TOP

    , 就隻銷毀本Activity上面的其他Activity, 本Activity會保留, 并調用

    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

要點
  • 添加

    FLAG_GRANT_PREFIX_URI_PERMISSION

    标志位後, 隻要Uri的字首相比對就有相應的權限
  • 如果沒有這個标志位, 需要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沒有啟動, 并且沒有指定

    BIND_AUTO_CREATE

    , 則不會啟動目标Service, 但傳回值為

    true

    , 當其他地方使用

    startService

    啟動Service時, 才會自動進行綁定
  • 在4.0之前, Service設定

    BIND_AUTO_CREATE

    标志位後, Service的優先級就會和調用Service的程序優先級一緻, 不指定就是背景任務
  • 在4.0之後, Service設定

    BIND_ADJUST_WITH_ACTIVITY

    标志位後, Service才會與調用Service的程序優先級一緻
  • 在4.0之後, 為了保持相容, 在不指定

    BIND_AUTO_CREATE

    标志位時保持和4.0版本之前的行為一緻, 系統會自動添加

    BIND_WAIVE_PRIORITY

    BIND_ADJUST_WITH_ACTIVITY

    标志位

BIND_DEBUG_UNBIND

要點
  • 因為使用時有記憶體洩漏問題, 建議僅用于debug模式
  • 記錄解綁操作

    unbindService()

    , 當再次調用解綁操作

    unbindService()

    時app會崩潰, 會把上次解綁的位置列印出來
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優先級也提升, 但是提升到什麼程度, 并沒有給出說明
  • 會忽略其他降低優先級的标志位