1. 程式人生 > >關於ActivityA中的FragmentA啟動ActivityB時,FragmentA中的startActivityForResult回撥的問題

關於ActivityA中的FragmentA啟動ActivityB時,FragmentA中的startActivityForResult回撥的問題

轉自:http://blog.csdn.net/buaaroid/article/details/48931883


如果我們在一個Fragment中去使用startActivityForResult時,又是一個什麼情況呢?先看流程圖:

---------------------------------------------------------------------

解決ActivityA中的FragmentA 中啟動Activity B時,ActivityA和FragmentA的請求碼的問題

時間 2014-12-05 14:06:07 
 CSDN部落格
原文   http://blog.csdn.net/fengyuzhengfan/article/details/41746863 主題 Activity

解決在Fragment中啟動Activity時傳遞請求碼的問題:

首先需要指出的是Fragment有startActivityForResult方法,而Activity中也有startActivityForResult方法:

從官方的解釋中可以看出Fragment A中的startActivityForResult方法是呼叫的是Activity 中的,而FragmentActivity A中的startActivityForResult方法是對Activity中的startActivityForResult進行了重寫。所以我們呼叫Fragment A的startActivityForResult方法,當啟動的Activity返回後,在FragmentActivity A的onActivityResult方法會被呼叫但無法獲取到正確的請求碼。如果呼叫FragmentActivity A中的startActivityForResult方法,當啟動的Activity返回後Fragment的onActivityResult方法是不會被呼叫的。

總結:

如果要在Fragment中啟動Activity並且要求返回結果,有兩種結果方案:

第一種:呼叫Fragment A的startActivityForResult方法,然後在Fragment A的onActivityResult的方法中處理返回的請求。(如果FragmentActivity A重寫了onActivityResult方法中,必須向下傳遞Result,即呼叫 super.onActivityResult(requestCode, resultCode, data) ,否則Fragment A收不到Result。)    

[java]  view plain  copy
  1. <span style="font-family:FangSong_GB2312;font-size:14px;">@Override//FragmentActivity A  
  2.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  3.         super.onActivityResult(requestCode, resultCode, data);// 傳遞給Fragment A  
  4.     if ( requestCode == TAKE_PICTURE && resultCode == RESULT_OK) {  
  5.   
  6.             }  
  7.     }</span>  


第二種:在Fragment中通過getActivity()方法獲取到Fragment所在的FragmentActivity物件,呼叫activity物件的startActivityForResult方法啟動Activity,然後在FragmentActivity的onActivityResult的方法中處理返回的請求。

2解決Fragment A 中startActivityForResult調轉Activity B時, Fragment A 中的onActivityResult先執行的問題

 原因是ActivityB:android:launchMode="singleTask"

問題出現的情況:

當Fragment A跳轉到下一個Activity B時,onActivityResult總是提前就接收到結果。發現,是呼叫的時候Activity B添加了new task標識(Activity B設定Android:launchMode="singleTask" 也同樣是啟動了新Task),等是一個新的任務,所以每當跳轉Activity B的時候Fragment A onActivityResult就會有結果。去掉new task標識就可以了。


--------------------------------------------------------------------- ---------------------------------------------------------------------
---------------------------------------------------------------------

Fragment呼叫startActivityForResult過程筆記1

在Fragment中呼叫startActivityForResult方法可以在onActivityResult方法中收到迴應。

Fragment中startActivityForResult部分原始碼:

[java]  view plain  copy
  1. <span style="font-family:FangSong_GB2312;font-size:14px;">    /** 
  2.      * Call {@link Activity#startActivityForResult(Intent, int)} on the fragment's 
  3.      * containing Activity. 
  4.      */  
  5.     public void startActivityForResult(Intent intent, int requestCode) {  
  6.         if (mActivity == null) {  
  7.             throw new IllegalStateException("Fragment " + this + " not attached to Activity");  
  8.         }  
  9.         mActivity.startActivityFromFragment(this, intent, requestCode);  
  10.     }</span>  
