天气预报项目学习总结(- ButterKnife - Retrofit 2.0(okhttp) - Rxjava - Jackson - Ormlite - Mosby简单使用总结)

该项目所用到的框架有:
- ButterKnife
- Retrofit 2.0(okhttp)
- Rxjava
- Jackson
- Ormlite
- Mosby
接下来会分别讨论使用各个框架后的一些感受。

1. ButterKnife

使用注解@,实现变量与方法与Anroid View的绑定。
消除findViewById,使用注解@BindView替代
将多个Views分组成一个列表或数组,一次性操作他们完成actions,setters或properties(PS:这部分功能没见到github上有示例代码)
使用@OnClick注解实现listner的绑定,消除匿名内部类
使用注解将变量与resource中的资源绑定

示例展示

 //代码示例
 class ExampleActivity extends Activity {
  @BindView(R.id.user) EditText username;
  @BindView(R.id.pass) EditText password;

  @BindString(R.string.login_error) String loginErrorMessage;

  @OnClick(R.id.submit) void submit() {
    // TODO call server...
  }

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);
    ButterKnife.bind(this);
    // TODO Use fields...
  }
}

Android ButterKnife Zelezny插件

同时还可以在AS中添加相关的ButterKnife插件Android ButterKnife Zelezny可以直接自动根据layout生成ButterKnife样板代码。

这里写图片描述

ButterKnife github地址

2.RxJava+Retrofit+Jackson

这三个在项目中一般是组合使用的,所以放在一个小节说了

RxJava(RxAndroid)

一个基于事件流编程的,用来处理异步任务的第三方库。

首先假设一个需求场景,我们需要通过网络请求得到一串数据,然后将得到的数据写入外存中的一个文件中。我们的处理应该是怎样的?

不用RxJava
//进行网络请求
httpClient.setCallBack(new CallBack(){
    void doOnSuccess(final Data data){
        //因为写入外存文件是耗时操作,所以要新开线程。
        new Thread(new Runnable(){
            public void run(){
                //将数据写入外存
                FileUtils.wirtToFile(data);
            }
        }).start();
    }).dopost(url);
}

通过模拟实现一个非常平常的异步任务需求,我们可以看到,代码已经存在多层嵌套了,匿名内部类里嵌套匿名内部类,代码易读性较差,相对的,当出了Bug或者需求改变了,在多层嵌套中进行调试和新添需求是十分痛苦且容易出错的。

使用RxJava
//第一步,通过网络请求获得Data
httpService.getData()
//第二步,将Data转化为Byte数组
.map(new Func1<Data,Byte[]>(){
    public Byte[] call(Data data){
        return data.toString.getByte();
    }
})
//在新线程中处理写入任务
.observerOn(Scheduler.newThread())
//第三部,处理Byte数组,将数据写入文件
.subscribe(new Action1<Byte[]>(){
    public void call(Byte[] byte){
        writeByteToFile(byte);
    }}
})

我们可以看到,在通过网络请求得到Data示例后,之后的数据解析转化,吸入外存的耗时操作都是在一个线性的编码中进行的,而不是像上一种实现方法需要嵌套再嵌套,考虑到实际需求中可能有更多层的加工以及操作,使用RxJava的结构明显更加的清晰易读。

Retrofit

首先Retrofit是一个网络请求的框架,底层使用的是Okhttp框架,使用注解简化网络请求中的部分配置。

使用Retrofit

第一步:
创建一个接口类,并做好网络请求部分参数配置

//创建业务请求
public interface MovieService {

    @GET("v2/movie/top250")
    Observable<HttpResult<List<Subjects>>> getMovie(@Query("start") int start, @Query("count") int count);
}

第二步:
创建Retrofit,实例化业务请求类,并发送请求

//示例化Retrofit
Retrofit retrofit1 = new Retrofit.Builder()
    .baseUrl(BASEURL)
    //将结果使用JackJson转化为Bean
    //.addConverterFactory(JacksonConverterFactory.create())
    //使网络请求接口可以返回Observable
    .//addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    //将结果转化为String
    .addConverterFactory(ScalarsConverterFactory.create())
    .build();
//实例化业务请求
MovieService movieService1 = retrofit1.create(MovieService.class);
//发送请求
movieService1.getMovieString(0,2).enqueue(new retrofit2.Callback<String>() {
    @Override
    public void onResponse(retrofit2.Call<String> call, retrofit2.Response<String> response) {
        Log.d(TAG,"onResponse:"+response.body().toString());
    }
    @Override
    public void onFailure(retrofit2.Call<String> call, Throwable t) {
        Log.d(TAG,"onFailure:"+t.getMessage());
    }
});
使用Okhttp
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
    .url("https://api.douban.com/v2/movie/top250?start=0&count=2")
    .build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
                        subscriber.onError(e);
                        subscriber.onCompleted();
                    }

    @Override
    public void onResponse(Call call, Response response) throws IOException
    {
        subscriber.onNext(response.body().string());
        subscriber.onCompleted();
    }
});

