2013年6月27日星期四

setContentView


Message


KeyEvent


TouchEvent


《Android内核剖析》读书笔记 第7章 理解Context

Context在我们实际开发中被广泛用到,比如 startActivity(…)/ getResources()/ getSharedPreferences(…)/ getSystemService(.)等等;书中把Context理解为一个应用场景,一个Activity实例就是一个应用场景,一个Service实例也是一种应用场景,只是他木有前台界面而已;与Context相关类的继承关系如下:
  1. Context只是一个抽象类,具体的逻辑实现全部都由ContextImpl完成;ContextImpl实例的创建几乎都是在ActivityThread中完成的,比如 handleBindApplication(…)/ createBaseContextForActivity(…)/ handleCreateService(…)分别对应加载APK应用、加载Activity、加载Service;
  2. ContextWrapper只是一个包装类,同样木有做任何具体的事情,他只是简单的将请求转发给ContextImpl实例完成具体的逻辑执行;
  3. ContextThemeWrapper内部新增了对主题Theme相关的支持,其主题可以在AndroidManifest.xml中通过android:theme标签为Application、Activity元素指定;
  4. Activity对应的前台界面,所以需要使用到主题,而后台的Service没有界面,不需要主题,所以直接继承ContextWrapper;
  5. Application对象是在应用程序第一次启动时最先创建的对象,每个应用有且只有一个Application对象,代表的是一个广义的应用场景,他也与界面无关,所以也直接继承ContextWrapper;开发者可以继承该类实现更多的业务逻辑,比如加入随系统启动时需要加载的资源或服务等;
系统中一共有多少个Context呢?
从上面的描述就可以看出啦,Context个数 = 1个Application + N个Activity + N个Service;

2013年6月25日星期二

《Android内核剖析》读书笔记 第13章 View【触摸消息派发】

相比按键消息,触摸消息也是由ViewRootImpl.WindowInputEventReceiver实例负责接收,然后判断消息类型之后执行不同的方法,对于触摸消息就是执行 deliverPointerEvent(.)方法;不同点主要以下几点:
  1. 触摸消息由消息获取模块InputManagerService直接派发给应用程序,而无需经过Wms内部的预处理,最新的版本中仅仅会对当屏幕关闭时执行 interceptMotionBeforeQueueingWhenScreenOff(.)
  2. 触摸消息在处理时,需要根据触摸点坐标计算该消息应该派发给哪个具体的View/ViewGroup,而在按键消息处理中却不存在该计算过程;
  3. 没有类似于“系统按键”之类的“系统触摸”,应用程序可以完全控制触摸行为;
  4. 子视图优先于父视图进行消息处理,这与按键消息的处理机制完全相反;
屏幕坐标/视图坐标/布局坐标三者间的关系
  1. 屏幕坐标:以屏幕左上方为(0,0)的坐标体系,X/Y轴的最大值即为物理屏幕分辨率的宽和高;
    触摸消息中MotionEvent.getX/getY取到的就是屏幕坐标值;
  2. 视图坐标:视图坐标是完全由视图内容的宽高决定的坐标体系,理论上他是没有边界的,不受物理屏幕大小限制;
    比如1000行的文本限定宽度为100px,每行高度为5px,那对应的X/Y最大坐标值为(100,5000);
  3. 布局坐标:子视图相对于父视图而言的相对屏幕坐标,以父视图的左上角为(0,0),而不关心父视图到底位于屏幕何处,X/Y最大坐标值为父视图的width/height;对于子视图而言,若内容过多,超过父视图分配的区域大小将由部分内容不能显示,此时就会出现滚动条,视图坐标=布局坐标+mScrollX/mScrollY;
    View体系下的getX/getY/getTop/getBottom/getLeft/getRight都是指的布局坐标;
    子视图的屏幕坐标=子视图的布局坐标+父视图的屏幕坐标;对于视图坐标和布局坐标两者之间可以进行转换,具体可以参见下图的事例,注意:空白区域不计尺寸,只是为了更明确的演示而已;
    alt
触摸消息总体派发过程
下面就介绍下ViewRootImpl接收到具体消息执行deliverPointerEvent(.)方法的具体过程:
  1. 进行物理像素到逻辑像素的转换;只有当物理屏幕像素与操作系统中识别的屏幕像素不一致时才需要,一般不需要这步;
  2. 如果是DOWN事件,调用ensureTouchMode(true)方法设置为触摸模式,这会引起相关视图状态的变化,详见后续说明;
  3. 将屏幕坐标转换成视图坐标,因为后续需要根据触摸点的坐标来确定到底由哪个视图来处理消息;
  4. 调用mView.dispatchTouchEvent()将消息派发给根视图,这里的mView实例为PhoneWindow.DecorView;
    1. 判断是否存在Window.Callback回调对象,其实就是Activity实例,若不存在,则直接调用DecorView.super.dispatchTouchEvent(ev),实际执行到的是ViewGroup.dispatchTouchEvent(.),注意:ViewGroup完全重载了该方法,未调用super方法;
      1. 执行 onInterceptTouchEvent(.);对当前事件进行拦截返回是否当前ViewGroup需要自己处理该消息,用来控制事件的传递方向,开发者可以通过调用 requestDisallowInterceptTouchEvent(.)设置是否允许做消息拦截;
      2. 若不需要,则遍历所有子视图,通过isTransformedTouchPointInView(…)判断当前触摸点是否在子视图以内,找到可以处理该消息的子视图,然后执行 dispatchTransformedTouchEvent(…),对坐标进行相应转换之后调用 child.dispatchTouchEvent(.);对于子视图child而言,若是ViewGroup实例,则循环之前的逻辑;若不是,则执行View.dispatchTouchEvent(.);
        1. 首先判断当前View是否有设置过View.OnTouchListener接口Listener,若有,则执行Listener实例的 onTouch(.)方法;
          注意:该Listener只对非ViewGroup子类有效,因为ViewGroup完全重载了dispatchTouchEvent方法,没有机会调用到该Listener的方法;
        2. 若没有,则直接执行onTouchEvent(.)方法;在基类View.onTouchEvent(.)方法中主要是完成了对tap、click、longClick三种点击事件的处理,将其转换为对 OnClickListener/ OnLongClickListener接口的回调,开发者只需要实现对应的Listener接口即可;其对应的事件处理模型如下图:
          alt
          注意:tap其实是没有单独的监听Listener接口的,他也并不会改变视图的状态,但会引起视图获得焦点,所以我们的一些selector中对pressed状态设置的UI效果对于tap而言是没有作用的;因为tap的时间间隔只有180ms,所以要想体验具体的UI效果,需要很快的轻触才行;
      3. 若需要自己处理,则直接执行dispatchTransformedTouchEvent(…);
    2. 若存在Activity回调实例,则执行Activity.dispatchTouchEvent()方法;
      1. 如果是DOWN消息,调用onUserInteraction();可以在消息被处理之前做点什么,该方法为按键和触摸消息通用,若重载了对两者都起效;
      2. 执行PhoneWindow.superDispatchTouchEvent();其实就是转交给DecorView.super.dispatchTouchEvent(.)处理,这点与没有Activity回调实例是一致的,即无论如何都是先由View自身先处理消息,然后才是Activity处理;
      3. 若View系统仍未处理消息,则调用Activity.onTouchEvent(.),开发者可以重载该方法实现自己的处理逻辑;
