layout | title | categories | description | keywords |
---|---|---|---|---|
post |
优雅地封装和使用 onActivityResult |
Blog |
onActivityResult |
onActivityResult |
作者:国风
调用时去掉 requestcode,更加简单清晰,内部使用 callback.hashcode 作为 requestcode
AvoidOnResult(this).startForResult(FetchDataActivity::class.java, object : AvoidOnResult.Callback {
override fun onActivityResult(resultCode: Int, data: Intent?) =
if (resultCode == Activity.RESULT_OK) {
val text = data?.getStringExtra("text")
Toast.makeText(this@MainActivity, "callback -> " + text, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@MainActivity, "callback canceled", Toast.LENGTH_SHORT).show()
}
})
在日常Android开发中,通过startActivityForResult跳转页面获取数据,然后在onActivityResult中处理的返回数据。这种方式想必大家早就习以为常了,我想大家再用的时候多少都会有些疑问?:为啥统一个逻辑分散在两个地方,而且 onActivityResult 经常会堆积着各种 if else。有没有办法能够像 setOnClickListener 或者 RxJava 那样,直接在一个地方处理调用和回调?答案是肯定的,这个问题以前我也想过,无奈之前不怎么善于真正发现问题、提出问题,而且解决问题的能力也不够。
问题的提出者,和部分解决者,文中作者有一种思路,就是想通过 hook 系统 onActivityResult 回调,统一分发,但是当时并未找到思路。而我之前做过插件框架,热修复,hybrid,Router。对 Hook 自己认为还是驾轻就熟的,而且已经 hook 过作者所找寻的点,所以就在该基础上写了 Hook版本github。
接下来介绍下 onActivityResult实现是三种思路,以及各自的优缺点。
Lifecycle 监听 activity 的声明周期原理是,它内部持有一个Fragment,这个fragment没有视图,只负责请求权限和返回结果,相当于一个桥梁的作用,在这个 Fragment 中,会把所有的回调转发出去,实现监听。
这里直接借用 anotherJack 的demo
public class AvoidOnResult {
private static final String TAG = "AvoidOnResult";
private AvoidOnResultFragment mAvoidOnResultFragment;
public AvoidOnResult(Activity activity) {
mAvoidOnResultFragment = getAvoidOnResultFragment(activity);
}
public AvoidOnResult(Fragment fragment){
this(fragment.getActivity());
}
private AvoidOnResultFragment getAvoidOnResultFragment(Activity activity) {
AvoidOnResultFragment avoidOnResultFragment = findAvoidOnResultFragment(activity);
if (avoidOnResultFragment == null) {
avoidOnResultFragment = new AvoidOnResultFragment();
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager
.beginTransaction()
.add(avoidOnResultFragment, TAG)
.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
}
return avoidOnResultFragment;
}
private AvoidOnResultFragment findAvoidOnResultFragment(Activity activity) {
return (AvoidOnResultFragment) activity.getFragmentManager().findFragmentByTag(TAG);
}
public Observable<ActivityResultInfo> startForResult(Intent intent, int requestCode) {
return mAvoidOnResultFragment.startForResult(intent, requestCode);
}
public Observable<ActivityResultInfo> startForResult(Class<?> clazz, int requestCode) {
Intent intent = new Intent(mAvoidOnResultFragment.getActivity(), clazz);
return startForResult(intent, requestCode);
}
public void startForResult(Intent intent, int requestCode, Callback callback) {
mAvoidOnResultFragment.startForResult(intent, requestCode, callback);
}
public void startForResult(Class<?> clazz, int requestCode, Callback callback) {
Intent intent = new Intent(mAvoidOnResultFragment.getActivity(), clazz);
startForResult(intent, requestCode, callback);
}
public interface Callback {
void onActivityResult(int requestCode, int resultCode, Intent data);
}
}```
监听的 Fragment 如下:
```java
public class AvoidOnResultFragment extends Fragment {
private Map<Integer, PublishSubject<ActivityResultInfo>> mSubjects = new HashMap<>();
private Map<Integer, AvoidOnResult.Callback> mCallbacks = new HashMap<>();
public AvoidOnResultFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public Observable<ActivityResultInfo> startForResult(final Intent intent, final int requestCode) {
PublishSubject<ActivityResultInfo> subject = PublishSubject.create();
mSubjects.put(requestCode, subject);
return subject.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
startActivityForResult(intent, requestCode);
}
});
}
public void startForResult(Intent intent, int requestCode, AvoidOnResult.Callback callback) {
mCallbacks.put(requestCode, callback);
startActivityForResult(intent, requestCode);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//rxjava方式的处理
PublishSubject<ActivityResultInfo> subject = mSubjects.remove(requestCode);
if (subject != null) {
subject.onNext(new ActivityResultInfo(requestCode, resultCode, data));
subject.onComplete();
}
//callback方式的处理
AvoidOnResult.Callback callback = mCallbacks.remove(requestCode);
if (callback != null) {
callback.onActivityResult(requestCode, resultCode, data);
}
}
}
扩展rxjava调用
//callback方式
callback.setOnClickListener {
AvoidOnResult(this).startForResult(FetchDataActivity::class.java, REQUEST_CODE_CALLBACK, object : AvoidOnResult.Callback {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) =
if (resultCode == Activity.RESULT_OK) {
val text = data?.getStringExtra("text")
Toast.makeText(this@MainActivity, "callback -> " + text, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@MainActivity, "callback canceled", Toast.LENGTH_SHORT).show()
}
})
}
//rxjava方式
rxjava.setOnClickListener {
AvoidOnResult(this)
.startForResult(FetchDataActivity::class.java, REQUEST_CODE_RXJAVA)
//下面可自由变换
.filter { it.resultCode == Activity.RESULT_OK }
.flatMap {
val text = it.data.getStringExtra("text")
Observable.fromIterable(text.asIterable())
}
.subscribe({
Log.d("-------> ", it.toString())
}, {
Toast.makeText(this, "error", Toast.LENGTH_SHORT).show()
}, {
Toast.makeText(this, "complete", Toast.LENGTH_SHORT).show()
})
}
在 BaseActivity 的 startActivityForResult和 onActivityResult中分别主动触发 OnResultManager.startForResult和 trigger 回调
public class OnResultManager {
private static final String TAG = "OnResultManager";
//HashMap的key Integer为requestCode
private static WeakHashMap<Activity,HashMap<Integer,Callback>> mCallbacks = new WeakHashMap<>();
private WeakReference<Activity> mActivity;
public OnResultManager(Activity activity) {
mActivity = new WeakReference<Activity>(activity);
}
public void startForResult(Intent intent, int requestCode, Callback callback){
Activity activity = getActivity();
if(activity == null){
return;
}
addCallback(activity,requestCode,callback);
activity.startActivityForResult(intent,requestCode);
}
public void trigger(int requestCode, int resultCode, Intent data){
Log.d(TAG,"----------- trigger");
Activity activity = getActivity();
if(activity == null){
return;
}
Callback callback = findCallback(activity,requestCode);
if(callback != null){
callback.onActivityResult(requestCode,resultCode,data);
}
}
//获取该activity、该requestCode对应的callback
private Callback findCallback(Activity activity,int requestCode){
HashMap<Integer,Callback> map = mCallbacks.get(activity);
if(map != null){
return map.remove(requestCode);
}
return null;
}
private void addCallback(Activity activity,int requestCode,Callback callback){
HashMap<Integer,Callback> map = mCallbacks.get(activity);
if(map == null){
map = new HashMap<>();
mCallbacks.put(activity,map);
}
map.put(requestCode,callback);
}
private Activity getActivity(){
return mActivity.get();
}
public interface Callback{
void onActivityResult(int requestCode, int resultCode, Intent data);
}
}
本质上也是用 2.2 的 OnResultManager ,只不过是不用在 BaseActivity中转发了,而是直接 Hook 系统的 onActivityResult Hook onActivityResult 方法如下:
/**
* Created by guofeng05 on 2018/1/4.
*/
public class ActivityThreadCallbackHook {
// Copy from ActivityThread.mH Handler
public static final int SEND_RESULT = 108;
public static void hook() {
try {
ActivityThread activityThread = ActivityThread.currentActivityThread();
// 由于ActivityThread一个进程只有一个,我们获取这个对象的mH
Field mHField;
mHField = ActivityThread.class.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(activityThread);
// 设置它的回调, 根据源码:
// 我们自己给他设置一个回调,就会替代之前的回调;
// public void dispatchMessage(Message msg) {
// if (msg.callback != null) {
// handleCallback(msg);
// } else {
// if (mCallback != null) {
// if (mCallback.handleMessage(msg)) {
// return;
// }
// }
// handleMessage(msg);
// }
// }
Field mCallBackField = Handler.class.getDeclaredField("mCallback");
mCallBackField.setAccessible(true);
// 塞入我们的 hook 对象
mCallBackField.set(mH, new MyHandlerCallback(mH));
Log.d("hook","success");
} catch (Exception e) {
// hook 失败,整个 callback 就 gg了
e.printStackTrace();
}
}
private static class MyHandlerCallback implements Handler.Callback {
private Handler mOldHandler;
public MyHandlerCallback(Handler mOldHandler) {
this.mOldHandler = mOldHandler;
}
@Override
public boolean handleMessage(Message msg) {
// 不干扰系统分发逻辑
mOldHandler.handleMessage(msg);
// 通知 ResultManager
if (msg.what == SEND_RESULT) {
Object obj = msg.obj;
try {
// step 1 reflect to get activity
Object token = ReflectUtils.on(obj).get("token");
ArrayMap mActivities = (ArrayMap) ReflectUtils.on(ActivityThread.currentActivityThread()).get("mActivities");
Object activityClientRecord = mActivities.get(token);
Activity activity = (Activity) ReflectUtils.on(activityClientRecord).get("activity");
// step2 reflect to get ResultInfo
// 注意这里的分发,无法分发到 Fragment 内部,所以采用动态塞入一个 Fragment 是最稳定的方案
ArrayList<ResultInfo> results = (ArrayList<ResultInfo>) ReflectUtils.on(obj).get("results");
for (ResultInfo result : results) {
OnResultManager.getInstance().trigger(activity, result.mRequestCode, result.mResultCode, result.mData);
}
} catch (RuntimeException e) {
e.printStackTrace();
}
}
// default
return true;
}
}
}
- fragment 版本是稳定性,易用性最好的
- BaseActivity 那种其次
- Hook 不建议线上使用,一是兼容性,二是各种插件化等 hook 相互影响,问题排插难度大