[关闭]
@natsumi 2017-08-23T04:32:13.000000Z 字数 12718 阅读 1690

Android四大组件 Activity (& Fragment)

Android


1. Activity

参考:Android面试题(一)——Activity的生命周期和启动模式
http://www.jianshu.com/p/e279b3137157

主要知识点包括

1.1Activity的生命周期

onCreate->onStart->onResume->Activity运行->新的Activity运行->onPause->onStop->onDestroy->Activity销毁

Activity生命周期

首先打开一个新的activity实例的时候,系统会依次调用onCreate() -> onStart() -> onResume() 然后开始running

running的时候被覆盖了(从它打开了新的activity或是被锁屏,lost focus but is still visible),系统调用onPause();

该方法执行activity暂停,通常用于提交未保存的更改到持久化数据,停止动画和其他的东西。但这个activity还是完全活着(它保持所有的状态和成员信息,并保持连接到窗口管理器)

接下来它有三条出路

如果用户返回到onStop()状态的activity(又显示在前台了),系统会调用onRestart() -> onStart() -> onResume() 然后重新running

在activity结束(调用finish())或是被系统杀死之前会调用onDestroy()方法释放所有占用的资源。

activity生命周期中三个嵌套的循环

1.1.1 Activity A调用Activity B时,A调用什么函数?

1.1.2 面试题:透明Activity的生命周期

A、B透明,C不透明。
A启动B,B启动C,C返回B,B返回A。
整个过程的依次调用了生命周期的哪些回调方法。

1.2 Activity的启动方式

四种启动模式,standard, singleTask, singleTop, singleInstance。

这里有栗子~
http://blog.csdn.net/shinay/article/details/7898492/

1.2.1 使用 manifest 文件定义启动模式

在 manifest 文件中activity声明时,利用 activity 元素的 launchMode 属性来设定 activity 与 task 的关系。

  1. <activity
  2. ......
  3. android:launchMode="standard"
  4. >
  5. .......
  6. </activity>

1.2.2 使用 Intent 标志定义启动模式

在要启动 activity 时,你可以在传给 startActivity() 的 intent 中包含相应标志,以修改 activity 与 task 的默认关系。

  1. Intent i = new Intent(this,NewActivity.class);
  2. i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  3. startActivity(i);

还有好多flag

1.2.3 onNewIntent的作用和调用时机?

调用时机:如果Activity的启动模式是:singleTop, singleTask, singleInstance,在复用这些Acitivity时就会在调用onStart方法前调用onNewIntent方法

作用:让已经创建的Activity处理新的Intent。

1.3 onSaveInstanceState和onRestoreInstanceState调用的过程和时机

作用:

在activity可能被回收之前调用,用来保存自己的状态和信息,以便回收后重建时恢复数据(在onCreate()或onRestoreInstanceState()中恢复)。

调用时机:

Activity的异常情况下(例如转动屏幕或者被系统回收)的情况下,会调用到onSaveInstanceState和onRestoreInstanceState。
按Home键或者启动新Activity仍然会单独触发onSaveInstanceState的调用。但其他情况在onPause()和onStop()状态的activity不一定会调用。

调用过程:

旧的Activity要被销毁时,由于是异常情况下的,所以除了正常调用onPause, onStop, onDestroy方法外,还会在调用onStop方法前,调用onSaveInstanceState方法。新的Activity重建时,我们就可以通过onRestoreInstanceState方法取出之前保存的数据并恢复,在onStart()和onPostCreate(Bundle)之间调用。

  • void onSaveInstanceState (Bundle outState)

这个方法在activity可能被kill之前调用,当它返回前台时我们可以恢复它的状态。例如,activity B被打开,覆盖了activity A,一些情况下为了回收资源,A会被kill,A将有机会通过这个方法保存当前的状态,当用户返回A时,UI的状态可以通过onCreate(Bundle)或者onRestoreInstanceState(Bundle)被恢复。