FragmentActivity中startActivityFromFragment部分原始碼:
[java]  view plain  copy
  1. <span style="font-family:FangSong_GB2312;font-size:14px;">    /** 
  2.      * Called by Fragment.startActivityForResult() to implement its behavior. 
  3.      */  
  4.     public void startActivityFromFragment(Fragment fragment, Intent intent,   
  5.             int requestCode) {  
  6.         if (requestCode == -1) {  
  7.             super.startActivityForResult(intent, -1);  
  8.             return;  
  9.         }  
  10.         if ((requestCode&0xffff0000) != 0) {  
  11.             throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");  
  12.         }  
  13.         super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff));  
  14.     }</span>  

注:<<操作符表示當前值數的二進位制左移16位,低位補0。對16進位制來說,就是左移4位,低位補0。

方法中判斷符合條件以後,在最後一句程式碼將Fragment在activity的index+1放入一個16進位制int值的高四位,將requestCode擷取低四位(已經限定requestCode不能大於0xffff)放入16進位制int值的低四位,組成一個requestCode引數使用。

FragmentActivity中startActivityFromFragment部分原始碼:

[java]  view plain  copy
  1. <span style="font-family:FangSong_GB2312;font-size:14px;">    /** 
  2.      * Dispatch incoming result to the correct fragment. 
  3.      */  
  4.     @Override  
  5.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  6.         mFragments.noteStateNotSaved();  
  7.         int index = requestCode>>16;  
  8.         if (index != 0) {  
  9.             index--;  
  10.             if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) {  
  11.                 Log.w(TAG, "Activity result fragment index out of range: 0x"  
  12.                         + Integer.toHexString(requestCode));  
  13.                 return;  
  14.             }  
  15.             Fragment frag = mFragments.mActive.get(index);  
  16.             if (frag == null) {  
  17.                 Log.w(TAG, "Activity result no fragment exists for index: 0x"  
  18.                         + Integer.toHexString(requestCode));  
  19.             } else {  
  20.                 frag.onActivityResult(requestCode&0xffff, resultCode, data);  
  21.             }  
  22.             return;  
  23.         }  
  24.           
  25.         super.onActivityResult(requestCode, resultCode, data);  
  26.     }</span>  
首先將回調的requestCode值16進位制右移四位取高四位值,如果不是0,表示這是Fragment呼叫startActivityForResult方法的回撥,需要回調Fragment的onActivityResult方法。如果是0,則是當前activity呼叫的startActivityForResult方法(在此方法中已經限定requestCode不能大於0xffff),直接使用super.onActivityResult(requestCode, resultCode, data);交給父類處理。

在if語句塊內,將requestCode拆分,16進位制高四位表示Fragment的index+1,低四位是Fragment的requestCode,回撥Fragment的onActivityResult方法。

流程結束。

另:當activity B設定啟動模式為singleInstance時,呼叫startActivityForResult方法會直接回調onActivityResult方法,所以此時FragmentA呼叫startActivityFroResult方法也不好使。

api說明:Note that this method should only be used with Intent protocols that are defined to return a result. In other protocols (such asIntent.ACTION_MAIN or Intent.ACTION_VIEW), you may not get the result when you expect. For example, if the activity you are launching uses the singleTask launch mode, it will not run in your task and thus you will immediately receive a cancel result.

---------------------------------------------------------------------
---------------------------------------------------------------------

淺談Android Fragment巢狀使用存在的一些BUG以及解決方法 筆記2

自從Android3.0引入了Fragment之後,使用Activity去巢狀一些Fragment的做法也變得更加流行,這確實是Fragment帶來的一些優點,比如說:Fragment可以使你能夠將activity分離成多個可重用的元件,每個都有它自己的生命週期和UI,更重要的是Fragment解決了Activity間的切換不流暢,實現了一種輕量及的切換,但是在官方提供的android.support.v4包中,Fragment還是或多或少的存在一些BUG,今天就與大家分享一下這些BUG和解決方法。

