Android Activity和Fragment通信 切换 数据传值

时间:2022-10-27 18:48:13


1.    Fragment的生命周期
2、Fragment如何与Activity交互,Acitivity和Fragment的通信
3.  如何管理Fragment回退栈

4.  Fragment的状态保存

5、Fragment 的startActivityForResult

6、Fragment 替换和隐藏的区别

7、使用Fragment创建对话框

8、Fragment常报的一些错



Fragment的产生与介绍:一个App可以同时适应手机和平板
Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fragment要多,
响应速度Fragment比Activty在中低端手机上快了很多,甚至能达到好几倍!如果你的app当前或以后有移植平板等平台时,可以让你节省大量时间和精力。


Fragment的生命周期:
Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期

Android     Activity和Fragment通信    切换     数据传值



Android     Activity和Fragment通信    切换     数据传值

好处: 更为重要的是,你可以动态的添加、替换和移除某个Fragment


2、Fragment与Activity通信

因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:

a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法

b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。

c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。



Fragment的状态保存:
Fragment也有onSaveInstanceState的方法

Activity的Fragment的之间的传值:Fragment Arguments,比传统的Intent可以达到更好的解耦标准
方法一:
public class ContentActivity extends SingleFragmentActivity
{
private ContentFragment mContentFragment;
@Override
protected Fragment createFragment()
{
String title = getIntent().getStringExtra(ContentFragment.ARGUMENT);

mContentFragment = ContentFragment.newInstance(title);

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null)
{
mArgument = bundle.getString(ARGUMENT);
}

}

public static ContentFragment newInstance(String argument)
{
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, argument);
ContentFragment contentFragment = new ContentFragment();
contentFragment.setArguments(bundle);
return contentFragment;
}



给Fragment添加newInstance方法,将需要的参数传入,设置到bundle中,然后setArguments(bundle),最后在onCreate中进行获取;

这样就完成了Fragment和Activity间的解耦。当然了这里需要注意:





Fragment的startActivityForResult

我们点击跳转到对应Activity的Fragment中,并且希望它能够返回参数,那么我们肯定是使用Fragment.startActivityForResult ; 

在Fragment中存在startActivityForResult()以及onActivityResult()方法,

但是呢,没有setResult()方法,用于设置返回的intent,这样我们就需要通过调用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);。

没有serResult的方法,就需要调用Activity的serResult方法!


案列:一个Activity里面包含一个fragment,然后fragment调整到另外一个Activity中,这个Activity里面也包含一个fragment。

public class ListTitleActivity extends SingleFragmentActivity
{
private ListTitleFragment mListFragment;

@Override
protected Fragment createFragment()
{
mListFragment = new ListTitleFragment();
return mListFragment;
}
}

public class ListTitleFragment extends ListFragment
{

public static final int REQUEST_DETAIL = 0x110;
private List<String> mTitles = Arrays.asList("Hello", "World", "Android");
private int mCurrentPos ;
private ArrayAdapter<String> mAdapter ;


@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
setListAdapter(mAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mTitles));
}

@Override
public void onListItemClick(ListView l, View v, int position, long id)
{
mCurrentPos = position ;
Intent intent = new Intent(getActivity(),ContentActivity.class);
intent.putExtra(ContentFragment.ARGUMENT, mTitles.get(position));
startActivityForResult(intent, REQUEST_DETAIL);
}


@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.e("TAG", "onActivityResult");
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_DETAIL)
{
mTitles.set(mCurrentPos, mTitles.get(mCurrentPos)+" -- ");
mAdapter.notifyDataSetChanged();
}
}
}

跳转的Acitivy和fragment
public class ContentActivity extends SingleFragmentActivity
{
private ContentFragment mContentFragment;
@Override
protected Fragment createFragment()
{
String title = getIntent().getStringExtra(ContentFragment.ARGUMENT);

mContentFragment = ContentFragment.newInstance(title);
return mContentFragment;
}
}

public class ContentFragment extends Fragment
{

private String mArgument;
public static final String ARGUMENT = "argument";
public static final String RESPONSE = "response";
public static final String EVALUATE_DIALOG = "evaluate_dialog";
public static final int REQUEST_EVALUATE = 0X110;

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null)
{
mArgument = bundle.getString(ARGUMENT);
}

}

public static ContentFragment newInstance(String argument)
{
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, argument);
ContentFragment contentFragment = new ContentFragment();
contentFragment.setArguments(bundle);
return contentFragment;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
Random random = new Random();
TextView tv = new TextView(getActivity());
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
tv.setLayoutParams(params);
tv.setText(mArgument);
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 30);
tv.setGravity(Gravity.CENTER);
tv.setBackgroundColor(Color.argb(random.nextInt(100),
random.nextInt(255), random.nextInt(255), random.nextInt(255)));
// set click
tv.setOnClickListener(new OnClickListener()
{

@Override
public void onClick(View v)
{
EvaluateDialog dialog = new EvaluateDialog();
//注意setTargetFragment
dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);
dialog.show(getFragmentManager(), EVALUATE_DIALOG);
}
});
return tv;
}

//接收返回回来的数据
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == REQUEST_EVALUATE)
{
String evaluate = data
.getStringExtra(EvaluateDialog.RESPONSE_EVALUATE);
Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show();
Intent intent = new Intent();
intent.putExtra(RESPONSE, evaluate);
getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
}

}


附:startActivityForResult接收返回问题
在support 23.2.0以下的支持库中,对于在嵌套子Fragment的startActivityForResult
()
,会发现无论如何都不能在onActivityResult()中接收到返回值,只有最顶层的父Fragment才能接收到,这是一个support v4库的一个BUG,不过在前两天发布的support 23.2.0库中,已经修复了该问题,嵌套的子Fragment也能正常接收到返回数据了!



onActivityResult

Activity里面的onActivityResult方法 还有Fragment里面的OnActivityResult的方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mAppManageFragment != null) {
mAppManageFragment.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==0){
adapter.refreshDeleteList();
}
}


在碎片中模拟返回栈


我们成功实现了向活动中动态添加碎片的功能,不过你尝试一下就会发现,通过点击按钮添加了一个碎片之后,这时按下Back键程序就会直接退出。如果这里我们想模仿类似于返回栈的效果,按下Back键可以回到上一个碎片,该如何实现呢?

 