View/ViewGroup中dispatchTouchEvent/onInterceptTouchEvent/onTouchEvent的具体逻辑关系
  1. 在多层嵌套的View系统中,触摸事件由顶层向底层传递,在ViewGroup中对dispatchTouchEvent(.)进行递归调用,子视图优先父视图进行消息处理,整体一般呈现U型递归,参考下图所示,其中LayoutView1中包括子视图LayoutView2,LayoutView2中包括子视图TextView;
  2. dispatchTouchEvent为事件处理的源头,View/ViewGroup两者中的具体处理逻辑完全不一样,详见上面的具体内容;
    onTouchEvent是为开发者开放的回调方法,用来处理具体的消息,当然在默认实现中系统进一步抽取了很多的Listener回调接口供开发者使用;
    onInterceptTouchEvent是ViewGroup特有,在dispatchTouchEvent中被调用,让ViewGroup有机会决定是自己处理触摸消息,还是传递给子视图进行处理;
  3. 触摸事件都以DOWN事件开始,中间会经过多次的MOVE,最后以UP结束;
  4. 若ViewGroup.onInterceptTouchEvent()对DOWN事件处理返回false,则表示该ViewGroup不拦截该事件,交由子视图处理;那么后续的move, up等事件将继续会先传递给该ViewGroup,之后再传递给子视图处理。
    注意:若子视图未处理该DOWN事件,然后当前ViewGroup.onTouchEvent处理了该事件,那之后的MOVE/UP事件将会跳过ViewGroup.onInterceptTouchEvent()而直接执行ViewGroup.onTouchEvent;
  5. 若ViewGroup.onInterceptTouchEvent()对DOWN事件处理返回true,那么后续的MOVE/UP事件将不再传递给onInterceptTouchEvent(),而是直接传递给该ViewGroup的onTouchEvent()处理;
    注意,这种情况下子视图View将接收不到任何事件,因为被父视图拦截了。
  6. 若子视图的onTouchEvent()返回了false,那么当前事件将被传递至父视图的onTouchEvent()处理,其后续事件将不会再传递至该子视图。
    注意:这里指的是对DOWN事件处理的情况,若是其他事件,即便子视图的onTouchEvent()返回了false,该事件也不会传递至父视图;
  7. 若子视图的onTouchEvent()返回了true,那么对于当前事件父视图的onTouchEvent()将不会被触发,其后续事件将继续由该子视图的onTouchEvent()处理,并且若当前子视图仍是ViewGroup,那onInterceptTouchEvent()也将被跳过不会执行。
  8. 若对于DOWN事件最后没有任何View.onTouchEvent()返回true,那后续的move、up事件将会被自动丢弃,不会进行任何传递;
  9. 若ViewGroup.onInterceptTouchEvent()对DOWN事件返回false,但却对接下来的MOVE事件返回true,那此时系统将对子视图发送一个CANCEL事件,当前MOVE事件的ViewGroup.onTouchEvent()不会被执行,即当前的MOVE事件被丢弃了,但接下去新的MOVE/UP消息将直接进入ViewGroup.onTouchEvent()执行;
