1. 程式人生 > >android和ios當前流行架構對比學習

android和ios當前流行架構對比學習

轉載請註明出處:http://blog.csdn.net/zone_?viewmode=contents
在工作中,我們 Android 開發都會或多或少地接觸到一些 IOS 的知識點,或許是討論一個 Android 和 IOS 都共通的問題,或許是兩端一起討論一個技術點的實現方案。我覺得了解一些 IOS 的基礎知識點時完全有必要的。
當我們學過一門語言之後,再學另外一門語言並且用這個新語言寫應用時,一般都會考慮這語言應該用什麼架構。那麼這就時從架構開始的。於是乎,我 Google 了一下 IOS 當前流行的架構,我得到的答案時 MVVM。那麼學習開始,再加上之前學習RxJava的時候搜尋到了一篇RxJava和RxSwift的對比文章,所以就催生了這篇文章,順便我自己也整理記錄一下。歡迎交流學習。

1,在兩端語言中都使用了 rx ,分別時 RxJava 和 RxSwift 。是 rx 系列的不同語言的實現,這裡就不介紹了。
2,Retrofit 和 Moya 兩者也很相似,可以說 Moya 是 Swift 版的 Retrofit 。兩者都要定義一個 BaseUrl,然後定義方法(get,post 等),路徑,引數。不同方法的路徑和引數不同。只是Retrofit不在同一個檔案中進行設定。
3,mvvm 是 mvp 的進一步的產物。據我個人認知 android 這邊架構流行mvp,Swift 這邊架構流行 mvvm,所以 Android 架構用的是 mvp,Swift 架構用的是mvvm。
這裡寫圖片描述

首先來看下目錄架構:

架構.jpg

執行效果:

初始狀態.jpg

兩端的 model

先從相似點入手:
1.Android 和 Swift 都有Model,但是 Android 的 Model 是處理網路請求的,它的功能是提供資料。而 Swift 的 Model 則記錄一些屬性,相當於 Android 的 bean。由於 swift 的 Model 相當於 bean 我就不過多介紹了。

Android Model

public class MainModelImpl implements MainContract.Model{
    RetrofitService retrofitService;
    public
MainModelImpl() { // Retrofit retrofit = new Retrofit.Builder() //生成例項 // .baseUrl("http://192.168.191.1:3000/") //基礎url,會拼接NetService中的引數 // .addConverterFactory(GsonConverterFactory.create()) //使用Gson解析 // .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //加入RxJava的介面卡 // .build(); retrofitService = RetrofitServiceInstance.getInstance(); } // 訪問網路 @Override public Observable<LoginBean> loginAPP(String username, String password) { return retrofitService.loginAPP(username,password) .compose(RxHelper.<LoginBean>handleResult()); } // 自己構造資料,模擬訪問 @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(); } // 可以自己判斷一些出現錯誤的情況 // subscriber.onError(new Exception("錯誤!")); } }); } }

在這裡用到了一些Rx的封裝,是從鴻洋大神的微信公眾號中引用過來的,這裡貼一下原作者連結
原文連線

兩端的邏輯控制器(presenter 和 viewmodel)

2.Presenter(A) 和 ViewModel(I):Presenter 處理業務邏輯,從 Model中獲取資料。ViewModel 則兩者都有。
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)//這裡我註釋掉了需要網路的方法並隱藏了我個人的測試介面,
// 如果懂得後臺語言的話,也可以自己寫個介面
        model.loginLocal(username, password)
                .flatMap(new Func1<LoginBean, Observable<String>>() {
                    @Override
                    public Observable<String> call(final LoginBean loginBean) {
                        L.d("進行儲存token的操作==>" + loginBean.getToken());
                        StringBuilder statue = new StringBuilder();
                        statue.append("該帳號的身份是:");
                        if (loginBean.getStatue().equals("0")) {
                            statue.append("管理員")  ;
                        } else {
                            statue.append("普通使用者")  ;
                        }
                        return Observable.just(statue.toString());
                    }
                })
                .subscribeOn(Schedulers.io())                            //釋出者在後臺執行緒中執行
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<String>() {
                    @Override
                    public void onCompleted() {
                        L.e("=========>onCompleted");
                    }

                    @Override
                    public void onError(Throwable e) {
                        getMyView().setResult("該帳號登入失敗!");
                        L.e("=========>onError");
                    }

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


}

Swift ViewModel
這裡文章中給出的程式碼和後面給出原始碼有點不同,做了點修改

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()//對映 json 資料
            .mapObject(type: BaseModel.self)
            .flatMap{ (value : BaseModel) -> Observable<String> in
                print("進行儲存token的操作==>\(value.data!["token"]!)")
                let str:String
                guard "\(value.data!["statue"]!)" != "0" else{
                    return Observable.just("登入錯誤")
                }
                if "\(value.data!["statue"]!)" == "0"{
                    str = "該賬號的身份是:管理員"
                }else{
                    str = "該賬號的身份是:普通使用者"
                }
                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("進行儲存token的操作==>\(value.data!["token"]!)")
                let str:String
                if "\(value.data!["statue"]!)" == "0"{
                    str = "該賬號的身份是:管理員"
                }else{
                    str = "該賬號的身份是:普通使用者"
                }
                return Observable.just(str)
        }
    }
}

兩端 view 的實現

3.View,在 Android 這邊 Activity 充當 View 的角色,在 Swift 中ViewController 充當這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的生命週期,避免持續的引用,造成記憶體洩漏,這裡了我沒有貼出來,在原始碼檢視。

Swift

let disposeBag = DisposeBag()
    let viewModel  = ViewModel()
    //以下三行相當於 findViewById()
    @IBOutlet weak var tfUsername: UITextField!//相當於 EditText
    @IBOutlet weak var ftPassword: UITextField!//
    @IBOutlet weak var lbResult: UILabel!//相當於 TextView

    //點選事件
    @IBAction func btClick(_ sender: UIButton) {
//        viewModel.loginLocal(username: tfUsername.text, password: ftPassword.text)//這裡我註釋了需要網路的方法介面,改用本地的
        viewModel.loginLocal(username: tfUsername.text!, password: ftPassword.text!)
            .asDriver(onErrorJustReturn: "")//幫你保證在UI執行緒中執行程式碼 等
            .drive(lbResult.rx.text)
            .addDisposableTo(disposeBag)//自動銷燬相關的訂閱
    }

view 這裡 Swift 也用了 Rx ,在學 swift 的時候看到一種觀點:view 不應該更改資料,只用來顯示資料。這裡我參考了這個觀點。

兩端的庫依賴

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

服務端的實現

為了真是測試這個小 demo,我用 nodejs 的 express 框架寫個簡單的後臺,其主要程式碼就是下面這段:

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"}});
    }
});

關注微信公眾號,獲取最新技術文章

zone_qrcode.jpg