其实很简单,FragmentTransaction中提供了一个addToBackStack()方法,可以用于将一个事务添加到返回栈中,修改MainActivity中的代码,如下所示:



public class MainActivity extends Activity implements OnClickListener {
……
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
AnotherRightFragment fragment
= new AnotherRightFragment();
FragmentManager fragmentManager
= getFragmentManager();
FragmentTransaction transaction
= fragmentManager. beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.addToBackStack(
null);
transaction.commit();
break;
default:
break;
}
}
}




add(), show(), hide(), replace()替换和隐藏Fragment的区别:


1、区别
show()hide()最终是让Fragment的View setVisibility(true还是false),不会调用生命周期;

replace()的话会销毁视图,即调用onDestoryView、onCreateView等一系列生命周期;

add()和 replace()不要在同一个阶级的FragmentManager里混搭使用。


使用场景
如果你有一个很高的概率会再次使用当前的Fragment,建议使用show()hide(),可以提高性能。

在我使用Fragment过程中,大部分情况下都是用show()hide(),而不是replace()

注意:如果你的app有大量图片,这时更好的方式可能是replace,配合你的图片框架在Fragment视图销毁时,回收其图片所占的内存



在多个 Fragment 之间进行切换的时候 
replace()和hide的区别 
首先replace 是代替,之前的Fragment会被清空掉,在再次切换回来的时候会进行重新加载。而hide是将之前的一个fragment 进行隐藏,将新的fragment 叠在上面进行显示.等在次调用的时候进行显示。不需要重新加载。测试这个最简单的方法就是 设计两个fragment 在同一位置上放一个按钮,一个有监听一个没有,但现实没有监听的按钮点击按钮的时候会响应一号按钮的监听事件。

另外一个对于 FragmentTransaction对象在commit之后就会失效。所以建议直接使用fragmentManager.beginTransaction() 这样就不会产生失效的问题了。

commit 于commitAllowingStateLoss();的区别 
简单来说就是避免报错。 
他们都调用了commitInternal 
public int commit() { 
return commitInternal(false); 
}