接下来针对以上描述的情况给出一些案例说明,简单的情况参见http://blog.csdn.net/ddna/article/details/5473293
但要注意:目前网络上大部分对触摸事件传递的描述其实都是不够准确的,大家可以结合以下的事例自己多琢磨琢磨;
因为触摸消息是一个事件系列,包括一次DOWN事件、多次MOVE事件、一次UP事件;对于每次事件都会涉及到onInterceptTouchEvent/onTouchEvent的执行,而每次执行你都可以根据需要返回true/false,这导致的组合情况就非常多了,但原则还是基于以上提到的这几条,下面来看看以下事例:
  1. 执行条件如下:
    LayoutView1.onIntercept始终返回false;
    LayoutView2.onIntercept始终返回true;
    LayoutView1.onTouchEvent始终返回true;
    LayoutView2.onTouchEvent始终返回false;
    MyTextView.onTouchEvent始终返回true;
    执行结果如下:
    06-25 16:32:41.779: D/LayoutView1(25253): onInterceptTouchEvent action:ACTION_DOWN06-25 16:32:41.779: D/LayoutView2(25253): onInterceptTouchEvent action:ACTION_DOWN
    06-25 16:32:41.784: D/LayoutView2(25253): onTouchEvent action:ACTION_DOWN
    06-25 16:32:41.784: D/LayoutView1(25253): onTouchEvent action:ACTION_DOWN
    06-25 16:32:41.789: D/LayoutView1(25253): onTouchEvent action:ACTION_MOVE
    06-25 16:32:41.804: D/LayoutView1(25253): onTouchEvent action:ACTION_MOVE
    06-25 16:32:41.974: D/LayoutView1(25253): onTouchEvent action:ACTION_MOVE
    06-25 16:32:41.979: D/LayoutView1(25253): onTouchEvent action:ACTION_UP
    解读如下:
    对于DOWN事件,LayoutView1.onIntercept执行但未拦截,LayoutView2.onIntercept执行并拦截,即可转交给LayoutView2.onTouchEvent处理,但由于返回false,所以事件交由父视图LayoutView1.onTouchEvent处理,并成功消耗返回true;
    对于后续MOVE/UP事件,直接由LayoutView1.onTouchEvent处理,LayoutView1/2的onIntercept均被略过了;
  2. 执行条件如下:
    LayoutView1.onIntercept始终返回false;
    LayoutView2.onIntercept对DOWN事件返回false,但对MOVE事件返回true;
    LayoutView1.onTouchEvent始终返回true;
    LayoutView2.onTouchEvent始终返回false;
    MyTextView.onTouchEvent始终返回false;
    执行结果如下:
    06-25 17:04:26.764: D/LayoutView1(21298): onInterceptTouchEvent action:ACTION_DOWN06-25 17:04:26.764: D/LayoutView2(21298): onInterceptTouchEvent action:ACTION_DOWN
    06-25 17:04:26.764: D/MyTextView(21298): onTouchEvent action:ACTION_DOWN
    06-25 17:04:26.764: D/LayoutView2(21298): onTouchEvent action:ACTION_DOWN
    06-25 17:04:26.764: D/LayoutView1(21298): onTouchEvent action:ACTION_DOWN
    06-25 17:04:26.804: D/LayoutView1(21298): onTouchEvent action:ACTION_MOVE
    06-25 17:04:26.819: D/LayoutView1(21298): onTouchEvent action:ACTION_MOVE
    06-25 17:04:27.059: D/LayoutView1(21298): onTouchEvent action:ACTION_MOVE
    06-25 17:04:27.064: D/LayoutView1(21298): onTouchEvent action:ACTION_UP
    解读如下:
    对于DOWN事件,LayoutView1.onIntercept执行但未拦截,LayoutView2.onIntercept也未拦截,交由MyTextView.onTouchEvent处理,但由于返回false,所以事件交由父视图LayoutView2.onTouchEvent执行,但可惜也返回false,继续交由LayoutView1.onTouchEvent执行,成功消耗消息返回true;
    对于后续MOVE/UP事件,直接由LayoutView1.onTouchEvent处理,LayoutView1/2的onIntercept均被略过了;
  3. 执行条件如下:
    LayoutView1.onIntercept始终返回false;
    LayoutView2.onIntercept对DOWN事件返回false,但对MOVE事件返回true;
    LayoutView1.onTouchEvent始终返回true;
    LayoutView2.onTouchEvent始终返回true;
    MyTextView.onTouchEvent始终返回true;
    执行结果如下:
    06-25 15:56:09.219: D/LayoutView1(28368): onInterceptTouchEvent action:ACTION_DOWN06-25 15:56:09.219: D/LayoutView2(28368): onInterceptTouchEvent action:ACTION_DOWN
    06-25 15:56:09.219: D/MyTextView(28368): onTouchEvent action:ACTION_DOWN
    06-25 15:56:09.254: D/LayoutView1(28368): onInterceptTouchEvent action:ACTION_MOVE
    06-25 15:56:09.254: D/LayoutView2(28368): onInterceptTouchEvent action:ACTION_MOVE
    06-25 15:56:09.254: D/MyTextView(28368): onTouchEvent action:ACTION_CANCEL
    06-25 15:56:09.274: D/LayoutView1(28368): onInterceptTouchEvent action:ACTION_MOVE
    06-25 15:56:09.274: D/LayoutView2(28368): onTouchEvent action:ACTION_MOVE
    06-25 15:56:09.389: D/LayoutView1(28368): onInterceptTouchEvent action:ACTION_MOVE
    06-25 15:56:09.389: D/LayoutView2(28368): onTouchEvent action:ACTION_MOVE
    06-25 15:56:09.389: D/LayoutView1(28368): onInterceptTouchEvent action:ACTION_UP
    06-25 15:56:09.389: D/LayoutView2(28368): onTouchEvent action:ACTION_UP
    解读如下:
    对于DOWN事件,LayoutView1/2均没有进行拦截,直接交由MyTextView处理;
    对于首次MOVE事件,LayoutView1执行了onInterceptTouchEvent方法但未拦截,LayoutView2执行onInterceptTouchEvent方法并进行了拦截,此次MOVE事件并未直接交由LayoutView2.onTouchEvent处理,而是向MyTextView发送了一次CANCEL事件,并执行MyTextView.onTouchEvent方法;
    对于后续MOVE/UP事件,LayoutView1执行了onInterceptTouchEvent方法仍未拦截,之后便直接进入LayoutView2.onTouchEvent处理了,一直到UP事件结束都不会执行LayoutView2.onInterceptTouchEvent方法了;
  4. 执行条件如下:
    LayoutView1.onIntercept始终返回false;
    LayoutView2.onIntercept对DOWN事件返回false,但对MOVE事件返回true;
    LayoutView1.onTouchEvent始终返回true;
    LayoutView2.onTouchEvent始终返回false;上个试验这里是返回true;
    MyTextView.onTouchEvent始终返回true;
    执行结果如下:
    06-25 17:18:48.914: D/LayoutView1(1819): onInterceptTouchEvent action:ACTION_DOWN06-25 17:18:48.914: D/LayoutView2(1819): onInterceptTouchEvent action:ACTION_DOWN
    06-25 17:18:48.914: D/MyTextView(1819): onTouchEvent action:ACTION_DOWN
    06-25 17:18:48.954: D/LayoutView1(1819): onInterceptTouchEvent action:ACTION_MOVE
    06-25 17:18:48.954: D/LayoutView2(1819): onInterceptTouchEvent action:ACTION_MOVE
    06-25 17:18:48.954: D/MyTextView(1819): onTouchEvent action:ACTION_CANCEL
    06-25 17:18:48.964: D/LayoutView1(1819): onInterceptTouchEvent action:ACTION_MOVE
    06-25 17:18:48.964: D/LayoutView2(1819): onTouchEvent action:ACTION_MOVE
    06-25 17:18:49.139: D/LayoutView1(1819): onInterceptTouchEvent action:ACTION_MOVE
    06-25 17:18:49.139: D/LayoutView2(1819): onTouchEvent action:ACTION_MOVE
    06-25 17:18:49.139: D/LayoutView1(1819): onInterceptTouchEvent action:ACTION_UP
    06-25 17:18:49.139: D/LayoutView2(1819): onTouchEvent action:ACTION_UP
    解读如下:
    对于DOWN事件,LayoutView1/2均没有进行拦截,直接交由MyTextView处理;
    对于首次MOVE事件,LayoutView1执行了onInterceptTouchEvent方法但未拦截,LayoutView2执行onInterceptTouchEvent方法并进行了拦截,此次MOVE事件并未直接交由LayoutView2.onTouchEvent处理,而是向MyTextView发送了一次CANCEL事件,并执行MyTextView.onTouchEvent方法;
    对于后续MOVE/UP事件,LayoutView1执行了onInterceptTouchEvent方法仍未拦截,之后便直接进入LayoutView2.onTouchEvent处理了,一直到UP事件结束都不会执行LayoutView2.onInterceptTouchEvent方法了;
    从执行结果来看和上个案例是完全一样,为什么LayoutView2.onTouchEvent返回false,该消息却未被交由父视图执行LayoutView1.onTouchEvent呢?原因是此时事件已经不是DOWN事件了,只有在触摸的初始DOWN事件中onTouchEvent才会向父视图传递;
  5. 执行条件如下:
    LayoutView1.onIntercept始终返回false;
    LayoutView2.onIntercept始终返回false;
    LayoutView1.onTouchEvent始终返回true;
    LayoutView2.onTouchEvent始终返回false;
    MyTextView.onTouchEvent对DOWN/UP事件返回true,但对MOVE事件返回false;
    执行结果如下:
    06-25 18:05:21.029: D/LayoutView1(9095): onInterceptTouchEvent action:ACTION_DOWN06-25 18:05:21.029: D/LayoutView2(9095): onInterceptTouchEvent action:ACTION_DOWN
    06-25 18:05:21.029: D/MyTextView(9095): onTouchEvent action:ACTION_DOWN
    06-25 18:05:21.039: D/LayoutView1(9095): onInterceptTouchEvent action:ACTION_MOVE
    06-25 18:05:21.039: D/LayoutView2(9095): onInterceptTouchEvent action:ACTION_MOVE
    06-25 18:05:21.039: D/MyTextView(9095): onTouchEvent action:ACTION_MOVE
    06-25 18:05:21.224: D/LayoutView1(9095): onInterceptTouchEvent action:ACTION_MOVE
    06-25 18:05:21.224: D/LayoutView2(9095): onInterceptTouchEvent action:ACTION_MOVE
    06-25 18:05:21.224: D/MyTextView(9095): onTouchEvent action:ACTION_MOVE
    06-25 18:05:21.239: D/LayoutView1(9095): onInterceptTouchEvent action:ACTION_UP
    06-25 18:05:21.239: D/LayoutView2(9095): onInterceptTouchEvent action:ACTION_UP
    06-25 18:05:21.239: D/MyTextView(9095): onTouchEvent action:ACTION_UP
    解读如下:
    对于DOWN/MOVE/UP事件,LayoutView1/2均执行了onInterceptTouchEvent方法但未拦截,都交由MyTextView.onTouchEvent处理;即便对DOWN事件处理返回false,该MOVE事件仍未交由父视图LayoutView2.onTouchEvent处理;
    该实例和上面的实例一样,只有为了说明:只有在触摸的初始DOWN事件中onTouchEvent才会向父视图传递;

