您的位置:首页 > 其它

一个小框架,基于rx_retrofit2_mvp

2016-11-04 15:11 627 查看
离职在即,也没什么事情做,就鼓捣了一下,随意搭建了一个小框架,看看以后能不能搞出自己的一个model,好了,不说别的,上代码

1,先上依赖库

compile 'io.reactivex:rxandroid:1.2.1'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
compile 'com.android.support:design:24.2.1'
compile 'com.android.support:recyclerview-v7:24.2.1'
compile 'com.android.support:cardview-v7:24.2.1'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.github.chrisbanes.photoview:library:1.2.3'


2, 依赖retrolambda

在app.build依赖

apply plugin: 'me.tatarka.retrolambda'
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}


然后在项目的app.build依赖

classpath 'me.tatarka:gradle-retrolambda:3.2.5'
       OK,到这里我们的环境搭建就完成了

3,搭建Http请求模块

搭建工具类RetrofitUtils
package mvpmaster.lht.com.lht.utils;

import com.squareup.okhttp.OkHttpClient;

import java.util.concurrent.TimeUnit;

import retrofit.GsonConverterFactory;
import retrofit.Retrofit;
import retrofit.RxJavaCallAdapterFactory;

/**
* Created by Ly on 2016/10/14.
*/

public class RetrofitUtils {
private static final int READ_TIMEOUT = 60;//读取超时时间 单位 秒
private static final int CONN_TIMEOUT = 60;//连接超时时间 单位 秒
private static Retrofit retrofit;

public RetrofitUtils() {
}

public static Retrofit getInstance(String url) {
retrofit = null;
// 初始化一个okhttpClicent的对象 不然ref会自己添加一个
OkHttpClient client = new OkHttpClient();
//        设置读取时间为1分钟
client.setReadTimeout(READ_TIMEOUT, TimeUnit.MINUTES);
//        设置链接时间为12s
client.setConnectTimeout(CONN_TIMEOUT, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit;
}
}
Ps,我发现这里并不能做到session的保持,在某些公司的后台并不能支持这样的请求库,我稍候会上传一个更新后的版本
这里写上我的一个interface(为什么我用了rx还要有callback???黑人问号脸)
package mvpmaster.lht.com.lht.conf;

/**
* Created by Ly on 2016/10/13.
*/

public interface OkHttpCallBack<T> {
void onSuccess(T t);//成功的回调

void onFaild(Throwable e);//失败的回调

void onFinish();
}


然后是一些比较通用的api文件了,我就不写注释了
package mvpmaster.lht.com.lht.conf;

/**
* Created by Ly on 2016/11/2.
*/

public class HttpConf {
private static final String ZHIHU_BASE_URL = "http://news-at.zhihu.com/api/4/";
private static final String GANK_BASE_URL = "http://gank.io/api/";
private static final String DAILY_BASE_URL = "http://app3.qdaily.com/app3/";

public static String getZhihuBaseUrl() {
return ZHIHU_BASE_URL;
}

public static String getGankBaseUrl() {
return GANK_BASE_URL;
}

public static String getDailyBaseUrl() {
return DAILY_BASE_URL;
}
}

package mvpmaster.lht.com.lht.conf;

/**
* Created by Ly on 2016/11/2.
*/

public class HttpStatusConf {
private static final int SUCCESS = 200;

public static int getSUCCESS() {
return SUCCESS;
}
}

package mvpmaster.lht.com.lht.utils;

import mvpmaster.lht.com.lht.ui.beanIml.DailyBean;
import mvpmaster.lht.com.lht.ui.beanIml.NewsDetailBean;
import mvpmaster.lht.com.lht.ui.beanIml.NewsTimeLine;
import retrofit.http.GET;
import retrofit.http.Path;
import rx.Observable;

/**
* Created by Ly on 2016/10/14.
*/

public interface APIService {

@GET("news/latest")
Observable<NewsTimeLine> getZhiHuList();

@GET("news/before/{time}")
Observable<NewsTimeLine> getBeforetNews(@Path("time") String time);

@GET("news/{id}")
Observable<NewsDetailBean> getDetailNews(@Path("id") String id);

//    for daily
@GET("homes/index/{num}.json")
Observable<DailyBean> getDailyTimeLine(@Path("num") String num);

}


4,网络请求搭建完了,重头戏来了,我们来搭建mvp的基本框架

我们的baseActivity
package mvpmaster.lht.com.lht.base;

import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.TypedValue;
import android.view.MenuItem;

import butterknife.ButterKnife;
import mvpmaster.lht.com.lht.R;

/**
* Created by Ly on 2016/11/2.
*/

public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity {

protected T mPresenter;
private AppBarLayout mAppBar;
private Toolbar mToolbar;
private SwipeRefreshLayout mRefreshLayout;

public Context mContext;
private boolean mIsRequestDataRefresh = false;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
// 允许为空 不是所有的都要实现这个模式
if (createPresenter() != null) {
mPresenter = createPresenter();
mPresenter.attachView((V) this);
}
setContentView(provideContentViewId());
ButterKnife.bind(this);

mAppBar = (AppBarLayout) findViewById(R.id.app_bar_layout);
mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar != null && mAppBar != null) {
setSupportActionBar(mToolbar); //把Toolbar当做ActionBar给设置

if (canBack()) {
ActionBar actionBar = getSupportActionBar();
if (null != actionBar) {
//设置ActionBar一个返回箭头,主界面没有,次级界面有
actionBar.setDisplayHomeAsUpEnabled(true);
}
if (Build.VERSION.SDK_INT >= 21) {
//Z轴浮动
mAppBar.setElevation(10.6F);
}
}
}
if (isSetRefresh()) {
setupSwipeRefresh();
}

}

