第一次寫文章音瓷,不好之處還請諒解梅肤。2015最值得android程序猿去學習的就是rxjava了葡公,關于rxjava就不再多描述了袜啃,本文是介紹怎么使用mvp+rxjava+retrofit來構(gòu)建一個新的項目彼水。 項目采用mvp的方式崔拥,參考了google的官方mvp項目极舔。
Hot是關于微信頭條分享的app
github地址
最近加入了dagger凤覆,傳送門
項目介紹
1.BaseActivity的設計:
/**
* Created by wukewei on 16/5/26.
*/
public abstract class BaseActivity<T extends IPresenter> extends AppCompatActivity {
protected T mPresenter;
protected Activity mContext;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayout());
ButterKnife.bind(this);
mContext = this;
mPresenter = getPresenter();
initEventAndData();
}
protected void setCommonBackToolBack(Toolbar toolbar, String title) {
toolbar.setTitle(title);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
toolbar.setNavigationOnClickListener(v -> onBackPressed());
}
@Override
protected void onDestroy() {
super.onDestroy();
ButterKnife.unbind(this);
if (mPresenter != null) mPresenter.detachView();
}
protected abstract T getPresenter();
protected abstract int getLayout();
protected abstract void initEventAndData();
}
最主要的設置了presenter的泛型,并且提供了初始化的函數(shù) protected abstract T getPresenter();拆魏。
2.BasePresenter的設計
/**
* Created by wukewei on 16/5/26.
*/
public abstract class BasePresenter<T extends IView> implements IPresenter {
protected Activity mActivity;
protected T mView;
protected CompositeSubscription mCompositeSubscription;
// protected static final HotApi mHotApi = HotFactory.getHotApi();
public BasePresenter(Activity activity, T view) {
this.mActivity = activity;
this.mView = view;
}
protected void handleError(Throwable throwable) {
ToastUtil.showShort(mActivity, ErrorHanding.handleError(throwable));
}
protected void unSubscribe() {
if (mCompositeSubscription != null) {
mCompositeSubscription.unsubscribe();
}
}
protected void addSubscrebe(Subscription subscription) {
if (mCompositeSubscription == null) {
mCompositeSubscription = new CompositeSubscription();
}
mCompositeSubscription.add(subscription);
}
@Override
public void detachView() {
this.mView = null;
unSubscribe();
}
}
也采用了范型來綁定view盯桦,使用了CompositeSubscription來進行避免內(nèi)存的泄漏。
3.Api的設計
在實際情況中每個公司的api的設計都是不一樣的渤刃,我采用如下的設計形式拥峦。
/**
* Created by wukewei on 16/5/26.
*/
public class ApiResponse<T> {
public static final int SUCCESS_CODE = 200;
private int code;
private String msg;
private T newslist;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getNewsList() {
return newslist;
}
public void setNewsList(T newsList) {
this.newslist = newsList;
}
public boolean isSuccess() {
if (this.code == SUCCESS_CODE) {
return true;
} else {
return false;
}
}
}
你們可以根據(jù)自己公司的實際情況修改返回的成功碼。
4.Rxjava的一些設計:
大家都知道rxjava就是能靈活的在線程之間進行切換卖子,在使用的時候我使用了Transformer操作符略号。
/**
* Created by wukewei on 16/5/26.
*/
public class SchedulersCompat {
private final static Observable.Transformer ioTransformer = o -> o.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
public static <T> Observable.Transformer<T, T> applyIoSchedulers() {
return (Observable.Transformer<T, T>) ioTransformer;
}
}
在數(shù)據(jù)處理的時候,
/**
* Created by wukewei on 16/5/26.
*/
public class RxResultHelper {
public static <T> Observable.Transformer<ApiResponse<T>, T> handleResult() {
return apiResponseObservable -> apiResponseObservable.flatMap((Func1<ApiResponse<T>, Observable<T>>) tApiResponse -> {
if (tApiResponse.isSuccess()) {
//表示成功
return createData(tApiResponse.getNewsList());
} else {
return Observable.error(new ServerException(tApiResponse.getMsg()));
}
});
}
public static <T> Observable<T> createData(T t) {
return Observable.create(new Observable.OnSubscribe<T>() {
@Override
public void call(Subscriber<? super T> subscriber) {
try {
subscriber.onNext(t);
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
});
}
}
在這里你可以根據(jù)自己的實際情況來添加洋闽,大部分app都是有token一說玄柠,你可以在這里判斷當token過期的時候可以跳到登錄界面。
在項目中的時候就是
Subscription subscription = mHotApi.getPopular(this.pn, Constants.PAGE_SIZE, type)
.compose(SchedulersCompat.applyIoSchedulers())
.compose(RxResultHelper.handleResult())
.subscribe(populars -> {
mView.showContent();
if (isRefresh()) {
if (populars.size() == 0) mView.showNotdata();
mView.addRefreshData(populars);
} else {
mView.addLoadMoreData(populars);
}
}, throwable -> {
mView.showError(ErrorHanding.handleError(throwable));
handleError(throwable);
});
addSubscrebe(subscription);
是不是覺得使用起來非常的爽啊诫舅。
還有一個就是在實際情況中很多一部分請求要設置一些數(shù)據(jù)好比來自android的還是ios的羽利,或者添加一些token在請求中,本項目沒有使用token刊懈,但是有個apikey代碼如下:
**
* Created by wukewei on 16/5/26.
*/
public class OkHttpManager {
private static OkHttpClient mOkHttpClient;
public static OkHttpClient getInstance() {
if (mOkHttpClient == null) {
synchronized (OkHttpManager.class) {
if (mOkHttpClient == null) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
Interceptor apikey = chain -> chain.proceed(chain.request().newBuilder()
.addHeader("apikey", Constants.Api_Key).build());
File cacheFile = new File(App.getAppContext().getCacheDir(), "cache");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb
mOkHttpClient = new OkHttpClient.Builder()
.readTimeout(Constants.HTTP_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
.connectTimeout(Constants.HTTP_CONNECT_TIMEOUT, TimeUnit.MILLISECONDS)
.addInterceptor(apikey)
.addInterceptor(loggingInterceptor)
.addNetworkInterceptor(new HttpCacheInterceptor())
.cache(cache)
.build();
}
}
}
return mOkHttpClient;
}
static class HttpCacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetWorkUtil.isNetConnected(App.getAppContext())) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
Log.d("Okhttp", "no network");
}
Response originalResponse = chain.proceed(request);
if (NetWorkUtil.isNetConnected(App.getAppContext())) {
//有網(wǎng)的時候讀接口上的@Headers里的配置这弧,你可以在這里進行統(tǒng)一的設置
String cacheControl = request.cacheControl().toString();
return originalResponse.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma")
.build();
} else {
return originalResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=2419200")
.removeHeader("Pragma")
.build();
}
}
}
}
你可以在獲取本地token的時候,當有的時候加上即可虚汛。
7月13日更新:
5.網(wǎng)上有很多在mvp的時候關于數(shù)據(jù)層的設計匾浪,我這邊是添加了DataManager來管理app數(shù)據(jù),在p層是不關心數(shù)據(jù)的來源,無論是網(wǎng)絡的還是本地緩存的數(shù)據(jù)卷哩,這個設計參考了別的大神的實現(xiàn)蛋辈。
/**
* Created by wukewei on 16/7/12.
* 這個類是管理app的數(shù)據(jù)來源無論從網(wǎng)絡獲取.內(nèi)存.還是磁盤
*/
public class DataManager {
private static Gson gson;
private static RxBus rxBus;
private static HotApi mHotApi;
private static CacheLoader cacheLoader;
private Handler mHandler;
private DataManager() {}
public static DataManager getInstance() {
return DataManagerHolder.INSTANCE;
}
public static class DataManagerHolder {
private final static DataManager INSTANCE = new DataManager();
}
public void initService() {
gson = new Gson();
rxBus = RxBus.getDefault();
cacheLoader = CacheLoader.getInstance(App.getAppContext());
HandlerThread ioThread = new HandlerThread("IoThread");
ioThread.start();
mHandler = new Handler(ioThread.getLooper());
mHandler.post(new Runnable() {
@Override
public void run() {
mHotApi = HotFactory.getHotApi();
}
});
}
/***
* 獲取分類的類型
* @param
* @param
* @return
*/
public List<String> getTabs() {
List<String> tabs = new ArrayList<>();
tabs.add("科技");
tabs.add("美女");
tabs.add("生活");
tabs.add("娛樂");
tabs.add("搞笑");
tabs.add("宅男");
return tabs;
}
/***
* 獲取列表
* @param pn 頁碼
* @param type 類別名稱
* @return
*/
public Observable<List<Popular>> getPopular(int pn, String type) {
return mHotApi.getPopular(pn, Constants.PAGE_SIZE, type)
.compose(SchedulersCompat.applyIoSchedulers())
.compose(RxResultHelper.handleResult())
.doOnNext(populars -> {
if (pn == 1) {
ListPopular popular = new ListPopular(populars);
cacheLoader.upNewData(type, popular);
}
});
}
/***
* 獲取緩存信息 默認只緩存第一頁
* @param type 類別名稱
* @param
* @return
*/
public Observable<List<Popular>> getCachePopular(String type) {
NetworkCache<ListPopular> networkCache = new NetworkCache<ListPopular>() {
@Override
public Observable<ListPopular> get(String key, Class<ListPopular> cls) {
return mHotApi.getPopular(1, Constants.PAGE_SIZE, type)
.compose(SchedulersCompat.applyIoSchedulers())
.compose(RxResultHelper.handleResult())
.flatMap(populars -> {
ListPopular popular = new ListPopular(populars);
return Observable.just(popular);
});
}
};
return cacheLoader.asDataObservable(Constants.NEW_LIST + type, ListPopular.class, networkCache)
.map(listPopular -> listPopular.data);
}
}
記得在app的時候初始化,
public class App extends Application {
private static App appContext;
@Override
public void onCreate() {
super.onCreate();
appContext = this;
DataManager.getInstance().initService();
}
public static App getAppContext() {
return appContext;
}
}
這樣在p中的使用如下
Subscription subscription = DataManager.getInstance().getPopular(pn, type)
.subscribe(populars -> {
mView.showContent();
if (isRefresh()) {
if (populars.size() == 0) mView.showNotdata();
mView.addRefreshData(populars);
} else {
mView.addLoadMoreData(populars);
}
}, throwable -> {
if (isRefresh())
mView.showError(ErrorHanding.handleError(throwable));
handleError(throwable);
});
結(jié)束
本人對rxjava認識不夠深入殉疼,還在不斷的學習中梯浪,要是有什么錯誤之處還望指正。要是覺得對你有所幫助的話瓢娜,歡迎Star挂洛。再次送上項目的github地址。