Android ontouch事件传递机制图解

目前网上对ontouch事件传递机制的文章很多,但没有全面的说明,也缺少一目了然的图解,因此本文将用图形的方式来解释。
     在android中控件分2类,一类是控件中还可以包含其他子控件,这类控件继承了viewgroup,如:Listviewgallerygridview,这类控件事件会传递 ,另外一类不能包含子控件,如textview,这类控件事件不会传递
     Touch的状态分为4, ACTION_DOWN, ACTION_MOVE (表示为移动手势),ACTION_UP(表示为离开屏幕),ACTION_CANCEL(表示取消手势,不会由用户产生,而是由程序产生的)

ViewGroup类的控件有3个重要方法:
1.  dispatchTouchEvent:用于分发TouchEvent, TouchEvent最先到达最顶层 view  dispatchTouchEvent ,然后由  dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则交给这个viewonTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个 view  interceptTouchEvent 方法来决定是否要拦截这个事件
2.  onInterceptTouchEvent(),用于拦截touchEvent,改变事件的传递方向,它的返回值是一个布尔值,决定了Touch事件是否要向它包含的子View继续传递,这个方法是从父View向子View传递。
3.  onTouchEvent():用于接收事件并处理,它的返回值也是一个布尔值,决定了事件及后续事件是否继续向上传递,这个方法是从子View向父View传递

图解:
alt

2013年5月6日星期一

[viewpager]FragmentPagerAdapter和FragmentStatePagerAdapter

1.FragmentPagerAdapter


 * Implementation of {@link android.support.v4.view.PagerAdapter} that
 * represents each page as a {@link Fragment} that is persistently
 * kept in the fragment manager as long as the user can return to the page.
 *
 * <p>This version of the pager is best for use when there are a handful of
 * typically more static fragments to be paged through, such as a set of tabs.
 * The fragment of each page the user visits will be kept in memory, though its
 * view hierarchy may be destroyed when not visible.  This can result in using
 * a significant amount of memory since fragment instances can hold on to an
 * arbitrary amount of state.

每个页面会长久的保存,适用于少量偏静态的fragment,虽然他们的视图会被销毁,但是每个页面保存在内存中。因为destroyItem的时候只是detach了(Detach the given fragment from the UI. This is the same state as when it is put on the back stack: the fragment is removed from the UI, however its state is still being actively managed by the fragment manager. When going into this state its view hierarchy is destroyed.

     public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        final long itemId = getItemId(position);

        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }

    public void destroyItem(ViewGroup container, int position, Object object) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        mCurTransaction.detach((Fragment)object);
    }


    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
        }
    }

2.FragmentStatePagerAdapter


* Implementation of {@link android.support.v4.view.PagerAdapter} that
 * uses a {@link Fragment} to manage each page. This class also handles
 * saving and restoring of fragment's state.
 *
 * <p>This version of the pager is more useful when there are a large number
 * of pages, working more like a list view.  When pages are not visible to
 * the user, their entire fragment may be destroyed, only keeping the saved
 * state of that fragment.  This allows the pager to hold on to much less
 * memory associated with each visited page as compared to
 * {@link FragmentPagerAdapter} at the cost of potentially more overhead when
 * switching between pages.


    public Object instantiateItem(ViewGroup container, int position) {
        // If we already have this item instantiated, there is nothing
        // to do.  This can happen when we are restoring the entire pager
        // from its saved state, where the fragment manager has already
        // taken care of restoring the fragments we previously had instantiated.
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                return f;
            }
        }

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        Fragment fragment = getItem(position);
        if (mSavedState.size() > position) {
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }
        while (mFragments.size() <= position) {
            mFragments.add(null);
        }
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        mFragments.set(position, fragment);
        mCurTransaction.add(container.getId(), fragment);

        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment)object;

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
        mFragments.set(position, null);
        mCurTransaction.remove(fragment);
    }