public static void toIntent(Context context, String... str) {
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 此时android.R.id.home即为返回箭头
if (item.getItemId() == android.R.id.home) {
onBackPressed();
finish();
return true;
} else {
return super.onOptionsItemSelected(item);
}
}

@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.detachView();
}
}

/**
* 生成下拉刷新
*/
private void setupSwipeRefresh() {
mRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
if (null != mRefreshLayout) {
mRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary);
mRefreshLayout.setProgressViewOffset(true,
0,
(int) TypedValue.applyDimension
(TypedValue.COMPLEX_UNIT_DIP, 24, getResources()
.getDisplayMetrics()));
}
}

/**
* 设置刷新
*
* @param requestDataRefresh
*/
public void setRefresh(boolean requestDataRefresh) {
if (mRefreshLayout == null) {
return;
}
if (!requestDataRefresh) {
mIsRequestDataRefresh = false;
mRefreshLayout.postDelayed(() -> {
if (mRefreshLayout != null) {
mRefreshLayout.setRefreshing(false);
}
}, 1000);
} else {
mRefreshLayout.setRefreshing(true);
}
}

/**
* 数据刷新
*/
public void requestDataRefresh() {
mIsRequestDataRefresh = true;
}

/**
* 判断当前 Activity 是否允许返回
* 主界面不允许返回,次级界面允许返回
*
* @return false
*/
public boolean canBack() {
return false;
}

/**
* 判断子Activity是否需要刷新功能
*
* @return false
*/
public Boolean isSetRefresh() {
return false;
}

/**
* 创建P
*
* @return T
*/
protected abstract T createPresenter();

/**
* 用于引入布局文件
*
* @return
*/
abstract protected int provideContentViewId();
}

package mvpmaster.lht.com.lht.base;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import butterknife.ButterKnife;
import mvpmaster.lht.com.lht.R;

/**
* Created by Ly on 2016/11/2.
*/

public abstract class BaseFragment<V, T extends BasePresenter<V>> extends Fragment {
protected Context mContext;
protected T mPresenter;

private boolean mIsRequestDataRefresh = false;
private SwipeRefreshLayout mRefreshLayout;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity();
mPresenter = createPresenter();
mPresenter.attachView((V) this);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(createViewLayoutId(), container, false);
ButterKnife.bind(this, rootView);
initView(rootView);
if (isSetRefresh()) {
setupSwipeRefresh(rootView);
}
return rootView;
}

@Override
public void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}

