關(guān)于RxAndroid+Mvp和Rxswift+Mvvm的對(duì)比學(xué)習(xí)

學(xué)swift也挺久了垦写,但是版本一直在迭代古毛,所以也就看看基礎(chǔ)的钦购,剛好這些天拿到了公司蘋果機(jī)的使用權(quán)且swift也升級(jí)到3.0,那就認(rèn)真地學(xué)習(xí)一下褂萧。
當(dāng)我想寫一個(gè)ios app時(shí)押桃,我在想ios架構(gòu)應(yīng)該用什么架構(gòu),于是乎Google了一把--->MVVM导犹。再加上之前學(xué)習(xí)RxAndroid的時(shí)候搜索到了一篇RxAcdroid和RxSwift的對(duì)比文章唱凯,所以就催生了這篇文章,順便我自己也整理記錄一下谎痢。歡迎交流學(xué)習(xí)磕昼。
1,RxAndroid和RxSwift是Rx系列的不同語言的實(shí)現(xiàn)节猿,這里就不介紹了票从。
2,Retrofit和Moya兩者也很相似滨嘱,可以說Moya是Swift版的Retrofit峰鄙。兩者都要定義一個(gè)BaseUrl,然后定義方法(get太雨,post等)吟榴,路徑,參數(shù)囊扳。不同方法的路徑和參數(shù)不同吩翻。只是Retrofit不在同一個(gè)文件中進(jìn)行設(shè)置。
3锥咸,mvp狭瞎,和mvvm。mvvm是mvp的進(jìn)一步的產(chǎn)物她君。據(jù)我個(gè)人認(rèn)知android這邊架構(gòu)流行mvp脚作,Swift這邊架構(gòu)流行mvvm,所以Android架構(gòu)用的是mvp缔刹,Swift用的是mvvm球涛。
首先來看下目錄架構(gòu):

架構(gòu).jpg

運(yùn)行效果:

初始狀態(tài).jpg

先從相似點(diǎn)入手:
1.Android和Swift都有Model,但是Android的Model是處理網(wǎng)絡(luò)請(qǐng)求的校镐,它的功能是提供數(shù)據(jù)亿扁。而Swift的Model則記錄一些屬性。在Android這邊鸟廓,記錄屬性的是Bean對(duì)象从祝,所以Bean對(duì)應(yīng)Model襟己。

2.Presenter和ViewModel:Presenter處理業(yè)務(wù)邏輯,從Model中獲取數(shù)據(jù)牍陌。ViewModel則兩者都有擎浴。

Android Model

public class MainModelImpl implements MainContract.Model{
    RetrofitService retrofitService;
    public MainModelImpl() {
//        Retrofit retrofit = new Retrofit.Builder()                              //生成實(shí)例
//                .baseUrl("http://192.168.191.1:3000/")                         //基礎(chǔ)url,會(huì)拼接NetService中的參數(shù)
//                .addConverterFactory(GsonConverterFactory.create())            //使用Gson解析
//                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())      //加入RxJava的適配器
//                .build();
        retrofitService = RetrofitServiceInstance.getInstance();
    }


//  訪問網(wǎng)絡(luò)
    @Override
    public Observable<LoginBean> loginAPP(String username, String password) {
        return retrofitService.loginAPP(username,password)
                .compose(RxHelper.<LoginBean>handleResult());
    }
    //   自己構(gòu)造數(shù)據(jù)毒涧,模擬訪問
    @Override
    public Observable<LoginBean> loginLocal(final String username, final String password) {
        L.e("========>"+username);
        L.e("========>"+password);
        return Observable.create(new Observable.OnSubscribe<LoginBean>() {
            @Override
            public void call(Subscriber<? super LoginBean> subscriber) {
                if (username.equals("123456") && password.equals("123456")) {
                    subscriber.onNext(new LoginBean("this token is local", "0"));
                    subscriber.onCompleted();
                } else {
                    subscriber.onNext(new LoginBean("this token is local", "1"));
                    subscriber.onCompleted();
                }
//                可以自己判斷一些出現(xiàn)錯(cuò)誤的情況
//                subscriber.onError(new Exception("錯(cuò)誤贮预!"));
            }

        });
    }
}

在這里用到了一些Rx的封裝,是從鴻洋大神的微信公眾號(hào)中引用過來的契讲,這里貼一下原作者鏈接
原文連接
Android Presenter