public int commitAllowingStateLoss() { 
return commitInternal(true); 

这个两个方法区别就在传参判断后的处理方法checkStateLoss 
当使用commit方法时,系统将进行状态判断,如果状态(mStateSaved)已经保存,将发生”Can not perform this action after onSaveInstanceState”错误。 
如果mNoTransactionsBecause已经存在,将发生”Can not perform this action inside of ” + mNoTransactionsBecause错误

Android     Activity和Fragment通信    切换     数据传值
Android     Activity和Fragment通信    切换     数据传值





多个fragment切换

http://www.yrom.net/blog/2013/03/10/fragment-switch-not-restart/


多个fragment问题呢http://www.tuicool.com/articles/eua2mm

不错的demohttp://blog.csdn.net/guolin_blog/article/details/13171191

fragment 特点


  • Fragment可以作为Activity界面的一部分组成出现;

  • 可以在一个Activity中同时出现多个Fragment,并且一个Fragment也可以在多个Activity中使用;

  • 在Activity运行过程中,可以添加、移除或者替换Fragment;

  • Fragment可以响应自己的输入事件,并且有自己的生命周期,它们的生命周期会受宿主Activity的生命周期影响。





    Menu菜单问题:fragment有菜单,activity有菜单的话,会把菜单综合在一起如果名称相同的话,也不会覆盖的

     Fragment中menu菜单注意事项  以前一般都是在Activity中添加menu菜单,一般是重写onCreateOptionsMenu和onOptionsItemSelected方法。

    现在用fragment用的多了,就在fragment里面添加menu菜单,也是重写了onCreateOptionsMenu和onOptionsItemSelected方法,但是发现没有效果。
    好吧,看了下源代码,原来跟一个mHasMenu的boolean变量有关系1// If set this fragment has menu items to contribute.2    boolean mHasMenu;这个变量控制fragment的menu菜单添加:01boolean performCreateOptionsMenu(Menu menu, MenuInflater inflater) {02        boolean show = false;03        if (!mHidden) {04            if (mHasMenu && mMenuVisible) {05                show = true;06                onCreateOptionsMenu(menu, inflater);07            }08            if (mChildFragmentManager != null) {09                show |= mChildFragmentManager.dispatchCreateOptionsMenu(menu, inflater);10            }11        }12        return show;13    }上面代码说明,如果mHasMenu为false,那么是不会执行onCreateOptionsMenu(menu, inflater)方法的,也就是不会添加fragment的menu菜单。
    所以,在fragment中使用menu菜单,需要在onCreate()方法里面添加语句setHasOptionsMenu(true);
    也就是这样:1<a href="http://home.51cto.com/index.php?s=/space/5017954" target="_blank">@Override</a>2    public void onCreate(Bundle savedInstanceState) {3        super.onCreate(savedInstanceState);4        setHasOptionsMenu(true);5    }嗯,很简单的东西,总结下,希望大家以后不要跟我一样犯错误哈。
    =============================================================
    Fragment滑动

    FragmentPagerAdapter与FragmentStatePagerAdapter

    相信这两个PagerAdapter的子类,大家都不陌生吧~~自从Fragment问世,使用ViewPager再结合上面任何一个实例的制作APP主页的案例特别多~~~

    那么这两个类有何区别呢?

    主要区别就在与对于fragment是否销毁,下面细说:

    FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图,并不会销毁fragment实例。

    FragmentStatePagerAdapter:会销毁不再需要的fragment,当当前事务提交以后,会彻底的将fragmeng从当前Activity的FragmentManager中移除,state标明,销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。

    如上所说,使用FragmentStatePagerAdapter当然更省内存,但是销毁新建也是需要时间的。一般情况下,如果你是制作主页面,就3、4个Tab,那么可以选择使用FragmentPagerAdapter,如果你是用于ViewPager展示数量特别多的条目时,那么建议使用FragmentStatePagerAdapter。



    fragment添加全部的还是添加2页(和viewpager有关)=====用的适配器不一样
    有点连oncreatView方法都没有执行
    两种适配器,一个返回的是fragment一个返回的view


    一个Activity里面嵌套多个Fragment,滑动加载的流程和生命周期1.只加载前面2个,没滑动一次加载一个以后什么方法都不执行
    2.只加载前面连个,没滑动加载一个再滑动执行onpause也执行oncreat方法
    3.只加载前面连个,没滑动加载一个

    第一个页面不为空的话,会执行下面的一些列的方法.以后的话就不会再执行里面的任何方法了
    如果页面为空或者重写的话,不会执行下面的方法

    如果第一个为空,第2个不为空的话,也没有问题
    如果 前面两个都为空的话就有问题了,是viewPager的问题


    Android     Activity和Fragment通信    切换     数据传值

    2.onCreateView---这样每次都用更新数据
    private View rootView;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if (null != rootView) {
    ViewGroup parent
    = (ViewGroup) rootView.getParent();
    if (null != parent) {
    parent.removeView(rootView);
    }
    }
    else {
    rootView
    = inflater.inflate(layoutId, null);
    initView(rootView);
    // 控件初始化 }
    return rootView;
    }

    Fragment之间切换时每次都会调用onCreateView方法,导致每次Fragment的布局都重绘,无法保持Fragment原有状态。
    解决办法:在Fragment onCreateView方法中缓存View.



    public abstract class SingleFragmentActivity extends FragmentActivity
    {

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_single_fragment);

    FragmentManager fm = getSupportFragmentManager();
    Fragment fragment =fm.findFragmentById(R.id.id_fragment_container);

    if(fragment == null )
    {
    fragment = createFragment() ;

    fm.beginTransaction().add(R.id.id_fragment_container,fragment).commit();
    }
    }

    为什么需要判null呢?

    主要是因为,当Activity因为配置发生改变(屏幕旋转)或者内存不足被系统杀死,造成重新创建时,

    我们的fragment会被保存下来,但是会创建新的FragmentManager,新的FragmentManager会首先会去获取保存下来的fragment队列,重建fragment队列,从而恢复之前的状态。



    DialogFragment的用法和setTargetFragment用法

    例如,点击当前Fragment中按钮,弹出一个对话框(DialogFragment),在对话框中的操作需要返回给触发的Fragment中,那么如何数据传递呢?对于对话框的使用推荐:Android 官方推荐 : DialogFragment 创建对话框

    tv.setOnClickListener(new OnClickListener()
    {

    @Override
    public void onClick(View v)
    {
    EvaluateDialog dialog = new EvaluateDialog();
    //注意setTargetFragment
    dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);
    dialog.show(getFragmentManager(), EVALUATE_DIALOG);
    }
    });
    return tv;
    }

    //接收返回回来的数据()
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data)
    {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == REQUEST_EVALUATE)
    {
    String evaluate = data
    .getStringExtra(EvaluateDialog.RESPONSE_EVALUATE);
    Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show();
    Intent intent = new Intent();
    intent.putExtra(RESPONSE, evaluate);
    getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
    }

    }


    public class EvaluateDialog extends DialogFragment
    {
    private String[] mEvaluteVals = new String[] { "GOOD", "BAD", "NORMAL" };
    public static final String RESPONSE_EVALUATE = "response_evaluate";

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

    builder.setTitle("Evaluate :").setItems(mEvaluteVals,
    new OnClickListener()
    {
    @Override
    public void onClick(DialogInterface dialog, int which)
    {
    setResult(which);
    }
    });
    return builder.create();
    }

    // 设置返回数据
    protected void setResult(int which)
    {
    // 判断是否设置了targetFragment
    if (getTargetFragment() == null)
    return;

    Intent intent = new Intent();
    intent.putExtra(RESPONSE_EVALUATE, mEvaluteVals[which]);
    getTargetFragment().onActivityResult(ContentFragment.REQUEST_EVALUATE,
    Activity.RESULT_OK, intent);

    }
    }


    fragment的缺点http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0605/2996.htmlSquare:从今天开始抛弃Fragment吧!

    Fragment transactions


    Transaction的出现:
    在AIDL里面生成的java文件中会有!

    Fragment 的 transaction 允许你执行一系列的 Fragment 操作,但不幸的是,提交 transaction 是异步操作,并且在 UI 线程的 Handler 队列的队尾被提交。

    这会在接收多个点击事件或配置发生改变时让你的 App 处在未知的状态。

    class BackStackRecord extends FragmentTransaction {    int commitInternal(boolean allowStateLoss) {        if (mCommitted)            throw new IllegalStateException("commit already called");        mCommitted = true;        if (mAddToBackStack) {            mIndex = mManager.allocBackStackIndex(this);        } else {            mIndex = -1;        }        mManager.enqueueAction(this, allowStateLoss);        return mIndex;    }}

    创建 Fragment 可能带来的问题

    Fragment 的实例能够通过 Fragment Manager 创建,例如下面的代码看起来没有什么问题:

    DialogFragment dialogFragment = new DialogFragment() {  @Override public Dialog onCreateDialog(Bundle savedInstanceState) { ... }};dialogFragment.show(fragmentManager, tag);

    然而,当我们需要存储 Activity 实例的状态时,Fragment Manager 可能会通过反射机制重新创建该 Fragment 的实例,又因为这是一个匿名内部类,该类有一个隐藏的构造器的参数正是外部类的引用,如果大家有看过这篇博文的话就会知道,拥有外部引用可能会带来内存泄漏的问题。


    结论:1.我们遇到的大多数难以解决的 Bug 都与 Fragment 的生命周期有关。2.我们只需要 View 创建响应式 UI,实现回退栈以及屏幕事件的处理,不用 Fragment 也能满足实际开发的需求。3.MVVP模式看待的话,解耦性不好!

    看了上面的介绍,你可能会觉得Fragment有点可怕。

    但是我想说,如果你只是浅度使用,比如一个Activity容器包含列表Fragment+详情Fragment这种简单情景下,

    不涉及到popBackStack/Immediate(tag/id)这些的方法,还是比较轻松使用的,出现的问题,网上都可以找到解决方案。


    getActivity()空指针

    可能你遇到过getActivity()返回null,或者平时运行完好的代码,在“内存重启”之后,调用getActivity()的地方却返回null,报了空指针异常。

    大多数情况下的原因:你在调用了getActivity()时,当前的Fragment已经onDetach()了宿主Activity。
    比如:你在pop了Fragment之后,该Fragment的异步任务仍然在执行,并且在执行完成后调用了getActivity()方法,这样就会空指针。

    解决办法:
    更"安全"的方法:(对于Fragment已经onDetach这种情况,我们应该避免在这之后再去调用宿主Activity对象,比如取消这些异步任务,但我们的团队可能会有粗心大意的情况,所以下面给出的这个方案会保证安全)

    在Fragment基类里设置一个Activity mActivity的全局变量,在onAttach(Activity activity)里赋值,使用mActivity代替getActivity(),保证Fragment即使在onDetach后,仍持有Activity的引用(有引起内存泄露的风险,但是异步任务没停止的情况下,本身就可能已内存泄漏,相比Crash,这种做法“安全”些),即:


    protected Activity mActivity;
    @Overridepublic void onAttach(Activity activity) {
    super.onAttach(activity);
    this.mActivity = activity;
    }

    /**
    * 如果你用了support 23的库,上面的方法会提示过时,有强迫症的小伙伴,可以用下面的方法代替
    */
    @Overridepublic void onAttach(Context context) {
    super.onAttach(context);
    this.mActivity = (Activity)context;
    }

    异常:Can not perform this action after onSaveInstanceState


    大致意思是说我使用的 commit方法是在Activity的onSaveInstanceState()之后调用的,这样会出错,因为onSaveInstanceState

    方法是在该Activity即将被销毁前调用,来保存Activity数据的,如果在保存玩状态后再给它添加Fragment就会出错。解决办法就

    是把commit()方法替换成 commitAllowingStateLoss()就行了,其效果是一样的。



    有很多小伙伴遇到这个异常,这个异常产生的原因是:

    在你离开当前Activity等情况下,系统会调用onSaveInstanceState()帮你保存当前Activity的状态、数据等,直到再回到该Activity之前(onResume()之前),你执行Fragment事务,就会抛出该异常!(一般是其他Activity的回调让当前页面执行事务的情况,会引发该问题)

    解决方法:

    • 1、该事务使用commitAllowingStateLoss()方法提交,但是有可能导致该次提交无效!(在此次离开时恰巧Activity被强杀时)
    • 2、在重新回到该Activity的时候(onResumeFragments()onPostResume()),再执行该事务,配合数据保存,可以做到事务的完整性,不会丢失事务

    示例代码 (以EventBus通知执行事务为例,其他场景思路一致):


      @Override// 如果是在Fragment内, 则复写onResumeFragments()改为onResume()即可protected void onResumeFragments() {
    super.onResumeFragments();
    mIsSaved = true;
    if (mTransactionEvent != null) {
    // 这里执行事务
    mTransactionEvent = null;
    }
    }

    @Overrideprotected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mIsSaved = false;
    }

    @Subscribe(sticky = true) // sticky事件可以保证即使Activity被强杀,也会在恢复后拿到数据public void onEvent(TransactionEvent event) {
    if (mIsSaved) {
    // 这里执行事务
    } else {
    mTransactionEvent = event;
    }
    }

    Fragment重叠异常-----正确使用hide、show的姿势

    在类onCreate()的方法加载Fragment,并且没有判断saveInstanceState==nullif(findFragmentByTag(mFragmentTag) == null),导致重复加载了同一个Fragment导致重叠。(PS:replace情况下,如果没有加入回退栈,则不判断也不会造成重叠,但建议还是统一判断下)

    @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {
    // 在页面重启时,Fragment会被保存恢复,而此时再加载Fragment会重复加载,导致重叠 ;if(saveInstanceState == null){
    // 或者 if(findFragmentByTag(mFragmentTag) == null)// 正常情况下去 加载根Fragment
    }
    }

    即在add()或者replace()时绑定一个tag,一般我们是用fragment的类名作为tag,然后在发生“内存重启”时,通过findFragmentByTag找到对应的Fragment,并hide()需要隐藏的fragment。

    下面是个标准恢复写法:

    @Overrideprotected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity);

    TargetFragment targetFragment;
    HideFragment hideFragment;

    if (savedInstanceState != null) { // “内存重启”时调用
    targetFragment = getSupportFragmentManager().findFragmentByTag(TargetFragment.class.getName);
    hideFragment = getSupportFragmentManager().findFragmentByTag(HideFragment.class.getName);
    // 解决重叠问题
    getFragmentManager().beginTransaction()
    .show(targetFragment)
    .hide(hideFragment)
    .commit();
    }else{ // 正常时
    targetFragment = TargetFragment.newInstance();
    hideFragment = HideFragment.newInstance();

    getFragmentManager().beginTransaction()
    .add(R.id.container, targetFragment, targetFragment.getClass().getName())
    .add(R.id,container,hideFragment,hideFragment.getClass().getName())
    .hide(hideFragment)
    .commit();
    }
    }


    (1)每个Fragment以及宿主Activity(继承自FragmentActivity)都会在创建时,初始化一个FragmentManager对象,处理好Fragment嵌套问题的关键,就是理清这些不同阶级的栈视图。


    Android     Activity和Fragment通信    切换     数据传值


    (2)对于宿主Activity,getSupportFragmentManager()获取的FragmentActivity的FragmentManager对象;

    对于Fragment,getFragmentManager()是获取的是父Fragment(如果没有,则是FragmentActivity)的FragmentManager对象,而getChildFragmentManager()是获取自己的FragmentManager对象。


    5、是使用单Activity+多Fragment的架构,还是多模块Activity+多Fragment的架构?


    单Activity+多Fragment:
    一个app仅有一个Activity,界面皆是Frament,Activity作为app容器使用。

    优点:性能高,速度最快。参考:新版知乎 、google系app

    缺点:逻辑比较复杂,尤其当Fragment之间联动较多或者嵌套较深时,比较复杂。

    多模块Activity+多Fragment:
    一个模块用一个Activity,比如
    1、登录注册流程:
    LoginActivity + 登录Fragment + 注册Fragment + 填写信息Fragment + 忘记密码Fragment
    2、或者常见的数据展示流程:
    DataActivity + 数据列表Fragment + 数据详情Fragment + ...

    优点:速度快,相比较单Activity+多Fragment,更易维护。

    我的观点:
    权衡利弊,我认为多模块Activity+多Fragment是最合适的架构,开发起来不是很复杂,app的性能又很高效。



     抽象的Activity是不需要注册的。只有启动的才需要!

    那些年踩过的坑

    http://www.jianshu.com/p/d9143a92ad94

    别人写的框架:Fragmentation


    fragment的demo地址:


