基于ARouter實現startActivityForResult的鏈式調用

Android

前言

什么是ARouter?如果你想知道什么是ARouter的話,現在就帶你研究... 什么是ARouter ...

開玩耍... 今天不對ARouter的使用做過多講解,如果有對ARouter不了解的童鞋可以移步ARouter 官方學習。今天主要是基于ARouter做一些擴展,來波不是騷操作的騷操作~

如何通過ARouter實現類Retrofit鏈式調用onActivityResult ?


我們都知道啟動另外一個Activity有倆種方式:

  1. 使用startActivity()
  2. 使用startActivityOnResult()

startActivityOnResult()startActivity()區別在于startActivityOnResult可以從B頁面回傳數據到A頁面。

傳統啟動方式:
startActivityForResult(new Intent(this,OtherActivity.class),REQUEST_CODE);

ARouter啟動方式:
ARouter.getInstance().build(String path)
    .navigation(Context mContext, int requestCode);

回調:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
}

那么問題來了!

我想通過Arouter直接回調拿到onActivityResult返回的數據呢?

就像... 這樣

ARouter.getInstance().build(String path)
    .navigation(Context mContext, int requestCode)
    .setOnResultCallback(new OnResultCallback(){
        @Override
        public void onResponse(Object data){
            
        }
    });

分析:

我們想要實現類似如上的方式,正常的ARouter跳轉方法肯定是實現不了,我們需要暴露方法提供給他人使用,同時還需要拿到ARouter相關的方法對象。

通過ARouter文檔發現,想要提供服務暴露給其他人使用,ARouter提供了一個接口:IProvider

public interface IProvider {

    /**
     * Do your init work in this method, it well be call when processor has been load.
     *
     * @param context ctx
     */
    void init(Context context);
}

嗯.. 這就好辦了,我們第一步需要實現的是在onActivityResult 方法中將返回的結果通過路由服務中的方法進行操作

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        SelectInfo selectInfo = (SelectInfo) data.getSerializableExtra("selectInfo");
        Log.e("Rain",selectInfo.getName() + "--------onActivityResult----------");
        XRouter.getRouter().getActivityManager().onActivityResult(this, requestCode, resultCode, data);
    }

服務的實現類

@Route(path = IActivityManagerService.PATH)
public class ActivityManagerImpl implements IProvider, IActivityManagerService {

    private Context mContext;

    private List<OnActivityResultListener> mOnActivityResultListeners;

    @Override
    public void init(Context context) {
        mContext = context;
    }

    @Override
    public Context getContext() {
        if (ActivityUtils.getTopActivity() == null)
        return mContext;
        else return ActivityUtils.getTopActivity();
    }

    @Override
    public void addOnActivityResultListener(OnActivityResultListener listener) {
        if(mOnActivityResultListeners == null){
            mOnActivityResultListeners = new ArrayList<>();
        }
        if(!mOnActivityResultListeners.contains(listener)){
            mOnActivityResultListeners.add(listener);
        }
    }

    @Override
    public void onActivityResult(Activity context, int requestCode, int resultCode, Intent data) {
        if(mOnActivityResultListeners != null && !mOnActivityResultListeners.isEmpty()){
            for(OnActivityResultListener listener : mOnActivityResultListeners){
                listener.onActivityResult(context, requestCode, resultCode, data);
            }
        }
    }
}

調用方式:

public IActivityManagerService getActivityManager() {
    ActivityManagerImpl activityManagerImpl = (ActivityManagerImpl)ARouter.getInstance()
    .build(IActivityManagerService.PATH)
    .navigation();
  return activityManagerImpl;
}

第二步我們需要做路由跳轉服務的實現 類似:

XRouter.getRouter()...startActivityForResult(new ResultCallback() {
    @Override
    public void onResponse(@NonNull Object data) {
       
    }
});

Arouter調用build方法后返回一個PostCard,官方解釋A container that contains the roadmap.這是個路由信息的存儲器,里面包含頁面跳轉的所有信息。

那么我們跳轉時傳遞參數必須先拿到一個PostCard,通過PostCard可以傳遞我們目標頁面所需的數據

 Postcard mPostcard = ARouter.getInstance().build(path);

通過new NavigatorBuilder(String path) 可以拿到NavigatorBuilder對象:

NavigatorBuilder navigator = new NavigatorBuilder(path);

