文章目录
  1. 1. 介绍
  2. 2. 生命周期
  3. 3. 通信
    1. 3.1. Activity<–>Fragment
    2. 3.2. Fragment<—>Fragment
  4. 4. 特殊嵌套
  5. 5. 构造的局限性
  6. 6. Fragment线程容器
    1. 6.1. 与Service线程容器的比较
    2. 6.2. Fragment线程容器的实现

介绍

Fragment 可以用于实现UI,可以说是小型的Activity,一个Activity可以有多个Fragment,有利于复用,大致可以用一下公式表示:

Fragment ≈ UI + Activity的部分行为

Fragment除了做UI显示的功能外,还能做Acitivity重启过程的不变实例,这里有个作用就是做线程容器。通过在Fragment onCreate()方法添加setRetainInstance(true),从而避免因为Activity的re-create过程中反复重复new线程

生命周期

生命周期直观上看,相比Acitivity,多出的生命期(attach,createView,activityCreated,destoryView,dettach)出现在onCreate与onDestroy前后
fragment 生命周期

其中,值得注意的是,当Fragment remove并用addToBackStack(null),这时候,Fragment不会Destory,其只会走到onDestroyView,等到popBackStack()再从onCreateView走起,当然,如果Activity在这个过程退出则Fragment还是会Destory的。
比较特别的是,Activity re-Create后,Activity实例变化,但是并不需要重新手动new Fragment实例,之前存在的Fragment,Acitivity会通过反射调用无惨构造器的方式new Fragment(通过saveInstanceState实现的),当然,如果Fragment在fragment.oncreate的生命周期调用了setRetainInstance的话,Fragment会保持唯一实例

我们知道savedInstanceState是在resume与destory之间,所以通过onCreateView中的savedInstanceState是否为空来判断是否加载新Fragment并不正确,事实上,除了onCreate中能用判断savedInstanceState是否为空加载新Fragment为正确的,其他均需要findFragmentByTag判断对应Fragment是否为空再选择加载新Fragment.

通信

Activity<–>Fragment

Fragment调用Activity方法: 这个过程用依赖导致,Frament内写一个需要调用到的接口,在Activity中实现,
通过Fragment中onAttach(Activity)获取到接口
过程如下
1.新建Fragment需要调用到的接口,通常写在相应的Fragment的内部,静态类形式
2.Activity实现这个接口
3.Fragment在onAttach(Activity)回调中尝试获取这个接口

可能对于onBackPressed()有人喜欢用上面的方式,其实可以通过focus主View,再对主View加Key监听,这种方式能减少代码间的关联。

Activity调用Fragment方法: 当然,在Acitivity中也有onAttach(Fragment),似乎可以反过来使用以上方式,但是在实践中Fragment的onAttach时机并不是很可控,因为fragmenttransaction.commit之后不是立即执行的(当然这可以用FragmentMananger的executePendingTransations破),所以谷歌文档提供方式:findFragmentByTag

Fragment<—>Fragment

让Activity作为中间人,也就是Fragment间的通信转化成Fragment与Acitvity的通信,还有getTargetFragment与setTargetFragment 的方法,这组方法保证获取到最新的实例,也可以通过这两个方法与Fragment.onActivityResult来配合模拟出类似startActivityForResult一样的返回结果。

特殊嵌套

一般嵌套用getFragmentManger做操作即可,但是有些嵌套情况比较特殊,必须用getChildFramentManger,比如DialogFragment内要嵌套其他Fragment,原因如下:

  • FragmentManger内对应了一个FragmentContainer的对象,这个对象限定了在哪个ViewGroup下查找。
  • 普通的getFragmentManager对应Activity的contentView,而在大多数情况下,Fragment也是加载contentView上的。DialogFragment虽然有界面,但是他自身的contentView并不会加载在contentView,从源码可以看到add(0,fragment)的操作添加上去。这时候就必须使用getChildFragment,因为getChildFragment内对应的容器是Fragment onCreateView生成的View。

综上,嵌套情况下优先使用getChildFragmentManger。

构造的局限性

Fragment在Activity re-create的过程中将重新通过反射调用其无参构造器初始化。这产生了两点限制:

  1. Fragment实现类必须拥有无参构造器以便re-create过程在框架中反射newInstance()
  2. Fragment如果拥有有参构造器,里面的传值必须统一传入Bundle,并最终用setArguments来设置,而且同时必须在onCreate的过程中getArguments来读取参数初始化,这是因为,在re-create过程中,Arguments将被保留。

Fragment线程容器

与Service线程容器的比较

Fragment可以作为线程容器,能实现线程容器的还有Service,但是Service的生命周期游离于Activity

  • Service做线程容器,这个线程更多的要求是后台服务,而不是跟随Acitivity生命周期,更确切的说,Service作为线程容器,这个线程要求后台,而不是一定绑定这Acitivity。

  • Fragment作为线程容器,主要是为了避免因为Activity因外界条件重启而导致的重复启动线程,多用于定时更新Activity的界面。

    Fragment线程容器的实现

    RFragment命名空间内有UIFragment与WorkerFragment,Activity直接加载RFragment

