Fragment的用法注意Google提供的范式(Fragment的正确传参方法)

之所以说是Fragment的真正用法是因为在我之前的一年的对Fragment的使用都是有问题的。
当时我对Fragment的学习是从《疯狂Android讲义》配合翻网上的博客什么的而来的可是在我使用的过程中不知道是因为自己翻看的blog比较少或者其他什么原因一直不知道自己存在的问题。知道前几个月在做我们接的一个小项目编译release版本的时候发现一个让自己头疼不已的问题,每次编译都报错。
当时的错误是这样:我给Fragment定义了一个有参的构造函数,报错说不能如此定义。这就尴尬了,我很复杂的数据总不能通过Intent封装传递吧(当然传递是可以的可是可定会对代码的可读性造成障碍,我向来更认同函数直接传参),当然出了这个问题都就开始各种百度(对了顺便说下,用Intent传参肯定是不好的,下面你就知道了)。
不知道大家发现没,很多时候你直接百度一些错误信息的时候往往会出现StackOverFlow这个网站。当时我们也是跳到了这里,硬着头皮看到了似乎是说要实现newInstance()这个方法来进行传参。于是又百度这个方法,可是或许是因为当时这个问题出现的并不频繁吧,并没有百度到很多相关的很详细的信息不过也够我们用了,下面给大家说下(当然我也知道现在网上似乎很多类似回答都已经被百度提到前排了,后面也会贴出筛选的其他的)。

其实这篇blog主要解决的是Fragment传参的问题。对于Fragment的相关生命周期方法、回退栈等相关内容并不涉及。为什么会整理这篇blog我上面已经提到,开篇的话我在总结下:在Activity中用到Fragment的时候有时候会有这样一种情况,我的网络请求是在Activity中实现的,获取到参数需要传递给Fragment,可是这个参数传递的过程想想似乎有很多方式,可是哪种更好一点呢?这就是我这篇blog要说的。Google给我们了一种范式我们照着这个模板来就好,其实这也是我很烦的地方,不知道是自己阅读demo太少的原因还是什么,很多Google的范式并不能意识到于是就在写代码的时候栽了很多坑。写出来算是给后面的人填坑吧,有谁像我一样不喜欢读demo又没有很心有灵犀的每次都注意的到Google的意图那就可以少载坑了,赞一个。

1. 实现

public class TestFragment extends Fragment{
    private type mArgument_one;
    private type mArgument_two;
    //......其他一些参数

    public static TestFragment newInstance(type argument_one, type argument_two, .....){
        //new 一个Bundle,将argument_one、argument_two等参数传入Bundle
        Bundle args = new Bundle();
        args.put("one",argument_one);

        //new一个Fragment对象并将你的Bundle对象set给Fragment
        Testfragment fragment = new TestFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        Bundle args = getArguments();
        //对参数进初始化
        mArgument_one = args.get("one");
        //......
    }
}

2. 解释

看上面代码我们发现

  • 我们定义了一个静态newInstance()方法来构造一个Fragment
  • 可以通过newInstance()方法来传递参数,并将参数保存到Bundle中
  • 将保存了参数的Bundle传递给Fragment对象,并将fragment对象返回
  • 在onCreate()方法中拿到Bundle的引用,然后通过这个引用对Fragment的全局对象进行初始化

3. 优势

我们先分析下给Fragment初始化数据的时候有几种方式:

1、定义一个含参的构造函数,通过构造函数来传递参数
2、在Activity中将数据封存到Intent中,然后在fragment 中getActivity().getIntent()来获取参数
3、在fragment中实现一个接口,通过这个接口进行值得传递
4、就是上面通过静态工厂来生成Fragment

咱们开始一项一项的来说:

1、定义一个含参的构造函数,通过构造函数来传递参数

这个方法几乎是不能考虑的,因为你在编译成发布版本APP的时候就会给你报错编译不成功(不过你在debug的时候程序一般是可以编译通过并运行的,有的人或许会遇到在构造函数那里有波浪线的情况,不过一般是不影响编译的,具体可以看你的编译的警告级别)。可是Google为什么不希望甚至不允许你用这个有参的构造方法呢? 你猜!其实是这样的,大家应该知道Activity存在销毁与创建的情况,不知道的可以参看这篇文章Android 恢复Activity现场。既然Fragment是依附于Activity的那不可避免的Fragment也会存在被系统销毁的时候。那系统重建Fragment的时候只会调用Fragment的无参构造方法,你创建一个有参的构造函数当然会被警告了,否则难道还调用你的有参函数并且给你自动传参吗(灬╹ω╹灬)。。。。Google也是为我们操碎了心,让我们在很多地方避免犯直接的错误,可是无意中却给我们增加了很多弯。也许有人会说Activity明明也要保存怎么没这么麻烦,(灬╹ω╹灬),你在Activity里面写过构造函数吗?逗比。。。在我看来这也是Activity采用Intent的一个好处,之所以说是一个好处而不说是原因是因为我相信Activity的这种启动模式最开始绝对不是为这个功能而设计的啊,至于Activity、Service为什么采用Intent来启动以及有什么好处我们下一篇来讨论。

2、在Activity中将数据封存到Intent中,然后在fragment 中getActivity().getIntent()来获取参数

这种方式可以说两点来反驳这种用法,当然,你要舍本逐末的话处理的好也是可以用的。第一点:intent是不支持传输大数据量的(扩充下:Intent是传值的并非传引用,自己去BaseBundle中查看下相关方法,有时间的话会把相关的仔细学习下并推一篇blog的)。第二点:intent传输的数据在系统销毁相关界面时并不会被系统保存(我发现这个地方存在个问题,我不确定这个Intent会不会被ActivityThread所保存,因为我这里暂时查看ActivityThread的源码有点不方便,网上百度了下也没有,那就先放一放。等最近我将Bundle的传值和Intent的传递整理好一并发出来)。

3、在fragment中实现一个接口,通过这个接口进行值得传递

呵呵,这样确实很直接的解决了Fragment不能定义有参构造函数的问题,可是问题也很明显,Google之所以不让你定义有参构造函数是为了当界面被系统销毁的时候还可以恢复回来而设置的,你这也很好的将Google这优良的设计给屏蔽掉了,想想都尴尬。当然也不是所有的时候都会出现这个问题,一般是当你有用到网络请求的时候因为多线程的原因而出现这个问题。当然想上一个方法一样通过一些反Google的方法也是可以实现程序的健壮性的,but何必呢,按规矩来不好吗,就为了穿个参数你还非得写个接口,然后出于某些原因你还得注意他们的生命周期函数的调用时机,想想都烦躁。好好地按照方法4来实现传参老大哥们。

4、就是上面通过静态工厂来生成Fragment

这个方法有什么好处呢?tell you。上面通过xxxArguments()这个函数设置的Bundle会被该进程给保存着,然后当出现被系统销毁的情况的时候就可以没有bug的恢复。

以上写了这么多,发现其实还是有很多地方需要去补充的,本来想的直接百度出链接放上来可是百度的时候才发现并没有或者也是自己没有百度出来。不过这个不重要,里面的相关结论一般是不会有什么问题的,毕竟我靠着这些结论也做了几个APP了,最近一段时间我会尽快把源码整理出来证明给大家的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值