更新,强迫症福音,onActivityResult方法hook到了java
Android中,经过startActivityForResult跳转页面获取数据应该没必要多说,可是这种全部获取到的结果都须要到onActivityResult中处理的方式实在使人蛋疼。android
试想一下,咱们敲着代码唱着歌。忽然,半路上跳出一群马匪,让咱们到另外一个页面获取一点数据,获取后还不让在当前代码位置处理逻辑,要去onActivityResult添加一个requestCode分支处理结果,处理完才让回来,等这一切都作完回来不免就会陷入这样的思考:我是谁,我在哪,我在干什么,我刚才写到哪了……git
再想一下,你跟同事的代码,跟到一个startActivityForResult,因而不耐烦地ctrl+f找到onActivityResult,发现里面充斥着大量的requestCode分支,而后忽然意识到刚才没记下requestCode是什么……github
问题的根源是全部处理结果的逻辑都要放到onActivityResult中,在里面根据requestCode做不一样处理。而咱们渴望的是能在发起startActivityForResult的时候捎带着把获取结果后处理的逻辑也传进去,并能在内部对requestCode判断好,不用咱们再判断一遍。bash
新建一个OnResultManager类,用来管理获取结果的回调,下面详细说。app
分析问题时说了,咱们但愿在发起startActivityForResult的时候就指定好处理结果的逻辑,这个简单,在OnResultManager中建立一个Callback接口,里面定义一个OnActivityResult方法,参数和Activity的OnActivityResult方法参数彻底同样,在发起start的时候除了intent和requestCode,再传一个callback进去。而OnresultManager负责控制在系统的onActivityResult触发时,调用对应callback的方法。ide
下面是OnResultManager的所有代码。函数
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);
}
}
复制代码
逻辑很简单,里面持有一个mActivity,使用弱引用以防止内存泄漏,在构造器中为其赋值。还有一个static的WeakHashMap<Activity,HashMap<Integer,Callback>> mCallbacks 用来存放全部的callback,先以activity分,在各个activity中又使用hashmap存储requestCode和callback的对应关系。工具
在startForResult时,最终仍是调用的activity的startActivityForResult,只不过在跳转页面以前,把callback存入了mCallbacks中。测试
而trigger方法则是根据activity和requestCode从mCallbacks中取出对应的callback,调用方法。
如今callback的存和取都搞定了,那么问题来了,何时触发“取”的操做呢,即trigger方法怎么触发呢?答案是在onActivityResult中调用,嫌麻烦能够在BaseActivity中调用。
使用示例:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
go.setOnClickListener {
val intent = Intent(this,SecondActivity::class.java)
onResultManager.startForResult(intent,REQUEST_CODE,{requestCode: Int, resultCode: Int, data: Intent? ->
if (resultCode == Activity.RESULT_OK){
val text = data?.getStringExtra("text")
Toast.makeText(this,"result -> "+text,Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(this,"canceled",Toast.LENGTH_SHORT).show()
}
})
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
onResultManager.trigger(requestCode,resultCode,data)
}
复制代码
但是这样好蠢啊,你是否是以为要是不用手动触发,能自动触发多好。我也是这么想的,因此有整整一天我一直在找有什么办法能hook到onActivityResult方法,最后hook了Instrumentation,也hook了AMS,可是都对这个onActivityResult无能为力,看源码发现好像是在ActivityThread中传递的,可是很不幸的是这个ActivityThread没办法hook,至少经过简单的反射和代理没办法作到(若是谁有办法hook到,恳请您能分享出来,我真的特别想知道,我不甘心啊)
这里感谢一下@world_hello的提醒,十分感谢。
以前看到ActivityThread的mH的时候总想着弄个代理继承它,而后重写handleMessage方法,来获取结果信息,但是却苦于继承不了,其实我一直忽视了Handler内部的一个Callback接口,其实彻底不用代理。下面详细讲解一下。
先说一下Handler,咱们通常使用的时候大可能是继承自Handler,而后重写handleMessage方法,在里面处理接收消息。其实Handler还有个构造函数能够接收一个Handler.Callback(不要和咱们本身定义的callback搞混了啊),在Handler.Callback的handleMessage方法中处理消息。
看一下Handler源码:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码
能够看到,若是mCallback不为null,就会执行mCallback的handleMessage方法。若是这个handleMessage方法的返回值为true,就会直接return,若是为false,就会继续执行Handler自己的handleMessage方法。
下面打开Activity源码,startActivityForResult方法中有这么一段
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
复制代码
ar是ActivityResult,若是不为null,就调用mMainThread的sendActivityResult方法,这个mMainThread就是ActivityThread类型的,因此继续打开ActivityThread源码,找到这个方法,顺着这个方法一直找一直找,这里中间的方法我就不贴了,最后会找到一个叫sendMessage的方法,这个方法最后一行是
mH.sendMessage(msg);
复制代码
这个mH是一个Handler的子类,因此很明显,activityresult在这个环节是经过handler传递的。
在H的handleMessage方法中有这样一个case分支
case SEND_RESULT:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
handleSendResult((ResultData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
复制代码
不用再多说了吧,就是这里了。它调用了handleSendResult((ResultData)msg.obj),因此这个ResultData应该就是存放了咱们须要的东西。只要能拦截到它,拿出咱们须要的数据,那就算是hook到了onActivityResult了,那么怎么拿到呢?如今就要提到咱们刚才提的Handler.Callback了,咱们建立一个callback,并记得让handleMessage的方法返回false,以避免影响mH自己的handleMessage方法的执行,而后经过反射把这个callback给mH set进去。下面须要您对反射有一点点了解。
public class HookUtil {
public static void hookActivityThreadHandler() throws Exception {
// 先获取到当前的ActivityThread对象
final Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
final Object currentActivityThread = currentActivityThreadField.get(null);
// 因为ActivityThread一个进程只有一个,咱们获取这个对象的mH
Field mHField = activityThreadClass.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(currentActivityThread);
Handler.Callback mHCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if(msg.what == 108){
Log.d("hook-------","onActivityResult");
try{
Object resultData = msg.obj;
Field mActivitiesField = activityThreadClass.getDeclaredField("mActivities");
mActivitiesField.setAccessible(true);
ArrayMap mActivities = (ArrayMap) mActivitiesField.get(currentActivityThread);
Class<?> resultDataClass = Class.forName("android.app.ActivityThread$ResultData");
Field tokenField = resultDataClass.getDeclaredField("token");
tokenField.setAccessible(true);
IBinder token = (IBinder) tokenField.get(resultData);
//r是ActivityClientRecord类型的
Object r = mActivities.get(token);
Class<?> ActivityClientRecordClass = Class.forName("android.app.ActivityThread$ActivityClientRecord");
Field activityField = ActivityClientRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
Activity activity = (Activity) activityField.get(r); //至此,终于拿到activity了
Field resultsField = resultDataClass.getDeclaredField("results");
resultsField.setAccessible(true);
List results = (List) resultsField.get(resultData);
//ResultInfo类型
Object resultInfo = results.get(0);
Class<?> resultInfoClass = Class.forName("android.app.ResultInfo");
Field mRequestCodeField = resultInfoClass.getDeclaredField("mRequestCode");
mRequestCodeField.setAccessible(true);
int mRequestCode = (int) mRequestCodeField.get(resultInfo); //拿到requestCode
Field mResultCodeField = resultInfoClass.getDeclaredField("mResultCode");
mResultCodeField.setAccessible(true);
int mResultCode = (int) mResultCodeField.get(resultInfo); //拿到resultCode
Field mDataField = resultInfoClass.getDeclaredField("mData");
mDataField.setAccessible(true);
Intent mData = (Intent) mDataField.get(resultInfo); //拿到intent
new OnResultManager(activity).trigger(mRequestCode,mResultCode,mData);
}catch (Exception e){
e.printStackTrace();
}
}
return false;
}
};
Field mCallBackField = Handler.class.getDeclaredField("mCallback");
mCallBackField.setAccessible(true);
mCallBackField.set(mH, mHCallback);
}
}
复制代码
hookActivityThreadHandler方法中就是先获取到ActivityThread对象,再经过activityThread获取到mH,而后建立了一个Handler.Callback,最后用反射把这个callback set给mH。
相比之下,Handler.Callback中的handleMessage方法看起来更长一些,但其实并不复杂,只是反射使它变得面目全非了,它主要就是为了获取咱们OnResultManager的trigger方法须要的数据,一个activity,以及requestCode、resultCode、data。
msg.what==108的判断,这里108就是SEND_RESULT,我图方便直接写死了,源码里看着为108,固然经过反射动态获取SEND_RESULT更规范一点,你们别在乎这些细节。
先看activity怎么获取,主要看ActivityThread的handleSendResult方法,它的第一行
private void handleSendResult(ResultData res) {
ActivityClientRecord r = mActivities.get(res.token);
复制代码
这个res就是msg.obj,而获取到ActivityClientRecord以后,它有一个名为activity的field,获取的就是咱们须要的activity对象了。自己很简单,只不过这短短的一行代码要用反射一点一点获取,就繁琐一些了。
另外三个数据都在一块儿,在ResultData里有个名为results的Field,它是一个List,里面就一个元素(可能有误,但我打断点看它一直都是一个),是ResultInfo类型,咱们须要的requestCode、resultCode、data就都在这个resultInfo里面了,因此,继续反射。
都获取完了,调用
new OnResultManager(activity).trigger(mRequestCode,mResultCode,mData);
复制代码
就是把咱们以前手动写在onActivityResult中的那句放到这里自动触发。
如今方法写完了,还要写个自定义Application,在onCreate中调用
HookUtil.hookActivityThreadHandler()
复制代码
大功告成啦!,赶忙测试一下吧,如今终于不用再在onActivityResult中手动触发啦!github上代码已经更新。
按理说咱们该在OnResultManager中定义个init方法,而后在init方法中去调hookActivityThreadHandler貌似更规范一些,不过这只算个demo,就不考虑这么多了。
还有,这种经过反射hook的方法在稳定性和兼容性上都没法保证,因此这算是一种仅供娱乐的方式吧!领略一下hook的魅力。
下面是world_hello实现的hook,虽然思路是同样的,可是编写的要比个人容易看懂得多,自愧不如,在此强烈推荐你们看一下。
前段时间又来了个小项目,领导扔给我了,而后在这个项目里就把以前没用过(没错,以前总用H5开发……)的rxjava、kotlin都加进来了。有一天作动态权限处理,惊奇地发现RxPermissions竟然摆脱了Activity的onRequestPermissionsResult方法!!!你们都知道,动态权限大致就是先调用requestPermissions方法,而后受权的结果要到onRequestPermissionsResult中处理,简直和startActivityForResult一模一样。那RxPermissions是怎么作到的呢!!!
接着在前几天不太忙的时候看了下RxPermissions的源码,发现它内部持有一个Fragment,这个fragment没有视图,只负责请求权限和返回结果,至关于一个桥梁的做用,咱们经过rxPermissions发起request的时候,其实并非activity去request,而是经过这个fragment去请求,而后在fragment的onRequestPermissionsResult中把结果发送出来,如此来避开activity的onRequestPermissionsResult方法。
当时,没见过什么大场面的我差点就给跪了。
一样,Fragment也有startActivityForResult方法啊,那咱们也能够采起相似的方法,随心所欲。
此次取名叫AvoidOnResult,主要就是AvoidOnResult和AvoidOnResultFragment两个类。先上代码:
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);
}
}
复制代码
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);
}
}
}
复制代码
先看AvoidOnResult,和RxPermissions同样,也持有一个无视图的fragment,在构造器中先去获取这个AvoidOnResultFragment,getAvoidOnResultFragment、findAvoidOnResultFragment这两个方法是从RxPermissions扒来的,大致就是先经过TAG获取fragment,若是是null就新建一个并add进去。
这个类内部也定义了一个Callback接口,没必要多说。
继续,这个类有多个startForResult方法,主要看public void startForResult(Intent intent, int requestCode, Callback callback),它自己不干实事,只是调用fragment的同名方法,全部的逻辑都是fragment中处理,待会儿咱们来看这个“同名方法”。
再看这个fragment,它持有一个mCallbacks,存着requestCode和callback的对应关系。而后找到上边说的同名方法startForResult,只有两行代码,一、把callback存起来,二、调用fragment的startActivityForResult
继续看fragment的onActivityResult方法,主要看注释有“callback方式的处理”的代码,就是从mCallbacks中拿到requestCode对应的callback,调用callback的onActivityResult方法。整体的就是这样了,是否是很简单。
能够看到除了返回值为void的startForResult方法外,还有几个返回值为Observable的,原理同样,只不过fragment中再也不是存callback,而是存subject,而后经过doOnSubscribe使它在subscribe的时候跳转页面,最后把获得的Observable返回。对应的,在onActivityResult中拿到对应的subject,经过onNext把数据发出去。
使用示例:
//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()
})
}
复制代码
全部的工具类都是用java写的,避免使用kotlin编写,出现java没法调用kotlin的状况。测试代码用的kotlin,不过没用太多kotlin的特性,因此即使没接触过kotlin的应该也很好看懂吧!
最后祝你们新年快乐!要是能赏几个star就更好啦!