1.    Fragment的生命周期
2、Fragment如何与Activity交互,Acitivity和Fragment的通信
3.  如何管理Fragment回退栈

4.  Fragment的状态保存

5、Fragment 的startActivityForResult

6、Fragment 替换和隐藏的区别

7、使用Fragment创建对话框

8、Fragment常报的一些错



Fragment的产生与介绍:一个App可以同时适应手机和平板
Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fragment要多,
响应速度Fragment比Activty在中低端手机上快了很多,甚至能达到好几倍!如果你的app当前或以后有移植平板等平台时,可以让你节省大量时间和精力。


Fragment的生命周期:
Fragment必须是依存与Activity而存在的,因此Activity的生命周期会直接影响到Fragment的生命周期

Android     Activity和Fragment通信    切换     数据传值



Android     Activity和Fragment通信    切换     数据传值

好处:更为重要的是,你可以动态的添加、替换和移除某个Fragment


2、Fragment与Activity通信

因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:

a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法

b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。

c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。



Fragment的状态保存:
Fragment也有onSaveInstanceState的方法

Activity的Fragment的之间的传值:Fragment Arguments,比传统的Intent可以达到更好的解耦标准
方法一:
public class ContentActivity extends SingleFragmentActivity
{
private ContentFragment mContentFragment;
@Override
protected Fragment createFragment()
{
String title = getIntent().getStringExtra(ContentFragment.ARGUMENT);

mContentFragment = ContentFragment.newInstance(title);

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null)
{
mArgument = bundle.getString(ARGUMENT);
}

}