对比:
1,后者能保存状态,包括viewpager的adapter的状态(各个fragment的状态)和当前位置的状态,因为destoryItem的时候还是保存了fragment的状态了,在instantiateItem时恢复了
2,后者不在mFragmentManager中保存fragment,在destoryItem的时候直接remove掉fragment,而前者只是detach了,detach的话还是保存了一定的信息
3,所以在tab场景中适合用前者,能快速的重构出,而在大量的pager中适合用后者。
4,viewpager的mOffscreenPageLimit决定了viewpager中左右需要保存的item项目个数,即决定了destroyItem和instantiateItem的时机!



[viewpager]探秘

1,ViewPager是什么?

    ViewPager extends ViewGroup,
    包含:
    private PagerAdapter mAdapter;
    private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();

      static class ItemInfo {
          Object object;
          int position;
          boolean scrolling;
          float widthFactor;
          float offset;
      }
   private Scroller mScroller;

2,mAdapter.instantiateItem和mAdapter.destroyItem在哪里调用?



   ItemInfo addNewItem(int position, int index) {
        ItemInfo ii = new ItemInfo();
        ii.position = position;
        ii.object = mAdapter.instantiateItem(this, position);
        ii.widthFactor = mAdapter.getPageWidth(position);
        if (index < 0 || index >= mItems.size()) {
            mItems.add(ii);
        } else {
            mItems.add(index, ii);
        }
        return ii;
    }

    //根据mOffscreenPageLimit添加和删除相应的item!!!populate的调用位置主要有:

  1. onTouchEvent: MotionEvent.ACTION_UP:
  2. setCurrentItem

    void populate(int newCurrentItem) {
        ....

        mAdapter.startUpdate(this);

        final int pageLimit = mOffscreenPageLimit;
        final int startPos = Math.max(0, mCurItem - pageLimit);
        final int N = mAdapter.getCount();
        final int endPos = Math.min(N-1, mCurItem + pageLimit);

        ....

        if (curItem == null && N > 0) {
            curItem = addNewItem(mCurItem, curIndex);
        }

        // Fill 3x the available width or up to the number of offscreen
        // pages requested to either side, whichever is larger.
        // If we have no current item we have no work to do.
        if (curItem != null) {
            float extraWidthLeft = 0.f;
            int itemIndex = curIndex - 1;
            ItemInfo ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
            final float leftWidthNeeded = 2.f - curItem.widthFactor;
            for (int pos = mCurItem - 1; pos >= 0; pos--) {
                if (extraWidthLeft >= leftWidthNeeded && pos < startPos) {
                    if (ii == null) {
                        break;
                    }
                    if (pos == ii.position && !ii.scrolling) {
                        mItems.remove(itemIndex);
                        mAdapter.destroyItem(this, pos, ii.object);
                        itemIndex--;
                        curIndex--;
                        ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                    }
                } else if (ii != null && pos == ii.position) {
                    extraWidthLeft += ii.widthFactor;
                    itemIndex--;
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                } else {
                    ii = addNewItem(pos, itemIndex + 1);
                    extraWidthLeft += ii.widthFactor;
                    curIndex++;
                    ii = itemIndex >= 0 ? mItems.get(itemIndex) : null;
                }
            }

            float extraWidthRight = curItem.widthFactor;
            itemIndex = curIndex + 1;
            if (extraWidthRight < 2.f) {
                ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                for (int pos = mCurItem + 1; pos < N; pos++) {
                    if (extraWidthRight >= 2.f && pos > endPos) {
                        if (ii == null) {
                            break;
                        }
                        if (pos == ii.position && !ii.scrolling) {
                            mItems.remove(itemIndex);
                            mAdapter.destroyItem(this, pos, ii.object);
                            ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                        }
                    } else if (ii != null && pos == ii.position) {
                        extraWidthRight += ii.widthFactor;
                        itemIndex++;
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                    } else {
                        ii = addNewItem(pos, itemIndex);
                        itemIndex++;
                        extraWidthRight += ii.widthFactor;
                        ii = itemIndex < mItems.size() ? mItems.get(itemIndex) : null;
                    }
                }
            }

            calculatePageOffsets(curItem, curIndex, oldCurInfo);
        }

        ....

        mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);

        mAdapter.finishUpdate(this);

        ....
    }



   public void setAdapter(PagerAdapter adapter) {
        if (mAdapter != null) {
            mAdapter.unregisterDataSetObserver(mObserver);
            mAdapter.startUpdate(this);
            for (int i = 0; i < mItems.size(); i++) {
                final ItemInfo ii = mItems.get(i);
                mAdapter.destroyItem(this, ii.position, ii.object);
            }
            mAdapter.finishUpdate(this);
            mItems.clear();
            removeNonDecorViews();
            mCurItem = 0;
            scrollTo(0, 0);
        }

        final PagerAdapter oldAdapter = mAdapter;
        mAdapter = adapter;

        if (mAdapter != null) {
            if (mObserver == null) {
                mObserver = new PagerObserver();
            }
            mAdapter.registerDataSetObserver(mObserver);
            mPopulatePending = false;
            mFirstLayout = true;
            if (mRestoredCurItem >= 0) {
                mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);
                setCurrentItemInternal(mRestoredCurItem, false, true);
                mRestoredCurItem = -1;
                mRestoredAdapterState = null;
                mRestoredClassLoader = null;
            } else {
                populate();
            }
        }

        if (mAdapterChangeListener != null && oldAdapter != adapter) {
            mAdapterChangeListener.onAdapterChanged(oldAdapter, adapter);
        }
    }

3.onSaveInstanceState和onRestoreInstanceState能保存当前位置和mAdapter!

    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.position = mCurItem;
        if (mAdapter != null) {
            ss.adapterState = mAdapter.saveState();
        }
        return ss;
    }


    public void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState)state;
        super.onRestoreInstanceState(ss.getSuperState());

        if (mAdapter != null) {
            mAdapter.restoreState(ss.adapterState, ss.loader);
            setCurrentItemInternal(ss.position, false, true);
        } else {
            mRestoredCurItem = ss.position;
            mRestoredAdapterState = ss.adapterState;
            mRestoredClassLoader = ss.loader;
        }
    }



 



 

 

2013年5月2日星期四

[fragment] transaction onstop之后saveAllState了不能再提交transaction,因为无法恢复,所以需要用commitAllowingStateLoss 忽略不能正确恢复的情况


final class BackStackState implements Parcelable {
  public BackStackState(FragmentManagerImpl fm, BackStackRecord bse) {
  }
  public BackStackRecord instantiate(FragmentManagerImpl fm) {
  }
}
完成BackStackState<-->BackStackRecord互转