对比结果
从逻辑上来看Retrofit并没有单纯使用Okhttp那么清晰,而且代码也更多了,但是优点在于,Retrofit进行了一定的解耦,在有多个请求时,使用接口加注解的方式更加方便,而且能够非常简便的集成我们之前说得RxJava和在实际项目中经常用到的Json解析,所以在一定体量的项目下,使用Retrofit能够省很多事,并且也有利于后期的维护,因为每个代码块已经分别解耦了。

3.Jackson

至于Jackson我觉得做过网络请求的同学应该都很熟悉了,毕竟现在毕竟流行的网络数据传输用的都是Json。而Retrofit还支持很多其他的转化,如下:

Gson: com.squareup.retrofit2:converter-gson
Jackson: com.squareup.retrofit2:converter-jackson
Moshi: com.squareup.retrofit2:converter-moshi
Protobuf: com.squareup.retrofit2:converter-protobuf
Wire: com.squareup.retrofit2:converter-wire
Simple XML: com.squareup.retrofit2:converter-simplexml
Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

至于公司为什么选用Jackson,我觉得也许是Jackson的性能比较不错吧,在这里不深究了。
各json解析库性能比较

4.Ormlite

Object Relational Mapping Lite(Ormlite)是为持久化Java对象到Sql数据库中,而提供一些简单轻量的功能,同时避免了繁琐和额外开销的标准化ORM包。

Ormlite基本使用

AndroidStudio中引入

compile 'com.j256.ormlite:ormlite-core:4.48'
compile 'com.j256.ormlite:ormlite-android:4.48'

配置Bean类

//使用注解进行数据库表配置
//设置表名为tb_test
@DatabaseTable(tableName = "tb_test")
public class TestDbBean {
    //将该变量绑定为name列
    @DatabaseField(columnName = "name")
    private String name;
    //设置该字段为id字段,且自增
    @DatabaseField(generatedId = true)
    private int id;

    public TestDbBean() {
    }