public class MainPresenterImpl extends BasePresenter<MainContract.View> implements MainContract.Presenter {
    MainContract.Model model;

    public MainPresenterImpl() {
        this.model = new MainModelImpl();
    }

    @Override
    public void loginApp(String username, String password) {
    //        model.loginAPP(username, password)//這里我注釋掉了需要網(wǎng)絡(luò)的方法并隱藏了我個(gè)人的測(cè)試接口仿吞,
// 如果懂得后臺(tái)語言的話,也可以自己寫個(gè)接口
        model.loginLocal(username, password)
                .flatMap(new Func1<LoginBean, Observable<String>>() {
                    @Override
                    public Observable<String> call(final LoginBean loginBean) {
                        L.d("進(jìn)行保存token的操作==>" + loginBean.getToken());
                        StringBuilder statue = new StringBuilder();
                        statue.append("該帳號(hào)的身份是:");
                        if (loginBean.getStatue().equals("0")) {
                            statue.append("管理員")  ;
                        } else {
                            statue.append("普通用戶")  ;
                        }
                        return Observable.just(statue.toString());
                    }
                })
                .subscribeOn(Schedulers.io())                            //發(fā)布者在后臺(tái)線程中運(yùn)行
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<String>() {
                    @Override
                    public void onCompleted() {
                        L.e("=========>onCompleted");
                    }

                    @Override
                    public void onError(Throwable e) {
                        getMyView().setResult("該帳號(hào)登錄失敿衿唤冈!");
                        L.e("=========>onError");
                    }

                    @Override
                    public void onNext(String s) {
                        L.e("=========>onNext");
                            getMyView().setResult(s);
                    }
                });
    }


}

Swift ViewModel
這里文章和源碼有點(diǎn)不同,做了部分修改

import Foundation
import RxSwift
import Moya
import RxDataSources

class ViewModel {
    private let provider = RxMoyaProvider<MoyaAPI>()
    func login(username:String,password:String) -> Observable<String> {
        return provider.request(.LoginAPP(username: username, password: password))
            .mapJSON()
            .mapObject(type: BaseModel.self)
            .flatMap{ (value : BaseModel) -> Observable<String> in
                print("進(jìn)行保存token的操作==>\(value.data!["token"]!)")
                let str:String
                guard "\(value.data!["statue"]!)" != "0" else{
                    return Observable.just("登錄錯(cuò)誤")
                }
                if "\(value.data!["statue"]!)" == "0"{
                    str = "該賬號(hào)的身份是:管理員"
                }else{
                    str = "該賬號(hào)的身份是:普通用戶"
                }
                return Observable.just(str)
        }
    }
    
    func loginLocal(username:String,password:String) -> Observable<String> {
        return Observable.create({ (subscribe) -> Disposable in
            if username == "123456" && password == "123456"{
                let basemodel = BaseModel(code: "200", message: "success", data: ["token" : "this token is local" as AnyObject,"statue":"0" as AnyObject])
                subscribe.onNext(basemodel)
            }else{
                let basemodel = BaseModel(code: "200", message: "success", data: ["token" : "this token is local" as AnyObject,"statue":"1" as AnyObject])
            subscribe.onNext(basemodel)
            }
            subscribe.onCompleted()
            return Disposables.create()
        })
            .flatMap{ (value : BaseModel) -> Observable<String> in
                print("進(jìn)行保存token的操作==>\(value.data!["token"]!)")
                let str:String
                if "\(value.data!["statue"]!)" == "0"{
                    str = "該賬號(hào)的身份是:管理員"
                }else{
                    str = "該賬號(hào)的身份是:普通用戶"
                }
                return Observable.just(str)
        }
    }
}

3.View银伟,在Android這邊Activity充當(dāng)這View的角色你虹,在Swift中ViewController充當(dāng)這View的角色。

Android

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    presenter = new MainPresenterImpl();
    presenter.attachView(this);//綁定view
    init();
    event();
}
private void event() {
    btLogin.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            presenter.loginApp(etUsername.getText().toString(), etPassword.getText().toString());
        }
    });
}
@Override
public void setResult(String result) {
    tvResult.setText(result);
}

在Android這邊枣申,Presenter一般需要綁定Activity的生命周期售葡,避免持續(xù)的引用,造成內(nèi)存泄漏忠藤,這里了我沒有貼出來挟伙,在源碼查看。

Swift

