我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android startActivityForResult怎么调用与封装

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android startActivityForResult怎么调用与封装

今天小编给大家分享一下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,一个是管理类:

    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    }}
    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 方法中调用。

    @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

    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 的效果了吗?

    我们定义一个接口,由于逻辑都封装在了别处,这里就尽量不改动之前的代码,只是定义初始化和提供对象两种方法。

    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怎么调用与封装”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    Android startActivityForResult怎么调用与封装

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档

    猜你喜欢

    Android startActivityForResult怎么调用与封装

    今天小编给大家分享一下Android startActivityForResult怎么调用与封装的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们
    2023-07-05

    Android startActivityForResult的调用与封装详解

    startActivityForResult 可以说是我们常用的一种操作了,目前有哪些方式实现 startActivityForResult 的功能呢?本文就来和大家详细聊聊
    2023-03-23

    Android startActivityForResult怎么使用

    在Android中,startActivityForResult()方法用于启动一个Activity,并且在该Activity完成后返回结果。以下是使用startActivityForResult()方法的步骤:1. 在调用startAct
    2023-08-11

    Android的startactivityforresult怎么使用

    使用startActivityForResult()方法可以启动一个Activity,并且在该Activity关闭后,可以获取到返回的结果。以下是使用startActivityForResult()方法的步骤:1. 在当前Activity中
    2023-08-08

    Android中startActivityForResult怎么使用

    在Android中,startActivityForResult()方法允许你启动一个新的Activity,并且在新的Activity结束后返回结果给调用它的Activity。首先,在调用startActivityForResult()方法
    2023-09-08

    Hooks怎么封装与使用

    今天小编给大家分享一下Hooks怎么封装与使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Hooks是什么?本篇文章主要介
    2023-07-04

    Android中HttpURLConnection与HttpClient的使用与封装

    1.写在前面 大部分andriod应用需要与服务器进行数据交互,HTTP、FTP、SMTP或者是直接基于SOCKET编程都可以进行数据交互,但是HTTP必然是使用最广泛的协议。 本文并不针对HTTP协议的具体内容,仅探讨an
    2022-06-06

    python怎么封装成可调用的库

    要将Python代码封装成可调用的库,可以按照以下步骤进行操作:将需要封装的代码放入一个独立的Python模块文件中,例如mylibrary.py。在mylibrary.py中定义需要暴露给外部使用的函数、类或变量。在需要使用该库的Py
    2023-10-23

    Android中怎么利用PagerAdapter封装类

    Android中怎么利用PagerAdapter封装类,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。ViewPager是android的support库中的一个控件,也是一
    2023-05-30

    python怎么封装api给其他人调用

    在Python中,封装API可以通过创建类或者函数来完成。一种常见的方式是创建一个类,并在类中定义各种需要封装的方法和属性。其他人可以通过实例化这个类,然后调用相应的方法来使用API提供的功能。以下是一个简单的示例:class MyAP
    2023-10-23

    怎么在Python项目中调用C++进行封装

    怎么在Python项目中调用C++进行封装?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Python主要用来做什么Python主要应用于:1、Web开发;2、数据科学研究;
    2023-06-06

    Android应用中怎么对RecyclerView Adapter进行封装

    本篇文章给大家分享的是有关Android应用中怎么对RecyclerView Adapter进行封装,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1、通常我们封装的时候,可以简
    2023-05-31

    python中类怎么定义与封装

    在Python中,可以使用class关键字来定义一个类。类的定义包括类名、类的属性和方法。类的封装是指将类的属性和方法封装起来,只允许通过类的接口访问和修改属性,以及调用方法。这样可以隐藏类的实现细节,提高代码的可维护性和安全性。下面是一个
    2023-10-19

    Android MediaPlayer音频播放器怎么封装

    要封装一个Android MediaPlayer音频播放器,你可以按照以下步骤进行:创建一个新的类,命名为AudioPlayer或类似的名称。在这个类中,你需要声明一个MediaPlayer对象作为成员变量。添加一个构造方法,用于初始化Me
    Android MediaPlayer音频播放器怎么封装
    2024-03-02

    python中的类怎么定义与封装

    在Python中,可以使用关键字`class`来定义一个类。类是一种抽象的数据类型,可用于封装数据和方法。下面是一个简单的类的定义与封装的示例:```pythonclass Person:def __init__(self, name, a
    2023-10-11

    在Android应用中怎么对Toast提示进行封装

    在Android应用中怎么对Toast提示进行封装?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Android Toast提示封装Android中经常用到Toa
    2023-05-31

    Android应用中怎么实现与H5互调

    Android应用中怎么实现与H5互调?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。案例一:Java与Js简单互调 首先,在Android代码中加
    2023-05-31

    怎么在Android中使用OkUSB封装一个串口通信

    这篇文章给大家介绍怎么在Android中使用OkUSB封装一个串口通信,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。OkUSB一个简洁的Android串口通信框架。功能简介支持设置波特率支持设置数据位支持设置停止位支持
    2023-05-30

    C++类与封装的概念是什么及怎么使用

    这篇文章主要介绍“C++类与封装的概念是什么及怎么使用”,在日常操作中,相信很多人在C++类与封装的概念是什么及怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++类与封装的概念是什么及怎么使用”的疑
    2023-06-30

    编程热搜

    • Python 学习之路 - Python
      一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
      Python 学习之路 - Python
    • chatgpt的中文全称是什么
      chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
      chatgpt的中文全称是什么
    • C/C++中extern函数使用详解
    • C/C++可变参数的使用
      可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
      C/C++可变参数的使用
    • css样式文件该放在哪里
    • php中数组下标必须是连续的吗
    • Python 3 教程
      Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
      Python 3 教程
    • Python pip包管理
      一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
      Python pip包管理
    • ubuntu如何重新编译内核
    • 改善Java代码之慎用java动态编译

    目录