天天看点

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。

android和ios当前流行架构对比学习

首先来看下目录架构:

android和ios当前流行架构对比学习

运行效果:

android和ios当前流行架构对比学习
android和ios当前流行架构对比学习
android和ios当前流行架构对比学习

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

源码下载链接 下载密码: k2i4

我的简书地址

参考文章:

https://github.com/devxoul/RxTodo/blob/master/README.md

http://blog.dianqk.org/2016/07/06/learn-rxtodo/

http://www.jianshu.com/p/178b6e24ba7e

关注微信公众号,获取最新技术文章

android和ios当前流行架构对比学习