不要讲这个方法和Activity生命周期的回调方法(如 onPause()和onStop())混淆,生命周期的回调方法在生命周期的相应阶段是一定会被调用的,而这个方法不然。一个例子onPause()和onStop()会被调用而这个方法不会被调用:用户从activity B返回activity A,这个情况不需要对B调用onSaveInstanceState(Bundle)因为B的那个实例不会再被恢复,所以系统不会调用这个方法 一个例子onPause()会被调用而这个方法不会被调用:activity B被打开,覆盖了activity A,如果在B的生命周期结束前A没有被系统回收,则系统不会对A调用onSaveInstanceState(Bundle)

The default implementation takes care of most of the UI per-instance state for you by calling onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState(Bundle)). If you override this method to save additional information not captured by each individual view, you will likely want to call through to the default implementation, otherwise be prepared to save all of the state of each view yourself.

这个方法如果被调用,会发生在onStop()之前,和onPause()的先后顺序无法保证

  • void onRestoreInstanceState (Bundle savedInstanceState)

当activity通过之前保存的状态重新初始化的过程中,这个方法在onStart()之后被调用。大多数的实现类会直接用onCreate(Bundle)恢复状态,但有时候在所有的初始化完成之后在恢复状态更方便。或者允许子类选择是否使用这个方法的默认实现。默认实现把之前onSaveInstanceState(Bundle)冻结的所有视图状态全部恢复。

在onStart()和onPostCreate(Bundle)之间调用

2. IntentFilter

android的3个核心组件——Activity、services、广播接收器——是通过intent传递消息的。intent消息用于在运行时绑定不同的组件。在 Android 的 AndroidManifest.xml 配置文件中可以通过 intent-filter 节点为一个 Activity 指定其 Intent Filter,以便告诉系统该 Activity 可以响应什么类型的 Intent。

2.1 intent-filter 的三大属性

2.1.1 Action

一个 Intent Filter 可以包含多个 Action,Action 列表用于标示 Activity 所能接受的“动作”,它是一个用户自定义的字符串。

  1. <intent-filter >
  2. <action android:name="android.intent.action.MAIN" />
  3. <action android:name="com.scu.amazing7Action" />
  4. ……
  5. </intent-filter>

在代码中使用以下语句便可以启动该Intent 对象:

  1. Intent i=new Intent();
  2. i.setAction("com.scu.amazing7Action");

Action 列表中包含了“com.scu.amazing7Action”的 Activity 都将会匹配成功

2.1.2 URI

在 intent-filter 节点中,通过 data节点匹配外部数据,也就是通过 URI 携带外部数据给目标组件。

  1. <data android:mimeType="mimeType"
  2. android:scheme="scheme"
  3. android:host="host"
  4. android:port="port"
  5. android:path="path"/>

注意:只有data的所有的属性都匹配成功时 URI 数据匹配才会成功

2.1.3 Category

为组件定义一个 类别列表,当 Intent 中包含这个类别列表的所有项目时才会匹配成功。

  1. <intent-filter . . . >
  2. <action android:name="code android.intent.action.MAIN" />
  3. <category android:name="android.intent.category.LAUNCHER" />
  4. </intent-filter>

2.2 Activity中Intent Filter 的匹配过程

3 fragment

3.1 fragment生命周期

fragment生命周期

fragment所生存的activity生命周期直接影响着fragment的生命周期,由此针对activity的每一个生命周期回调都会引发一个fragment类似的回调。例如,当activity接收到onPause()时,这个activity之中的每个fragment都会接收到onPause()。   

Fragment有一些额外的生命周期回调方法(创建和销毁fragment界面)

当fragment被绑定到activity时调用(Activity会被传入)

  1. onAttach()

在创建fragment时系统会调用此方法。在实现代码中,你可以初始化想要在fragment中保持的那些必要组件,当fragment处于暂停或者停止状态之后可重新启用它们。

  1. onCreate()

