把老項(xiàng)目的Callback形式換成RxJava的Observable形式

以下基于Rxjava1.x氢橙, 主要面向目標(biāo)是有多種Callback的改造沃测。

現(xiàn)有功能

假設(shè)現(xiàn)有功能如下:

public class MockService {
    void getName(String url, StringCallBack stringCallBack){
        stringCallBack.callString("Hello world!");
  }

    void getAge(String url, IntCallback intCallback){
        intCallback.callInt(18);
  }
}

public abstract class IntCallback {
    public  abstract void callInt(int value);
}

public abstract class StringCallback {
    public abstract void callString(String string);
}

現(xiàn)在希望可以把這些callback方式遂铡,變成返回Observable<T>的方式器仗。

思考

下面的Observable 都指Rxjava中的Observable

方案一

Callback與Observable的結(jié)合就是需要在Callback調(diào)用Observable綁定Observer的onNext之類(lèi)的方法。所以我們需要生成兩部分內(nèi)容漱抓,一個(gè)特殊的Callback和一個(gè)特殊的Observable表锻。用一個(gè)類(lèi)來(lái)存儲(chǔ)這兩個(gè)信息:

public class ResultHolder<T, R> {
    public Observable<T> observable;
    public R callback;
}
// 使用的時(shí)候大概這種感覺(jué)
public static void main(String[] args){
    MockService mockService = new MockService();
    ResultHolder<MockData, StringCallback> holder = ...;
    mockService.getName("xxxx", holder.callback);
    holder.observable.subscribe(mockData -> {});
}

下面思考如何生成這個(gè)holder。我們有多種Callback返回的類(lèi)型也是多種的所以需要設(shè)計(jì)一個(gè)如下的東西:

public <T,R> ResultHolder<T,R> create(Type resultType, Type callbackType){
    ...
}

我們需要得到返回Observable<T> 和R表示的callback,這兩個(gè)類(lèi)型是不確定的乞娄。所以需要在參數(shù)中傳入這兩個(gè)類(lèi)型瞬逊,這樣函數(shù)內(nèi)部就可以確定外部想要的期望結(jié)果,并進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換补胚。

public <T,R> ResultHolder<T,R> create(Type resultType, Type callbackType){
    ResultHolder<T, R> resultHolder = new ResultHolder<>();
    PublishSubject<T> subject = PublishSubject.create();
    resultHolder.observable = subject;
    TypeAdapter<T> typeAdapter = getAdapter(callbackType);
    resultHolder.callback = typeAdapter.getCallback(getConverter(resultType), subject);
    return resultHolder;
}

Observable就使用Subject對(duì)象, 這樣它既是Observable又是Observer码耐。為了能讓Observable和Callback做出關(guān)聯(lián)追迟,需要把Observer傳入到Callback的具體實(shí)現(xiàn)中溶其,這里Callback的具體實(shí)現(xiàn)是有限個(gè)的。所以可以把有限的Callback實(shí)現(xiàn)先存起來(lái)敦间,用的時(shí)候再找到使用就可以了瓶逃。根據(jù)這個(gè)要求完成該類(lèi):

public class RxJavaCallbackHelper {
    private final Converter.Factory mConverterFactory;
    private Map<TypeToken<?>, TypeAdapter<?>> mCached;


    public RxJavaCallbackHelper(Converter.Factory factory){
        mConverterFactory = factory;
        mCached = new HashMap<>();
        mCached.put(TypeToken.get(StringCallback.class), new StringCallbackAdapter<>());
    }

    public <T,R> ResultHolder<T,R> create(Type resultType, Type callbackType){
        ResultHolder<T, R> resultHolder = new ResultHolder<>();
        PublishSubject<T> subject = PublishSubject.create();
        resultHolder.observable = subject;
        TypeAdapter<T> typeAdapter = getAdapter(callbackType);
        resultHolder.callback = typeAdapter.getCallback(getConverter(resultType), subject);
        return resultHolder;
    }

    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> getAdapter(Type type){
        return (TypeAdapter<T>) mCached.get(TypeToken.get(type));
    }

    @SuppressWarnings("unchecked")
    public <T> Converter<T> getConverter(Type type){
        return (Converter<T>) mConverterFactory.createConverter(type);
    }
}

public interface TypeAdapter<T>{
    <R> R getCallback(Converter<T> converter, Observer<T> observer);
}

public interface Converter<T> {
    T convert(String str);

    interface Factory{
        Converter<?> createConverter(Type type);
    }
}

TypeToken是Gson里的類(lèi),使用TypeToken是因?yàn)槠鋵?shí)現(xiàn)了判斷兩個(gè)Type是否相等的方法廓块。這里有兩處使用了強(qiáng)制類(lèi)型轉(zhuǎn)換厢绝,不過(guò)這兩個(gè)地方都通過(guò)參數(shù)傳入了期望的Type所以強(qiáng)制類(lèi)型轉(zhuǎn)換也是按預(yù)期進(jìn)行的。

public class GsonConverterFactory implements Converter.Factory {
    private Gson mGson;

    public GsonConverterFactory(Gson gson){
        mGson = gson;
    }


    @Override
    public Converter<?> createConverter(Type type) {
        return new GsonConverter<>(mGson, type);
    }
}

public class GsonConverter<T> implements Converter<T> {
    private final Gson mGson;
    private final Type mType;

    public GsonConverter(Gson gson, Type type){
        mGson = gson;
        mType = type;
    }

    @Override
    public T convert(String str) {
        return mGson.fromJson(str, mType);
    }
}

public class StringCallbackAdapter<T> implements TypeAdapter<T> {
    @SuppressWarnings("unchecked")
    @Override
    public <R> R getCallback(Converter<T> converter, Observer<T> observer) {
        return (R) new StringCallbackWrapped<>(converter, observer);
    }
}