    public TestDbBean(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "TestDbBean{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

配置Dao类

public class TestDbDao {

    private Context context;

    public TestDbDao(Context context) {
        this.context = context;
    }

    public void DbTestAdd()
    {
        MyDatabaseHelper helper = MyDatabaseHelper.getHelper(context);
        Dao<TestDbBean,Integer> dao = helper.getTestDao();

        try {
            TestDbBean testBean = new TestDbBean();
            testBean.setName("myy");
            dao.create(testBean);
            testBean.setName("myy1");
            dao.create(testBean);
            testBean.setName("myy2");
            dao.create(testBean);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //helper关闭后无法再次使用,需要获取新的helper
            helper.close();
        }
    }

    public void DbTestDelete()
    {
        MyDatabaseHelper helper = MyDatabaseHelper.getHelper(context);
        Dao<TestDbBean,Integer> dao = helper.getTestDao();

        try {
            dao.deleteById(2);
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            helper.close();
        }


    }

    public void DbTestUpdate()
    {
        MyDatabaseHelper helper = MyDatabaseHelper.getHelper(context);
        Dao<TestDbBean,Integer> dao = helper.getTestDao();
        try {
            TestDbBean testBean = new TestDbBean();
            testBean.setName("myy_update");
            testBean.setId(3);
            dao.update(testBean);
//            这是更新ID用的
//            dao.updateId()
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            helper.close();
        }
    }

    public List<TestDbBean> DbTestQueryAll()
    {
        MyDatabaseHelper helper = MyDatabaseHelper.getHelper(context);
        Dao<TestDbBean,Integer> dao = helper.getTestDao();
        List<TestDbBean> beans = null;
        try {
            beans = dao.queryForAll();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            helper.close();
        }
        return  beans;
    }
}

实现DBHelper并继承OrmLiteSqliteOpenHelper类

public class MyDatabaseHelper extends OrmLiteSqliteOpenHelper {

    private static final String DB_NAME = "db_test";
    private static final int DB_VERSION = 2;
    private Dao<TestDbBean,Integer> dao;

    public MyDatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {
        try{
    //创建数据库表
               TableUtils.createTable(connectionSource,TestDbBean.class);
        }catch (SQLException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) {
        try {
            TableUtils.dropTable(connectionSource,TestDbBean.class,true);
            onCreate(database,connectionSource);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private static MyDatabaseHelper instance;

    public static  MyDatabaseHelper getHelper(Context context) {
        if(instance==null) {
            synchronized (MyDatabaseHelper.class){
                if(instance==null) {
                    instance = new MyDatabaseHelper(context);
                }
            }
        }
        return instance;
    }

    public Dao<TestDbBean,Integer> getTestDao()
    {
        if(dao==null)
        {
            try {
                dao = getDao(TestDbBean.class);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return dao;
    }

    @Override
    public void close() {
        super.close();
        //关闭后就无法使用了,置空以便gc回收
        dao=null;
        instance=null;
    }

}

相对于使用原生的数据库操作方式,Ormlite简化了数据库的创建,(使用@注解对bean进行配置即可完成表的配置)对于创建,增删改查等操作,使用TableUtils就能完成,非常方便。

5.Mosby

Mosby是一个服务于Android的MVP库。它旨在使用一个清晰的MVP架构,帮助我们开发一个时髦的Android应用。并且,Mosby还能通过引入ViewState和维持Presenter来处理横竖屏切换带来的问题(切换后数据丢失)。
Ps:不得不吐槽Mosby库的名字居然来自于一部电视剧《How I Met Your Mother》,原谅我没听说过

网上找的Mvp解析

引入

dependencies {
    compile 'com.hannesdorfmann.mosby:mvp:2.0.1'
    compile 'com.hannesdorfmann.mosby:viewstate:2.0.1'
}

设计View接口

View在App中对应于Activity或者Fragment等完成相对独立的业务逻辑的显示类。设计出来的View接口将对Presenter调用,应该只暴露更新相关UI的接口,至于接口的粒度可以根据具体业务进行划分。
具体代码借用官方的例子:
Mosby官方例子

public interface LoginView extends MvpView {

  // Shows the login form
  public void showLoginForm();

  // Called if username / password is incorrect
  public void showError();

  // Shows a loading animation while checking auth credentials
  public void showLoading();

  // Called if sign in was successful. Finishes the activity. User is authenticated afterwards.
  public void loginSuccessful();
}

实现View

public class LoginFragment extends MvpViewStateFragment<LoginView, LoginPresenter>
    implements LoginView {

  @InjectView(R.id.username) EditText username;
  @InjectView(R.id.password) EditText password;
  @InjectView(R.id.loginButton) ActionProcessButton loginButton;
  @InjectView(R.id.errorView) TextView errorView;
  @InjectView(R.id.loginForm) ViewGroup loginForm;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
      return inflater.inflate(R.layout.fragment_login, container, false);
    }

  @Override public ViewState createViewState() {
    return new LoginViewState();
  }

  @Override public LoginPresenter createPresenter() {
    return new LoginPresenter();
  }

  @OnClick(R.id.loginButton) public void onLoginClicked() {

    // Check for empty fields
    String uname = username.getText().toString();
    String pass = password.getText().toString();

    loginForm.clearAnimation();

    // Start login
    presenter.doLogin(new AuthCredentials(uname, pass));
  }

  // Called first time the fragment starts
  @Override public void onNewViewStateInstance() {
    showLoginForm();
  }

  @Override public void showLoginForm() {

    LoginViewState vs = (LoginViewState) viewState;
    vs.setShowLoginForm();

    errorView.setVisibility(View.GONE);
    setFormEnabled(true);
    loginButton.setLoading(false);
  }

  @Override public void showError() {

    LoginViewState vs = (LoginViewState) viewState;
    vs.setShowError();

    loginButton.setLoading(false);

    if (!isRestoringViewState()) {
      // Enable animations only if not restoring view state
      loginForm.clearAnimation();
      Animation shake = AnimationUtils.loadAnimation(getActivity(), R.anim.shake);
      loginForm.startAnimation(shake);
    }

    errorView.setVisibility(View.VISIBLE);
  }

  @Override public void showLoading() {

    LoginViewState vs = (LoginViewState) viewState;
    vs.setShowLoading();
    errorView.setVisibility(View.GONE);
    setFormEnabled(false);

    loginButton.setLoading(true);
  }

  private void setFormEnabled(boolean enabled) {
    username.setEnabled(enabled);
    password.setEnabled(enabled);
    loginButton.setEnabled(enabled);
  }

  // Called when login was successful
  @Override public void loginSuccessful() {
    getActivity().finish();
  }
}

继承Presenter

继承了MvpBasePresenter,并在这个类里完成相关业务逻辑的组装

public class LoginPresenter extends MvpBasePresenter<LoginView> {

  private AccountManager accountManager;
  private EventBus eventBus;

  @Inject public LoginPresenter(AccountManager accountManager,
      EventBus eventBus) {
    this.accountManager = accountManager;
    this.eventBus = eventBus;
  }

  public void doLogin(AuthCredentials credentials) {

    if (isViewAttached()) {
      getView().showLoading();
    }

    // Kind of "callback"
    subscriber = new Subscriber<Account>() {
      @Override public void onCompleted() {
        if(isViewAttached()){
          getView().loginSuccessful();
        }
      }

      @Override public void onError(Throwable e) {
        if (isViewAttached()){
          getView().showError();
        }
      }

      @Override public void onNext(Account account) {
        eventBus.post(new LoginSuccessfulEvent(account));
      }
    };

    // do the login
    accountManager.doLogin(credentials).subscribe(subscriber);
  }
}

总体来说MVP架构中的事件链如下:
View触发事件->调用Presenter中的业务方法->Presenter从View中获取相关状态->Presenter调用Model,如:网络请求,数据库操作等->得到结果,调用View更新UI

ViewState

如果要引入ViewState解决横竖屏切换问题只需要用MvpLceViewStateFragment 代替MvpLceFragment然后实现createViewState()和getData()就好。

从使用后的体验来看,其实Mosby所做的事情大部分是一种规范作用,并且还是比较轻量级的,当然Mosby还提供了对应于LEC结构(Loading-Error-Content)的类和接口,封装好了对应于LEC的接口,只需要继承并实现就好。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值