将本身的布局构建到activity中去(fragment作为activity界面的一部分)。在第一次为fragment绘制用户界面时系统会调用此方法。为fragment绘制用户界面,这个函数必须要返回所绘出的fragment的根View。如果fragment没有用户界面可以返回空。

  1. onCreateView()

栗子~

  1. @Override
  2. public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { 
  3. // Inflate the layout for this fragment
  4. return inflater.inflate(R.layout.example_fragment, container, false);
  5. }

inflate()函数需要以下三个参数:

  • 要inflate的布局的资源ID。 
  • 被inflate的布局的父ViewGroup。
  • 一个布尔值,表明在inflate期间被infalte的布局是否应该附上ViewGroup(第二个参数container)。(在这个例子中传入的是false,因为系统已经将被inflate的布局插入到容器中(container)——传入true会在最终的布局里创建一个多余的ViewGroup。) 

当activity的onCreate()函数返回时被调用。

  1. onActivityCreated()

系统回调用该函数作为用户离开fragment的第一个预兆(尽管这并不总意味着fragment被销毁)。在当前用户会话结束之前,通常要在这里提交任何应该持久化的变化(因为用户可能不再返回)。

  1. onPause()

当与fragment关联的视图体系正被移除时被调用。

  1. onDestroyView()

当fragment正与activity解除关联时被调用。

  1. onDetach()

一旦activity处于resumed状态,则可以在activity中自由的添加或者移除fragment。因此,只有当activity处于resumed状态时,fragment的生命周期才可以独立变化。fragment会在activity离开恢复状态时,再一次被activity推入它的生命周期中。

管理fragment生命周期与管理activity生命周期很相像。像activity一样,fragment也有三种状态:

如果activity的进程被杀掉了,在activity被重新创建时,你需要恢复fragment状态。可以执行fragment的onSaveInstanceState()来保存状态(注意在fragment是在onCreate(),onCreateView(),或onActvityCreate()中进行恢复)。

在生命周期方面,activity与fragment之间一个很重要的不同,就是在各自的后台栈中是如何存储的。当activity停止时,默认情况下activity被安置在由系统管理的activity后台栈中;fragment仅当在一个事务被移除时,通过显式调用addToBackStack()请求保存的实例,该fragment才被置于由宿主activity管理的后台栈。

要创建一个fragment,必须创建一个fragment的子类。一般情况下,我们至少需要实现以下几个fragment生命周期方法:

3.2 将fragment添加到activity之中

可以通过在activity布局文件中声明fragment,用fragment标签把fragment插入到activity的布局中,或者是用应用程序源码将它添加到一个存在的ViewGroup中。但fragment并不是一个定要作为activity布局的一部分,fragment也可以为activity隐身工作。

3.2.1 在activity的布局文件里声明fragment

可以像为view一样为fragment指定布局属性。例如:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="horizontal"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"> 
  6. <fragment android:name="com.example.test.FragmentOne"
  7. android:id="@+id/fo"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent" />
  10. </LinearLayout>

fragment标签中的android:name属性指定了布局中实例化的Fragment类。

当系统创建activity布局时,它实例化了布局文件中指定的每一个fragment,并为它们调用onCreateView()函数,以获取每一个fragment的布局。系统直接在元素的位置插入fragment返回的View。

注意:每个fragment都需要一个唯一的标识,如果重启activity,系统可用来恢复fragment(并且可用来捕捉fragment的事务处理,例如移除)。为fragment提供ID有三种方法:

3.2.2 通过编码将fragment添加到已存在的ViewGroup中

在activity运行的任何时候,你都可以将fragment添加到activity布局中。

要管理activity中的fragment,可以使用FragmentManager。可以通过在activity中调用getFragmentManager()获得。使用FragmentManager 可以做如下事情,包括:

在Android中,对Fragment的事务操作都是通过FragmentTransaction来执行。操作大致可以分为两类:

调用show() & hide()方法时,Fragment的生命周期方法并不会被执行,仅仅是Fragment的View被显示或者隐藏。