public static ContentFragment newInstance(String argument)
{
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, argument);
ContentFragment contentFragment = new ContentFragment();
contentFragment.setArguments(bundle);
return contentFragment;
}



给Fragment添加newInstance方法,将需要的参数传入,设置到bundle中,然后setArguments(bundle),最后在onCreate中进行获取;

这样就完成了Fragment和Activity间的解耦。当然了这里需要注意:





Fragment的startActivityForResult

我们点击跳转到对应Activity的Fragment中,并且希望它能够返回参数,那么我们肯定是使用Fragment.startActivityForResult ; 

在Fragment中存在startActivityForResult()以及onActivityResult()方法,

但是呢,没有setResult()方法,用于设置返回的intent,这样我们就需要通过调用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);。

没有serResult的方法,就需要调用Activity的serResult方法!


案列:一个Activity里面包含一个fragment,然后fragment调整到另外一个Activity中,这个Activity里面也包含一个fragment。

public class ListTitleActivity extends SingleFragmentActivity
{
private ListTitleFragment mListFragment;

@Override
protected Fragment createFragment()
{
mListFragment = new ListTitleFragment();
return mListFragment;
}
}

public class ListTitleFragment extends ListFragment
{

public static final int REQUEST_DETAIL = 0x110;
private List<String> mTitles = Arrays.asList("Hello", "World", "Android");
private int mCurrentPos ;
private ArrayAdapter<String> mAdapter ;


@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
setListAdapter(mAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mTitles));
}

@Override
public void onListItemClick(ListView l, View v, int position, long id)
{
mCurrentPos = position ;
Intent intent = new Intent(getActivity(),ContentActivity.class);
intent.putExtra(ContentFragment.ARGUMENT, mTitles.get(position));
startActivityForResult(intent, REQUEST_DETAIL);
}


@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
Log.e("TAG", "onActivityResult");
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_DETAIL)
{
mTitles.set(mCurrentPos, mTitles.get(mCurrentPos)+" -- ");
mAdapter.notifyDataSetChanged();
}
}
}

跳转的Acitivy和fragment
public class ContentActivity extends SingleFragmentActivity
{
private ContentFragment mContentFragment;
@Override
protected Fragment createFragment()
{
String title = getIntent().getStringExtra(ContentFragment.ARGUMENT);

mContentFragment = ContentFragment.newInstance(title);
return mContentFragment;
}
}

public class ContentFragment extends Fragment
{

private String mArgument;
public static final String ARGUMENT = "argument";
public static final String RESPONSE = "response";
public static final String EVALUATE_DIALOG = "evaluate_dialog";
public static final int REQUEST_EVALUATE = 0X110;

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
if (bundle != null)
{
mArgument = bundle.getString(ARGUMENT);
}

}

public static ContentFragment newInstance(String argument)
{
Bundle bundle = new Bundle();
bundle.putString(ARGUMENT, argument);
ContentFragment contentFragment = new ContentFragment();
contentFragment.setArguments(bundle);
return contentFragment;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
Random random = new Random();
TextView tv = new TextView(getActivity());
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
tv.setLayoutParams(params);
tv.setText(mArgument);
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 30);
tv.setGravity(Gravity.CENTER);
tv.setBackgroundColor(Color.argb(random.nextInt(100),
random.nextInt(255), random.nextInt(255), random.nextInt(255)));
// set click
tv.setOnClickListener(new OnClickListener()
{

@Override
public void onClick(View v)
{
EvaluateDialog dialog = new EvaluateDialog();
//注意setTargetFragment
dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);
dialog.show(getFragmentManager(), EVALUATE_DIALOG);
}
});
return tv;
}

//接收返回回来的数据
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == REQUEST_EVALUATE)
{
String evaluate = data
.getStringExtra(EvaluateDialog.RESPONSE_EVALUATE);
Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show();
Intent intent = new Intent();
intent.putExtra(RESPONSE, evaluate);
getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
}

}


附:startActivityForResult接收返回问题
在support 23.2.0以下的支持库中,对于在嵌套子Fragment的startActivityForResult
()
,会发现无论如何都不能在onActivityResult()中接收到返回值,只有最顶层的父Fragment才能接收到,这是一个support v4库的一个BUG,不过在前两天发布的support 23.2.0库中,已经修复了该问题,嵌套的子Fragment也能正常接收到返回数据了!



onActivityResult

Activity里面的onActivityResult方法还有Fragment里面的OnActivityResult的方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mAppManageFragment != null) {
mAppManageFragment.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==0){
adapter.refreshDeleteList();
}
}


在碎片中模拟返回栈


我们成功实现了向活动中动态添加碎片的功能,不过你尝试一下就会发现,通过点击按钮添加了一个碎片之后,这时按下Back键程序就会直接退出。如果这里我们想模仿类似于返回栈的效果,按下Back键可以回到上一个碎片,该如何实现呢?

 

