亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android?startActivityForResult怎么調用與封裝

發布時間:2023-03-23 16:34:32 來源:億速云 閱讀:235 作者:iii 欄目:開發技術

今天小編給大家分享一下Android startActivityForResult怎么調用與封裝的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    前言

    startActivityForResult 可以說是我們常用的一種操作了,用于啟動新頁面并拿到這個頁面返回的數據,是兩個 Activity 交互的基本操作。

    雖然可以通過接口,消息總線,單例池,ViewModel 等多種方法來間接的實現這樣一個功能,但是 startActivityForResult 還是使用最方便的。

    目前有哪些方式實現 startActivityForResult 的功能呢?

    有新老兩種方式,過時的方法是原生Activity/Fragment的 startActivityForResult 方法。另一種方法是 Activity Result API 通過 registerForActivityResult 來注冊回調。

    一、原生的使用

    不管是Activity還是Fragment,我們都可以使用 startActivityForResult

    Android?startActivityForResult怎么調用與封裝

        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            super.onActivityResult(requestCode, resultCode, data)
            if (requestCode == 120 && resultCode == -1) {
                toast("接收到返回的數據:" + data?.getStringExtra("text"))
            }
        }

    可以看到雖然標記過時了,但是 startActivityForResult 這種方法是可以用的,我們一直這么用的,老項目中有很多頁面都是這么定義的。也并沒有什么問題。

    不過既然谷歌推薦我們使用 Result Api 我們在以后使用 startActivityForResult 的時候還是推薦使用新的方式。

    二、對原生的封裝Ghost

    在之前我們使用 startActivityForResult 這種方式的時候,為了更加方便的私有,有一種很流行的方式 Ghost 。

    它使用一種 GhostFragment 的空視圖當做一次中轉,這種思路在現在看來已經不稀奇了,很多框架如Glide,權限申請等都是用的這種方案。

    它的大致實現流程為:

    Activty/Fragment -> add GhostFragment -> onAttach 中 startActivityForResult -> GhostFragment onActivityResult接收結果 -> callback回調給Activty/Fragment

    總體需要兩個類就可以完成這個邏輯,一個是中轉Fragment,一個是管理類:

    /**
     * 封裝Activity Result的API
     * 使用空Fragemnt的形式調用startActivityForResult并返回回調
     *
     * Activty/Fragment——>add GhostFragment——>onAttach中startActivityForResult
     * ——>GhostFragment onActivityResult接收結果——>callback回調給Activty/Fragment
     */
    class GhostFragment : Fragment() {
    
        private var requestCode = -1
        private var intent: Intent? = null
        private var callback: ((result: Intent?) -> Unit)? = null
    
        fun init(requestCode: Int, intent: Intent, callback: ((result: Intent?) -> Unit)) {
            this.requestCode = requestCode
            this.intent = intent
            this.callback = callback
        }
    
        private var activityStarted = false
    
        override fun onAttach(activity: Activity) {
            super.onAttach(activity)
            if (!activityStarted) {
                activityStarted = true
                intent?.let { startActivityForResult(it, requestCode) }
            }
        }
    
        override fun onAttach(context: Context) {
            super.onAttach(context)
            if (!activityStarted) {
                activityStarted = true
                intent?.let { startActivityForResult(it, requestCode) }
            }
        }
    
        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            super.onActivityResult(requestCode, resultCode, data)
            if (resultCode == Activity.RESULT_OK && requestCode == this.requestCode) {
                callback?.let { it1 -> it1(data) }
            }
        }
    
        override fun onDetach() {
            super.onDetach()
            intent = null
            callback = null
        }
    
    }
    /**
     * 管理GhostFragment用于StartActivityForResult
     * 啟動的時候添加Fragment 返回的時移除Fragment
     */
    object Ghost {
        var requestCode = 0
            set(value) {
                field = if (value >= Integer.MAX_VALUE) 1 else value
            }
    
        inline fun launchActivityForResult(
            starter: FragmentActivity?,
            intent: Intent,
            crossinline callback: ((result: Intent?) -> Unit)
        ) {
            starter ?: return
            val fm = starter.supportFragmentManager
            val fragment = GhostFragment()
            fragment.init(++requestCode, intent) { result ->
                callback(result)
                fm.beginTransaction().remove(fragment).commitAllowingStateLoss()
            }
            fm.beginTransaction().add(fragment, GhostFragment::class.java.simpleName)
                .commitAllowingStateLoss()
        }
    
    }

    如此我們就可以使用Kotlin的擴展方法來對它進行進一步的封裝

    //真正執行AcytivityForResult的方法,使用Ghost的方式執行
    inline fun <reified T> FragmentActivity.gotoActivityForResult(
        flag: Int = -1,
        bundle: Array<out Pair<String, Any?>>? = null,
        crossinline callback: ((result: Intent?) -> Unit)
    ) {
        val intent = Intent(this, T::class.java).apply {
            if (flag != -1) {
                this.addFlags(flag)
            }
            if (bundle != null) {
                //調用自己的擴展方法-數組轉Bundle
                putExtras(bundle.toBundle()!!)
            }
        }
        Ghost.launchActivityForResult(this, intent, callback)
    }

    使用起來就超級簡單了:

        gotoActivityForResult<Demo10Activity> {
            val text = it?.getStringExtra("text")
            toast("拿到返回數據:$text")
        }
    
        gotoActivityForResult<Demo10Activity>(bundle = arrayOf("id" to "123", "name" to "zhangsan")) {
            val text = it?.getStringExtra("text")
            toast("拿到返回數據:$text")
        }

    三、Result Api 的使用

    其實看Ghost的原來就看得出,他本質上還是對 startActivityForResult 的調用與封裝,還是過期的方法,那么如何使用新的方式,谷歌推薦我們怎么用?

    Activity Result API :

    它是 Jetpack 的一個組件,這是官方用于替代 startActivityForResult() 和 onActivityResult() 的工具,我們以Activity 1.2.4版本為例:

    implementation "androidx.activity:activity-ktx:1.2.4"

    那么如何基礎的使用它呢:

      
        private val safLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            if (result.resultCode == RESULT_OK) {
                val data = result.data?.getStringExtra("text")
                toast("拿到返回數據:$data")
            }
        }
        
        //在方法中使用
        safLauncher?.launch(Intent(mActivity, Demo10Activity::class.java))

    看起來實現很簡單,但是有幾點要注意,Launcher 的創建需要在onStart生命周期之前,并且回調是在 Launcher 中處理的。并且 這些 Launcher 并不是只能返回Activity的Result的,還有其他的啟動方式:

    StartActivityForResult()
    StartIntentSenderForResult()
    RequestMultiplePermissions()
    RequestPermission()
    TakePicturePreview()
    TakePicture()
    TakeVideo()
    PickContact()
    GetContent()
    GetMultipleContents()
    OpenDocument()
    OpenMultipleDocuments()
    OpenDocumentTree()
    CreateDocument()

    可以看到這些方式其實對我們來說很多沒必要,在真正的開發中只有 StartActivityForResult 這一種方式是我們的剛需。

    為什么?畢竟現在誰還用這種方式申請權限,操作多媒體文件。相信大家也都是使用框架來處理了,所以我們這里只對 StartActivityForResult 這一種方式做處理。畢竟這才是我們使用場景最多的,也是我們比較需要的。

    經過分析,對Result Api的封裝,我們就剩下的兩個重點問題:

    • 我們把 Launcher 的回調能在啟動的方法中觸發。

    • 實現 Launcher 在 Activity/Fragment 中的自動注冊。

    下面我們就來實現吧。

    四、Result Api 的封裝

    我們需要做的是:

    第一步我們把回調封裝到launch方法中,并簡化創建的對象方式

    第二步我們嘗試自動注冊的功能

    4.1 封裝簡化創建方式

    首先第一步,我們對 Launcher 對象做一個封裝, 把 ActivityResultCallback 回調方法在 launch 方法中調用。

    /**
     * 對Result-Api的封裝,支持各種輸入與輸出,使用泛型定義
     */
    @SuppressWarnings("unused")
    public class BaseResultLauncher<I, O> {
    
        private final androidx.activity.result.ActivityResultLauncher<I> launcher;
        private final ActivityResultCaller caller;
        private ActivityResultCallback<O> callback;
        private MutableLiveData<O> unprocessedResult;
    
        public BaseResultLauncher(@NonNull ActivityResultCaller caller, @NonNull ActivityResultContract<I, O> contract) {
            this.caller = caller;
            launcher = caller.registerForActivityResult(contract, (result) -> {
                if (callback != null) {
                    callback.onActivityResult(result);
                    callback = null;
                }
            });
        }
    
        public void launch(@SuppressLint("UnknownNullness") I input, @NonNull ActivityResultCallback<O> callback) {
            launch(input, null, callback);
        }
    
        public void launch(@SuppressLint("UnknownNullness") I input, @Nullable ActivityOptionsCompat options, @NonNull ActivityResultCallback<O> callback) {
            this.callback = callback;
            launcher.launch(input, options);
        }
    
    }

    上門是對Result的基本封裝,由于我們只想要 StartActivityForResult 這一種方式,所以我們定義一個特定的 GetSAFLauncher

    /**
     * 一般我們用這一個-StartActivityForResult 的 Launcher
     */
    class GetSAFLauncher(caller: ActivityResultCaller) :
        BaseResultLauncher<Intent, ActivityResult>(caller, ActivityResultContracts.StartActivityForResult()) {
    
        //封裝另一種Intent的啟動方式
        inline fun <reified T> launch(
            bundle: Array<out Pair<String, Any?>>? = null,
            @NonNull callback: ActivityResultCallback<ActivityResult>
        ) {
    
            val intent = Intent(commContext(), T::class.java).apply {
                if (bundle != null) {
                    //調用自己的擴展方法-數組轉Bundle
                    putExtras(bundle.toBundle()!!)
                }
            }
    
            launch(intent, null, callback)
    
        }
    
    }

    注意這里調用的是 ActivityResultContracts.StartActivityForResult() 并且泛型的兩個參數是 Intent 和 ActivityResult。

    如果大家想獲取文件,可以使用 GetContent() 泛型的參數就要變成 String 和 Uri 。由于我們通常不使用這種方式,所以這里不做演示。

    封裝第一步之后我們就能這么使用了。

        var safLauncher: GetSAFLauncher? = null
    
        //其實就是 onCreate 方法
        override fun init() {
            safLauncher = GetSAFLauncher(this@Demo16RecordActivity)
        }
    
        //AFR
        fun resultTest() {
    
            safLauncher?.launch(Intent(mActivity, Demo10Activity::class.java)) { result ->
                val data = result.data?.getStringExtra("text")
                toast("拿到返回數據:$data")
            }
        }

    //或者使用我們自定義的簡潔方式

        fun resultTest() {
    
           safLauncher?.launch<Demo10Activity> { result ->
                val data = result.data?.getStringExtra("text")
                toast("拿到返回數據:$data")
            }
    
            safLauncher?.launch<Demo10Activity>(arrayOf("id" to "123", "name" to "zhangsan")) { result ->
                val data = result.data?.getStringExtra("text")
                toast("拿到返回數據:$data")
            }
        }

    使用下來是不是簡單了很多了,我們只需要創建一個對象就可以了,拿到這個對象調用launch即可實現 startActivityForResult 的功能呢!

    4.2 自動注冊/按需注冊

    可以看到相比原始的用法,雖然我們現在的用法就簡單了很多,但是我們還是要在oncreate生命周期中創建 Launcher 對象,不然會報錯:

    LifecycleOwners must call register before they are STARTED.

    那我們有哪些方法處理這個問題?

    1)基類定義

    我們都已經封裝成對象使用了,我們把創建的邏輯定義到BaseActivity/BaseFragment不就行了嗎?

    abstract class AbsActivity() : AppCompatActivity(){
    
        protected var safLauncher: GetSAFLauncher? = null
    
        ...
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView()
    
            //Result-Api
            safLauncher = GetSAFLauncher(this)
    
            ...
        }
    
    }

    這樣不就行了嗎?可以正常使用的。那有人可能說,你這個對象可能用不到,又不是每一個Activity都會用到 Launcher 對象,你這么無腦創建出來消耗內存。

    有辦法,按需加載!

    2).懶加載

    懶加載可以吧,我需要的時候就創建。

    abstract class AbsActivity() : AppCompatActivity(){
    
        val safLauncher by lazy { GetSAFLauncher(this) }
    
        ...
    }

    額,等等,這樣的懶加載貌似是不行的,這在用的時候才初始化,一樣會報錯:

    LifecycleOwners must call register before they are STARTED.

    我們只能在頁面創建的時候就要明確,這個頁面是否需要這個 Launcher 對象,如果要就要在onCreate中創建對象,如果確定不要 Launcher 對象,那么就不必創建對象。

    那我們就這么做:

    abstract class AbsActivity() : AppCompatActivity(){
    
        protected var safLauncher: GetSAFLauncher? = null
    
        ...
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView()
    
            if (needLauncher()) {
                //Result-Api
                safLauncher = GetSAFLauncher(this)
            }
    
            ...
        }
    
        open protected fun needLauncher(): Boolean = false
    
    }

    我們使用一個flag判斷不就行了嗎?這個頁面如果需要 Launcher 對象,重寫方法返回true就行了。默認是不創建這個對象的。

    3).Kotlin委托

    我們可以使用Kotlin的委托方式,把初始化的代碼和 Launcher 的對象獲取用接口封裝,然后提供對應的實現類,不就可以完成按需添加 Launcher 的效果了嗎?

    我們定義一個接口,由于邏輯都封裝在了別處,這里就盡量不改動之前的代碼,只是定義初始化和提供對象兩種方法。

    /**
     * 定義是否需要SAFLauncher
     */
    interface ISAFLauncher {
    
        fun <T : ActivityResultCaller> T.initLauncher()
    
        fun getLauncher(): GetSAFLauncher?
    
    }

    接著定義這個實現類

    class SAFLauncher : ISAFLauncher {
    
        private var safLauncher: GetSAFLauncher? = null
    
        override fun <T : ActivityResultCaller> T.initLauncher() {
            safLauncher = GetSAFLauncher(this)
        }
    
        override fun getLauncher(): GetSAFLauncher? = safLauncher
    
    }

    然后我們就可以使用了:

    class Demo16RecordActivity : BaseActivity, ISAFLauncher by SAFLauncher() {
    
        //onCreate中直接初始化對象
        override fun init() {
            initLauncher()
        }
    
        
        //獲取到對象直接用即可,還是之前的幾個方法,沒有變。
        fun resultTest() {
    
           getLauncher()?.launch<Demo10Activity> { result ->
                val data = result.data?.getStringExtra("text")
                toast("拿到返回數據:$data")
            }
        }
    
    }

    效果都是一樣的:

    Android?startActivityForResult怎么調用與封裝

    這樣通過委托的方式,我們就能自己管理初始化,自己隨時獲取到對象調用launch方法。

    如果你當前的Activity不需要 startActivityForResult 這種功能,那么你不實現這個接口即可,如果想要 startActivityForResult 的功能,就實現接口委托實現,從而實現按需加載的邏輯。

    我們再回顧一下 Result Api 需要封裝的兩個痛點與優化步驟:

    • 第一步我們把回調封裝到launch方法中,并簡化創建的對象方式

    • 第二步我們嘗試自動注冊的功能

    同時我們還對一些步驟做了更多的可能性分析,對主動注冊的方式我們有三種方式,(當然其實還有更多別的方式來實現,我只寫了我認為比較簡單方便的幾種方式)。

    到此對 Result Api的封裝就此結束。

    以上就是“Android startActivityForResult怎么調用與封裝”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    渝北区| 通海县| 屯昌县| 呼伦贝尔市| 荆门市| 岚皋县| 保山市| 雅江县| 遂平县| 会昌县| 白河县| 汕尾市| 南陵县| 东乌| 临高县| 灌阳县| 鄂托克前旗| 武定县| 德兴市| 三穗县| 承德市| 广汉市| 九龙城区| 城口县| 溧阳市| 资兴市| 武功县| 白朗县| 综艺| 苗栗县| 罗甸县| 白银市| 于田县| 大方县| 克东县| 漳平市| 长治市| 九寨沟县| 获嘉县| 鄂托克旗| 鄂温|