class StringCallbackWrapped<T> extends StringCallback {
    private final Converter<T> mConverter;
    private final Observer<T> mObserver;

    StringCallbackWrapped(Converter<T> converter, Observer<T> observer){
        mConverter = converter;
        mObserver = observer;
    }

    @Override
    public void callString(String string) {
        try {
            mObserver.onNext(mConverter.convert(string));
            mObserver.onCompelete();
        }catch (Exception e){
            mObserver.onError(e);
        }
    }
}

寫(xiě)個(gè)Test測(cè)試下:

    public static void main(String[] args){
        MockService mockService = new MockService();
        RxJavaCallbackHelper helper = new RxJavaCallbackHelper(new GsonConverterFactory(new Gson()));
        ResultHolder<MockData, StringCallback> holder = helper.create(MockData.class, StringCallback.class);
        mockService.getName("", holder.callback);
        holder.observable.subscribe(mockData -> System.out.println("mock data code:" + mockData.code),
                (e)->{}, ()->System.out.println("completed!"));
    }

然后沒(méi)有結(jié)果輸出带猴,因?yàn)檫@里的MockService是同步操作昔汉,在Observable訂閱之前就已經(jīng)結(jié)束操作了。需要對(duì)上面的代碼進(jìn)行一些改動(dòng)

// RxJavaCallbackHelper
    public <T,R> ResultHolder<T,R> create(Type resultType, Type callbackType){
        ResultHolder<T, R> resultHolder = new ResultHolder<>();
        BehaviorSubject<T> subject = BehaviorSubject.create(); // 可以把最近一次的結(jié)果發(fā)送給訂閱者
        resultHolder.observable = rx.Observable.defer(() -> subject.take(1));// 訂閱的時(shí)候才創(chuàng)建一個(gè)只能發(fā)送一次數(shù)據(jù)的Observable
        TypeAdapter<T> typeAdapter = getAdapter(callbackType);
        resultHolder.callback = typeAdapter.getCallback(getConverter(resultType), subject);
        return resultHolder;
    }
// StringCallbackWrapped
    public void callString(String string) {
        try {
            mObserver.onNext(mConverter.convert(string));
//          mObserver.onCompleted();    刪除這行拴清,結(jié)束通過(guò)上面的take(1) 處理       
        }catch (Exception e){
            mObserver.onError(e);
        }
    }

方案二

//直接返回一個(gè)Observable
public <T, R> CallbackObservable<T, R> create(Type resultType, Type callbackType){
        ...
}
//獲取Callback用來(lái)實(shí)際執(zhí)行
CallbackObservable<T, R> callbackObservable;
Callback callback = callbackObservable.getCallback();

需要看看如何從Subject類(lèi)里進(jìn)行擴(kuò)展吧靶病,還沒(méi)研究

方案三

public <T,R> rx.Observable<T> create(Type resultType, Type callbackType, OnCallbackCreated<R> onCallbackCreated){
        ...
    }

在OnCallbackCreated里把封裝好的Callback傳出來(lái),這種形式還有個(gè)好處是口予,可以真正做到在subscribe的時(shí)候才去執(zhí)行娄周,上面兩種只適合拿到Observable就馬上訂閱的, 不適合先持有等到某個(gè)時(shí)間再訂閱沪停。

結(jié)語(yǔ)

主要就是為了封裝細(xì)節(jié)煤辨,減小外部調(diào)用時(shí)的學(xué)習(xí)成本裳涛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市众辨,隨后出現(xiàn)的幾起案子端三,更是在濱河造成了極大的恐慌,老刑警劉巖鹃彻,帶你破解...
    沈念sama閱讀 212,185評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痪欲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡堆巧,警方通過(guò)查閱死者的電腦和手機(jī)赋铝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,445評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)泳挥,“玉大人然痊,你說(shuō)我怎么就攤上這事√敕” “怎么了剧浸?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,684評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)矗钟。 經(jīng)常有香客問(wèn)我唆香,道長(zhǎng),這世上最難降的妖魔是什么吨艇? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,564評(píng)論 1 284
  • 正文 為了忘掉前任躬它,我火速辦了婚禮,結(jié)果婚禮上东涡,老公的妹妹穿的比我還像新娘冯吓。我一直安慰自己,他們只是感情好疮跑,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,681評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布组贺。 她就那樣靜靜地躺著,像睡著了一般祖娘。 火紅的嫁衣襯著肌膚如雪失尖。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,874評(píng)論 1 290
  • 那天渐苏,我揣著相機(jī)與錄音掀潮,去河邊找鬼。 笑死整以,一個(gè)胖子當(dāng)著我的面吹牛胧辽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播公黑,決...
    沈念sama閱讀 39,025評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼邑商,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼摄咆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起人断,我...
    開(kāi)封第一講書(shū)人閱讀 37,761評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吭从,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后恶迈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體涩金,經(jīng)...
    沈念sama閱讀 44,217評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,545評(píng)論 2 327
  • 正文 我和宋清朗相戀三年暇仲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了步做。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,694評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奈附,死狀恐怖全度,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斥滤,我是刑警寧澤将鸵,帶...
    沈念sama閱讀 34,351評(píng)論 4 332
  • 正文 年R本政府宣布,位于F島的核電站佑颇,受9級(jí)特大地震影響顶掉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜挑胸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,988評(píng)論 3 315
  • 文/蒙蒙 一痒筒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗜暴,春花似錦凸克、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,778評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)咐容。三九已至舆逃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間戳粒,已是汗流浹背路狮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,007評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蔚约,地道東北人奄妨。 一個(gè)月前我還...
    沈念sama閱讀 46,427評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像苹祟,于是被迫代替她去往敵國(guó)和親砸抛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子评雌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,580評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容