android架構(gòu)篇
mvp+rxjava+retrofit+eventBus
高層不應(yīng)該知道低層的細節(jié)距境,應(yīng)該是面向抽象的編程拥坛。業(yè)務(wù)的實現(xiàn)交給實現(xiàn)的接口的類恼布。高層只負責(zé)調(diào)用耳贬。
首先,要介紹一下一個項目中好架構(gòu)的好處:好的軟件設(shè)計必須能夠幫助開發(fā)者發(fā)展和擴充解決方案攒射,保持代碼清晰健壯醋旦,并且可擴展,易于維護会放,而不必每件事都重寫代碼饲齐。面對軟件存在的問題,必須遵守SOLID原則(面向?qū)ο笪宕笤瓌t)咧最,不要過度工程化捂人,盡可能降低框架中模塊的依賴性。
之前的一段時間,學(xué)習(xí)了一些新的技術(shù),并把自己關(guān)注的技術(shù)整合了一下,是的,相似的技術(shù)有很多,自己擇優(yōu)選擇,將它們的思想和技術(shù)應(yīng)用到了自己的搭建的項目框架中.
限于自己能力水平有限,自己搭建的項目可能還有些不足,歡迎大家指正批評,讓自己的想法和設(shè)計思想走向正軌.O(∩_∩)O謝謝~
在框架中
1.項目整體框架: 利用google-clean-architecture的思想 來負責(zé)項目的整體MVP架構(gòu).**
- MVP是模型(Model)矢沿、視圖(View)滥搭、主持人(Presenter)的縮寫,分別代表項目中3個不同的模塊捣鲸。我以登錄為例子,進行說明.
這里每個業(yè)務(wù)首先要有一個管理接口Contract,在這里面有三個接口來面向接口編程, (Model),(View),(Presenter). 將三個接口放在一起便于管理.
/**
* 登錄關(guān)聯(lián)接口類
*
* Created by ccj on 2016/7/7.
*/
public interface LoginContract {
interface View extends BaseView {
void showProgress();
void hideProgress();
void showError(String error);
void navigateToMain();
void navigateToRegister();
}
interface Presenter extends BasePresenter {
void login(String username, String password);
void onDestroy();
}
interface Model{
void saveUserInfo(User user);
void saveLoginState(Boolean isLogin);
void saveRememberPass(User user);
}
}
模型(Model):實現(xiàn) implements LoginContract.Model 負責(zé)處理數(shù)據(jù)的加載或者存儲瑟匆,比如從網(wǎng)絡(luò)或本地數(shù)據(jù)庫獲取數(shù)據(jù)等;這里的login 涉及到的業(yè)務(wù)邏輯比較少請求網(wǎng)絡(luò) 采用了rxjava +retroft+gsons 相當(dāng)于 model層. 如果處理的出具多,就采用此model ,就像圖片保存顯示等等.
視圖(View):采用接口的方式,讓activity實現(xiàn)該接口,接口中有關(guān)于視圖的方法,例如”initVIew()”,”showDialog()”,”hideDialog()”等等, 負責(zé)界面數(shù)據(jù)的展示栽惶,與用戶進行交互愁溜;
public class LoginActivity extends BaseActivity implements LoginContract.View {
//省略bufferknife 注解
private LoginPresenter presenter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
presenter=new LoginPresenter(this);
presenter.start();//初始化控制層
}
//實現(xiàn)于view的方法
@Override
public void navigateToMain() {
Intent intent =new Intent(getBaseContext(),MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
- 主持人(Presenter):持有 view和model的對象,操作兩者的方法.相當(dāng)于協(xié)調(diào)者无午,是模型與視圖之間的橋梁,將模型與視圖分離開來,對view 和model 進行調(diào)度操作祝谚。
/**
* login的presenter層 進行對view 和 model 的控制,
* Created by ccj on 2016/7/7.
*/
public class LoginPresenter implements LoginContract.Presenter {
private LoginContract.View loginView;
public LoginPresenter(LoginContract.View loginView) {
this.loginView = loginView;
}
@Override
public void login(String username, String password) {
loginView.showProgress();
Observable<User> userObservable = APIService.userLogin(username, password);
userObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<User>() {
@Override
public void onCompleted() {
loginView.hideProgress();
}
@Override
public void onError(Throwable e) {
TLog.log(e.getMessage().toString());
loginView.hideProgress();
loginView.showError(e.getMessage().toString());
}
@Override
public void onNext(User getIpInfoResponse) {
TLog.log(getIpInfoResponse.toString());
loginView.navigateToMain();
}
});
}
@Override
public void start() {
}
2.網(wǎng)絡(luò)訪問: 采用rxjava+retrofit+gson進行網(wǎng)絡(luò)訪問,并輕松的將json轉(zhuǎn)為對象,結(jié)構(gòu)清晰,使用方便.
- 在APIService中初始化retrofit
/**
* 調(diào)用后臺的接口,架構(gòu)網(wǎng)絡(luò)層采用Retroft+Rxjava+gson
* Created by ccj on 2016/7/1.
*
*/
public class APIService {
private static final String TAG = "APIService";
public static final String URL_HOST ="http://123.234.82.23" ;//服務(wù)器端口
/**
* 基礎(chǔ)地址
* 初始化 retroft
*/
private static final Retrofit sRetrofit = new Retrofit.Builder()
.baseUrl(URL_HOST)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 使用RxJava作為回調(diào)適配器
.build();
private static final RetrofitRequest apiManager = sRetrofit.create(RetrofitRequest.class);
/**
* 登錄,返回,我這邊用的是json格式的post,大家可以進行選擇
* @param city
* @return
*/
public static Observable<User> userLogin(String format, String city) {
HashMap<String,String> hashMap =new HashMap<>();
hashMap.put("UserPhone", format);
hashMap.put("UserPassWord", city);
TLog.log(hashMap.toString());
Observable<User> ss = apiManager.userLogin(hashMap);
return ss;
}
/**********************仿照上面的方法,進行請求數(shù)據(jù)****************************/
- 用retrofit訪問 返回observable的對象
public interface RetrofitRequest {
boolean isTest=true; //是否在測試環(huán)境下
//發(fā)布之前更改
String BASE_URL_TEST = "/flyapptest/";//測試服務(wù)器
String BASE_URL_OFFICAL = "/flyapp/";//正式服務(wù)器
String BASE_URL = isTest?BASE_URL_TEST:BASE_URL_OFFICAL;//發(fā)布服務(wù)器
/**
* 登錄返回(json post)
* @param body
* @return
*/
@Headers( "Content-Type: application/json" )
@POST(BASE_URL+"Login.ashx/")
Observable<User> userLogin(@Body HashMap<String, String> body);
3.異步處理: 采用rxjava響應(yīng)式框架進行優(yōu)雅的異步處理,簡化代碼邏輯,并且很好的解決內(nèi)存泄漏 問題.(相關(guān)模塊在TakePhoto業(yè)務(wù)中)
/**
* rxjava 進行異步操作 eventBus進行時間傳遞
* @param data
*/
@Override
public void savePhoto(final Intent data) {
TLog.log("savePhoto", "data-->" + data.getData().toString());
Log.e("Tlog-->", "data-->" + data.getData().toString());
saveObservable = Observable.fromCallable(new Callable<String>() {
@Override
public String call() throws Exception {//通知調(diào)用 并返回string
return savePic(data);//此方法在io線程中調(diào)用 并返回
}
});
saveSubscription = saveObservable
.subscribeOn(Schedulers.io())//observable在調(diào)度中的IO線程中進行調(diào)度進行
.observeOn(AndroidSchedulers.mainThread())//在主線程中進行觀察
.subscribe(new Observer<String>() {//訂閱觀察者
@Override
public void onCompleted() {
Log.e("Tlog-->", "onCompleted-->");
}
@Override
public void onError(Throwable e) {
Log.e("Tlog-->", "Throwable-->" + e.getMessage().toString());
EventBus.getDefault().post(new EventUtils.ObjectEvent(e.getMessage().toString()));
}
@Override
public void onNext(String s) {//帶參數(shù)的下一步,在此就是當(dāng)
Log.e("Tlog-->", "s-->" + s);
EventBus.getDefault().post(new EventUtils.ObjectEvent(bitmap));
}
});
}
4.事件訂閱: 采用EventBus作為事件總線,進行線程間,組件之間的通信.
/**
* 事件總線 用于組件或線程通信,可替代回調(diào),廣播等
* Created by ccj on 2016/4/14.
*/
public class EventUtils {
/**
* object類型(即傳統(tǒng)的所有類型,都可以強轉(zhuǎn)進行傳遞事件)
*/
public static class ObjectEvent{
private Object object;
public ObjectEvent(Object object) {
// TODO Auto-generated constructor stub
this.object = object;
}
public Object getMsg(){
return object;
}
}
}
5.代碼分包: 根據(jù)業(yè)務(wù)區(qū)分進行分包,便于對代碼進行管理 .
6. 工具類: TDeviceUtils設(shè)備狀態(tài)的工具類,,SeriliazebleUtils 序列化工具類,SharepreferenceUtils保存工具類,
相關(guān)請參考代碼
7.app棧管理: 基于baseActivity,很好的釋放內(nèi)存,管理內(nèi)存.
相關(guān)請參考代碼
待后期完成
異常捕獲(待完善)
測試框架Espresso/JUnit/Mockito/Robolectric (待完善)
總結(jié)
1.層次分明宪迟,各層級之間都不管對方如何實現(xiàn),只關(guān)注結(jié)果交惯;
2.在視圖層(Presentation Layer)使用MVP架構(gòu)次泽,使原本臃腫的Activity(或Fragment)變得簡單,其處理方法都交給了Presenter席爽。
3.易于做測試意荤,只要基于每個模塊單獨做好單元測試就能確保整體的穩(wěn)定性。
4.易于快速迭代只锻,基于代碼的低耦合玖像,只需在業(yè)務(wù)邏輯上增加接口,然后在相應(yīng)的層級分別實現(xiàn)即可齐饮,絲毫不影響其他功能捐寥。