目录
1.1running-----paused------stopped-----killed
2.1从A页面Activity跳转到B页面Activity,然后关闭B页面Activity,回到A页面Activity
1.Activity的运行状态
1.1running-----paused------stopped-----killed
- running->当前显示在屏幕的activity(位于任务栈的顶部),用户可见状态。
- paused->依旧在用户可见状态,但是界面焦点已经失去,此Activity无法与用户进行交互。
- stopped->用户看不到当前界面,也无法与用户进行交互完全被覆盖.
- killed->当前界面被销毁,等待这系统被回收
由上图我们得知:
- Starting ——–>Running 所执行的生命周期顺序 onCreate()->onstart()->onResume()
当前称为活动状态(Running),此activity所处于任务栈的top中,可以与用户进行交互。
- Running ——>Paused 所执行Activity生命周期中的onPause()
当前称为暂停状态(Paused),该Activity已失去了焦点但仍然是可见的状态(包括部分可见)。
- Paused ——>Running所执行的生命周期为:OnResume()
当前重新回到活动状态(Running),此情况用户操作home键,然后重新回到当前activity界面发生。
- Paused ——>Stoped所执行的生命周期为:onStop()
该Activity被另一个Activity完全覆盖的状态,该Activity变得不可见,所以系统经常会由于内存不足而将该Activity强行结束。
- Stoped——>killed所执行的生命周期为:onDestroy()
该Activity被系统销毁。当一个Activity处于暂停状态或停止状态时就随处可能进入死亡状态,因为系统可能因内存不足而强行结束该Activity。
注:还有一种情况由于系统内存不足可能在Paused状态中直接被系统杀死达到killed状态。
上面图概括了android生命周期的各个环节,描述了activity从生成到销毁的过程。
- onCreate():
当我们点击activity的时候,系统会调用activity的oncreate()方法,在这个方法中我们会初始化当前布局setContentLayout()方法。
- onStart():
onCreate()方法完成后,此时activity进入onStart()方法,当前activity是用户可见状态,但没有焦点,与用户不能交互,一般可在当前方法做一些动画的初始化操作。
- onResume():
onStart()方法完成之后,此时activity进入onResume()方法中,当前activity状态属于运行状态 (Running),可与用户进行交互。
- onPause():
当另外一个activity覆盖当前的acitivty时,此时当前activity会进入到onPause()方法中,当前activity是可见的,但不能与用户交互状态。
- onStop():
onPause()方法完成之后,此时activity进入onStop()方法,此时activity对用户是不可见的,在系统内存紧张的情况下,有可能会被系统进行回收。所以一般在当前方法可做资源回收。
- onDestory():
onStop()方法完成之后,此时activity进入到onDestory()方法中,结束当前activity。
- onRestart():
onRestart()方法在用户按下home()之后,再次进入到当前activity的时候调用。调用顺序 onPause()->onStop()->onRestart()->onStart()->onResume().
2.页面跳转及一些特殊情况
2.1从A页面Activity跳转到B页面Activity,然后关闭B页面Activity,回到A页面Activity
①针对开启的B页面Activity,第一次启动,回调如下:onCreate()->onStart()->onResume()
②用户打开B页面Activiy的时候,A页面的Activity【处于不可见】的回调如下:onPause()->onStop()
③再次从B页面回到A页面原Activity时,A页面【从不可见到可见】回调如下:onRestart()->onStart()->onResume()
④按back键回退时,B页面Activity回调如下:onPause()->onStop()->onDestory()
⑤按Home键切换到桌面后又回到A页面该Actitivy,回调如下:onPause()->onStop()->onRestart()->onStart()->onResume()
⑥调用finish()方法后,回调如下:onDestory()(以在onCreate()方法中调用为例,不同方法中回调不同,通常都是在onCreate()方法中调用)
2.2 横竖屏切换
第一种情况,销毁当前的Activity后重建,这种也尽量避免。
- 在横竖屏切换的过程中,会发生Activity被销毁并重建的过程。
- 在了解这种情况下的生命周期时,首先应该了解这两个回调:onSaveInstanceState和onRestoreInstanceState。
- 在Activity由于异常情况下终止时,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法的调用是在onStop之前,它和onPause没有既定的时序关系,该方法只在Activity被异常终止的情况下调用。当异常终止的Activity被重建以后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象参数同时传递给onRestoreInstanceState和onCreate方法。因此,可以通过onRestoreInstanceState方法来恢复Activity的状态,该方法的调用时机是在onStart之后。其中onCreate和onRestoreInstanceState方法来恢复Activity的状态的区别: onRestoreInstanceState回调则表明其中Bundle对象非空,不用加非空判断。onCreate需要非空判断。建议使用onRestoreInstanceState。
- 横竖屏切换的生命周期:onPause()->onSaveInstanceState()-> onStop()->onDestroy()->onCreate()->onStart()->onRestoreInstanceState->onResume()
第二种情况,当前的Activity不销毁,设置Activity的属性。
应用场景:视频播放器就经常会涉及屏幕旋转场景。
- 可以通过在AndroidManifest文件的Activity中指定如下属性:
-
<activity android:name=".activity.VideoDetailActivity" android:configChanges="orientation|keyboardHidden|screenSize" android:screenOrientation="portrait"/>
- 为了避免横竖屏切换时,Activity的销毁和重建,而是回调了下面的方法:
-
//重写旋转时方法,不销毁activity @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); }
第三种情况:资源内存不足导致优先级低的Activity被杀死
- Activity优先级的划分和下面的Activity的三种运行状态是对应的。
- (1) 前台Activity——正在和用户交互的Activity,优先级最高。
- (2) 可见但非前台Activity——比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户交互。
- (3) 后台Activity——已经被暂停的Activity,比如执行了onStop,优先级最低。
- 当系统内存不足时,会按照上述优先级从低到高去杀死目标Activity所在的进程。我们在平常使用手机时,能经常感受到这一现象。这种情况下数组存储和恢复过程和上述情况一致,生命周期情况也一样。
3.Activity的启动模式
Android提供了四种Activity启动方式:
- 标准模式(standard)
- 栈顶复用模式(singleTop)
- 栈内复用模式(singleTask)
- 单例模式(singleInstance)
详情请移步:Activity四种启动模式
4.Fragment
4.1什么是Fragment?
- 什么是Fragment
- 可以简单的理解为,Fragment是显示在Activity中的Activity。它可以显示在Activity中,然后它也可以显示出一些内容。因为它拥有自己的生命周期,可以接受处理用户的事件,并且你可以在一个Activity中动态的添加,替换,移除不同的Fragment,因此对于信息的展示具有很大的便利性。
- 作为 view 界面的一部分,Fragment 的存在必须依附于 FragmentActivity使用,并且与 FragmentActivity 一样,拥有自己的独立的生命周期,同时处理用户的交互动作。同一个 FragmentActivity 可以有一个或多个 Fragment 作为界面内容,同样Fragment也可以拥有多个子Fragment,并且可以动态添加、删除 Fragment,让UI的重复利用率和易修改性得以提升,同样可以用来解决部分屏幕适配问题。
- Fragment是组件还是控件
- 严格意义上来说Fragment并不是一个显示控件,而只是一个显示组件。为什么这么说呢?其实像我们的Activity,Dialog,PopupWindow以及Toast类的内部都管理维护着一个Window对象,这个Window对象不但是一个View组件的集合管理对象,它也实现了组件的加载与绘制流程,而我们的Fragment组件如果看过源码的话,严格意义上来说,只是一个View组件的集合并通过控制变量实现了其特定的生命周期,但是其由于并没有维护Window类型的成员变量,所以其不具备组件的加载与绘制功能,因此其不能单独的被绘制出来,这也是我把它称之为组件而不是控件的原因。
- 使用条件
- 宿主Activity 必须继承自 FragmentActivity;
- 使用getSupportFragmentManager() 方法获取 FragmentManager 对象;
4.2 Fragment生命周期
- ragment是依附于Activity存在的,因此它的生命周期收到Activity的生命周期影响
- Fragment比Activity多了几个生命周期的回调方法
- onAttach(Activity) 当Fragment与Activity发生关联的时候调用
- onCreateView(LayoutInflater, ViewGroup, Bundle) 创建该Fragment的视图
- onViewCreated(View view, Bundle savedInstanceState) 试图创建后调用该方法
- onActivityCreated(Bundle) 当Activity的onCreated方法返回时调用
- onDestroyView() 与onCreateView方法相对应,当该Fragment的视图被移除时调用
- onDetach() 与onAttach方法相对应,当Fragment与Activity取消关联时调用
- 注意:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现
4.3 Fragment使用
- Activity创建Fragment的方式是什么?
- 静态创建具体步骤
- 首先我们同样需要注册一个xml文件,然后创建与之对应的java文件,通过onCreatView()的返回方法进行关联,最后我们需要在Activity中进行配置相关参数即在Activity的xml文件中放上fragment的位置。
- 动态创建具体步骤
- (1)创建待添加的碎片实例
- (2)获取FragmentManager,在活动中可以直接通过调用 getSupportFragmentManager()方法得到。
- (3)开启一个事务,通过调用beginTransaction()方法开启。
- (4)向容器内添加或替换碎片,一般使用repalce()方法实现,需要传入容器的id和待添加的碎片实例。
- (5)提交事务,调用commit()方法来完成。
- 静态创建具体步骤
- 接下来就分别详细地理解一下两种创建fragment的方式的细节。
4.3.1静态使用
- 大概步骤:
- ① 创建一个类继承Fragment,重写onCreateView方法,来确定Fragment要显示的布局
- ② 在Activity中声明该类,与普通的View对象一样
- 代码演示
- 继承Frgmanet的类MyFragment【请注意导包的时候导v4的Fragment的包】
public class MyFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/*
* 参数1:布局文件的id
* 参数2:容器
* 参数3:是否将这个生成的View添加到这个容器中去
* 作用是将布局文件封装在一个View对象中,并填充到此Fragment中
* */
View v = inflater.inflate(R.layout.item_fragment, container, false);
return v;
}
}
- Activity对应的布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.yczbj.fragment.MainActivity">
<fragment
android:id="@+id/search_fragment"
android:name="com.yczbj.fragment.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
- Activity中的显示和隐藏fragment代码
phoneNumFragment = (PhoneNumFragment) getSupportFragmentManager()
.findFragmentById(R.id.search_fragment);
//处理返回键
@Override
public void onBackPressed() {
if (phoneNumFragment != null && !phoneNumFragment.isHidden()) {
hideSearch();
return;
}
super.onBackPressed();
}
private void hideSearch() {
getSupportFragmentManager()
.beginTransaction()
.hide(phoneNumFragment)
.commit();
}
private void showSearch() {
getSupportFragmentManager()
.beginTransaction()
.show(phoneNumFragment)
.commit();
}
4.3.2 动态使用
代码如下所示,这种是平时开发最常用的
OrderStatesFragment fragment = new OrderStatesFragment();
Bundle bundle = new Bundle();
bundle.putSerializable("confirmOrderModel",confirmOrderModel);
fragment.setArguments(bundle);
FragmentManager fragmentManager = activity.getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setCustomAnimations(R.anim.push_bottom_in, 0);
//transaction.show(fragment); //暂时不用这个
transaction.add(android.R.id.content, fragment, "OrderStatesFragment");
transaction.commitAllowingStateLoss();
部分代码说明
- ① 创建一个fragment的实例
- ② 通过getSupportFragmentManager()方法新建Fragment管理器对象
- ③ 然后通过Fragment管理器对象调用beginTransaction()方法,实例化FragmentTransaction对象,有人称之为事务
- ④ FragmentTransaction对象【以下直接用transaction代替】,transaction的方法主要有以下几种:
- transaction.add() 向Activity中添加一个Fragment
- transaction.remove() 从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁
- transaction.replace() 使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体
- transaction.hide() 隐藏当前的Fragment,仅仅是设为不可见,并不会销毁,它只会触发onHiddenChange()方法。
- transaction.show() 显示之前隐藏的Fragment,它只会触发onHiddenChange()方法。
- detach() 会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护
- attach() 重建view视图,附加到UI上并显示
- ransatcion.commit() 提交事务
- 注意:在add/replace/hide/show以后都要commit其效果才会在屏幕上显示出来
4.4Fragment回退栈
- Fragment的回退栈是用来保存每一次Fragment事务发生的变化
- 如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。
- 在某个activity上添加fragment,如果不处理宿主activity中返回键逻辑,点击返回键,关闭了fragment同时也关闭了activity。
OrderStatesFragment fragment = new OrderStatesFragment(); FragmentManager fragmentManager = activity.getSupportFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.setCustomAnimations(R.anim.push_bottom_in, 0); //transaction.show(fragment); //暂时不用这个 transaction.add(android.R.id.content, fragment, "OrderStatesFragment"); transaction.commitAllowingStateLoss();
- 在某个activity上添加fragment,如果不处理宿主activity中返回键逻辑,点击返回键,关闭了fragment,回到宿主activity页面。
OrderStatesFragment fragment = new OrderStatesFragment(); FragmentManager fragmentManager = activity.getSupportFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.setCustomAnimations(R.anim.push_bottom_in, 0); //transaction.show(fragment); //暂时不用这个 transaction.add(android.R.id.content, fragment, "OrderStatesFragment"); transaction.commitAllowingStateLoss(); transaction.addToBackStack(null);
05.Fragment与Activity通信
- Fragment依附于Activity存在,因此与Activity之间的通信可以归纳为以下几点:
- 如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法
- 如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作
- Fragment中可以通过getActivity()得到当前绑定的Activity的实例,然后进行操作。不过不建议这样获取activity的实例,后面会提到的,容易报空指针异常问题。
06.Fragment与Activity通信的优化
- 因为要考虑Fragment的重复使用,所以必须降低Fragment与Activity的耦合,而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。
- 针对直接创建fragment,一般可以这样写
QrCodeLoginFragment fragment = new QrCodeLoginFragment(); Bundle bundle = new Bundle(); bundle.putString("unique_id", content); fragment.setArguments(bundle); FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.add(android.R.id.content, fragment, "qrCodeLoginFragment"); transaction.commit(); transaction.addToBackStack(null);
- 针对有些频繁show或者hide的fragment,可以这样处理
/** * 展示页面 */ private void showPlayingFragment() { if (isPlayFragmentShow) { return; } FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.setCustomAnimations(R.anim.fragment_slide_up, 0); if (mPlayFragment == null) { mPlayFragment = PlayMusicFragment.newInstance("Main"); ft.replace(android.R.id.content, mPlayFragment); } else { ft.show(mPlayFragment); } ft.commitAllowingStateLoss(); isPlayFragmentShow = true; } /** * 隐藏页面 */ private void hidePlayingFragment() { if(mPlayFragment!=null){ FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.setCustomAnimations(0, R.anim.fragment_slide_down); ft.hide(mPlayFragment); ft.commitAllowingStateLoss(); isPlayFragmentShow = false; } }
06.Fragment旋转场景
- 在Activity的学习中都知道,当屏幕旋转时,是对屏幕上的视图进行了重新绘制。
- 因为当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建,用脚趾头都明白...横屏和竖屏显示的不一样肯定是进行了重新绘制视图的操作。所以,不断的旋转就不断绘制,这是一种很耗费内存资源的操作,那么如何来进行优化?
- 首先看看Fragment代码
public class FragmentOne extends Fragment { private static final String TAG = "FragmentOne"; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.e(TAG, "onCreateView"); View view = inflater.inflate(R.layout.fragment_one, container, false); return view; } @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); Log.e(TAG, "onCreate"); } @Override public void onDestroyView() { // TODO Auto-generated method stub super.onDestroyView(); Log.e(TAG, "onDestroyView"); } @Override public void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); Log.e(TAG, "onDestroy"); } }
- 然后你多次翻转屏幕都会打印如下log
07-20 08:18:46.651: E/FragmentOne(1633): onCreate 07-20 08:18:46.651: E/FragmentOne(1633): onCreate 07-20 08:18:46.651: E/FragmentOne(1633): onCreate 07-20 08:18:46.681: E/FragmentOne(1633): onCreateView 07-20 08:18:46.831: E/FragmentOne(1633): onCreateView 07-20 08:18:46.891: E/FragmentOne(1633): onCreateView
- 因为当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建;这样造成当旋转的时候,本身存在的Fragment会重新启动,然后当执行Activity的onCreate时,又会再次实例化一个新的Fragment,这就是出现的原因。
- 如何解决
- 通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建
- 默认的savedInstanceState会存储一些数据,包括Fragment的实例
- 简单改一下代码,判断只有在savedInstanceState==null时,才进行创建Fragment实例
public class MainActivity extends Activity { private static final String TAG = "FragmentOne"; private FragmentOne mFOne; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); Log.e(TAG, savedInstanceState+""); if(savedInstanceState == null) { mFOne = new FragmentOne(); FragmentManager fm = getFragmentManager(); FragmentTransaction tx = fm.beginTransaction(); tx.add(R.id.id_content, mFOne, "ONE"); tx.commit(); } } }
- 现在无论进行多次旋转都只会有一个Fragment实例在Activity中,现在还存在一个问题,就是重新绘制时,Fragment发生重建,原本的数据如何保持?
- 和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以。
07.Fragment使用建议
- 关于使用Fragment操作的使用建议
- 如果Fragment视图被频繁的使用,或者一会要再次使用,建议使用show/hide方法,这样可以提升响应速度和性能。
- 如果Fragment占用大量资源,使用完成后,可以使用replace方法,这样可以及时的释放资源。
- 传递参数建议
- Fragment的数据传递通过setArguments/getArguments进行,这样在Activity重启时,系统会帮你保存数据,这点和Activity很相似。
08.FragmentAdapter选择
- FragmentPageAdapter和FragmentPageStateAdapter的区别?
- FragmnetPageAdapter在每次切换页面时,只是将Fragment进行分离,适合页面较少的Fragment使用以保存一些内存,对系统内存不会多大影响
- FragmentPageStateAdapter在每次切换页面的时候,是将Fragment进行回收,适合页面较多的Fragment使用,这样就不会消耗更多的内存
- 如何使用FragmentPageStateAdapter?
public class BasePagerStateAdapter extends FragmentStatePagerAdapter { private List<?> mFragment; private List<String> mTitleList; /** * 接收首页传递的标题 */ public BasePagerStateAdapter(FragmentManager fm, List<?> mFragment, List<String> mTitleList) { super(fm); this.mFragment = mFragment; this.mTitleList = mTitleList; } @Override public Fragment getItem(int position) { return (Fragment) mFragment.get(position); } @Override public int getCount() { return mFragment==null ? 0 : mFragment.size(); } /** * 首页显示title,每日推荐等.. * 若有问题,移到对应单独页面 */ @Override public CharSequence getPageTitle(int position) { if (mTitleList != null) { return mTitleList.get(position); } else { return ""; } } }
- 如何使用FragmentPagerAdapter?
public class BasePagerAdapter extends FragmentPagerAdapter { private List<?> mFragment; private List<String> mTitleList; /** * 普通,主页使用 */ public BasePagerAdapter(FragmentManager fm, List<?> mFragment) { super(fm); this.mFragment = mFragment; } /** * 接收首页传递的标题 */ public BasePagerAdapter(FragmentManager fm, List<?> mFragment, List<String> mTitleList) { super(fm); this.mFragment = mFragment; this.mTitleList = mTitleList; } @Override public Fragment getItem(int position) { return (Fragment) mFragment.get(position); } @Override public int getCount() { return mFragment==null ? 0 : mFragment.size(); } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); } /** * 首页显示title,每日推荐等.. * 若有问题,移到对应单独页面 */ @Override public CharSequence getPageTitle(int position) { if (mTitleList != null) { return mTitleList.get(position); } else { return ""; } } public void addFragmentList(List<?> fragment) { this.mFragment.clear(); this.mFragment = null; this.mFragment = fragment; notifyDataSetChanged(); } } //具体使用 FragmentManager supportFragmentManager = getSupportFragmentManager(); BasePagerAdapter myAdapter = new BasePagerAdapter(supportFragmentManager, mFragments, mTitleList); vpContent.setAdapter(myAdapter); tabLayout.setupWithViewPager(vpContent);
关于Fragment的具体实现和应用,可移步fragment具体实现及应用。