編輯:關於Android編程
因為要讓別人看得懂我們的代碼,使代碼更利於維護,簡單講就是模塊化,使各個包下的類各在其位,各司其職。比如怎樣請求數據和它被用來干什麼是沒有關系的,所以要分離,這樣做的好處是:一,大量減少了UI層業務邏輯的代碼量。二,即使UI界面變了,也不會影響到業務層的代碼。使兩者充分解耦,也就達到了我們的目的了。
下圖是我理解的MVP的職責圖示:
對於它的解釋是 model把封裝好的數據通過接口回調給Presenter ,View 負責UI層例如Dialog,Empty data, no network,Datas done的顯示,讓Presenter持有View的引用,當model和view在presenter中匯合時,再由presenter去決定View 顯示的時機,也就是presenter的調度。這樣,model無需關心數據的流向,view不用關系數據的來源,activity也不用處理兩者之間的復雜關系,只需要在需要的地方設置“Adapter”就行了,更多的事交給Presenter去處理。
光說不練假把式,來嘗試一個簡單的功能
下面是一個采用MVP模式設計的功能非常的APP的效果圖,就是從網絡獲取一段字符串,在開始獲取的時候Toast:開始加載數據,在成功獲取到數據後,直接顯示到ListView,怎麼樣,簡單得不能再簡單了!
根據上面的內容,在寫代碼前,要先‘’分清職責‘’
首先,Model負責訪問數據接口,並把數據回調給Presenter 所以IHomeListLoad肯定會有loadData()這個方法去加載數據。所以它的代碼應該是這樣的
public interface IHomeListLoad { //加載數據 void loadData(Context context,ILoadDataComplete iLoadDataComplete); //數據加載後的回調 interface ILoadDataComplete{ void dataComing(List在這裡我做了兩件事,一是加載數據,二是預制了ILoadDataComlete數據回調接口方便獲取數據。現在我不僅獲取了數據,並且還知道數據是什麼時候開始加載的,什麼時候結束的,加載是否出錯。。datas); void dataError(Exception e); void onStart(); } }
public interface IMainView { void showLoading(); void loadFinish(List前面的Model雖然把數據請求下來了,但是它並不知道該把數據交給誰,因為它只負責去請求數據。View也並不知道該什麼時候去呈現各種定義好的視圖效果。這時候Presenter出場了,通通交給它去調度,如圖所示是它的一般調度關系。datas); void loadError(Exception e); }
這就是我們所說的中間層的作用,它帶來好處是即使model和view發生變化,只要他們的調度關系還在,我就不會去改寫presenter的代碼,使其充分解耦。要知道,presenter可占了很大一部分代碼量O(∩_∩)O~。
讓我們來實現它
前面都是通過接口編寫了抽象方法,想好了操作步驟,接下來就去編寫它的實現類,但在這之前我們必須做一些准備工作,以提高代碼的復用性
一、封裝統一的HTTP RESOULT類 BaseHttpResponse
public class BaseHttpResponse{ private String error_code; private String reason; private T result;//不確定的數據類型 public String getError_code() { return error_code; } public void setError_code(String error_code) { this.error_code = error_code; } public String getReason() { return reason; } public void setReason(String reason) { this.reason = reason; } public T getResult() { return result; } public void setResult(T result) { this.result = result; } }
二、繼承RXJAVA中Func1()類,並重寫Call()方法增加,對返回的數據重新整理。在BaseHttpResponse中,我們真正關心的數據只有當error_code=0時result的數據(這裡我規定error_code=0時,且result不為null表示獲取成功),所以如果我們不進行預處理的話使用RXJAVA時的map函數將是這樣處理數據的
.map(new Func1>, List >() { @Override public List call(BaseHttpResponse > baseHttpResponse) { if("0".equals(baseHttpResponse.getError_code())) { if(baseHttpResponse.getResult() != null) { return baseHttpResponse.getResult(); } else { //自定義異常類 throw new DataResponseException("數據為空");//DataResponseException } } else { throw new DataResponseException("請求發生錯誤"); } } })
(PS:此過程中拋出的異常是會回調到OnError函數中的,源碼中的注釋是@parame the exception encountered by the Observable)實在太麻煩,所以干脆重寫一下Func1()的方法算了!
public class BetterFuncimplements Func1 ,T> { @Override public T call(BaseHttpResponse tBaseHttpResponse) { String error_code = tBaseHttpResponse.getError_code(); String reason = tBaseHttpResponse.getReason(); T result = tBaseHttpResponse.getResult(); if("0".equals(error_code)){ if(result !=null){ return result; }else { throw new DataResponseException("數據為空"); } } else { throw new DataResponseException(reason); } } }
.map(new BetterFunc我靠( ‵o′)凸,簡直不要太嗨!准備工作完成了接下來是真正的實現類>())
①Model implement
public class IHomeListLoadIml implements IHomeListLoad { @Override public void loadData(Context context, final ILoadDataComplete iLoadDataComplete) { RetrofitUtils.createApi(context, AnswerService.class) .getData("CN", "7568f1905fde469e90009614e8c167b0") .subscribeOn(Schedulers.io()) .map(new BertterFunc②Viewimplement(肯定是Activity啊)>()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber
>() { @Override public void onCompleted() {} @Override public void onStart() { super.onStart(); iLoadDataComplete.onStart(); } @Override public void onError(Throwable e) { iLoadDataComplete.dataError((Exception) e); } @Override public void onNext(List
xxxes) { iLoadDataComplete.dataComing(xxxes); } }); } }
public class MainActivity extends AppCompatActivity implements IMainView { private ListView mListView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListView= (ListView) findViewById(R.id.mList); //IMainView ---》presenter MainPresenter presenter = new MainPresenter(this,this); presenter.getData(); } @Override public void showLoading() { Toast.makeText(this,"開始加載數據",Toast.LENGTH_LONG).show(); } @Override public void loadFinish(Listdatas) { mListView.setAdapter(new HomeListAdapter(this,datas)); } @Override public void loadError(Exception e) { Toast.makeText(this,"數據加載出錯",Toast.LENGTH_LONG).show(); } }
public class MainPresenter { private IMainView iMainView; private Context context; private IHomeListLoad iHomeListLoad = new IHomeListLoadIml(); public MainPresenter(Context context,IMainView iMainView) { this.iMainView = iMainView; this.context = context; } //請求數據 public void getData(){ iHomeListLoad.loadData(context,new IHomeListLoad.ILoadDataComplete() { @Override public void dataComing(Listdatas) { //datas--->activity iMainView.loadFinish(datas); } @Override public void dataError(Exception e) { //view --->error iMainView.loadError(e); } @Override public void onStart() { //view --->dialog iMainView.showLoading(); } }); } }
哦,對了本文涉及到RXJAVA和Retrofit的用法也會抽時間整理一下留作筆記,代碼等我完善後也將隨後上傳至GITHUB並補上鏈接。個人能力受限,還請各位前輩不吝賜教!完
第二種主界面風格則是以網易新聞、鳳凰新聞以及新推出的新浪博客(閱讀版)為代表,使用ViewPager+Fragment,即ViewPager裡適配器裡放的不是一般的Vie
Android消息提示類viewbadger ,效果如下: 代碼如下 BadgeView.java import android.content.Context
服務的生命周期 服務的生命周期跟啟動服務的方法有關: 當采用Context.startService()方法啟動服務,與之有關的生命周期方法 onCreate()
引言 Cordova(PhoneGap)采用的是HTML5+JavaScript混合模式來開發移動手機APP,因此當頁面需要獲取手機內部某些信息時(例如:聯系人信息,坐