let disposeBag = DisposeBag()
    let viewModel  = ViewModel()
    @IBOutlet weak var tfUsername: UITextField!
    @IBOutlet weak var ftPassword: UITextField!
    @IBOutlet weak var lbResult: UILabel!
    
    @IBAction func btClick(_ sender: UIButton) {
//        viewModel.loginLocal(username: tfUsername.text, password: ftPassword.text)//這里我注釋了需要網(wǎng)絡(luò)的方法接口模孩,改用本地的
        viewModel.loginLocal(username: tfUsername.text!, password: ftPassword.text!)
            .asDriver(onErrorJustReturn: "")//幫你保證在UI線程中執(zhí)行代碼 等
            .drive(lbResult.rx.text)
            .addDisposableTo(disposeBag)//自動(dòng)銷毀相關(guān)的訂閱
    }

view這里Swift也用了Rx,在學(xué)swift的時(shí)候看到一種觀點(diǎn):ViewController 不應(yīng)該更改數(shù)據(jù)尖阔。
這里我參考了這個(gè)觀點(diǎn)。
Android Gradle

compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
compile 'io.reactivex:rxandroid:1.2.0'
compile 'io.reactivex:rxjava:1.1.0'

Swift Cocopods

use_frameworks!

target 'Login_rxswift' do
pod 'RxSwift',    '~> 3.0.0-beta.1'
pod 'RxCocoa',    '~> 3.0.0-beta.1'
pod 'ObjectMapper', '~> 2.2'
pod 'Moya/RxSwift', git: 'https://github.com/Moya/Moya.git', tag: '8.0.0-beta.4'
pod 'RxDataSources', '~> 1.0'
pod 'SnapKit', '~> 3.0.2'
end

post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '3.0'
config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.10'
end
end
end

服務(wù)端使用nodejs的Express框架編寫的榨咐,這里進(jìn)行了簡(jiǎn)單的判斷介却,返回json數(shù)據(jù)

router.post('/login2', function (req, res) {
    if (req.body.username == "123456" && req.body.password == "123456") {
        res.json({"code": "200", "message": "success", "data": {"token": "this token is from server", "statue": "0"}});
    } else {
        res.json({"code": "200", "message": "success", "data": {"token": "this token is from server", "statue": "1"}});
    }
});

源碼在評(píng)論區(qū),哈哈

參考文章:
https://github.com/devxoul/RxTodo/blob/master/README.md
http://blog.dianqk.org/2016/07/06/learn-rxtodo/
http://www.reibang.com/p/178b6e24ba7e

我的公眾號(hào)

公眾號(hào)內(nèi)容描述

zone7 公眾號(hào)块茁,與您一起學(xué)習(xí)分享后端知識(shí)齿坷。本公眾號(hào)涉及的知識(shí)點(diǎn)將會(huì)有:nodejs,python数焊,docker永淌,kubernetes,后端架構(gòu)等


image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末佩耳,一起剝皮案震驚了整個(gè)濱河市遂蛀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌干厚,老刑警劉巖李滴,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件螃宙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡所坯,警方通過查閱死者的電腦和手機(jī)谆扎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芹助,“玉大人燕酷,你說我怎么就攤上這事≈芟梗” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵饵蒂,是天一觀的道長(zhǎng)声诸。 經(jīng)常有香客問我,道長(zhǎng)退盯,這世上最難降的妖魔是什么彼乌? 我笑而不...
    開封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮渊迁,結(jié)果婚禮上慰照,老公的妹妹穿的比我還像新娘。我一直安慰自己琉朽,他們只是感情好毒租,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著箱叁,像睡著了一般墅垮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上耕漱,一...
    開封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天算色,我揣著相機(jī)與錄音,去河邊找鬼螟够。 笑死灾梦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妓笙。 我是一名探鬼主播若河,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼给郊!你這毒婦竟也來了牡肉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤淆九,失蹤者是張志新(化名)和其女友劉穎统锤,沒想到半個(gè)月后毛俏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饲窿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年煌寇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逾雄。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡阀溶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸦泳,到底是詐尸還是另有隱情银锻,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布做鹰,位于F島的核電站击纬,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏钾麸。R本人自食惡果不足惜更振,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饭尝。 院中可真熱鬧肯腕,春花似錦、人聲如沸钥平。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽帖池。三九已至奈惑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間睡汹,已是汗流浹背肴甸。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留囚巴,地道東北人原在。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像彤叉,于是被迫代替她去往敵國(guó)和親庶柿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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