其实很简单,FragmentTransaction中提供了一个addToBackStack()方法,可以用于将一个事务添加到返回栈中,修改MainActivity中的代码,如下所示:



public class MainActivity extends Activity implements OnClickListener {
……
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
AnotherRightFragment fragment
= new AnotherRightFragment();
FragmentManager fragmentManager
= getFragmentManager();
FragmentTransaction transaction
= fragmentManager. beginTransaction();
transaction.replace(R.id.right_layout, fragment);
transaction.addToBackStack(
null);
transaction.commit();
break;
default:
break;
}
}
}




add(), show(), hide(), replace()替换和隐藏Fragment的区别:


1、区别
show()hide()最终是让Fragment的View setVisibility(true还是false),不会调用生命周期;

replace()的话会销毁视图,即调用onDestoryView、onCreateView等一系列生命周期;

add()和 replace()不要在同一个阶级的FragmentManager里混搭使用。


使用场景
如果你有一个很高的概率会再次使用当前的Fragment,建议使用show()hide(),可以提高性能。

在我使用Fragment过程中,大部分情况下都是用show()hide(),而不是replace()

注意:如果你的app有大量图片,这时更好的方式可能是replace,配合你的图片框架在Fragment视图销毁时,回收其图片所占的内存



在多个 Fragment 之间进行切换的时候 
replace()和hide的区别 
首先replace 是代替,之前的Fragment会被清空掉,在再次切换回来的时候会进行重新加载。而hide是将之前的一个fragment 进行隐藏,将新的fragment 叠在上面进行显示.等在次调用的时候进行显示。不需要重新加载。测试这个最简单的方法就是 设计两个fragment 在同一位置上放一个按钮,一个有监听一个没有,但现实没有监听的按钮点击按钮的时候会响应一号按钮的监听事件。

另外一个对于 FragmentTransaction对象在commit之后就会失效。所以建议直接使用fragmentManager.beginTransaction() 这样就不会产生失效的问题了。

commit 于commitAllowingStateLoss();的区别 
简单来说就是避免报错。 
他们都调用了commitInternal 
public int commit() { 
return commitInternal(false); 
}

