Analysis of Android4.0-Fragment Framework Implementation (1)

1. What is Fragment?

Fragment is contained in Activity. Fragment can only exist in the context of Activity. Fragment cannot be used without Activity, so Fragment can only be created in the context of Activity. Fragment can be part of Activity, Fragment is very similar to Activity, Fragment has a view hierarchy related to her, and has a life cycle very similar to Activity.

2. Why use Fragment?

Activity usage limitations: Multiple Activity activity interfaces cannot be placed on the screen and displayed. Therefore, Fragment was created to make up for the limitations of Activity. Fragment can respond to Activity-like functions such as the Back key like Activity.

3. When implementing Fragment, why should there be a default constructor?

Speaking of this, we have to talk about the structure of Fragment. The structure of Fragment includes: view hierarchy, package of initialization parameters

First, let's take a look at the inheritance relationship between Fragment and Activity, as shown in Figure 1-1, 1-2:

 
          

 

 

                          Figure 1-1 Fragment class inheritance relationship                                                                                                              Figure 1-2 Activity class inheritance relationship                         

It is easy to see from the figure that Fragment is directly inherited from Object, while Activity is a subclass of Context. So we can conclude: Fragment is not an extension of Activity. But like Activity, when we use Fragment, we always extend Fragment (or its subclass), and can change her behavior through subclass.

 Fragments can have a view hierarchy that interacts with the user, the same view hierarchy as an Activity's view hierarchy, or it can be created (extended) by an XML layout specification or created by code. (Note: If the view hierarchy needs to be displayed to the user, the Fragment's view hierarchy must be attached to the Activity's view hierarchy)

So much has been introduced before, just to give some clues, let’s get to the point, why Fragment must contain a default constructor (in Java classes, if there is no constructor, a default constructor with no parameters will be automatically created at compile time . Therefore, if there is no other constructor in the Java class, the default function can be omitted, and the compiler will automatically create it; if there are other constructors in the Java class, the system will not create the default constructor at compile time, so if there is a non-default constructor When the default constructor is required, and the default constructor needs to appear at compile time, it must be explicitly written in the Java class) The package of initialization parameters - similar to activities, fragments can be automatically saved by the system and restored later. When the system restores the Fragment, she calls the default constructor and then restores the parameter pack to the newly created Fragment. Subsequent callbacks executed by the Fragment have access to these parameters and can restore the fragment to its previous state. Therefore, when using Fragment, be sure to ensure the following two points:

  1. Make sure that the Fragment class has a default constructor;
  2. Add a parameter bundle (Bundle) immediately after the Fragment is created, so that the Fragment can be set correctly when the Fragment is rebuilt, and the Android system can correctly restore the Fragment when necessary.

Summary: A subclass of Fragment must have a default constructor and a parameter pack, because Fragment will call the default constructor when it is recreated, and will save the state to a Bundle object when it is recreated ( Note: Note the distinction between the object package and the parameter package mentioned above ), the package (Bundle) object will be echoed back to the Fragment's onCreate() callback. This saved bundle (Bundle) is also passed to onInflate()\onCreateView()\onActivityCreated(). (Note: this is not a package attached as an initialization parameter. Probably the current state of the Fragment is stored in this package, not the value that should be used to initialize her)

4. How is the life cycle of Fragment integrated with the life cycle of Activity?

 Before using Fragment, be sure to understand the life cycle of Fragment. The life cycle of Fragment is more complex than that of Activity, and understanding when to deal with Fragment is crucial. Since Fragment is dependent on Activity, let's take a look at the similarities and differences between the life cycles of the two. The life cycles of Fragment and Activity are shown in Figures 1-3 and 1-4:

                        

                    Figure 1-3 Fragment life cycle when Activity is running Figure 1-4 Activity life cycle