final class BackStackRecord extends FragmentTransaction implements
        FragmentManager.BackStackEntry, Runnable {

   op链表
   static final class Op {
        Op next;
        Op prev;
        int cmd;
        Fragment fragment;
        int enterAnim;
        int exitAnim;
        int popEnterAnim;
        int popExitAnim;
        ArrayList<Fragment> removed;
    }

    public FragmentTransaction add()
    public FragmentTransaction replace()
    public FragmentTransaction hide(Fragment fragment)
    public FragmentTransaction show(Fragment fragment)
    public FragmentTransaction detach(Fragment fragment)
    public FragmentTransaction attach(Fragment fragment)
    都是调用addOp

    void addOp(Op op) {
        if (mHead == null) {
            mHead = mTail = op;
        } else {
            op.prev = mTail;
            mTail.next = op;
            mTail = op;
        }
        op.enterAnim = mEnterAnim;
        op.exitAnim = mExitAnim;
        op.popEnterAnim = mPopEnterAnim;
        op.popExitAnim = mPopExitAnim;
        mNumOp++;
    }

 
   public void run() {
   ....
   case OP_ADD:mManager.addFragment(f, false);
   case OP_REPLACE:mManager.removeFragment then mManager.addFragment
   case OP_REMOVE:mManager.removeFragment
   ....

   if (mAddToBackStack) {
            mManager.addBackStackState(this);
        }

   }
   操作与run()相反
   public void popFromBackStack(boolean doStateMove) {
   ....
   case OP_ADD: mManager.removeFragment
   case OP_REPLACE:mManager.removeFragment then mManager.addFragment
   case OP_REMOVE:mManager.addFragment
   case OP_HIDE:mManager.showFragment
   ....
   }

   最有趣的是:

   public int commit() {
        return commitInternal(false);
    }

Caution: You can commit a transaction using commit() only prior to the activity saving its state (when the user leaves the activity). If you attempt to commit after that point, an exception will be thrown. This is because the state after the commit can be lost if the activity needs to be restored. For situations in which its okay that you lose the commit, use commitAllowingStateLoss().
    public int commitAllowingStateLoss() {
        return commitInternal(true);
    }
 
    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;
    }

 

}

----------------------------------------------
final class FragmentManagerImpl extends FragmentManager {



   public void enqueueAction(Runnable action, boolean allowStateLoss) {
        if (!allowStateLoss) {
            checkStateLoss();
        }
        synchronized (this) {
            if (mActivity == null) {
                throw new IllegalStateException("Activity has been destroyed");
            }
            if (mPendingActions == null) {
                mPendingActions = new ArrayList<Runnable>();
            }
            mPendingActions.add(action);
            if (mPendingActions.size() == 1) {
                mActivity.mHandler.removeCallbacks(mExecCommit);
                mActivity.mHandler.post(mExecCommit);
            }
        }
    }


    public boolean execPendingActions() {
        ....
        boolean didSomething = false;

        while (true) {
            int numActions;
         
            synchronized (this) {
                if (mPendingActions == null || mPendingActions.size() == 0) {
                    break;
                }
             
                numActions = mPendingActions.size();
                if (mTmpActions == null || mTmpActions.length < numActions) {
                    mTmpActions = new Runnable[numActions];
                }
                mPendingActions.toArray(mTmpActions);
                mPendingActions.clear();
                mActivity.mHandler.removeCallbacks(mExecCommit);
            }
         
            mExecutingActions = true;
            for (int i=0; i<numActions; i++) {
                mTmpActions[i].run();
                mTmpActions[i] = null;
            }
            mExecutingActions = false;
            didSomething = true;
        }
     
        if (mHavePendingDeferredStart) {
            boolean loadersRunning = false;
            for (int i=0; i<mActive.size(); i++) {
                Fragment f = mActive.get(i);
                if (f != null && f.mLoaderManager != null) {
                    loadersRunning |= f.mLoaderManager.hasRunningLoaders();
                }
            }
            if (!loadersRunning) {
                mHavePendingDeferredStart = false;
                startPendingDeferredFragments();
            }
        }
        return didSomething;
    }


  boolean popBackStackState(Handler handler, String name, int id, int flags) {
        if (mBackStack == null) {
            return false;
        }
        if (name == null && id < 0 && (flags&POP_BACK_STACK_INCLUSIVE) == 0) {
            int last = mBackStack.size()-1;
            if (last < 0) {
                return false;
            }
            final BackStackRecord bss = mBackStack.remove(last);
            bss.popFromBackStack(true);
            reportBackStackChanged();
        } else {
            int index = -1;
            if (name != null || id >= 0) {
                // If a name or ID is specified, look for that place in
                // the stack.
                index = mBackStack.size()-1;
                while (index >= 0) {
                    BackStackRecord bss = mBackStack.get(index);
                    if (name != null && name.equals(bss.getName())) {
                        break;
                    }
                    if (id >= 0 && id == bss.mIndex) {
                        break;
                    }
                    index--;
                }
                if (index < 0) {
                    return false;
                }
                if ((flags&POP_BACK_STACK_INCLUSIVE) != 0) {
                    index--;
                    // Consume all following entries that match.
                    while (index >= 0) {
                        BackStackRecord bss = mBackStack.get(index);
                        if ((name != null && name.equals(bss.getName()))
                                || (id >= 0 && id == bss.mIndex)) {
                            index--;
                            continue;
                        }
                        break;
                    }
                }
            }
            if (index == mBackStack.size()-1) {
                return false;
            }
            final ArrayList<BackStackRecord> states
                    = new ArrayList<BackStackRecord>();
            for (int i=mBackStack.size()-1; i>index; i--) {
                states.add(mBackStack.remove(i));
            }
            final int LAST = states.size()-1;
            for (int i=0; i<=LAST; i++) {
                states.get(i).popFromBackStack(i == LAST);
            }
            reportBackStackChanged();
        }
        return true;
    }


    public void popBackStack() {
        enqueueAction(new Runnable() {
            @Override public void run() {
                popBackStackState(mActivity.mHandler, null, -1, 0);
            }
        }, false);
    }


    public boolean popBackStackImmediate() {
        checkStateLoss();
        executePendingTransactions();
        return popBackStackState(mActivity.mHandler, null, -1, 0);
    }





    关于mStateSaved:
          3.0-                   3.0+
         saveAllState()   
    pause 
                                 saveAllState()设置mStateSaved = true;
    stop 设置mStateSaved = true; 设置mStateSaved = true;


    public void dispatchStop() {
        // See saveAllState() for the explanation of this.  We do this for
        // all platform versions, to keep our behavior more consistent between
        // them.
        mStateSaved = true;

        moveToState(Fragment.STOPPED, false);
    }


