Creating a Fragment: constructor vs newInstance()
I recently grew tired of constantly having to know String
keys to pass arguments into Bundles
when creating my Fragments
. So I decided to make constructors for my Fragments
that would take the parameters I wanted to set, and put those variables into the Bundles
with the correct String
keys, therefore eliminating the need for other Fragments
and Activities
needing to know those keys.
public ImageRotatorFragment() {
super();
Log.v(TAG, "ImageRotatorFragment()");
} public ImageRotatorFragment(int imageResourceId) {
Log.v(TAG, "ImageRotatorFragment(int imageResourceId)"); // Get arguments passed in, if any
Bundle args = getArguments();
if (args == null) {
args = new Bundle();
}
// Add parameters to the argument bundle
args.putInt(KEY_ARG_IMAGE_RES_ID, imageResourceId);
setArguments(args);
}
And then I pull out those arguments like normal.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(TAG, "onCreate"); // Set incoming parameters
Bundle args = getArguments();
if (args != null) {
mImageResourceId = args.getInt(KEY_ARG_IMAGE_RES_ID, StaticData.getImageIds()[0]);
}
else {
// Default image resource to the first image
mImageResourceId = StaticData.getImageIds()[0];
}
}
However, Lint took issue with this, saying not to have subclasses of Fragment
with constructors with other parameters, requiring me to use @SuppressLint("ValidFragment")
to even run the app. The thing is, this code works perfectly fine. I can use ImageRotatorFragment(int imageResourceId)
or the old school method ImageRotatorFragment()
and call setArguments()
manually on it. When Android needs to recreate the Fragment (orientation change or low memory), it calls the ImageRotatorFragment()
constructor and then passes the same argument Bundle
with my values, which get set correctly.
So I have been searching for the "suggested" approach and see a lot of examples using newInstance()
to create Fragments
with parameters, which seems to do the same thing my constructor is. So I made my own to test it, and it works just as flawlessly as before, minus Lint whining about it.
public static ImageRotatorFragment newInstance(int imageResourceId) {
Log.v(TAG, "newInstance(int imageResourceId)"); ImageRotatorFragment imageRotatorFragment = new ImageRotatorFragment(); // Get arguments passed in, if any
Bundle args = imageRotatorFragment.getArguments();
if (args == null) {
args = new Bundle();
}
// Add parameters to the argument bundle
args.putInt(KEY_ARG_IMAGE_RES_ID, imageResourceId);
imageRotatorFragment.setArguments(args); return imageRotatorFragment;
}
I personally find that using constructors is a much more common practice than knowing to use newInstance()
and passing parameters. I believe you can use this same constructor technique with Activities and Lint will not complain about it. So basically my question is, why does Google not want you to use constructors with parameters for Fragments
?
My only guess is so you don't try to set an instance variable without using the Bundle
, which won't get set when the Fragment
gets recreated. By using a static newInstance()
method, the compiler won't let you access an instance variable.
public ImageRotatorFragment(int imageResourceId) {
Log.v(TAG, "ImageRotatorFragment(int imageResourceId)"); mImageResourceId = imageResourceId;
}
I still don't feel like this is enough reason to disallow the use of parameters in constructors. Anyone else have insight into this?
1#
I personally find that using constructors is a much more common practice than knowing to use newInstance() and passing parameters.
The factory method pattern is used fairly frequently in modern software development.
So basically my question is, why does Google not want you to use constructors with parameters for Fragments?
You answered your own question:
My only guess is so you don't try to set an instance variable without using the Bundle, which won't get set when the Fragment gets recreated.
Correct.
I still don't feel like this is enough reason to disallow the use of parameters in constructors.
You are welcome to your opinion. You are welcome to disable this Lint check, either on a per-constructor or per-workspace fashion.
#2
EDIT: Although this works and can be useful, it is risky as private fields once set using this pattern maybe will not be retained when changing orientation and the references, usually from a previously viewed fragment, may be cleared by the system reclaiming memory. Using a bundle and making your objects parseable is advised.
I have come across a nice instantiation pattern using newInstance that means you don't have to call setArguments or getArguments which means you are not limited to just the data types you can set in a bundle.
Fragments need to have empty constructors to enable the system to re-instantiate and fragment constructors with parameters won't get called when re-instantiating the fragment. Hence using newInstance is a much better idea to actually add object references and data to your fragment instance.
I also don't really like using setArguments, getArguments. It is extra code and null checks and just seems restrictive as you can only set Parcelable's and primitive types. You also need extra boiler plate code to set up static key name strings etc etc...
So, you can achieve the same result but with viewer restrictions using newInstance like this:
private int resourceId;
private SomeView someView;
private OtherObject otherObject; // empty constructor to allow fragment re-instantiation
public ImageRotatorFragment() {} public static ImageRotatorFragment newInstance
(int resourceId, SomeView someView, OtherObject otherObject) {
ImageRotatorFragment fragment = new ImageRotatorFragment();
fragment.resourceId = resourceId;
fragment.someView = someView;
fragment.otherObject = otherObject;
return fragment;
}
If done this way, you can access the private member variables/fields of your fragment anywhere in your code with out have to call getArguments etc....
Hopefully this helps with writing cleaner more flexible code.
1.But... your private fields will be lost if the screen is rotated. Calling setArguments internally avoids that. The point of the question was what's the difference between using a constructor and a static newInstance. (Not much as far as I can see). Writing directly to private fields could equally be done using a constructor. – Stuart Whitehouse
2.I'm the wrong person to ask - that's why I found this question ;-) But AFAIK you'll need to bundle them somehow to either add them to the arguments or stored state of the fragment. In my case all I needed to actually do was store the ID of the object (a Guid which can be converted to/from a string) and that lets me recover the object from the data store if the activity is re-created. – Stuart Whitehouse Sep 10 '13 at 14:03
3.@domji84 while this code will work when you first instantiate the Fragment
. If the screen rotates or the Fragment
is destroyed for memory needs, when it's recreated it will have it's empty constructor called and it will not set those instance variables again. This solution would only work if you also saved all of this state in onSaveInstanceState()
and restored it in onCreate()
, which means you still would have to use a Bundle
. – Steven Byle Sep 19 '13 at 13:32
public class TestFragment extends Fragment {
private static final String TAG = "TestFragment";
private String hello;// = "hello android";
private String defaultHello = "default value"; static TestFragment newInstance(String s) {
TestFragment newFragment = new TestFragment();
Bundle bundle = new Bundle();
bundle.putString("hello", s);
newFragment.setArguments(bundle);
return newFragment; } @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "TestFragment-----onCreate");
Bundle args = getArguments();
hello = args != null ? args.getString("hello") : defaultHello;
} @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
Log.d(TAG, "TestFragment-----onCreateView");
View view = inflater.inflate(R.layout.lay1, container, false);
TextView viewhello = (TextView) view.findViewById(R.id.tv_hello);
viewhello.setText(hello);
return view; } @Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "TestFragment-----onDestroy");
} }
Creating a Fragment: constructor vs newInstance()的更多相关文章
- listview,fragment结合起来
这是csdn上的以个demo,很适合初学者.来源:http://download.csdn.net/detail/endlife99/7274419,侵删. MainActiviy: package ...
- Android Fragment 详解(未完...)
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. 之前写过一篇关于 Fragment 生命周期的文章 ...
- fragment的介绍与使用
稍稍摘录一段Fragment.java中的说明文档. /** * A Fragment is a piece of an application's user interface or behavio ...
- 如何向一个Fragment传递参数---setArguments方法的介绍
在我们平常开发中经常会用到Fragment,当我们使用Fragment时一般是通过new Fragment的构造方法来实现,如果我问你怎么向一个Fragment传递参数,你是不是会首先想到通过构造方法 ...
- java中new关键字和newInstance()方法有什么区别?
1.new可以调用带参数的构造函数,newInstance不可以. 2.new 是 java 的关键字,用来构造一个类的实例.而 newInstance 是 Class 的一个方法,不过两个写法的效果 ...
- Fragment官方解析
由于fragment和activity的生命周期很类似,对activity不熟悉的可以参考–深入了解Activity-生命周期, 深入理解Activity-任务,回退栈,启动模式, 概要 A Frag ...
- ViewPager+Fragment的结合使用,实现QQ界面的理解
http://www.cssxt.com/html/2449/2449.html 效果如图: 实现代码解析:MainActivity.java1.引入布局文件2.4个标题控件的初始化以及点击事件的监听 ...
- Android Fragment详解(二):Fragment创建及其生命周期
Fragments的生命周期 每一个fragments 都有自己的一套生命周期回调方法和处理自己的用户输入事件. 对应生命周期可参考下图: 创建片元(Creating a Fragment) To c ...
- Java中创建对象的5种方式 &&new关键字和newInstance()方法的区别
转载:http://www.kuqin.com/shuoit/20160719/352659.html 用最简单的描述来区分new关键字和newInstance()方法的区别:newInstance: ...
随机推荐
- POJ3710 Christmas Game 博弈论 sg函数 树的删边游戏
http://poj.org/problem?id=3710 叶子节点的 SG 值为0:中间节点的SG值为它的所有子节点的SG值加1后的异或和. 偶环可以视作一个点,奇环视为一条边(连了两个点). 这 ...
- 【暴力】Gym - 100851A - Adjustment Office
题意:给你一个n*n的矩阵,初始时,(x,y)的值为x+y.可能有两类操作,一类是对某一行求和,并将这一行置零:另一类是对某一列求和,并将这一列置零. 维护四个值:一个是列标号之和,一个是当前存在的列 ...
- [HEOI2013]SAO
题目大意: 一个有向无环图上有n个结点, 现在告诉你n-1个条件(x,y),表示x和y的先后关系. 问原图共有几种可能的拓扑序? 思路: 树形DP. f[i][j]表示对于第i个结点,有j个点在它前面 ...
- ElasticSearch学习笔记--2、ES相关配置
1.配置文件 ES的配置文件位置:config/elasticsearch.yml可以直接搜索elasticsearch.yml 2.配置远程api访问 network.host: 192.168.1 ...
- bzoj 2055: 80人环游世界 -- 上下界网络流
2055: 80人环游世界 Time Limit: 10 Sec Memory Limit: 64 MB Description 想必大家都看过成龙大哥的<80天环游世界>,里面 ...
- CDOJ 1294 天行廖的游戏 dp 容斥
天行廖的游戏 题目连接: http://acm.uestc.edu.cn/#/problem/show/1294 Description 天行健,君子以自强不息.地势坤,廖爷以厚德载物 一日在喵哈哈村 ...
- HDU 5644 King's Pilots 费用流
King's Pilots 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5644 Description The military parade w ...
- Java实现-每天三道剑指Offre(2-4)
实现一个单例模式 /** * 面试题2:实现单例模式 * * @author qiuyong 饿汉式 */ public class Singleton01 { private Singleton01 ...
- zabbix install
Auth: Jin Date: 20140714 用了5 6年的监控工具 http://zabbix.org/wiki/InstallOnCentOS_RHEL Server Install yum ...
- 在iOS 7中使用storyboard(part 1)
原文:Storyboards Tutorial in iOS 7: Part 1 感谢翻译小组成员heartasice热心翻译.如果您有不错的原创或译文,欢迎提交给我们,更欢迎其他朋友加 ...