亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android?startActivityForResult的調(diào)用與封裝詳解

 更新時間:2023年03月23日 14:16:06   作者:newki  
startActivityForResult?可以說是我們常用的一種操作了,目前有哪些方式實現(xiàn)?startActivityForResult?的功能呢?本文就來和大家詳細(xì)聊聊

前言

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

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

目前有哪些方式實現(xiàn) startActivityForResult 的功能呢?

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

我們一起看看都是如何使用,使用起來方便嗎?通常我們又都是如何封裝的呢?

一、原生的使用

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

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

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

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

二、對原生的封裝Ghost

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

它使用一種 GhostFragment 的空視圖當(dāng)做一次中轉(zhuǎn),這種思路在現(xiàn)在看來已經(jīng)不稀奇了,很多框架如Glide,權(quán)限申請等都是用的這種方案。

它的大致實現(xiàn)流程為:

Activty/Fragment -> add GhostFragment -> onAttach 中 startActivityForResult -> GhostFragment onActivityResult接收結(jié)果 -> callback回調(diào)給Activty/Fragment

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

/**
 * 封裝Activity Result的API
 * 使用空Fragemnt的形式調(diào)用startActivityForResult并返回回調(diào)
 *
 * Activty/Fragment——>add GhostFragment——>onAttach中startActivityForResult
 * ——>GhostFragment onActivityResult接收結(jié)果——>callback回調(diào)給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的擴(kuò)展方法來對它進(jìn)行進(jìn)一步的封裝

//真正執(zhí)行AcytivityForResult的方法,使用Ghost的方式執(zhí)行
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) {
            //調(diào)用自己的擴(kuò)展方法-數(shù)組轉(zhuǎn)Bundle
            putExtras(bundle.toBundle()!!)
        }
    }
    Ghost.launchActivityForResult(this, intent, callback)
}

使用起來就超級簡單了:

    gotoActivityForResult<Demo10Activity> {
        val text = it?.getStringExtra("text")
        toast("拿到返回數(shù)據(jù):$text")
    }

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

三、Result Api 的使用

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

Activity Result API :

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

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

那么如何基礎(chǔ)的使用它呢:

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

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

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

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

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

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

  • 我們把 Launcher 的回調(diào)能在啟動的方法中觸發(fā)。
  • 實現(xiàn) Launcher 在 Activity/Fragment 中的自動注冊。

下面我們就來實現(xiàn)吧。

四、Result Api 的封裝

我們需要做的是:

第一步我們把回調(diào)封裝到launch方法中,并簡化創(chuàng)建的對象方式

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

4.1 封裝簡化創(chuàng)建方式

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

/**
 * 對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) {
                //調(diào)用自己的擴(kuò)展方法-數(shù)組轉(zhuǎn)Bundle
                putExtras(bundle.toBundle()!!)
            }
        }

        launch(intent, null, callback)

    }

}

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

如果大家想獲取文件,可以使用 GetContent() 泛型的參數(shù)就要變成 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("拿到返回數(shù)據(jù):$data")
        }
    }

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

    fun resultTest() {

       safLauncher?.launch<Demo10Activity> { result ->
            val data = result.data?.getStringExtra("text")
            toast("拿到返回數(shù)據(jù):$data")
        }

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

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

4.2 自動注冊/按需注冊

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

LifecycleOwners must call register before they are STARTED.

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

1)基類定義

我們都已經(jīng)封裝成對象使用了,我們把創(chuàng)建的邏輯定義到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 對象,你這么無腦創(chuàng)建出來消耗內(nèi)存。

有辦法,按需加載!

2).懶加載

懶加載可以吧,我需要的時候就創(chuàng)建。

abstract class AbsActivity() : AppCompatActivity(){

    val safLauncher by lazy { GetSAFLauncher(this) }

    ...
}

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

LifecycleOwners must call register before they are STARTED.

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

那我們就這么做:

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就行了。默認(rèn)是不創(chuàng)建這個對象的。

3).Kotlin委托

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

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

/**
 * 定義是否需要SAFLauncher
 */
interface ISAFLauncher {

    fun <T : ActivityResultCaller> T.initLauncher()

    fun getLauncher(): GetSAFLauncher?

}