    Parcelable saveAllState() {
        // Make sure all pending operations have now been executed to get
        // our state update-to-date.
        execPendingActions();

        if (HONEYCOMB) {
            // As of Honeycomb, we save state after pausing.  Prior to that
            // it is before pausing.  With fragments this is an issue, since
            // there are many things you may do after pausing but before
            // stopping that change the fragment state.  For those older
            // devices, we will not at this point say that we have saved
            // the state, so we will allow them to continue doing fragment
            // transactions.  This retains the same semantics as Honeycomb,
            // though you do have the risk of losing the very most recent state
            // if the process is killed...  we'll live with that.
            mStateSaved = true;
        }
        ......
    }



}

public class FragmentActivity extends Activity {

  public void onBackPressed() {
        if (!mFragments.popBackStackImmediate()) {
            finish();
        }
    }
}





[fragment] save and restore, 为什么不用构造函数,activity恢复oncreate的时候只有argment能找回


save:
FragmentActivity.onSaveInstanceState()
->FragmentManagerImpl.saveAllState()
-->new FragmentState(f)
-->FragmentManagerImpl.saveFragmentBasicState(f)
--->Fragment.performSaveInstanceState()->Fragment.onSaveInstanceState()
--->Fragment.mInnerView.saveHierarchyState


restore:
FragmentActivity.onCreate
->FragmentManagerImpl.restoreAllState()
-->Fragment f = fs.instantiate(mActivity, mParent)


public class FragmentActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
       ......
       if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
        }
      ......
   }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
    }
}
------------------------------------------------------
final class FragmentManagerImpl extends FragmentManager {


    void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
        ....
        f.onViewCreated(f.mView, f.mSavedFragmentState);

        f.performActivityCreated(f.mSavedFragmentState);
        if (f.mView != null) {
            f.restoreViewState(f.mSavedFragmentState);
        }
        f.mSavedFragmentState = null;
        ....

   }


    Parcelable saveAllState() {
        // Make sure all pending operations have now been executed to get
        // our state update-to-date.
        execPendingActions();

        if (HONEYCOMB) {
            // As of Honeycomb, we save state after pausing.  Prior to that
            // it is before pausing.  With fragments this is an issue, since
            // there are many things you may do after pausing but before
            // stopping that change the fragment state.  For those older
            // devices, we will not at this point say that we have saved
            // the state, so we will allow them to continue doing fragment
            // transactions.  This retains the same semantics as Honeycomb,
            // though you do have the risk of losing the very most recent state
            // if the process is killed...  we'll live with that.
            mStateSaved = true;
        }

        if (mActive == null || mActive.size() <= 0) {
            return null;
        }
     
        // First collect all active fragments.
        int N = mActive.size();
        FragmentState[] active = new FragmentState[N];
        boolean haveFragments = false;
        for (int i=0; i<N; i++) {
            Fragment f = mActive.get(i);
            if (f != null) {
                if (f.mIndex < 0) {
                    throwException(new IllegalStateException(
                            "Failure saving state: active " + f
                            + " has cleared index: " + f.mIndex));
                }

                haveFragments = true;
             
                FragmentState fs = new FragmentState(f);
                active[i] = fs;
             
                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                    fs.mSavedFragmentState = saveFragmentBasicState(f);

                    if (f.mTarget != null) {
                        if (f.mTarget.mIndex < 0) {
                            throwException(new IllegalStateException(
                                    "Failure saving state: " + f
                                    + " has target not in fragment manager: " + f.mTarget));
                        }
                        if (fs.mSavedFragmentState == null) {
                            fs.mSavedFragmentState = new Bundle();
                        }
                        putFragment(fs.mSavedFragmentState,
                                FragmentManagerImpl.TARGET_STATE_TAG, f.mTarget);
                        if (f.mTargetRequestCode != 0) {
                            fs.mSavedFragmentState.putInt(
                                    FragmentManagerImpl.TARGET_REQUEST_CODE_STATE_TAG,
                                    f.mTargetRequestCode);
                        }
                    }

                } else {
                    fs.mSavedFragmentState = f.mSavedFragmentState;
                }

            }
        }
     
        if (!haveFragments) {
            return null;
        }
     
        int[] added = null;
        BackStackState[] backStack = null;
     
        // Build list of currently added fragments.
        if (mAdded != null) {
            N = mAdded.size();
            if (N > 0) {
                added = new int[N];
                for (int i=0; i<N; i++) {
                    added[i] = mAdded.get(i).mIndex;
                    if (added[i] < 0) {
                        throwException(new IllegalStateException(
                                "Failure saving state: active " + mAdded.get(i)
                                + " has cleared index: " + added[i]));
                    }
                }
            }
        }
     
        // Now save back stack.
        if (mBackStack != null) {
            N = mBackStack.size();
            if (N > 0) {
                backStack = new BackStackState[N];
                for (int i=0; i<N; i++) {
                    backStack[i] = new BackStackState(this, mBackStack.get(i));
                }
            }
        }
     
        FragmentManagerState fms = new FragmentManagerState();
        fms.mActive = active;
        fms.mAdded = added;
        fms.mBackStack = backStack;
        return fms;
    }


   void saveFragmentViewState(Fragment f) {
        if (f.mInnerView == null) {
            return;
        }
        if (mStateArray == null) {
            mStateArray = new SparseArray<Parcelable>();
        } else {
            mStateArray.clear();
        }
        f.mInnerView.saveHierarchyState(mStateArray);
        if (mStateArray.size() > 0) {
            f.mSavedViewState = mStateArray;
            mStateArray = null;
        }
    }


   Bundle saveFragmentBasicState(Fragment f) {
        Bundle result = null;

        if (mStateBundle == null) {
            mStateBundle = new Bundle();
        }

        f.performSaveInstanceState(mStateBundle);
        if (!mStateBundle.isEmpty()) {
            result = mStateBundle;
            mStateBundle = null;
        }

        if (f.mView != null) {
            saveFragmentViewState(f);
        }
        if (f.mSavedViewState != null) {
            if (result == null) {
                result = new Bundle();
            }
            result.putSparseParcelableArray(
                    FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
        }
        if (!f.mUserVisibleHint) {
            if (result == null) {
                result = new Bundle();
            }
            // Only add this if it's not the default value
            result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
        }

        return result;
    }


    @Override
    public void putFragment(Bundle bundle, String key, Fragment fragment) {
        if (fragment.mIndex < 0) {
            throwException(new IllegalStateException("Fragment " + fragment
                    + " is not currently in the FragmentManager"));
        }
        bundle.putInt(key, fragment.mIndex);
    }



  void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
        // If there is no saved state at all, then there can not be
        // any nonConfig fragments either, so that is that.
        if (state == null) return;
        FragmentManagerState fms = (FragmentManagerState)state;
        if (fms.mActive == null) return;
     
        // First re-attach any non-config instances we are retaining back
        // to their saved state, so we don't try to instantiate them again.
        if (nonConfig != null) {
            for (int i=0; i<nonConfig.size(); i++) {
                Fragment f = nonConfig.get(i);
                if (DEBUG) Log.v(TAG, "restoreAllState: re-attaching retained " + f);
                FragmentState fs = fms.mActive[f.mIndex];
                fs.mInstance = f;
                f.mSavedViewState = null;
                f.mBackStackNesting = 0;
                f.mInLayout = false;
                f.mAdded = false;
                f.mTarget = null;
                if (fs.mSavedFragmentState != null) {
                    fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
                    f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
                            FragmentManagerImpl.VIEW_STATE_TAG);
                }
            }
        }
     
        // Build the full list of active fragments, instantiating them from
        // their saved state.
        mActive = new ArrayList<Fragment>(fms.mActive.length);
        if (mAvailIndices != null) {
            mAvailIndices.clear();
        }
        for (int i=0; i<fms.mActive.length; i++) {
            FragmentState fs = fms.mActive[i];
            if (fs != null) {
                Fragment f = fs.instantiate(mActivity, mParent);
                mActive.add(f);
                // Now that the fragment is instantiated (or came from being
                // retained above), clear mInstance in case we end up re-restoring
                // from this FragmentState again.
                fs.mInstance = null;
            } else {
                mActive.add(null);
                if (mAvailIndices == null) {
                    mAvailIndices = new ArrayList<Integer>();
                }
                mAvailIndices.add(i);
            }
        }
     
        // Update the target of all retained fragments.
        if (nonConfig != null) {
            for (int i=0; i<nonConfig.size(); i++) {
                Fragment f = nonConfig.get(i);
                if (f.mTargetIndex >= 0) {
                    if (f.mTargetIndex < mActive.size()) {
                        f.mTarget = mActive.get(f.mTargetIndex);
                    } else {
                        Log.w(TAG, "Re-attaching retained fragment " + f
                                + " target no longer exists: " + f.mTargetIndex);
                        f.mTarget = null;
                    }
                }
            }
        }

        // Build the list of currently added fragments.
        if (fms.mAdded != null) {
            mAdded = new ArrayList<Fragment>(fms.mAdded.length);
            for (int i=0; i<fms.mAdded.length; i++) {
                Fragment f = mActive.get(fms.mAdded[i]);
                if (f == null) {
                    throwException(new IllegalStateException(
                            "No instantiated fragment for index #" + fms.mAdded[i]));
                }
                f.mAdded = true;
                if (mAdded.contains(f)) {
                    throw new IllegalStateException("Already added!");
                }
                mAdded.add(f);
            }
        } else {
            mAdded = null;
        }
     
        // Build the back stack.
        if (fms.mBackStack != null) {
            mBackStack = new ArrayList<BackStackRecord>(fms.mBackStack.length);
            for (int i=0; i<fms.mBackStack.length; i++) {
                BackStackRecord bse = fms.mBackStack[i].instantiate(this);
                mBackStack.add(bse);
                if (bse.mIndex >= 0) {
                    setBackStackIndex(bse.mIndex, bse);
                }
            }
        } else {
            mBackStack = null;
        }
    }