路由跳轉時我們需要的參數大致有path路徑,requestCode以及傳遞的數據,如果使用靜態代理去實現的話,后期可能不太好維護,那么一個更好的解決辦法就是使用注解+動態代理,可以實現類Retrofit式使用,方便擴展維護。

創建注解類:

//方法注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Route {

    /**
     * 跳轉路徑
     * @return
     */
    String path();

    /**
     * 請求碼
     * @return
     */
    int requestCode() default -1;

}
//方法參數注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Extras {
}

使用:

public interface AppNavigator {
    String _selectPage = "/select/contactActivity";

    @Route(path = _selectPage)
    ActivityNavigator<SelectInfo> toSelectContactPage(@Extras Bundle bundle);
}

這時我們可以通過反射拿到AppNavigator 對象。

這里其實使用的是動態代理,內部也是通過Java反射機制實現的,即已知的一個對象,然后在運行時動態調用其方法,這樣在調用前后作一些相應的處理。

 try {
     o = Proxy.newProxyInstance(navigator.getClassLoader(), new Class[]{navigator}, new InvocationHandler() {
         LruCache<Method, NavigationMethod> mNavigatorMethods;
         @Override
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             if (mNavigatorMethods == null) {
                 mNavigatorMethods = new LruCache<>(5);
             }
             NavigationMethod navigationMethod = mNavigatorMethods.get(method);
             if (navigationMethod == null) {
                 // create method's processer
                 navigationMethod = new NavigationMethod(method);
                 mNavigatorMethods.put(method, navigationMethod);
             }
             return navigationMethod.invoke(args);
         }
     });
 }catch (Exception e){
     Log.e("Rain",e.getMessage() + "-----------");
 }
 
//CLassLoader loader:被代理對象的類加載器 
//Class<?> interfaces:被代理類全部的接口 
//InvocationHandler h:實現InvocationHandler接口的對象,在調用方式時會調用它的invoke方法。

調用Proxy的newProxyInstance方法可以生成代理對象 ,實現InvocationHandler接口的對象,在調用方式時會調用它的invoke方法,可以看到這個方法中調用了被代理對象的方法: method.invoke(),所以我們可以在這里加上我們的業務邏輯。

NavigationMethod.class:
public Object invoke(Object[] args) {
   NavigatorBuilder builder = XRouter.getRouter().build(mRoute.path());
   Navigator navigator = builder
           .withRequestCode(mRoute.requestCode())
           .navigator();
   return navigator;
}

我們在ContactSelectInfoCallback中解析了返回的intent對象,并通過onResponse方法返回具體的數據對象。

public abstract class RouteCallback<T>{

    public void onResponse(int requestCode, int resultCode, Intent data){
        if(data != null){
            try{
                T parseData = parseData(requestCode, resultCode, data);
                if(parseData != null){
                    onResponse(parseData);
                }else{
                    onError(new RuntimeException("no data parsed"));
                }
            }catch(Exception e){
                onError(new RuntimeException("an exception been catched when parsing data", e));
            }
        }else{
            onCancel();
        }
    }

    public abstract T parseData(int requestCode, int resultCode, @NonNull Intent data);

    public abstract void onResponse(@NonNull T data);

    public void onCancel(){}

    public void onError(Throwable throwable){}

}

ok,基本工作完成

使用方式

1. 聲明Navigator接口
public interface AppNavigator {
    String _selectPage = "/select/contactActivity";

    //聲明返回類型為Navigator<T>, T為需要解析的回傳數據類型
    @Route(path = _selectPage)
    Navigator<SelectInfo> toSelectContactPage(@Extras Bundle ss);
}
2. 復寫onActivityResult方法
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    XRouter.getRouter().getActivityManager().onActivityResult(this, requestCode, resultCode, data);
}
3. 實現Callback,對回傳數據進行解析處理
public abstract class ContactSelectInfoCallback extends RouteCallback<SelectInfo> {
    @Override
    public SelectInfo parseData(int requestCode, int resultCode, @NonNull Intent data) {
        return (SelectInfo)data.getSerializableExtra("selectInfo");
    }
}
4. 發起路由
XRouter.getRouter()
        .create(AppNavigator.class)
        .toSelectContactPage(bundle)
        .startActivityForResult(new ContactSelectInfoCallback() {
            @Override
            public void onResponse(@NonNull SelectInfo data) {
                Log.e("Rain", data.getName() + "-----------onResponse--------");
            }
        });

輸出結果:

E: Rain--------onActivityResult----------
E: Rain-----------onResponse--------

完整代碼及解析使用移步github項目:XRouter

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容