今回は Support Package で Fragment を使う場合の注意点です。
まず、FragmentActivity で startActivityForResult() を使う場合、requestCode は 16bit 以下にしなければなりません。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/support/v4/java/android/support/v4/app/FragmentActivity.java#654
- 654
-
-
-
- 658 @Override
- 659 public void startActivityForResult(Intent intent, int requestCode) {
- 660 if (requestCode != -1 && (requestCode&0xffff0000) != 0) {
- 661 throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
- 662 }
- 663 super.startActivityForResult(intent, requestCode);
- 664 }
654 /**
655 * Modifies the standard behavior to allow results to be delivered to fragments.
656 * This imposes a restriction that requestCode be <= 0xffff.
657 */
658 @Override
659 public void startActivityForResult(Intent intent, int requestCode) {
660 if (requestCode != -1 && (requestCode&0xffff0000) != 0) {
661 throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
662 }
663 super.startActivityForResult(intent, requestCode);
664 }
このように、16bit 以上の場合は IllegalArgumentException が発行されるようになっています。
なぜこのような処理をしているかというと、requestCode を使って Activity から呼ばれた場合と、Fragment から呼ばれた場合を区別するようにしているからです。
requestCode が 0x0000ffff より小さい場合 → Activity から呼ばれた
requestCode が 0x0000ffff より大きい場合 → Fragment から呼ばれた
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/support/v4/java/android/support/v4/app/FragmentActivity.java#666
- 666
-
-
- 669 public void startActivityFromFragment(Fragment fragment, Intent intent,
- 670 int requestCode) {
- 671 if (requestCode == -1) {
- 672 super.startActivityForResult(intent, -1);
- 673 return;
- 674 }
- 675 if ((requestCode&0xffff0000) != 0) {
- 676 throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
- 677 }
- 678 super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));
- 679 }
666 /**
667 * Called by Fragment.startActivityForResult() to implement its behavior.
668 */
669 public void startActivityFromFragment(Fragment fragment, Intent intent,
670 int requestCode) {
671 if (requestCode == -1) {
672 super.startActivityForResult(intent, -1);
673 return;
674 }
675 if ((requestCode&0xffff0000) != 0) {
676 throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
677 }
678 super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));
679 }
さらに、どの Fragment から呼ばれたかも requestCode に反映されます。
ポイントは
- super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));
super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));
ですね。
FragmentActivity では、自身が持っている Fragment にそれぞれ index が振られます。それが mIndex です。
つまり、Fragment から Fragment#startActivityForResult() を呼んだ場合、最終的に Activity#startActivityForResult() に渡される requestCode は、上位16ビットがその Fragment の index、下位16ビットが Fragment#startActivityForResult() に渡した requestCode になります。
で、どこではまるかというと、Fragment#startActivityForResult() から呼んだ Intent を FragmentActivity#onActivityResult() で受ける場合です。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/support/v4/java/android/support/v4/app/FragmentActivity.java#128
- 128
-
-
- 131 @Override
- 132 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- 133 int index = requestCode>>16;
- 134 if (index != 0) {
- 135 index--;
- 136 if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {
- 137 Log.w(TAG, "Activity result fragment index out of range: 0x"
- 138 + Integer.toHexString(requestCode));
- 139 return;
- 140 }
- 141 Fragment frag = mFragments.mActive.get(index);
- 142 if (frag == null) {
- 143 Log.w(TAG, "Activity result no fragment exists for index: 0x"
- 144 + Integer.toHexString(requestCode));
- 145 }
- 146 frag.onActivityResult(requestCode&0xffff, resultCode, data);
- 147 return;
- 148 }
- 149
- 150 super.onActivityResult(requestCode, resultCode, data);
- 151 }
128 /**
129 * Dispatch incoming result to the correct fragment.
130 */
131 @Override
132 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
133 int index = requestCode>>16;
134 if (index != 0) {
135 index--;
136 if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {
137 Log.w(TAG, "Activity result fragment index out of range: 0x"
138 + Integer.toHexString(requestCode));
139 return;
140 }
141 Fragment frag = mFragments.mActive.get(index);
142 if (frag == null) {
143 Log.w(TAG, "Activity result no fragment exists for index: 0x"
144 + Integer.toHexString(requestCode));
145 }
146 frag.onActivityResult(requestCode&0xffff, resultCode, data);
147 return;
148 }
149
150 super.onActivityResult(requestCode, resultCode, data);
151 }
このように FragmentActivity#onActivityResult() では、requestCode の値を見て、それが Fragment から呼ばれたものなら、Fragment の onActivityResult() を呼んでいます。このときちゃんと requestCode の下位16ビットだけ渡しています。
Fragment#startActivityForResult() で呼んだ Intent を同じ Fragment の onActivityResult() で受け取っている場合には特に問題はないのですが、
Fragment#startActivityForResult() で呼んだ Intent を FragmentActivity の onActivityResult() で処理する場合には、渡された requestCode の下位16ビットだけを比較対象にする必要があります。
具体的に書くと、
- public class ActivityA extends FragmentActivity {
-
- public static final int REQUEST_CODE_3 = 3;
- FragmentA mFragment;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mFragment = new FragmentA();
- getSupportFragmentManager().beginTransaction().add(R.id.container, mFragment).commit();
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
-
- if(resultCode == RESULT_OK && (requestCode & 0xffff) == REQUEST_CODE_3) {
-
- }
- }
-
- class FragmentA extends Fragment {
- ...
-
- private void openNewActivity() {
- Intent intent = new Intent(getActivity(), ActivityB.class);
- startActivityForResult(intent, ActivityA.REQUEST_CODE_3);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent intent) {
- super.onActivityResult(requestCode, resultCode, intent);
-
- }
- }
- }
public class ActivityA extends FragmentActivity {
public static final int REQUEST_CODE_3 = 3;
FragmentA mFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFragment = new FragmentA();
getSupportFragmentManager().beginTransaction().add(R.id.container, mFragment).commit();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// ここでは requestCode は 3 ではないので、下位16ビットを比較
if(resultCode == RESULT_OK && (requestCode & 0xffff) == REQUEST_CODE_3) {
// do something
}
}
class FragmentA extends Fragment {
...
private void openNewActivity() {
Intent intent = new Intent(getActivity(), ActivityB.class);
startActivityForResult(intent, ActivityA.REQUEST_CODE_3);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
// ここでの requestCode は 3 = REQUEST_CODE_3
}
}
}
このように、 ActivityA の中の FragmentA から requestCode = 3 で startActivityForResult() を呼んだ場合、FragmentA の onActivityResult() で渡される requestCode は 3 ですが、ActivityA の onActivityResult() で渡される requestCode は例えば 665539 のような値になります。
なので、
Fragment から startActivityForResult() で呼んだ Intent を FragmentActivity の onActivityResult() で処理する場合は、下位16ビットだけで比較しましょう。