private void setupSwipeRefresh(View view) {
mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
if (mRefreshLayout != null) {
mRefreshLayout.setColorSchemeResources(R.color.refresh_progress_1,
R.color.refresh_progress_2, R.color.refresh_progress_3);
mRefreshLayout.setProgressViewOffset(true, 0, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
mRefreshLayout.setOnRefreshListener(this::requestDataRefresh);
}
}

public void requestDataRefresh() {
mIsRequestDataRefresh = true;
}

public void setRefresh(boolean requestDataRefresh) {
if (mRefreshLayout == null) {
return;
}
if (!requestDataRefresh) {
mIsRequestDataRefresh = false;
mRefreshLayout.postDelayed(() -> {
if (mRefreshLayout != null) {
mRefreshLayout.setRefreshing(false);
}
}, 1000);
} else {
mRefreshLayout.setRefreshing(true);
}
}

protected abstract T createPresenter();

protected abstract int createViewLayoutId();

protected void initView(View rootView) {
}

public Boolean isSetRefresh() {
return true;
}
}
还有其他的一些base,我就不一一上传了。
说一下我理解的mvp,在我的看法中,mvp,m是要做一些耗时操作的,像读取网络数据,数据库数据,sp数据啊...这些脏活苦活全部都丢给它去做,我们在contract中给它定义好了interface,model类在自己本身去实现它,然后按着上层的要求去做那些苦活累活;而View呢?我觉得一般是指我们的activity或者fragment巴,他们就负责一些比较轻松的东西了,像显示个toast啊,show一下dialog啊,拿一下editext的数据啊,最苦力也就是设置个适配器啊,监听一下滑动啊之类的,反正最轻松的那个就是它了;然后就是presenter了,这个类我觉得挺难弄的,类似于红娘吧,它也要实现Contract的interface,并且要持有model和view的引用,在interface的回调里面去操控model类去做耗时操作,然后在相应的callback(怎么又是callback?)去操控view去实现各种交互。(不要喷我说得那么模糊,但是这种东西写不出来,用了就会有这种想法,而且!用了一次mvp,你就不会再想去用mvc了)
我们举一个例子,首页那里拿取了知乎的信息,用了一个recyclerview去显示拿到的数据,我们就用它来讲,我先上传一波项目目录,不然太懵逼了。


我们先看我们的fragment 
package mvpmaster.lht.com.lht.ui.fragment.zhuhu;

import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.Toast;

import butterknife.Bind;
import mvpmaster.lht.com.lht.R;
import mvpmaster.lht.com.lht.base.BaseFragment;
import mvpmaster.lht.com.lht.ui.adapter.ZhiHuAdapter;
import mvpmaster.lht.com.lht.ui.beanIml.NewsTimeLine;

/**
* Created by Ly on 2016/11/2.
*/

public class ZhiHuFragment extends BaseFragment<ZhiHuContract.ZhiHuView, ZhiHuPresenter> implements ZhiHuContract.ZhiHuView {
@Bind(R.id.content_list)
RecyclerView mRlvZhiHu;

private LinearLayoutManager mLayoutManager;
private ZhiHuAdapter zhiHuAdapter;

// 最后一个可见的视图
private int lastVisibleItem;
// 是否加载过更多
private boolean isLoadMore = false;
// 知乎日报需要的下一个参数
private String time;

/**
* 初始化配置
*/
private void initConf() {
//        适配器
zhiHuAdapter = new ZhiHuAdapter(getActivity());
//        manager
mLayoutManager = new LinearLayoutManager(getActivity());
mRlvZhiHu.setLayoutManager(mLayoutManager);
mRlvZhiHu.setAdapter(zhiHuAdapter);
//        启动自动刷新配置
setDataRefresh(true);
//        获取第一次的数据
mPresenter.getDataList();
//        检测recView的滑动状态
scrollRecycleView();
}

/**
* recyclerView Scroll listener , maybe in here is wrong ?
*/
public void scrollRecycleView() {
mRlvZhiHu.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
lastVisibleItem = mLayoutManager
.findLastVisibleItemPosition();
if (mLayoutManager.getItemCount() == 1) {
zhiHuAdapter.updateLoadStatus(zhiHuAdapter.getLOAD_MORE());
return;
}
if (lastVisibleItem + 1 == mLayoutManager
.getItemCount()) {
zhiHuAdapter.updateLoadStatus(zhiHuAdapter.getLOAD_PULL_TO());
isLoadMore = true;
zhiHuAdapter.updateLoadStatus(zhiHuAdapter.getLOAD_MORE());
mPresenter.getBeforeDateList(time);
}
}
}

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();
}
});
}

@Override
public void requestDataRefresh() {
super.requestDataRefresh();
setDataRefresh(true);
mPresenter.getDataList();
}

