Android使用RxJava+Retrofit2+Okhttp+MVP練習的APP
項目截圖


這是我的目錄結構
五步使用RxJava+Retrofit2+Okhttp+RxCache
第一步:導包
compile 'io.reactivex:rxjava:1.1.8'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
compile 'com.github.VictorAlbertos.RxCache:core:1.4.6'
第二步:新建API接口
public interface GanHuoService {
@GET("data/{type}/{number}/{page}")
Observable<DataResults> getDataResults(
@Path("type") String type,
@Path("number") int number,
@Path("page") int page
);
}
/**
* 緩存API接口
*
* @LifeCache設置緩存過期時間. 如果沒有設置@LifeCache , 數據將被永久緩存理除非你使用了 EvictProvider, EvictDynamicKey or EvictDynamicKeyGroup .
* EvictProvider可以明確地清理清理所有緩存數據.
* EvictDynamicKey可以明確地清理指定的數據 DynamicKey.
* EvictDynamicKeyGroup 允許明確地清理一組特定的數據. DynamicKeyGroup.
* DynamicKey驅逐與一個特定的鍵使用EvictDynamicKey相關的數據。比如分頁,排序或篩選要求
* DynamicKeyGroup。驅逐一組與key關聯的數據,使用EvictDynamicKeyGroup。比如分頁,排序或篩選要求
*/
public interface CacheProviders {
//緩存時間 1天
@LifeCache(duration = 7, timeUnit = TimeUnit.DAYS)
Observable<Reply<List<DataResults>>> getHomeTypes(Observable observable, DynamicKey userName, EvictDynamicKey evictDynamicKey);
}
public abstract class RetrofitUtils {
private static Retrofit mRetrofit;
private static OkHttpClient mOkHttpClient;
/**
* 獲取Retrofit對象
*
* @return
*/
protected static Retrofit getRetrofit() {
if (null == mRetrofit) {
if (null == mOkHttpClient) {
mOkHttpClient = OkHttp3Utils.getOkHttpClient();
}
//Retrofit2后使用build設計模式
mRetrofit = new Retrofit.Builder()
//設置服務器路徑
.baseUrl(Constant.API_SERVER + "/")
//添加轉化庫,默認是Gson
.addConverterFactory(GsonConverterFactory.create())
//添加回調庫,采用RxJava
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
//設置使用okhttp網絡請求
.client(mOkHttpClient)
.build();
}
return mRetrofit;
}
}
/*
* 所有的請求數據的方法集中地
* 根據MovieService的定義編寫合適的方法
* 其中observable是獲取API數據
* observableCahce獲取緩存數據
* new EvictDynamicKey(false) false使用緩存 true 加載數據不使用緩存
*/
public class HttpData extends RetrofitUtils {
private static File cacheDirectory = FileUtil.getcacheDirectory();
private static final CacheProviders providers = new RxCache.Builder()
.persistence(cacheDirectory)
.using(CacheProviders.class);
protected static final GanHuoService service = getRetrofit().create(GanHuoService.class);
private static class SingletonHolder {
private static final HttpData INSTANCE = new HttpData();
}
public static HttpData getInstance() {
return SingletonHolder.INSTANCE;
}
public void getHomeInfo(Observer<DataResults> observer, boolean isUseCache,String type, int number, int page) {
Observable observable= service.getDataResults(type,number,page);
Observable observableCahce=providers.getHomeTypes(observable,new DynamicKey("首頁"),new EvictDynamicKey(!isUseCache)).map(new HttpResultFuncCcche<List<DataResults>>());
setSubscribe(observableCahce,observer);
}
/**
* 插入觀察者
*
* @param observable
* @param observer
* @param <T>
*/
public static <T> void setSubscribe(Observable<T> observable, Observer<T> observer) {
observable.subscribeOn(Schedulers.io())
.subscribeOn(Schedulers.newThread())//子線程訪問網絡
.observeOn(AndroidSchedulers.mainThread())//回調到主線程
.subscribe(observer);
}
/**
* 用來統一處理RxCacha的結果
*/
private class HttpResultFuncCcche<T> implements Func1<Reply<T>, T> {
@Override
public T call(Reply<T> httpResult) {
return httpResult.getData();
}
}
}
public class HomeFragmentModel {
public void loadData(final OnLoadDataListListener listener,boolean isUseCache ,String type, int number, int page) {
HttpData.getInstance().getHomeInfo(new Observer<DataResults>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
listener.onFailure(e);
}
@Override
public void onNext(DataResults homeDto) {
listener.onSuccess(homeDto);
}
}, isUseCache,type,number,page);
}
}
MVC (Model-View-Controller)
M是指邏輯模型,V是指視圖模型,C則是控制器。一個邏輯模型可以對於多種視圖模型
使用MVC的目的是將M和V的實現代碼分離,方便擴展,便於以后的管理
從開發者的角度,MVC把應用程序的邏輯層與界面是完全分開的,最大的好處是:界面設計人員可以直接參與到界面開發,程序員就可以把精力放在邏輯層上。
雖然理論上可以實現,但實踐起來還是感覺不能完全分開...
Android中也可以說采用了當前比較流行的MVC框架,在Android中:
1) 視圖層(View):一般采用XML文件進行界面的描述,使用的時候可以非常方便的引入,但是用xml編寫了,又需要在Acitvity聲明並且實例化。
2) 控制層(Controller):Android的控制層的重任通常落在了眾多的Acitvity的肩上,要通過Activity交割Model業務邏輯層處理,這樣做的另外一個原因是Android中的Acitivity的響應時間是5s,如果耗時的操作放在這里,程序就很容易被回收掉。
3) 模型層(Model):對數據庫的操作、對網絡等的操作都應該在Model里面處理,當然對業務計算等操作也是必須放在的該層的。
MVP
MVP 就是基於MVC 的模式上的一個演化版本。在MVC模式中,Activity應該是屬於View這一層。而實質上,它既承擔了View,同時也包含一些Controller的東西在里面。隨着項目的迭代更新,這對開發很不友好,耦合度也原來越高,項目越來越難維護,而MVP 就是解決這樣的痛點。把Activity的View和Controller抽離出來就變成了View和Presenter。
MVP的優點:
-
模型與視圖完全分離,我們可以修改視圖而不影響模型
-
可以更高效地使用模型,因為所有的交互都發生在一個地方——Presenter內部
-
我們可以將一個Presenter用於多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因為視圖的變化總是比模型的變化頻繁。
-
如果我們把邏輯放在Presenter中,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)
public class HomeFragmentModel {
public void loadData(final OnLoadDataListListener listener,boolean isUseCache ,String type, int number, int page) {
HttpData.getInstance().getHomeInfo(new Observer<DataResults>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
listener.onFailure(e);
}
@Override
public void onNext(DataResults homeDto) {
listener.onSuccess(homeDto);
}
}, isUseCache,type,number,page);
}
}
public interface HomeFragmentView {
//顯示加載頁
void showProgress();
//關閉加載頁
void hideProgress();
//加載新數據
void newDatas(DataResults data);
//顯示加載失敗
void showLoadFailMsg();
}
public class HomePresenter implements OnLoadDataListListener<DataResults> {
private HomeFragmentView mView;
private HomeFragmentModel mModel;
public HomePresenter(HomeFragmentView mView) {
this.mView = mView;
this.mModel=new HomeFragmentModel();
mView.showProgress();
}
public void getDataResults(boolean isUseCache,String type, int number, int page) {
mModel.loadData(this,isUseCache,type,number,page);
}
@Override
public void onSuccess(DataResults data) {
mView.newDatas(data);
mView.hideProgress();
}
@Override
public void onFailure(Throwable e) {
Log.e("onFailure",e.toString());
mView.showLoadFailMsg();
}
}
public class DiscoveryFragment extends BaseFragment implements HomeFragmentView {
private HomePresenter homePresenter;
@Override
protected View initView(LayoutInflater inflater, ViewGroup container) {
return inflater.inflate(R.layout.fragment_list, container, false);
}
@Override
protected void initData(Bundle savedInstanceState) {
homePresenter = new HomePresenter(this);
}
@Override
protected void loadData() {
getData(isFirst);
}
private void getData(boolean isUseCache) {
switch (mTitle) {
case "首頁":
if (isTop) {
NOW_PAGE_FI = 1;
}
homePresenter.getDataResults(isUseCache,"all", fi_num, NOW_PAGE_FI);
break;
}
}
@Override
public void newDatas(DataResults dataResults) {
if (dataResults.isError()) {
Snackbar.make(recyclerview, "服務器出問題啦", Snackbar.LENGTH_SHORT).show();
} else {
if (mTitle.equals("干貨")) {
ganhuo_list = new ArrayList<>();
ganhuo_list.addAll(dataResults.getResults());
}
}
}
private void clearAdapterResults() {
switch (mTitle) {
case "首頁":
partAdapter.getResults().clear();
break;
case "妹紙":
girlyAdapter.getResults().clear();
break;
}
}
@Override
public void showLoadFailMsg() {
Snackbar.make(recyclerview, "網絡不順暢嘞,更新不了數據啦", Snackbar.LENGTH_SHORT).show();
}
@Override
public void showProgress() {
}
@Override
public void hideProgress() {
}
}