Case 1:當使用Fragment去巢狀另外一些子Fragment的時候,我們需要去管理子Fragment,這時候需要呼叫ChildFragmentManager去管理這些子Fragment,由此可能產生的Exception主要是: 
Java.lang.IllegalStateException: No activity

首先我們來分析一下Exception出現的原因: 
通過DEBUG發現,當第一次從一個Activity啟動Fragment,然後再去啟動子Fragment的時候,存在指向Activity的變數,但當退出這些Fragment之後回到Activity,然後再進入Fragment的時候,這個變數變成null,這就很容易明瞭為什麼丟擲的異常是No activity

這個Exception是由什麼原因造成的呢?如果想知道造成異常的原因,那就必須去看Fragment的相關程式碼,發現Fragment在detached之後都會被reset掉,但是它並沒有對ChildFragmentManager做reset,所以會造成ChildFragmentManager的狀態錯誤。

找到異常出現的原因後就可以很容易的去解決問題了,我們需要在Fragment被detached的時候去重置ChildFragmentManager,即:

@Override
  public void onDetach() {
    super.onDetach();
    try {
      Field childFragmentManager = Fragment.class
          .getDeclaredField("mChildFragmentManager");
      childFragmentManager.setAccessible(true);
      childFragmentManager.set(this, null);

    } catch (NoSuchFieldException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    }
  }

Case 2:當我們從一個Activity啟動了一個Fragment,然後在這個Fragment中又去例項化了一些子Fragment,在子Fragment中去有返回的啟動了另外一個Activity,即通過startActivityForResult方式去啟動,這時候造成的現象會是,子Fragment接收不到OnActivityResult,如果在子Fragment中是以getActivity.startActivityForResult方式啟動,那麼只有Activity會接收到OnActivityResult,如果是以getParentFragment.startActivityForResult方式啟動,那麼只有父Fragment能接收(此時Activity也能接收),但無論如何子Fragment接收不到OnActivityResult。

這是一個非常奇怪的現象,按理說,應該是讓子Fragment接收到OnActivityResult才對,究竟是什麼造成的呢?這是由於某位寫程式碼的員工抱怨沒發獎金,稍稍偷懶了,少寫了一部分程式碼,沒有考慮到Fragment再去巢狀Fragment的情況。

我們來看看FragmentActivity中的程式碼:

protected void onActivityResult(int requestCode, int resultCode, Intent data)
  {
    this.mFragments.noteStateNotSaved();
    int index = requestCode >> 16;
    if (index != 0) {
      index--;
      if ((this.mFragments.mActive == null) || (index < 0) || (index >= this.mFragments.mActive.size())) {
        Log.w("FragmentActivity", "Activity result fragment index out of range: 0x" + Integer.toHexString(requestCode));

        return;
      }
      Fragment frag = (Fragment)this.mFragments.mActive.get(index);
      if (frag == null) {
        Log.w("FragmentActivity", "Activity result no fragment exists for index: 0x" + Integer.toHexString(requestCode));
      }
      else {
        frag.onActivityResult(requestCode & 0xFFFF, resultCode, data);
      }
      return;
    }

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

很顯然,設計者把Fragment的下標+1左移16位來標記這個request是不是Fragment的,拿到result再解碼出下標,直接取對應的Fragment,這樣並沒有去考慮對Fragment巢狀Fragment做一個Map對映,所以出現了這種BUG。

但是如果我們需要在OnActivityResult的時候處理一些事情的話,我們可以通過在子Fragment中以getParentFragment.startActivityForResult的方式來啟動,然後在父Fragment中去接收資料,我們需要在子Fragment中提供一個方法,如:getResultData(Object obj),通過父Fragment中的子Fragment例項去呼叫這個方法,把相應的資料傳過去,然後去更新子Fragment。

以上是在使用Fragment去巢狀Fragment的時候可能會遇到的BUG,瞭解了BUG存在的原因之後,就可以完美的解決問題。


轉自:http://blog.csdn.net/buaaroid/article/details/48931883