对于搞android开发的人来讲,有些时候我们为了做一件很简单的事却要写大量的代码,这让人感到很不爽,比如在实现一个Fragment的时候.但庆幸的是java支持一个强大的工具:Annotation Processors
在使用Fragment的时候,我们可能需要从外部传递一些数据给他,很多刚接触android开发的人可能会像下面这样实现它:
public class MyFragment extends Fragment {
private int id;
private String title;
public static MyFragment newInstance(int id, String title) {
MyFragment f = new MyFragment();
f.id = id;
f.title = title;
return f;
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
Toast.makeText(getActivity(), "Hello " + title.substring(0, 3),
Toast.LENGTH_SHORT).show();
}
}
像这样写它可以工作,但是当你尝试将你的手机从纵屏旋转到横屏并尝试访问id或title的时候你的应用程序会产生NullPointerException而导致程序crash掉。
你可能会说我的应用程序是锁定在横屏或纵屏的,因此这个问题永远也不会发生。事情真的像你所想象的那样吗?
但是你不要忘了,android是一个真正的多任务操作系统。在同一时间可能运行着多个应用程序。假如系统的内存不足时,android系统会销毁掉运行在后台的应用程序中的Activity(和包含的Fragment)。在平时的开发中,你可能并没有注意到这个问题,然而,一旦你的应用程序发布到应用商店之后,你会得到你的应用程序crash的通知。你可能会好奇为什么会产生crash。你的应用程序的用户在同一时间段里使用了多个应用程序,当你的应用程序是运行在后台的时候,他很有可能会被系统给销毁。例如,一个用户打开了你的应用程序,MyFragment是被显示在手机屏幕上,之后用户按下手机的Home键(你的应用程序进入后台),然后用户打开了另外的应用程序。这个时候如果手机的内存不足,系统为了释放掉内存,将销毁运行在后台的程序,也就是说你的应用程序在这时会被系统销毁。一段时间过后用户又回到了你的应用程序,例如按下多任务按钮,回到你的应用程序。现在android将会做些什么呢?android将恢复你的应用程序到之前的状态,恢复MyFragment到之前的状态。MyFragment尝试访问title,但是title将为空,因为它并没有被持久化保存。
为了持久化的保存Fragment的数据,我们需要使用Fragment的setArguments(Bundle)
and getArguments()
这两个方法。这样即使Fragment被销毁,他的数据也能恢复到之前的状态。但是这样做比较痛苦的是你使用setArguments方法写入数据之后必须调用getArguments方法获取数据,就像下边的代码这样:
public class MyFragment extends Fragment {
private static String KEY_ID ="key.id";
private static String KEY_TITLE = "key.title";
private int id;
private String title;
public static MyFragment newInstance(int id, String title) {
MyFragment f = new MyFragment();
Bundle b = new Bundle();
b.putInt(KEY_ID, id);
b.putString(KEY_TITLE, title);
f.setArguments(b);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
// onCreate it's a good point to read the arguments
Bundle b = getArguments();
this.id = b.getInt(KEY_ID);
this.title = b.getString(KEY_TITLE);
}
@Override
public View onCreate(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// No NullPointer here, because onCreate() is called before this
Toast.makeText(getActivity(), "Hello " + title.substring(0, 3),
Toast.LENGTH_SHORT).show();
}
}
你可能觉得这样做比较麻烦。那么有没有什么方法既能持久化的保存Fragment的数据状态,又不用写这么多代码呢。当然有,这就是我接下来要介绍给大家的一个开源库FragmentArgs。
FragmentArgs开源库的地址:https://github.com/sockeqwe/fragmentargs
FragmentArgs是一个轻量级的开源库,他用来为你的Frgament生成准确java代码,这个库使用简单。下边是使用FragmentArgs的示例代码:
import com.hannesdorfmann.fragmentargs.FragmentArgs;
import com.hannesdorfmann.fragmentargs.annotation.Arg;
public class MyFragment extends Fragment {
@Arg
int id;
@Arg
String title;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
FragmentArgs.inject(this); // read @Arg fields
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
Toast.makeText(getActivity(), "Hello " + title,
Toast.LENGTH_SHORT).show();
}
}
大家会明显的发现使用FragmentArgs后我们少写了很多代码,这就是FrgamentArgs的强大之处。FragmentArgs会为你的Fragment中的注解属性生成样板代码。在你的Activity中你将使用FragmentArgs生成的Builder类代替new MyFragment(int id,String title)或静态的MyFragment.newInstance(int id,String title)方法。示例代码如下:
public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
int id = 123;
String title = "test";
// Using the generated Builder
Fragment fragment =
new MyFragmentBuilder(id, title)
.build();
// Fragment Transaction
getFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment)
.commit();
}
}
你可能注意到Fragment的onCreate()方法里边的FragmentArgs.inject(this)语句。当调用该语句的时候FragmentArgs将会为你的Fragment生成相应的代码。你可能会问那我是不是必须要在我的每一个Fragment的onCreate方法里边都要调用这个方法呀?你并不需要在你的每一个Fragment的onCreate方法里边都调用这个语句,FragmentsArgs.inject(this)语句支持继承,你只需要写一个Fragment基类,在基类的onCreate方法里调用FragmentArgs.inject(this)语句,然后让其它的Fragment继承这个基类就行了。就像下边的代码这样
public class BaseFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
FragmentArgs.inject(this); // 读取 @Arg 属性
}
}
public class MyFragment extends BaseFragment {
@Arg
String title;
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
Toast.makeText(getActivity(), "Hello " + title,
Toast.LENGTH_SHORT).show();
}
}
关于FragmentArgs的用法就讲解完了,下一篇 FragmentArgs (让你的Fragment的代码更少)讲解二中 我将 讲解在使用FragemtArgs需要注意的一些问题。由于本人水平有限,如有错误和不足之处还请大家能够为我指出。