------------------------------------------------------
final class FragmentState implements Parcelable {

 
    public FragmentState(Fragment frag) {
        mClassName = frag.getClass().getName();
        mIndex = frag.mIndex;
        mFromLayout = frag.mFromLayout;
        mFragmentId = frag.mFragmentId;
        mContainerId = frag.mContainerId;
        mTag = frag.mTag;
        mRetainInstance = frag.mRetainInstance;
        mDetached = frag.mDetached;
        mArguments = frag.mArguments;
    }



    public Fragment instantiate(FragmentActivity activity, Fragment parent) {
        if (mInstance != null) {
            return mInstance;
        }
     
        if (mArguments != null) {
            mArguments.setClassLoader(activity.getClassLoader());
        }
     
        mInstance = Fragment.instantiate(activity, mClassName, mArguments);
     
        if (mSavedFragmentState != null) {
            mSavedFragmentState.setClassLoader(activity.getClassLoader());
            mInstance.mSavedFragmentState = mSavedFragmentState;
        }
        mInstance.setIndex(mIndex, parent);
        mInstance.mFromLayout = mFromLayout;
        mInstance.mRestored = true;
        mInstance.mFragmentId = mFragmentId;
        mInstance.mContainerId = mContainerId;
        mInstance.mTag = mTag;
        mInstance.mRetainInstance = mRetainInstance;
        mInstance.mDetached = mDetached;
        mInstance.mFragmentManager = activity.mFragments;

        if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
                "Instantiated fragment " + mInstance);

        return mInstance;
    }

}

------------------------------------------------------
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener {


    public static Fragment instantiate(Context context, String fname, Bundle args) {
        try {
            Class<?> clazz = sClassMap.get(fname);
            if (clazz == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = context.getClassLoader().loadClass(fname);
                sClassMap.put(fname, clazz);
            }
            Fragment f = (Fragment)clazz.newInstance();
            if (args != null) {
                args.setClassLoader(f.getClass().getClassLoader());
                f.mArguments = args;
            }
            return f;
        } catch (ClassNotFoundException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        } catch (java.lang.InstantiationException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        } catch (IllegalAccessException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        }
    }



   void performSaveInstanceState(Bundle outState) {
        onSaveInstanceState(outState);
        if (mChildFragmentManager != null) {
            Parcelable p = mChildFragmentManager.saveAllState();
            if (p != null) {
                outState.putParcelable(FragmentActivity.FRAGMENTS_TAG, p);
            }
        }
    }


   final void restoreViewState(Bundle savedInstanceState) {
        if (mSavedViewState != null) {
            mInnerView.restoreHierarchyState(mSavedViewState);
            mSavedViewState = null;
        }
        mCalled = false;
        onViewStateRestored(savedInstanceState);
        if (!mCalled) {
            throw new SuperNotCalledException("Fragment " + this
                    + " did not call through to super.onViewStateRestored()");
        }
    }