[Android]Activity跳转传递任意类型的数据、Activity为SingleTask时代替StartActivityForResult的解决方案


以下内容为原创,欢迎转载,转载请注明

来自天天博客: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

 



来源博客:Wang Jie's Blog
本文链接:https://blog.wangjiegulu.com/2015/04/03/Android-Activity跳转传递任意类型的数据、Activity为SingleTask时代替StartActivityForResult的解决方案/
版权声明:本博客所有文章除特别声明外,均采用 CC BY 4.0 CN协议 许可协议。转载请注明出处。