By comparing Fragment and Activity, you will find many differences, mainly because of the interaction between Activity and Fragment . I believe that everyone is very familiar with the life cycle of Activity, so I will not introduce too much here, but directly cut to the topic to introduce the life cycle of Fragment. At the beginning of Fragment, Fragment will exist in memory in the form of object. There are two situations in which Fragment instances are created:

  1. Create Fragment instance;
  2. 系统从保存状态重新创建Fragment的情况下,将初始化参数添加到碎片对象中。当系统从保存的状态还原Fragment时,会调用默认的构造函数,然后附件初始化参数包;

使用代码创建Fragment的实例:

[java]  view plain  copy
 
  1. public static MyFragment newInstance(int index){  
  2.     MyFragment mf = new MyFragment();  
  3.     Bundle args = new Bundle();  
  4.     args.putInt("index",index);  
  5.     mf.setArguments(args);  
  6.     return mf;  
  7. }  
1.onInflate()回调

API文档:onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState)--Called when a fragment is being created as part of a view layout inflation, typically from setting the content view of an activity.

Called when a fragment is being created as part of a view layout inflation, typically from setting the content view of an activity. This may be called immediately after the fragment is created from a tag in a layout file. Note this is before the fragment's onAttach(Activity) has been called; all you should do here is parse the attributes and save them away.

如果Fragment是由<fragment>标记定义的(通常是在活动调用setContentView()来设置自己的主要布局),Fragment将调用自己的onInflate()回调。这一过程中传入一个AttributeSet(包含来自<fragment>标记的特性)和一个保存的包。如果重新创建碎片,并且之前在onSaveInstanceState()中保存了某种状态,此包(Bundle)将包含保存的状态值。onInflate()预计开发人员会读取特性值并保存她们供以后使用。在Fragment的onInflate()这一生命阶段,对用户界面执行任何操作都尚早,因为Fragment甚至还没有与其Activity关联。

(备注:onInflate()文档与实际使用不符,文档表明onInflate()始终在onAttach()之前调用。实际上,在Activity重新启动后,onInflate()可能在onCreateView()之后调用。这对于将值设置到包(Bundle)中并调用setArguments()而言太迟了,Android官方文档中Fragment生命周期的图示中也没有将onInflate()包含在生命周期,看来Android的大牛们也很难预测onInflate()会在何时回调)

2.onAttach()回调

好了,讨论了那么纠结的问题,相比大家都被我绕晕了吧,但是追求技术的道路中是不能有半点怠慢的,透彻分析问题才是我们追求技术的使命。如果大家都头有点晕的话,建议大家先看部喜剧片吧,清醒一下头脑!如果还能坚持的,就跟随我的思路继续探寻。onAttach()回调,回头一看,MyGod,该方法终于出现在Fragment生命周期的图解中了,总算是引领大家步入正轨了。

API文档:public void onAttach (Activity activity) --Called when a fragment is first attached to its activity. onCreate(Bundle)will be called after this.

onAttach()回调将在Fragment与其Activity关联之后调用。需要使用Activity的引用或者使用Activity作为其他操作的上下文,将在此回调方法中实现。

注意:Fragment类有一个getActivity()方法,返回与Fragment关联的Activity。在Fragment的整个生命周期中,初始化参数包(Bundle)可以从碎片的getArguments()方法获得。

切忌:将Fragment附加到Activity以后,就无法再次调用setArguments()——除了在最开始,无法向初始化参数添加内容。

3.onCreate()回调

接下来回调的方法就是onCreate(),大家可以不要与Activity的onCreate()回调方法混淆了。此回调获取传入的参数包(备注:如果在创建时设置了参数包(Bundle)的话就可以获得),不应该将需要依赖于Activity视图层次结构的存在性的代码放在此回调方法中,尽管Fragment现在可能已经与其Activity关联,但是我们还没有获得Activity的onCreate()已完成的通知,所以不能将依赖于Activity视图层次结构存在性的代码放入此回调方法中。

(备注:在onCreate()回调方法中,我们应该尽量避免耗时操作(避免阻塞UI线程),在实际项目中触发后台线程进行准备非常有用,阻塞调用应该位于后台线程中。)

示例代码:

