Fragment的onActivityResult方法

情景分析:
 Activity : PhotoActivity
 FragmentActivity: HomeActivity
 Fragment: MenuLeftFragment
 Fragment:TestFragment
 Fragment: HomeFragment
 Fragment: childFragment --> is a child of HomeFragment

这是一个有侧拉菜单的应用。进入应用加载MenuLeftFragment和TestFragment。

getSupportFragmentManager().beginTransaction()
            .replace(R.id.home_content, TestFragment).commit();
getSupportFragmentManager().beginTransaction()
            .replace(R.id.menu_Left, mMenuFragment).commit();

然后点击侧栏按钮进入HomeFragment,HomeFragment内部加载一个childFragment.

在childFragment中点击按钮,跳转到PhotoActivity,PhotoActivity返回时,childFragment获取数据。

childFragment代码如下

...
final int requestCode = 2;
...

void goPhotoActivty(){
    Intent xzIntent = new Intent(getActivity(),PhotoActivity.class);
    startActivityForResult(xzIntent, requestCode);
}

...

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    Log.v(TAG,"======  childFragment onActivityResult  ====");
}

PhotoActivity代码如下:

...
void backActivity(){
    Intent it = new Intent();
    setResult(RESULT_OK,it);
}
...

ok,问题来了,当我们调用PhotoActivity中的backActivity()方法时,并没有日志打印输出,也就是说没有触发childFragmentonActivityResult()方法。

下面我们在HomeActivityonActivityResult方法中打印日志:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        Log.v(TAG, "===== HomeActivity onActivityResult  =====");
        super.onActivityResult(requestCode, resultCode, data);
}

然后当我们调用PhotoActivity中的backActivity()方法时,会输出日志:

===== HomeActivity onActivityResult  =====

然后我们重写HomeFragmentonActivityResult方法,会发现也会被调用。 ok,通过源码分析我们会发现主要原因是:

  • 当一个childFragment想要实现onActivityResult方法时,必须要在parentFragment的onActivityResult方法内再次分发。

在FragmentActivity的中:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        mFragments.noteStateNotSaved();
        int index = requestCode>>16;
        if (index != 0) {
            index--;
            if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {
                Log.w(TAG, "Activity result fragment index out of range: 0x"
                        + Integer.toHexString(requestCode));
                return;
            }
            Fragment frag = mFragments.mActive.get(index);
            if (frag == null) {
                Log.w(TAG, "Activity result no fragment exists for index: 0x"
                        + Integer.toHexString(requestCode));
            } else {
                frag.onActivityResult(requestCode&0xffff, resultCode, data);
            }
            return;
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

我们会发现FragmentActivityonActivityResult方法被调用时首先要从活动fragment列表中获取一个Fragment对象,如果该对象存在,则调用fragment.onActivityReuslt方法。不会调用activity.onActivityResult方法。 下面我们在看Fragment.onActivityResult:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
}

在我们问题中,当调用PhotoActivity.backActivity方法时,首先HomeActivity.onActivityResult方法被调用,我们在super.方法之前打印日志会输出,在super.方法中,会获取当前活动列表中的一个fragment--HomeFragment,然后调用HomeFragment.onActivityResult方法。这是我们HomeFragment.onActivityResult方法内部的日志会输出,但是因为Fragment.onActivityResult方法内部为空,所以childFragment.onAcitivtyResult方法不会被调用。我们必须手动在HomeFragment中重新分发。

HomeFragment代码如下:

...
@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        childFragment.onActivityResult(requestCode, resultCode, data);

    }
...

ok,大功告成!

但是!!!

随着代码越来越多,问题也随之而来。突然childFragment.onActivityResult没有调用. 从头看源码:

  • childFragment中我们调用:

    void goPhotoActivty(){
      Intent xzIntent = new Intent(getActivity(),PhotoActivity.class);
      startActivityForResult(xzIntent, requestCode);
    }
    

    查看Fragment.startActivityForResult

    public void startActivityForResult(Intent intent, int requestCode) {
          if (mActivity == null) {
              throw new IllegalStateException("Fragment " + this + " not attached to Activity");
          }
          mActivity.startActivityFromFragment(this, intent, requestCode);
      }
    

    在该方法中首先判断如果当前上下文环境为空,则抛出异常,否则调用activity.startActivityFromFragment,并传递三个参数分别是:当前Fragment对象childFragment,intent,requestCode.

    下面我们继续查看activity.startActivityFromFragment的代码:

      /**
       * Called by Fragment.startActivityForResult() to implement its behavior.
       */
      public void startActivityFromFragment(Fragment fragment, Intent intent,
              int requestCode) {
          if (requestCode == -1) {
              super.startActivityForResult(intent, -1);
              return;
          }
          if ((requestCode&0xffff0000) != 0) {
              throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
          }
          super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));
      }
    

    在这里我们发现最终会调用Activity.startActivityForResult方法。并传第一个requestCode参数。因为我们传递的requestCode必须是一个小于0xffff的16位二进制数,这里把fragment.mIndex参数左移16位后加上原来的requestCode传递给Activity.startActivityForResult.接下来Activity.onActivityForResult中回被调用。

  • Activity的onActivityForResult方法会被首先调用:

      /**
       * Dispatch incoming result to the correct fragment.
       */
      @Override
      protected void onActivityResult(int requestCode, int resultCode, Intent data) {
          mFragments.noteStateNotSaved();
          int index = requestCode>>16;
          if (index != 0) {
              index--;
              if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {
                  Log.w(TAG, "Activity result fragment index out of range: 0x"
                          + Integer.toHexString(requestCode));
                  return;
              }
              Fragment frag = mFragments.mActive.get(index);
              if (frag == null) {
                  Log.w(TAG, "Activity result no fragment exists for index: 0x"
                          + Integer.toHexString(requestCode));
              } else {
                  frag.onActivityResult(requestCode&0xffff, resultCode, data);
              }
              return;
          }
    
          super.onActivityResult(requestCode, resultCode, data);
      }
    

    这上面的代码中我们会发现首先通过位运算从requestCode中取出mIndex变量。这个变量标识着当前FragmentFragmentManagerList类型的mActive变量中的下标。然后根据这个下标从当前FragmentManager中取出对应的Fragment,就是我们在调用fragment.startActivityFromFragment传递的第一个参数:Fragment。如果当前Fragment依然有效。则调用这个fragment.onActivityResult方法。反之,则调用activity.onActivityResult. 看到这里我们就明白来,原来我们调用fragment.startActivityFromFragment传递的第一个参数是childFragment.而在activity.onActivityResult中就会根据childFragment.mIndex获取一个Fragment对象,调用该对象的onActivityResult方法。至于我们为什么第一次时可以正确调用。就是一个巧合,刚好mIndex对应HomeFragment

    关于mIndex方面的内容可以查看FragmentManager中的:makeActivemakeInactive等代码。至于第二次为什么不行了,就是因为根据mIndex找不到HomeFragment无法完成分发到childFragment的回调了。

小结:

当我们在Fragment中实现onActivityResult方法时,必须判断当前Fragment是否作为另一个Fragment的child。如果是,必须使用parentFragment的startActivityForResult方法。然后在parentFragment的onActivityResult方法内部进行再次分发。

results matching ""

    No results matching ""