接著定義這個實現(xiàn)類

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("拿到返回數(shù)據(jù):$data")
        }
    }

}

效果都是一樣的:

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

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

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

  • 第一步我們把回調(diào)封裝到launch方法中,并簡化創(chuàng)建的對象方式
  • 第二步我們嘗試自動注冊的功能

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

到此對 Result Api的封裝就此結(jié)束。

總結(jié)

總的來說 Result Api 的封裝其實也不難,使用起來也是很簡單了。如果大家是Kotlin項目我推薦使用委托的方式,如果是Java語言開發(fā)的也可以用flag的方式實現(xiàn)按需加載的邏輯。

而不想使用 Result Api 那么使用原始的 startActivityForResult 也能實現(xiàn),那么我推薦你使用 Ghost 框架,可以更加方便快速的實現(xiàn)返回的功能。

本文對于 Result Api 的封裝也只是限于 startActivityForResult 這一個場景,不過我們這種方式是很方便擴(kuò)展的,如果大家想使用Result Api的方式來操作權(quán)限,文件等,都可以在 BaseResultLauncher 基礎(chǔ)上進(jìn)行擴(kuò)展。

到此這篇關(guān)于Android startActivityForResult的調(diào)用與封裝詳解的文章就介紹到這了,更多相關(guān)Android startActivityForResult內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android編程之重力感應(yīng)用法分析

    Android編程之重力感應(yīng)用法分析

    這篇文章主要介紹了Android編程之重力感應(yīng)用法,結(jié)合實例形式較為詳細(xì)的分析了重力感應(yīng)的原理、相關(guān)概念與實現(xiàn)技巧,需要的朋友可以參考下
    2016-10-10
  • 淺析Android手機衛(wèi)士手機定位的原理

    淺析Android手機衛(wèi)士手機定位的原理

    手機定位的三種方式:網(wǎng)絡(luò)定位,基站定位,GPS定位。本文給大家介紹Android手機衛(wèi)士手機定位的原理,感興趣的朋友一起學(xué)習(xí)吧
    2016-04-04
  • Android自定義View構(gòu)造函數(shù)詳解

    Android自定義View構(gòu)造函數(shù)詳解

    這篇文章主要為大家詳細(xì)介紹了Android自定義View構(gòu)造函數(shù),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Android實現(xiàn)多媒體錄音筆

    Android實現(xiàn)多媒體錄音筆

    這篇文章主要介紹了Android實現(xiàn)多媒體錄音筆的相關(guān)資料,以及在實現(xiàn)過程中遇到問題的解決方法,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Flutter渲染原理深入解析

    Flutter渲染原理深入解析

    眾所周知?Flutter是由Google推出的開源的高性能跨平臺框架,一個2D渲染引擎。在Flutter中,Widget是Flutter用戶界面的基本構(gòu)成單元,可以說一切皆Widget。下面來看下Flutter框架的整體結(jié)構(gòu)組成
    2023-04-04
  • Android ActionBar制作時鐘實例解析

    Android ActionBar制作時鐘實例解析

    這篇文章主要為大家詳細(xì)介紹了Android ActionBar制作時鐘的實現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-05-05
  • Android開發(fā)自學(xué)筆記(四):APP布局下

    Android開發(fā)自學(xué)筆記(四):APP布局下

    這篇文章主要介紹了Android開發(fā)自學(xué)筆記(四):APP布局下,本文是上一篇的補充,需要的朋友可以參考下
    2015-04-04
  • Android實現(xiàn)多段顏色進(jìn)度條效果

    Android實現(xiàn)多段顏色進(jìn)度條效果

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)多段顏色進(jìn)度條效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • android實現(xiàn)文件下載功能

    android實現(xiàn)文件下載功能

    這篇文章主要為大家詳細(xì)介紹了android實現(xiàn)文件下載功能,android在網(wǎng)絡(luò)上下載文件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Android編程實現(xiàn)PendingIntent控制多個鬧鐘的方法

    Android編程實現(xiàn)PendingIntent控制多個鬧鐘的方法

    這篇文章主要介紹了Android編程實現(xiàn)PendingIntent控制多個鬧鐘的方法,涉及PendingIntent屬性設(shè)置與使用的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-12-12

最新評論