[java]  view plain  copy
 
  1. /** 
  2.      * During creation, if arguments have been supplied to the fragment 
  3.      * then parse those out. 
  4.      */  
  5.     @Override public void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.   
  8.         Bundle args = getArguments();  
  9.         if (args != null) {  
  10.             mLabel = args.getCharSequence("label", mLabel);  
  11.         }  
  12.     }  
4.onCreateView()回调

接下来的回调方法是onCreateView(),在该回调方法中应该返回该Fragment的一个视图层次结构。

API文档:public View onCreateView (LayoutInflater inflater, ViewGroup ContainerBundle savedInstanceState)--Called to have the fragment instantiate its user interface view. This is optional, and non-graphical fragments can return null (which is the default implementation). This will be called betweenonCreate(Bundle)andonActivityCreated(Bundle).

 其中的Bundle为状态包(备注:必须和前面所说的参数包(Bundle)区分开来)注意:不要将视图层次结构附加到传入的ViewGroup父元素中,该关联会自动完成。如果在此回调中将碎片的视图层次结构附加到父元素,很可能会出现异常。实例代码如下所示:

 

[java]  view plain  copy
 
  1. /** 
  2.     * Create the view for this fragment, using the arguments given to it. 
  3.     */  
  4.    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  5.            Bundle savedInstanceState) {  
  6.        View v = inflater.inflate(R.layout.hello_world, container, false);// 不能将Fragment的视图附加到此回调的容器元素,因此attachToRoot参数必须为false   
  7.        View tv = v.findViewById(R.id.text);  
  8.        ((TextView)tv).setText(mLabel != null ? mLabel : "(no label)");  
  9.        tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));  
  10.        return v;  
  11.    }  
5.onActivityCreated()回调

终于到了与用户交互的时刻了,onActivityCreated()回调会在Activity完成其onCreate()回调之后调用。在调用onActivityCreated()之前,Activity的视图层次结构已经准备好了,这是在用户看到用户界面之前你可对用户界面执行的最后调整的地方。(备注:如果Activity和她的Fragment是从保存的状态重新创建的,此回调尤其重要,也可以在这里确保此Activity的其他所有Fragment已经附加到该活动中了)

6. Fragment与Activity相同生命周期调用

接下来的onStart()\onResume()\onPause()\onStop()回调方法将和Activity的回调方法进行绑定,也就是说与Activity中对应的生命周期相同,因此不做过多介绍。

7.onDestroyView()回调

该回调方法在视图层次结构与Fragment分离之后调用。

8.onDestroy()回调
不再使用Fragment时调用。(备注:Fragment仍然附加到Activity并任然可以找到,但是不能执行其他操作)
9.onDetach()回调
Fragme生命周期最后回调函数,调用后,Fragment不再与Activity绑定,释放资源  
通过以上说明,纵观Fragment生命周期和Activity生命周期整合后如图1-5所示:
                          
                 图1-5 Activity和Fragment生命周期整合                                                                                        图1-6 Fragment生命周期
10.巧妙使用setRetainInstance()

为什么会在这儿花一定的篇幅详细说明setRetainInstance()方法呢?因为此方法可以有效地提高系统的运行效率,对流畅性要求较高的应用可以适当采用此方法进行设置。

 Fragment has a very powerful function - it can not completely destroy Fragment when Activity is recreated, so that Fragment can be restored. Calling the setRetainInstance(true/false) method in the onCreate() method is the best place. The life cycle of a Fragment when it is restored is shown in Figure 1-6. Note the red arrow in the figure. When setRetainInstance(true) is called in the onCreate() method, the onCreate() and onDestroy() methods will be skipped when the Fragment is restored, so you cannot put some initialization logic in onCreate(), don't!

5. How to manage Fragment? 

Since Fragment must exist in the context of Activity, how to manage Fragment is the next topic we need to care about. The FragmentManager component is responsible for managing fragments belonging to an activity (including fragments on the back stack and free fragments). The fragment manager can be obtained using the getFragmentManager() method on an Activity or an attached Fragment . The code looks like this:

[java]  view plain copy  
 
  1. FragmentManager fragmentManager = getFragmentManager()  
  2. FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();  

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326853468&siteId=291194637