以下內容為原創,歡迎轉載,轉載請注明
來自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4389674.html
需求:在ActivityA跳轉到ActivityB,然后在ActivityB操作完返回數據給ActivityA。
這個很普遍的需求,一般情況是使用startActivityForResult的方式去完成。
但是當ActivityB為SingleTask時,這個方式就無效了。你會發現當你執行startActivityForResult后,onActivityResult方法馬上就會被回調。至於為什么會出現這種情況,參考這位老兄的文章就可以理解http://blog.csdn.net/sodino/article/details/22101881。
解決這種情況的方法,第一種是把ActivityA也設置為SingleTask,然后在ActivityB中startActivity(context, ActivityA.class),然后ActivityA在onNewIntent(Intent intent)方法中去獲取傳遞數據,這樣的方式不僅破壞了ActivityA的lauchMode,而且還需要ActivityB中啟動指定的ActivityA。
所以,如果能把ActivityA的當前對象(實現某個接口)傳到ActivityB中,然后ActivityB中通過接口直接回調那就解決問題了。
但是問題是怎么把當前對象傳過去,使用Intent顯然不行。
思路是維護一個StoragePool,里面可以暫存需要傳遞的數據。相當於一個暫存區,ActivityA跳轉前,把數據放入這個暫存區,獲得一個唯一標識,然后把這個唯一標識使用Intent的方式傳遞給ActivityB,然后ActivityB拿到這個唯一標識后去暫存區去取數據就好了。
暫存區StoragePool代碼如下:
1 /** 2 * Author: wangjie 3 * Email: tiantian.china.2@gmail.com 4 * Date: 3/30/15. 5 */ 6 public class StoragePool { 7 /** 8 * key -- 標識是哪一個intent的(UUID) 9 * 10 * |- key -- 存儲的對象標識(StorageKey,使用UUID唯一) 11 * value --| 12 * |- value -- 存儲的內容 13 */ 14 private static ConcurrentHashMap<String, HashMap<StorageKey, WeakReference<Object>>> storageMapper = new ConcurrentHashMap<>(); 15 16 private StoragePool() { 17 } 18 19 public static void storage(String tagUUID, StorageKey key, Object content) { 20 if (null == key || null == content) { 21 return; 22 } 23 HashMap<StorageKey, WeakReference<Object>> extraMapper = storageMapper.get(tagUUID); 24 if (null == extraMapper) { 25 extraMapper = new HashMap<>(); 26 storageMapper.put(tagUUID, extraMapper); 27 } 28 extraMapper.put(key, new WeakReference<>(content)); 29 } 30 31 public static Object remove(String tagUUID, StorageKey key) { 32 if (null == key) { 33 return null; 34 } 35 HashMap<StorageKey, WeakReference<Object>> extraMapper = storageMapper.get(tagUUID); 36 if (null == extraMapper) { 37 return null; 38 } 39 40 WeakReference<Object> ref = extraMapper.remove(key); 41 if (ABTextUtil.isEmpty(extraMapper)) { 42 storageMapper.remove(tagUUID); 43 } 44 return null == ref ? null : ref.get(); 45 } 46 47 public static HashMap<StorageKey, WeakReference<Object>> remove(String tagUUID) { 48 if (null == tagUUID) { 49 return null; 50 } 51 return storageMapper.remove(tagUUID); 52 } 53 54 public static void clear() { 55 storageMapper.clear(); 56 } 57 58 }
如上代碼,StoragePool維護了一個HashMap,key是一個UUID,代表唯一的一個Intent跳轉,ActivityA跳轉時會把這個UUID傳遞到ActivityB,ActivityB就是通過這個UUID來獲取這次跳轉需要傳遞的數據的。value也是一個HashMap,里面存儲了某次跳轉傳遞的所有數據。key是StorageKey,實質上也是一個UUID,value是任意的數據。
跳轉前的存儲數據和真正的StartActivity都需要使用StorageIntentCenter來進行操作,代碼如下:
1 /** 2 * Author: wangjie 3 * Email: tiantian.china.2@gmail.com 4 * Date: 3/31/15. 5 */ 6 public class StorageIntentCenter { 7 public static final String STORAGE_INTENT_CENTER_KEY_UUID = StorageIntentCenter.class.getSimpleName() + "_UUID"; 8 private static final String TAG = StorageIntentCenter.class.getSimpleName(); 9 10 private Intent intent; 11 private String uuid; 12 private HashMap<StorageKey, Object> extras; 13 private boolean isUsed; 14 public StorageIntentCenter() { 15 intent = new Intent(); 16 uuid = java.util.UUID.randomUUID().toString(); 17 intent.putExtra(STORAGE_INTENT_CENTER_KEY_UUID, uuid); 18 isUsed = false; 19 } 20 21 public StorageIntentCenter putExtra(String intentKey, Object content){ 22 if (null == content) { 23 return this; 24 } 25 StorageKey storageKey = new StorageKey(content.getClass()); 26 intent.putExtra(intentKey, storageKey); 27 if(null == extras){ 28 extras = new HashMap<>(); 29 } 30 extras.put(storageKey, content); 31 return this; 32 } 33 34 public void startActivity(Context packageContext, Class<?> cls){ 35 if(isUsed){ 36 Logger.e(TAG, this + " can not be reuse!"); 37 return; 38 } 39 intent.setClass(packageContext, cls); 40 if(!ABTextUtil.isEmpty(extras)){ 41 Set<Map.Entry<StorageKey, Object>> entrySet = extras.entrySet(); 42 for(Map.Entry<StorageKey, Object> entry : entrySet){ 43 StoragePool.storage(uuid, entry.getKey(), entry.getValue()); 44 } 45 } 46 isUsed = true; 47 packageContext.startActivity(intent); 48 } 49 50 51 }
每個StorageIntentCenter都維護了一個真正跳轉的Intent,一個此次跳轉的uuid和所有需要傳遞的數據。
使用方式(以從MainActivity跳轉到OtherActivity為例):
MainActivity中:
@AILayout(R.layout.main) public class MainActivity extends BaseActivity implements ICommunicate { private static final String TAG = MainActivity.class.getSimpleName(); @Override @AIClick({R.id.ac_test_a_btn}) public void onClickCallbackSample(View view) { switch (view.getId()) { case R.id.ac_test_a_btn: new StorageIntentCenter() .putExtra("iCommunicate", this) .putExtra("testString", "hello world") .putExtra("testFloat", 3.2f) .startActivity(context, OtherActivity.class); break; } } @Override public void hello(String content) { Logger.d(TAG, "hello received: " + content); } }
OtherActivity繼承了BaseActivity。
BaseActivity:
1 /** 2 * Author: wangjie 3 * Email: tiantian.china.2@gmail.com 4 * Date: 4/2/15. 5 */ 6 public class BaseActivity extends AIActivity { 7 private String storageIntentCenterUUID; 8 9 @Override 10 protected void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12 13 initExtraFromStorage(); 14 // remove extra from StoragePool 15 StoragePool.remove(storageIntentCenterUUID); 16 } 17 18 protected void initExtraFromStorage() { 19 } 20 21 protected final <T> T getExtraFromStorage(String key, Class<T> contentType) { 22 StorageKey storageKey = (StorageKey) getIntent().getSerializableExtra(key); 23 if (null == storageIntentCenterUUID) { 24 storageIntentCenterUUID = getIntent().getStringExtra(StorageIntentCenter.STORAGE_INTENT_CENTER_KEY_UUID); 25 } 26 return (T) StoragePool.remove(storageIntentCenterUUID, storageKey); 27 } 28 29 }
Line15:為了防止跳轉到OtherActivity后,如果沒有去暫存區把數據取出來從而導致暫存區有無用的數據(甚至內存泄漏,暫存區使用軟引用也是為了防止這種情況的發生),所以這里提供一個initExtraFromStorage方法讓子類重寫,子類可以在這個方法中去把數據取出來。然后在initExtraFromStorage方法執行完畢后,再及時把暫存區的數據刪除。
Line21~27:這里提供了從暫存區提取數據的方法供子類調用。
OtherActivity:
/** * Author: wangjie * Email: tiantian.china.2@gmail.com * Date: 4/2/15. */ @AILayout(R.layout.other) public class OtherActivity extends BaseActivity{ private static final String TAG = OtherActivity.class.getSimpleName(); private ICommunicate iCommunicate; private String testString; private Float testFloat; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override protected void initExtraFromStorage() { iCommunicate = getExtraFromStorage("iCommunicate", ICommunicate.class); testString = getExtraFromStorage("testString", String.class); testFloat = getExtraFromStorage("testFloat", Float.class); } @Override @AIClick({R.id.other_btn}) public void onClickCallbackSample(View view) { switch(view.getId()){ case R.id.other_btn: if(null == iCommunicate){ return; } Logger.d(TAG, "iCommunicate: " + iCommunicate); iCommunicate.hello("content from ACTestBActivity!"); Logger.d(TAG, "testString: " + testString); Logger.d(TAG, "testFloat: " + testFloat); finish(); break; } } }
如上代碼OtherActivity中獲取了從MainActivity中傳遞過來的MainActivity實例,在點擊事件發生后通過MainActivity實例進行直接回調。
日志打印如下:
04-03 12:09:52.184 25529-25529/com.wangjie.androidstorageintent D/OtherActivity﹕ iCommunicate: com.wangjie.androidstorageintent.sample.MainActivity@42879ff8 04-03 12:09:52.184 25529-25529/com.wangjie.androidstorageintent D/MainActivity﹕ hello received: content from ACTestBActivity! 04-03 12:09:52.184 25529-25529/com.wangjie.androidstorageintent D/OtherActivity﹕ testString: hello world 04-03 12:09:52.184 25529-25529/com.wangjie.androidstorageintent D/OtherActivity﹕ testFloat: 3.2
MainActivity被回調,並獲取了數據“content from ACTestBActivity!”字符串。
注:
1. 以上使用的代碼已托管到github:https://github.com/wangjiegulu/AndroidStorageIntent
2. 上面的注解實現使用AndroidInject:https://github.com/wangjiegulu/androidInject