有两个比较重要的方法

  • setRetainInstance,默认是false,Fragment每次重启都废弃原先的fragment实例,这个跟Acitivity一样,但是Fragment由于其特殊,可以添加这个方法设置true时候,Fragment每次重启都是用同一个实例,重启过程中会避开onCreate,onDestory,但是onDetach,onAttach,onActivityCreated依然有,这是因为Fragment重新绑定了新的Activity。
  • setTargetFragment,getTargetFragment,这个方法锁定的Fragment,会自动获取到最新的对应fragment实例。不会因为Fragment因为重启重置实例而获取旧的Fragment实例,只会获取最新实例

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
* Copyright (C) 2010 The Android开源工程
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package com.example.android.apis.app;

import com.example.android.apis.R;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;

/**
* This example shows how you can use a Fragment to easily propagate state
* (such as threads) across activity instances when an activity needs to be
* restarted due to, for example, a configuration change. This is a lot
* easier than using the raw Activity.onRetainNonConfiguratinInstance() API.
*/

public class FragmentRetainInstance extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// First time init, create the UI.
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().add(android.R.id.content,
new UiFragment()).commit();
}
}

/**
* This is a fragment showing UI that will be updated from work done
* in the retained fragment.
*/

public static class UiFragment extends Fragment {
RetainedFragment mWorkFragment;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{

View v = inflater.inflate(R.layout.fragment_retain_instance, container, false);

// Watch for button clicks.
Button button = (Button)v.findViewById(R.id.restart);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mWorkFragment.restart();
}
});

return v;
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

FragmentManager fm = getFragmentManager();

// Check to see if we have retained the worker fragment.
mWorkFragment = (RetainedFragment)fm.findFragmentByTag("work");

// If not retained (or first time running), we need to create it.
if (mWorkFragment == null) {
mWorkFragment = new RetainedFragment();
// Tell it who it is working with.
mWorkFragment.setTargetFragment(this, 0);
fm.beginTransaction().add(mWorkFragment, "work").commit();
}
}

}

/**
* This is the Fragment implementation that will be retained across
* activity instances. It represents some ongoing work, here a thread
* we have that sits around incrementing a progress indicator.
*/

public static class RetainedFragment extends Fragment {
ProgressBar mProgressBar;
int mPosition;
boolean mReady = false;
boolean mQuiting = false;

/**
* This is the thread that will do our work. It sits in a loop running
* the progress up until it has reached the top, then stops and waits.
*/

final Thread mThread = new Thread() {
@Override
public void run() {
// We'll figure the real value out later.
int max = 10000;

// This thread runs almost forever.
while (true) {

// Update our shared state with the UI.
synchronized (this) {
// Our thread is stopped if the UI is not ready
// or it has completed its work.
while (!mReady || mPosition >= max) {
if (mQuiting) {
return;
}
try {
wait();
} catch (InterruptedException e) {
}
}

// Now update the progress. Note it is important that
// we touch the progress bar with the lock held, so it
// doesn't disappear on us.
mPosition++;
max = mProgressBar.getMax();
mProgressBar.setProgress(mPosition);
}

// Normally we would be doing some work, but put a kludge
// here to pretend like we are.
synchronized (this) {
try {
wait(50);
} catch (InterruptedException e) {
}
}
}
}
};

/**
* Fragment initialization. We way we want to be retained and
* start our thread.
*/

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Tell the framework to try to keep this fragment around
// during a configuration change.
setRetainInstance(true);

// Start up the worker thread.
mThread.start();
}

/**
* This is called when the Fragment's Activity is ready to go, after
* its content view has been installed; it is called both after
* the initial fragment creation and after the fragment is re-attached
* to a new activity.
*/

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);

// Retrieve the progress bar from the target's view hierarchy.
mProgressBar = (ProgressBar)getTargetFragment().getView().findViewById(
R.id.progress_horizontal);

// We are ready for our thread to go.
synchronized (mThread) {
mReady = true;
mThread.notify();
}
}

/**
* This is called when the fragment is going away. It is NOT called
* when the fragment is being propagated between activity instances.
*/

@Override
public void onDestroy() {
// Make the thread go away.
synchronized (mThread) {
mReady = false;
mQuiting = true;
mThread.notify();
}

super.onDestroy();
}

/**
* This is called right before the fragment is detached from its
* current activity instance.
*/

@Override
public void onDetach() {
// This fragment is being detached from its activity. We need
// to make sure its thread is not going to touch any activity
// state after returning from this function.
synchronized (mThread) {
mProgressBar = null;
mReady = false;
mThread.notify();
}

super.onDetach();
}

/**
* API for our UI to restart the progress thread.
*/

public void restart() {
synchronized (mThread) {
mPosition = 0;
mThread.notify();
}
}
}
}

文章目录
  1. 1. 介绍
  2. 2. 生命周期
  3. 3. 通信
    1. 3.1. Activity<–>Fragment
    2. 3.2. Fragment<—>Fragment
  4. 4. 特殊嵌套
  5. 5. 构造的局限性
  6. 6. Fragment线程容器
    1. 6.1. 与Service线程容器的比较
    2. 6.2. Fragment线程容器的实现