public int commitAllowingStateLoss() { 
return commitInternal(true); 

这个两个方法区别就在传参判断后的处理方法checkStateLoss 
当使用commit方法时,系统将进行状态判断,如果状态(mStateSaved)已经保存,将发生”Can not perform this action after onSaveInstanceState”错误。 
如果mNoTransactionsBecause已经存在,将发生”Can not perform this action inside of ” + mNoTransactionsBecause错误

Android     Activity和Fragment通信    切换     数据传值
Android     Activity和Fragment通信    切换     数据传值





多个fragment切换

http://www.yrom.net/blog/2013/03/10/fragment-switch-not-restart/


多个fragment问题呢http://www.tuicool.com/articles/eua2mm

不错的demohttp://blog.csdn.net/guolin_blog/article/details/13171191

fragment 特点


  • Fragment可以作为Activity界面的一部分组成出现;

  • 可以在一个Activity中同时出现多个Fragment,并且一个Fragment也可以在多个Activity中使用;

  • 在Activity运行过程中,可以添加、移除或者替换Fragment;

  • Fragment可以响应自己的输入事件,并且有自己的生命周期,它们的生命周期会受宿主Activity的生命周期影响。





    Menu菜单问题:fragment有菜单,activity有菜单的话,会把菜单综合在一起如果名称相同的话,也不会覆盖的

     Fragment中menu菜单注意事项  以前一般都是在Activity中添加menu菜单,一般是重写onCreateOptionsMenu和onOptionsItemSelected方法。

    现在用fragment用的多了,就在fragment里面添加menu菜单,也是重写了onCreateOptionsMenu和onOptionsItemSelected方法,但是发现没有效果。
    好吧,看了下源代码,原来跟一个mHasMenu的boolean变量有关系1// If set this fragment has menu items to contribute.2    boolean mHasMenu;这个变量控制fragment的menu菜单添加:01boolean performCreateOptionsMenu(Menu menu, MenuInflater inflater) {02        boolean show = false;03        if (!mHidden) {04            if (mHasMenu && mMenuVisible) {05                show = true;06                onCreateOptionsMenu(menu, inflater);07            }08            if (mChildFragmentManager != null) {09                show |= mChildFragmentManager.dispatchCreateOptionsMenu(menu, inflater);10            }11        }12        return show;13    }上面代码说明,如果mHasMenu为false,那么是不会执行onCreateOptionsMenu(menu, inflater)方法的,也就是不会添加fragment的menu菜单。
    所以,在fragment中使用menu菜单,需要在onCreate()方法里面添加语句setHasOptionsMenu(true);
    也就是这样:1<a href="http://home.51cto.com/index.php?s=/space/5017954" target="_blank">@Override</a>2    public void onCreate(Bundle savedInstanceState) {3        super.onCreate(savedInstanceState);4        setHasOptionsMenu(true);5    }嗯,很简单的东西,总结下,希望大家以后不要跟我一样犯错误哈。
    =============================================================
    Fragment滑动

    FragmentPagerAdapter与FragmentStatePagerAdapter

    相信这两个PagerAdapter的子类,大家都不陌生吧~~自从Fragment问世,使用ViewPager再结合上面任何一个实例的制作APP主页的案例特别多~~~

    那么这两个类有何区别呢?

    主要区别就在与对于fragment是否销毁,下面细说:

    FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图,并不会销毁fragment实例。

    FragmentStatePagerAdapter:会销毁不再需要的fragment,当当前事务提交以后,会彻底的将fragmeng从当前Activity的FragmentManager中移除,state标明,销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。

    如上所说,使用FragmentStatePagerAdapter当然更省内存,但是销毁新建也是需要时间的。一般情况下,如果你是制作主页面,就3、4个Tab,那么可以选择使用FragmentPagerAdapter,如果你是用于ViewPager展示数量特别多的条目时,那么建议使用FragmentStatePagerAdapter。



    fragment添加全部的还是添加2页(和viewpager有关)=====用的适配器不一样
    有点连oncreatView方法都没有执行
    两种适配器,一个返回的是fragment一个返回的view


    一个Activity里面嵌套多个Fragment,滑动加载的流程和生命周期1.只加载前面2个,没滑动一次加载一个以后什么方法都不执行
    2.只加载前面连个,没滑动加载一个再滑动执行onpause也执行oncreat方法
    3.只加载前面连个,没滑动加载一个

    第一个页面不为空的话,会执行下面的一些列的方法.以后的话就不会再执行里面的任何方法了
    如果页面为空或者重写的话,不会执行下面的方法

    如果第一个为空,第2个不为空的话,也没有问题
    如果 前面两个都为空的话就有问题了,是viewPager的问题


    Android     Activity和Fragment通信    切换     数据传值

    2.onCreateView---这样每次都用更新数据
    private View rootView;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    if (null != rootView) {
    ViewGroup parent
    = (ViewGroup) rootView.getParent();
    if (null != parent) {
    parent.removeView(rootView);
    }
    }
    else {
    rootView
    = inflater.inflate(layoutId, null);
    initView(rootView);
    // 控件初始化 }
    return rootView;
    }

    Fragment之间切换时每次都会调用onCreateView方法,导致每次Fragment的布局都重绘,无法保持Fragment原有状态。
    解决办法:在Fragment onCreateView方法中缓存View.



    public abstract class SingleFragmentActivity extends FragmentActivity
    {

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_single_fragment);

    FragmentManager fm = getSupportFragmentManager();
    Fragment fragment =fm.findFragmentById(R.id.id_fragment_container);

    if(fragment == null )
    {
    fragment = createFragment() ;

    fm.beginTransaction().add(R.id.id_fragment_container,fragment).commit();
    }
    }

    为什么需要判null呢?

    主要是因为,当Activity因为配置发生改变(屏幕旋转)或者内存不足被系统杀死,造成重新创建时,

    我们的fragment会被保存下来,但是会创建新的FragmentManager,新的FragmentManager会首先会去获取保存下来的fragment队列,重建fragment队列,从而恢复之前的状态。



    DialogFragment的用法和setTargetFragment用法

    例如,点击当前Fragment中按钮,弹出一个对话框(DialogFragment),在对话框中的操作需要返回给触发的Fragment中,那么如何数据传递呢?对于对话框的使用推荐:Android 官方推荐 : DialogFragment 创建对话框

    tv.setOnClickListener(new OnClickListener()
    {

    @Override
    public void onClick(View v)
    {
    EvaluateDialog dialog = new EvaluateDialog();
    //注意setTargetFragment
    dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);
    dialog.show(getFragmentManager(), EVALUATE_DIALOG);
    }
    });
    return tv;
    }

    //接收返回回来的数据()
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data)
    {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == REQUEST_EVALUATE)
    {
    String evaluate = data
    .getStringExtra(EvaluateDialog.RESPONSE_EVALUATE);
    Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show();
    Intent intent = new Intent();
    intent.putExtra(RESPONSE, evaluate);
    getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
    }

    }


    public class EvaluateDialog extends DialogFragment
    {
    private String[] mEvaluteVals = new String[] { "GOOD", "BAD", "NORMAL" };
    public static final String RESPONSE_EVALUATE = "response_evaluate";

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

    builder.setTitle("Evaluate :").setItems(mEvaluteVals,
    new OnClickListener()
    {
    @Override
    public void onClick(DialogInterface dialog, int which)
    {
    setResult(which);
    }
    });
    return builder.create();
    }

    // 设置返回数据
    protected void setResult(int which)
    {
    // 判断是否设置了targetFragment
    if (getTargetFragment() == null)
    return;

    Intent intent = new Intent();
    intent.putExtra(RESPONSE_EVALUATE, mEvaluteVals[which]);
    getTargetFragment().onActivityResult(ContentFragment.REQUEST_EVALUATE,
    Activity.RESULT_OK, intent);

    }
    }


    fragment的缺点http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0605/2996.htmlSquare:从今天开始抛弃Fragment吧!

    Fragment transactions


    Transaction的出现:
    在AIDL里面生成的java文件中会有!

    Fragment 的 transaction 允许你执行一系列的 Fragment 操作,但不幸的是,提交 transaction 是异步操作,并且在 UI 线程的 Handler 队列的队尾被提交。

    这会在接收多个点击事件或配置发生改变时让你的 App 处在未知的状态。

    class BackStackRecord extends FragmentTransaction {    int commitInternal(boolean allowStateLoss) {        if (mCommitted)            throw new IllegalStateException("commit already called");        mCommitted = true;        if (mAddToBackStack) {            mIndex = mManager.allocBackStackIndex(this);        } else {            mIndex = -1;        }        mManager.enqueueAction(this, allowStateLoss);        return mIndex;    }}

    创建 Fragment 可能带来的问题

    Fragment 的实例能够通过 Fragment Manager 创建,例如下面的代码看起来没有什么问题:

    DialogFragment dialogFragment = new DialogFragment() {  @Override public Dialog onCreateDialog(Bundle savedInstanceState) { ... }};dialogFragment.show(fragmentManager, tag);

    然而,当我们需要存储 Activity 实例的状态时,Fragment Manager 可能会通过反射机制重新创建该 Fragment 的实例,又因为这是一个匿名内部类,该类有一个隐藏的构造器的参数正是外部类的引用,如果大家有看过这篇博文的话就会知道,拥有外部引用可能会带来内存泄漏的问题。


    结论:1.我们遇到的大多数难以解决的 Bug 都与 Fragment 的生命周期有关。2.我们只需要 View 创建响应式 UI,实现回退栈以及屏幕事件的处理,不用 Fragment 也能满足实际开发的需求。3.MVVP模式看待的话,解耦性不好!

    看了上面的介绍,你可能会觉得Fragment有点可怕。

    但是我想说,如果你只是浅度使用,比如一个Activity容器包含列表Fragment+详情Fragment这种简单情景下,

    不涉及到popBackStack/Immediate(tag/id)这些的方法,还是比较轻松使用的,出现的问题,网上都可以找到解决方案。


    getActivity()空指针

    可能你遇到过getActivity()返回null,或者平时运行完好的代码,在“内存重启”之后,调用getActivity()的地方却返回null,报了空指针异常。

    大多数情况下的原因:你在调用了getActivity()时,当前的Fragment已经onDetach()了宿主Activity。
    比如:你在pop了Fragment之后,该Fragment的异步任务仍然在执行,并且在执行完成后调用了getActivity()方法,这样就会空指针。

    解决办法:
    更"安全"的方法:(对于Fragment已经onDetach这种情况,我们应该避免在这之后再去调用宿主Activity对象,比如取消这些异步任务,但我们的团队可能会有粗心大意的情况,所以下面给出的这个方案会保证安全)

    在Fragment基类里设置一个Activity mActivity的全局变量,在onAttach(Activity activity)里赋值,使用mActivity代替getActivity(),保证Fragment即使在onDetach后,仍持有Activity的引用(有引起内存泄露的风险,但是异步任务没停止的情况下,本身就可能已内存泄漏,相比Crash,这种做法“安全”些),即:


    protected Activity mActivity;
    @Overridepublic void onAttach(Activity activity) {
    super.onAttach(activity);
    this.mActivity = activity;
    }

    /**
    * 如果你用了support 23的库,上面的方法会提示过时,有强迫症的小伙伴,可以用下面的方法代替
    */
    @Overridepublic void onAttach(Context context) {
    super.onAttach(context);
    this.mActivity = (Activity)context;
    }

    异常:Can not perform this action after onSaveInstanceState


    大致意思是说我使用的 commit方法是在Activity的onSaveInstanceState()之后调用的,这样会出错,因为onSaveInstanceState

    方法是在该Activity即将被销毁前调用,来保存Activity数据的,如果在保存玩状态后再给它添加Fragment就会出错。解决办法就

    是把commit()方法替换成 commitAllowingStateLoss()就行了,其效果是一样的。



    有很多小伙伴遇到这个异常,这个异常产生的原因是:

    在你离开当前Activity等情况下,系统会调用onSaveInstanceState()帮你保存当前Activity的状态、数据等,直到再回到该Activity之前(onResume()之前),你执行Fragment事务,就会抛出该异常!(一般是其他Activity的回调让当前页面执行事务的情况,会引发该问题)

    解决方法:

    • 1、该事务使用commitAllowingStateLoss()方法提交,但是有可能导致该次提交无效!(在此次离开时恰巧Activity被强杀时)
    • 2、在重新回到该Activity的时候(onResumeFragments()onPostResume()),再执行该事务,配合数据保存,可以做到事务的完整性,不会丢失事务

    示例代码 (以EventBus通知执行事务为例,其他场景思路一致):


      @Override// 如果是在Fragment内, 则复写onResumeFragments()改为onResume()即可protected void onResumeFragments() {
    super.onResumeFragments();
    mIsSaved = true;
    if (mTransactionEvent != null) {
    // 这里执行事务
    mTransactionEvent = null;
    }
    }

    @Overrideprotected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    mIsSaved = false;
    }

    @Subscribe(sticky = true) // sticky事件可以保证即使Activity被强杀,也会在恢复后拿到数据public void onEvent(TransactionEvent event) {
    if (mIsSaved) {
    // 这里执行事务
    } else {
    mTransactionEvent = event;
    }
    }

    Fragment重叠异常-----正确使用hide、show的姿势

    在类onCreate()的方法加载Fragment,并且没有判断saveInstanceState==nullif(findFragmentByTag(mFragmentTag) == null),导致重复加载了同一个Fragment导致重叠。(PS:replace情况下,如果没有加入回退栈,则不判断也不会造成重叠,但建议还是统一判断下)

    @Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {
    // 在页面重启时,Fragment会被保存恢复,而此时再加载Fragment会重复加载,导致重叠 ;if(saveInstanceState == null){
    // 或者 if(findFragmentByTag(mFragmentTag) == null)// 正常情况下去 加载根Fragment
    }
    }

    即在add()或者replace()时绑定一个tag,一般我们是用fragment的类名作为tag,然后在发生“内存重启”时,通过findFragmentByTag找到对应的Fragment,并hide()需要隐藏的fragment。

    下面是个标准恢复写法:

    @Overrideprotected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity);

    TargetFragment targetFragment;
    HideFragment hideFragment;

    if (savedInstanceState != null) { // “内存重启”时调用
    targetFragment = getSupportFragmentManager().findFragmentByTag(TargetFragment.class.getName);
    hideFragment = getSupportFragmentManager().findFragmentByTag(HideFragment.class.getName);
    // 解决重叠问题
    getFragmentManager().beginTransaction()
    .show(targetFragment)
    .hide(hideFragment)
    .commit();
    }else{ // 正常时
    targetFragment = TargetFragment.newInstance();
    hideFragment = HideFragment.newInstance();

    getFragmentManager().beginTransaction()
    .add(R.id.container, targetFragment, targetFragment.getClass().getName())
    .add(R.id,container,hideFragment,hideFragment.getClass().getName())
    .hide(hideFragment)
    .commit();
    }
    }


    (1)每个Fragment以及宿主Activity(继承自FragmentActivity)都会在创建时,初始化一个FragmentManager对象,处理好Fragment嵌套问题的关键,就是理清这些不同阶级的栈视图。


    Android     Activity和Fragment通信    切换     数据传值


    (2)对于宿主Activity,getSupportFragmentManager()获取的FragmentActivity的FragmentManager对象;

    对于Fragment,getFragmentManager()是获取的是父Fragment(如果没有,则是FragmentActivity)的FragmentManager对象,而getChildFragmentManager()是获取自己的FragmentManager对象。


    5、是使用单Activity+多Fragment的架构,还是多模块Activity+多Fragment的架构?


    单Activity+多Fragment:
    一个app仅有一个Activity,界面皆是Frament,Activity作为app容器使用。

    优点:性能高,速度最快。参考:新版知乎 、google系app

    缺点:逻辑比较复杂,尤其当Fragment之间联动较多或者嵌套较深时,比较复杂。

    多模块Activity+多Fragment:
    一个模块用一个Activity,比如
    1、登录注册流程:
    LoginActivity + 登录Fragment + 注册Fragment + 填写信息Fragment + 忘记密码Fragment
    2、或者常见的数据展示流程:
    DataActivity + 数据列表Fragment + 数据详情Fragment + ...

    优点:速度快,相比较单Activity+多Fragment,更易维护。

    我的观点:
    权衡利弊,我认为多模块Activity+多Fragment是最合适的架构,开发起来不是很复杂,app的性能又很高效。



     抽象的Activity是不需要注册的。只有启动的才需要!

    那些年踩过的坑

    http://www.jianshu.com/p/d9143a92ad94

    别人写的框架:Fragmentation


    fragment的demo地址: