Android技能树 — Fragment总体小结,2024年最新腾讯面试gm

简介: Android技能树 — Fragment总体小结,2024年最新腾讯面试gm
xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
xmlns:tools=“http://schemas.android.com/tools”
android:orientation=“vertical”
android:layout_width=“match_parent”
android:layout_height=“match_parent”>


2. 在代码中动态添加,比如我们添加到一个LinearLayout中:

Button button = new Button(mActivity);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
button.setLayoutParams(params);
container.addView(button);

所以Fragment也很简单,就把它当做一个简单的View(但其实更像是“子 Activity”),然后添加方式也是一样。

1. 直接在Layout.xml中添加:

<?xml version="1.0" encoding="utf-8"?>

2. 直接在代码中添加:

Fragment one = new FragmentOne();//自定义的Fragment类
//要先获取FragmentManager对象
FragmentManager fragmentManager = getSupportFragmentManager();
//开启一个FragmentTransaction事务
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.framelayout_view,one).commit();

其中添加到R.id.framelayout_view的这个idViewGourp可以是<FrameLayout/>,也可以是其他的比如<LinearLayout/>等。


2. Fragment的基本操作


看到我们上面的动态代码添加的时候需要获取FragmentTransactionFragmentManager

2.1 FragmentManager相关

1. getFragmentManager():

获取Fragment父容器的管理器,但是现在该方法在Activity中已经被标记不推荐使用了。

/**
• Return the FragmentManager for interacting with fragments associated
• with this activity.
• @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()}
*/
@Deprecated
public FragmentManager getFragmentManager() {
return mFragments.getFragmentManager();
}

2. getSupportFragmentManager():

v4包下的这个方法,与上一个效果一样,不过是Android推荐使用的方法(毕竟可以兼容Android所有版本)

3. getChildFragmentManager():

我们提过,Fragment更像是一个“子Activity”,那你说"子Activity"中能否再添加Fragment,答案当然是可以。那么在Fragment内部中的Fragment的管理器,就需要使用getChildFragmentManager()来获取了。

2.2 FragmentTransaction相关


我们可以看到有添加删除各种方法操作。

1. attach/detach方法:

  • detach(Fragment fragment) : 分离指定Fragment的UI视图
  • attach(Fragment fragment) : 重新关联一个Fragment(当这个Fragment的detach执行之后)
  1. 当Fragment被detach后,Fragment的生命周期执行完onDestroyView就终止了,这意味着Fragment的实例并没有被销毁,只是UI界面被移除了(注意和remove是有区别的)。
  2. 当Fragment被detach后,执行attach操作,会让Fragment从onCreateView开始执行,一直执行到onResume。
  3. attach无法像add一样单独使用,单独使用会抛异常。方法存在的意义是对detach后的Fragment进行界面恢复。

2.add/remove方法:

我想这二个是用的最多的了,add()和remove()是将fragment添加和移除. remove()比detach()要彻底一些, 如果不加入到回退栈中, remove()的时候, fragment的生命周期会一直走到onDetach();如果加入了回退栈,则会只执行到onDestoryView(),Fragment对象还是存在的。

add一个fragment,如果加到的是同一个id的话,有点像我们的Activity栈,启动多个Activity时候,Activity一个个叠在上面,fragment也是类似,一个个fragment叠在上面。

3.replace方法:

replace = remove + add , 所以可以理解为先把相同id下的Fragment移除掉,然后再加入这个当前的fragment。

所以如果你觉得Fragment存在太多,影响性能,可以用replace来切换各个界面,就可以保证当前只有一个Fragment,但是因为每次切换后,Fragment都会重建,所以如果这个界面有网络请求相关的,你就会发现这个界面又重新去请求网络接口了,显得很多此一举。

4.hide/show方法:

就是字面意思,让一个Fragment隐藏,让一个Fragment显示。你可以理解为Button设置了View.GONE和View.VISIBLE。常常配合有多个Fragment及有TAB等切换方式的时候,选中某个按钮,然后根据相应的让对应的Fragment显示,其他Fragment隐藏。

5.commit/commitAllowingStateLoss:

我估计很多人认识这个commitAllowingStateLoss大部分是因为自己的代码有闪退异常:

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState

在你离开当前Activity等情况下,系统会调用onSaveInstanceState()帮你保存当前Activity的状态、数据等,直到再回到该Activity之前(onResume()之前),你执行Fragment事务,就会抛出该异常。然后网上有很多教程,叫你提交的时候使用commitAllowingStateLoss()方法,虽然说不会抛出错误,但是如果在Activity已经保存状态完之后提交了它,到时候Ativity意外崩溃,再恢复数据的时候就不会恢复在Activity保存状态之后提交的fragment的更新,造成状态丢失了。

额外补充:

1.commit()方法并不立即执行transaction中包含的动作,而是把它加入到UI线程队列中. 如果想要立即执行,可以在commit之后立即调用FragmentManager的executePendingTransactions()方法.

2. commit()方法必须在状态存储之前调用,否则会抛出异常,如果觉得状态丢失没关系, 可以调用commitAllowingStateLoss(). 但是除非万不得已, 一般不推荐用这个方法, 会掩盖很多错误.

6. addToBackStack:

我们可以看到FragmentTransaction里面有加入回退栈方法,但是没有退出的方法:popBackStack。这是因为这个方法在FragmentManager里面。

也就是如下图:


一般反应是,addToBackStack和popBackStack不是应该像上面的类似add和remove一样,都一个层级的吗??所以popBackStack不也应该是FragmentTransaction下的一个方法???

所以我们单从图片所示就能知道,popBackStackFragmentTransaction是一个层级,所以popBackStack操作的其实也是《fragment事务》(FragmentTransaction),所以可以理解为addToBackStack把我们前面的FragmentTransaction事务(比如add,remove,replace等一系列操作)加入到了回退栈(!!!记住不是把fragment加入到了回退栈),而popBackStack是操作回退栈里面的事务。

当然具体的源码过程分析,细讲的话又是很多,都可以另外专门写一篇文章,所以直接借鉴网上别人已经写好的文章:

Fragment那点事①Fragment栈管理

额外补充:

1.加入回退栈:remove掉的fragment执行onDestoryView,并没有执行onDestory,fragment实例对象还是存在,当回退时候,fragment从onCreateView处执行

2. 未加入回退栈:remove掉的fragment 执行 onDestoryView和onDestory,彻底销毁移除


3.Fragment中获取Context


我们可以直接在fragment代码里面直接使用getActivity()getContext()方法。

但是有时候获取为空,所以一般我们使用的是:

Class xxxFragment extends Fragment {
private Context mContext;
//‘高版本后,都是回调这个方法’
@Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
}
//‘API低于 23 的版本的时候,是会回调这个方法’
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mContext = activity;
}
}

4.Fragment配合ViewPager


ViewPager配合Fragment的时候,主要使用FragmentPagerAdapterFragmentStatePagerAdapter这二个Adapter。其实使用很简单(一般的最最简单的写法):

public class FragmentAdapter extends FragmentPagerAdapter{
private ArrayList list;
//通过构造获取fragment集合
public Fragment_pager(FragmentManager fm,ArrayList list) {
super(fm);
this.list=list;
}
//设置具体position的fragment
@Override
public Fragment getItem(int position) {
// TODO Auto-generated method stub
return list.get(position);
}
//设置有多少个fragment
@Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}
}

然后ViewPager.setAdapter(xxxx);

但是大家会奇怪为啥有二个Adapter:FragmentPagerAdapterFragmentStatePagerAdapter,他们的区别我们可以看具体的源码:

FragmentPagerAdapter源码:

public abstract class FragmentPagerAdapter extends PagerAdapter {
//‘初始化创建Item:’
@NonNull
public Object instantiateItem(@NonNull ViewGroup container, int position) {
if (this.mCurTransaction == null) {
this.mCurTransaction = this.mFragmentManager.beginTransaction();
}
long itemId = this.getItemId(position);
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = this.mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
//‘后面使用fragment是通过FragmentTransaction.attach方式加进来的,’
//‘只是重新绘制了UI,fragment对象还在。’
this.mCurTransaction.attach(fragment);
} else {
//‘我们知道刚返回fragment使用的是getItem(position)方法’
//‘我们可以看到第一次使用fragment是通过FragmentTransaction.add方式加进来的’
fragment = this.getItem(position);
this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
}
if (fragment != this.mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
//‘销毁item:’
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
if (this.mCurTransaction == null) {
this.mCurTransaction = this.mFragmentManager.beginTransaction();
}
//‘我们可以看到FragmentTransaction只是单纯的detach了fragment,视图不在了,但是fragment对象还在’
this.mCurTransaction.detach((Fragment)object);
}
}

我们可以看到fragment并没有真的销毁,FragmentPageAdapter则适用于固定的,少量的Fragment情况,例如和TabLayout共同使用时。

FragmentStatePagerAdapter源码:

public abstract class FragmentStatePagerAdapter extends PagerAdapter {
@NonNull
public Object instantiateItem(@NonNull ViewGroup container, int position) {
Fragment fragment;
if (this.mFragments.size() > position) {
fragment = (Fragment)this.mFragments.get(position);
if (fragment != null) {
return fragment;
}
}
if (this.mCurTransaction == null) {
this.mCurTransaction = this.mFragmentManager.beginTransaction();
}
fragment = this.getItem(position);
if (this.mSavedState.size() > position) {
SavedState fss = (SavedState)this.mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while(this.mFragments.size() <= position) {
this.mFragments.add((Object)null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
this.mFragments.set(position, fragment);
//‘我们可以看到fragment都是add进来的’
this.mCurTransaction.add(container.getId(), fragment);
return fragment;
}
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment)object;
if (this.mCurTransaction == null) {
this.mCurTransaction = this.mFragmentManager.beginTransaction();
}
while(this.mSavedState.size() <= position) {
this.mSavedState.add((Object)null);
}
this.mSavedState.set(position, fragment.isAdded() ? this.mFragmentManager.saveFragmentInstanceState(fragment) : null);
this.mFragments.set(position, (Object)null);
//‘可以看到都是通过remove的方式移除了’
this.mCurTransaction.remove(fragment);
}
相关文章
|
25天前
|
编译器 Android开发 开发者
Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
Lambda表达式和匿名函数都是Kotlin中强大的特性,帮助开发者编写简洁而高效的代码。理解它们的区别和适用场景,有助于选择最合适的方式来解决问题。希望本文的详细讲解和示例能够帮助你在Kotlin开发中更好地运用这些特性。
29 9
|
4月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
111 6
|
4月前
|
Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
4月前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
4月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
4月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
41 3
|
4月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
33 0
|
存储 缓存 Java
Android--面试中遇到的问题总结(一)
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/chaoyu168/article/details/56479430 一、handl...
1348 0
|
Android开发 容器 缓存
Android--面试中遇到的问题总结(二)
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/chaoyu168/article/details/56480392 一、开发中都...
1256 0
|
算法 Java 程序员
Android--面试中遇到的问题总结(三)
《Android 开发工程师面试指南 LearningNotes 》,作者是陶程,由梁观全贡献部分。大家可以去知乎关注这两位用心的少年。
3035 0

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 3
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 6
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 7
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 8
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
  • 9
    美团面试:MySQL为什么 不用 Docker部署?
  • 10
    阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?