执行replace()时(至少两个Fragment),会执行第二个Fragment的onAttach()方法、执行第一个Fragment的onPause()-onDetach()方法,同时containerView会detach第一个Fragment的View。

add()方法执行onAttach()-onResume()的生命周期,相对的remove()就是执行完成剩下的onPause()-onDetach()周期。

可以像下面这样从Activity中取得FragmentTransaction的实例:

  1. FragmentManager fragmentManager = getFragmentManager() 
  2. FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

可以用add()函数添加fragment,并指定要添加的fragment以及要将其插入到哪个视图(view)之中(注意commit事务):

  1. ExampleFragment fragment = new ExampleFragment();
  2. fragmentTransaction.add(R.id.fragment_container, fragment);
  3. fragmentTransaction.commit();

3.2.3 添加没有界面的fragment

也可以使用fragment为activity提供后台动作,却不呈现多余的用户界面。

想要添加没有界面的fragment ,可以使用add(Fragment, String)(为fragment提供一个唯一的字符串“tag”,而不是视图(view)ID)。这样添加了fragment,但是,因为还没有关联到activity布局中的视图(view),收不到onCreateView()的调用。所以不需要实现这个方法。

对于无界面fragment,字符串标签是唯一识别它的方法。如果之后想从activity中取到fragment,需要使用findFragmentByTag()。 

3.3 fragment事务后台栈

在调用commit()之前,可以将事务添加到fragment事务后台栈中(通过调用addToBackStatck())。这个后台栈由activity管理,并且允许用户通过按BACK键回退到前一个fragment状态。

下面的代码中一个fragment代替另一个fragment,并且将之前的fragment状态保留在后台栈中:

  1. Fragment newFragment = new ExampleFragment();
  2. FragmentTransaction transaction = getFragmentManager().beginTransaction();
  3. transaction.replace(R.id.fragment_container, newFragment);
  4. transaction.addToBackStack(null);
  5. transaction.commit();

注意:

如果添加多个变更事务(例如另一个add()或者remove())并调用addToBackStack(),那么在调用commit()之前的所有应用的变更被作为一个单独的事务添加到后台栈中,并且BACK键可以将它们一起回退。

当移除一个fragment时,如果调用了addToBackStack(),那么之后fragment会被停止,如果用户回退,它将被恢复过来。

调用commit()并不立刻执行事务,相反,而是采取预约方式,一旦activity的界面线程(主线程)准备好便可运行起来。然而,如果有必要的话,你可以从界面线程调用executePendingTransations()立即执行由commit()提交的事务。

只能在activity保存状态(当用户离开activity时)之前用commit()提交事务。如果你尝试在那时之后提交,会抛出一个异常。这是因为如果activity需要被恢复,提交后的状态会被丢失。对于这类丢失提交的情况,可使用commitAllowingStateLoss()

3.4 fagement和Activity的通信

有四种方法

  1. View listView = getActivity().findViewById(R.id.list);
  1. ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

在fragment中定义一个回调接口,并要求宿主Activity实现它。当Activity通过该接口受到回调时,可以根据需要与布局中的其他片段共享这些信息。

  1. public static class FragmentA extends ListFragment {
  2. ...
  3. // Container Activity must implement this interface
  4. public interface OnArticleSelectedListener {
  5. public void onArticleSelected(Uri articleUri);
  6. }
  7. ...
  8. @Override
  9. public void onAttach(Activity activity) {
  10. super.onAttach(activity);
  11. try {
  12. mListener = (OnArticleSelectedListener) activity;
  13. } catch (ClassCastException e) {
  14. throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
  15. }
  16. }
  17. @Override
  18. public void onListItemClick(ListView l, View v, int position, long id) {
  19. // Append the clicked item's row ID with the content provider Uri
  20. Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
  21. // Send the event and Uri to the host activity
  22. mListener.onArticleSelected(noteUri);
  23. }
  24. }
  1. public void setArguments(Bundle args)
  2. final pubilc Bundle getArguments()
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注