@Override
protected ZhiHuPresenter createPresenter() {
return new ZhiHuPresenter(this);
}

@Override
protected int createViewLayoutId() {
return R.layout.fragment_zhihu;
}

@Override
public void setDataRefresh(boolean refresh) {
setRefresh(refresh);
}

public static ZhiHuFragment newInstance() {
Bundle args = new Bundle();
ZhiHuFragment fragment = new ZhiHuFragment();
fragment.setArguments(args);
return fragment;
}

@Override
public void loadFinishAndReset(NewsTimeLine newsTimeLine, String time) {
zhiHuAdapter.resetData(newsTimeLine);
setDataRefresh(false);
this.time = time;
}

@Override
public void loadFinishAndAdd(NewsTimeLine newsTimeLine, String time) {
zhiHuAdapter.addData(newsTimeLine);
this.time = time;
}

@Override
public void loadFailure() {
setDataRefresh(false);
zhiHuAdapter.updateLoadStatus(zhiHuAdapter.getLOAD_FAILURE());
}

@Override
protected void initView(View rootView) {
super.initView(rootView);
initConf();
}

@Override
public void TsShow(String msg) {
Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT).show();
}
}
我们在这个看到一个mPresenter,这个是什么鬼?是从哪里来的?其实这个就是Presenter的对象了,也是BaseActivity的泛型里面的那个

我们点取查看它的引用,发现在这个fragment里面一共被引用了3次,第一次是刚进去页面的时候,第一次读取数据,第二个是下拉加载的时候,读取了下一波的数据,再有一次是上拉刷新的时候,我们重新刷新了一次页面拿取了最新的一波数据,但是?拿数据的在哪里呢?网络请求呢?在哪里??
我们看Presenter里面做的操作吧:
package mvpmaster.lht.com.lht.ui.fragment.zhuhu;

import mvpmaster.lht.com.lht.base.BasePresenter;
import mvpmaster.lht.com.lht.conf.OkHttpCallBack;
import mvpmaster.lht.com.lht.ui.beanIml.NewsTimeLine;

/**
* Created by Ly on 2016/11/2.
*/

public class ZhiHuPresenter extends BasePresenter<ZhiHuContract.ZhiHuView> implements ZhiHuContract.ZhiHuPresenter {
private ZhiHuContract.ZhiHuView zhiHuView;
private ZhiHuContract.ZhiHuModel zhiHuModel;

public ZhiHuPresenter(ZhiHuContract.ZhiHuView zhiHuView) {
this.zhiHuView = zhiHuView;
zhiHuModel = new ZhiHuModel();
}

@Override
public void getDataList() {
zhiHuModel.getDataList(new OkHttpCallBack<NewsTimeLine>() {
@Override
public void onSuccess(NewsTimeLine newsTimeLine) {
zhiHuView.loadFinishAndReset(newsTimeLine, newsTimeLine.getDate());
}

@Override
public void onFaild(Throwable e) {
loadError(e);
zhiHuView.loadFailure();
}

@Override
public void onFinish() {

}
});
}

@Override
public void getBeforeDateList(String time) {
zhiHuModel.getBeforeDateList(time, new OkHttpCallBack<NewsTimeLine>() {
@Override
public void onSuccess(NewsTimeLine newsTimeLine) {
zhiHuView.loadFinishAndAdd(newsTimeLine, newsTimeLine.getDate());
}

@Override
public void onFaild(Throwable e) {
loadError(e);
zhiHuView.loadFailure();
}

@Override
public void onFinish() {

}
});
}

private void loadError(Throwable throwable) {
throwable.printStackTrace();
zhiHuView.TsShow(throwable.getMessage());
}
}
我们看到有三个ovver的方法,分别是刷新(第一次读取),加载,读取失败 三种情况:我们可以看到,这个类持有了View和Model两个模块,在方法体里面,我们调用了model的方法去做耗时,在结果方法体里面我们调用了view的方法去修改UI,同时presenter这个模块又被view持有了,view可以在声明周期里面去调用特定的方法,view和presenter相互沟通,view和model完全隔离,presenter调控model,presenter沟通全局。
补上我的git地址:https://github.com/LinHuanTanLy/mvpMaster

补上另外一个请求框架:https://github.com/LinHuanTanLy/OkHttpWithSession
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Ly