使用這個標題先表示對老王的尊敬
api所有開放 可是服務器使用的是美國服務器 訪問速度特別慢 只用於學習
快速開發框架是我整理出來的一套框架 使用簡單 實現快速 GitHub地址,喜歡的童鞋歡迎star
MVP是一種開發模式 按照你本身理解和編程習慣的去實現就好 沒有必要一股腦的照搬
可能理論什麼的我也不蠻會說,接下來了部分,我帶你真正的打一場戰役
看到這裏若是你感興趣我建議你先下載app跑一遍,知道咱們須要作的是什麼
項目的源碼地址Freebookjavascript
萬事開頭難,實質上只要你走出第一步了,後面的路就能迎刃而解html
在這裏我要先介紹一下個人底層框架LCRapidDevelop,這個框架能幹嗎呢?java
功能呢列舉到這裏就差很少了,接下來咱們須要把LCRapidDevelop添加到咱們的項目裏並編譯項目 react
導入後編譯一下若是沒有報錯咱們進行下一步,新建好相應的文件夾android
而後就是Application的編寫了git
/* *自定義Application * 用於初始化各類數據以及服務 * */
public class MyApplication extends Application {
//記錄當前棧裏全部activity
private List
activities =
new ArrayList
(); @Override public
void onCreate() {
super.onCreate(); instance =
this;
//異常友好管理初始化 Recovery.getInstance() .debug(
true) .recoverInBackground(
false) .recoverStack(
true) .mainPage(WelcomeActivity.class)
// .skip(H5PayActivity.class) 若是應用集成支付寶支付 記得加上這句代碼 沒時間解釋了 快上車 老司機發車了 .init(
this); }
/** * 應用實例 **/ private
static MyApplication instance;
/** * 得到實例 * * @return */ public
static MyApplication getInstance() {
return instance; }
/** * 新建了一個activity * * @param activity */ public
void addActivity(Activity activity) { activities.add(activity); }
/** * 結束指定的Activity * * @param activity */ public
void finishActivity(Activity activity) {
if (activity !=
null) {
this.activities.remove(activity); activity.finish(); activity =
null; } }
/** * 應用退出,結束全部的activity */ public
void exit() {
for (Activity activity : activities) {
if (activity !=
null) { activity.finish(); } } System.exit(
0); } }
複製代碼
而且在AndroidManifest.xml中使用這個android:name=".MyApplication.MyApplication"github
而後就是BaseActivity和BaseFragment的編寫了
在MVP文件夾內新建文件夾Base 而後新建BaseActivity.classspring
public abstract class BaseActivity extends AppCompatActivity implements View.OnClickListener {
protected Context mContext;
private ConnectivityManager manager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);// 鎖定豎屏
mContext = getActivityContext();
initView();
ButterKnife.bind(this);
initdata();
MyApplication.getInstance().addActivity(this);
}
/** * 初始activity方法 */
private void initView() {
loadViewLayout();
}
private void initdata(){
findViewById();
setListener();
processLogic();
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
MyApplication.getInstance().finishActivity(this);
}
/** * 加載頁面layout */
protected abstract void loadViewLayout();
/** * 加載頁面元素 */
protected abstract void findViewById();
/** * 設置各類事件的監聽器 */
protected abstract void setListener();
/** * 業務邏輯處理,主要與後端交互 */
protected abstract void processLogic();
/** * Activity.this */
protected abstract Context getActivityContext();
/** * 彈出Toast * * @param text */
public void showToast(String text) {
Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
public boolean checkNetworkState() {
boolean flag = false;
//獲得網絡鏈接信息
manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
//去進行判斷網絡是否鏈接
if (manager.getActiveNetworkInfo() != null) {
flag = manager.getActiveNetworkInfo().isAvailable();
}
return flag;
}
}複製代碼
而後就是BaseFragment.class編程
/** * 這個是最簡單的 你們實際使用時 可添加我自想要的元素 */
public abstract class BaseFragment extends Fragment{
private View mRootView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mRootView = initView(inflater,container);
ButterKnife.bind(this, mRootView);//綁定到butterknife
return mRootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initListener();
initData();
}
protected abstract View initView(LayoutInflater inflater,ViewGroup container);
protected abstract void initListener();
protected abstract void initData();
}複製代碼
到這裏基本上底層框架搭建就搭建好了,若是熟練了的話,這個過程複製粘貼不到兩分鐘就能搞定, 第一次搭建的話算個10分鐘吧後端
網絡請求框架實質上就是上面咱們提到的Data文件
網絡請求採用的是 RxJava +Retrofit2.0 + 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'複製代碼
關於這個網絡請求框架的搭建我就不細說了,掘金的文章太多了,懶得去了解的朋友呢直接複製個人代碼,我教你怎麼使用好了,就跟我賜予你一把寶劍,知道使用就好乾嗎還要去了解寶劍是怎麼製造的,哈哈 固然這是一句玩笑話啦
首先是BookService.class的編寫 api文檔地址
/** * API接口 * 由於使用RxCache做爲緩存策略 因此這裏不須要寫緩存信息 */
public interface BookService {
//獲取首頁詳情
@GET("api/getHomeInfo")
Observable
> getHomeInfo();
//獲取書籍詳情 @GET(
"api/getBookInfo") Observable
> getBookInfo(@Query(
"id") int id);
//獲取類別列表 @GET(
"api/getTypeConfigList") Observable
>> getTypeList();
//根據類別獲取書籍列表 @GET(
"api/getTypeBooks") Observable
>> getBookList(@Query(
"type")int type,@Query(
"pageIndex")int pageIndex);
//根據關鍵詞獲取搜索書籍列表 @GET(
"api/getSearchList") Observable
>> getSearchList(@Query(
"key")
String key);
//獲取熱門搜索標籤 @GET(
"api/getSearchLable") Observable
<
String>>> getHotLable(); }
<
複製代碼
而後就是緩存api的編寫CacheProviders.class
/** * 緩存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
>> getBookList(Observable
> oRepos, DynamicKey userName, EvictDynamicKey evictDynamicKey);
//獲取書庫分類信息緩存數據 緩存時間 永久 Observable
>> getTypeList(Observable
> oRepos, DynamicKey userName, EvictDynamicKey evictDynamicKey);
//獲取首頁配置數據 banner 最熱 最新 緩存時間7天 @LifeCache(duration =
7, timeUnit = TimeUnit.DAYS) Observable
> getHomeInfo(Observable
oRepos, DynamicKey userName, EvictDynamicKey evictDynamicKey);
//獲取搜索標籤 緩存時間7天 @LifeCache(duration =
7, timeUnit = TimeUnit.DAYS) Observable
<
String>>> getHotLable(Observable
<
String>> oRepos, DynamicKey userName, EvictDynamicKey evictDynamicKey); //獲取書籍詳情 緩存時間7天 @LifeCache(duration = 7, timeUnit = TimeUnit.DAYS) Observable
> getBookInfo(Observable
oRepos, DynamicKey userName, EvictDynamicKey evictDynamicKey);
//根據關鍵詞獲取搜素列表 緩存時間1天 @LifeCache(duration =
1, timeUnit = TimeUnit.DAYS) Observable
> getSearchList(Observable
oRepos, DynamicKey userName, EvictDynamicKey evictDynamicKey); }
<
<
複製代碼
最後就是HttpData.class的使用了
/* *全部的請求數據的方法集中地 * 根據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 BookService service = getRetrofit().create(BookService.class);
//在訪問HttpMethods時建立單例
private static class SingletonHolder {
private static final HttpData INSTANCE = new HttpData();
}
//獲取單例
public static HttpData getInstance() {
return SingletonHolder.INSTANCE;
}
//獲取app書本類別
public void getBookTypes(Observer
> observer){ Observable observable=service.getTypeList().map(
new HttpResultFunc
>()); Observable observableCahce=providers.getTypeList(observable,
new DynamicKey(
"書本類別"),
new EvictDynamicKey(
false)).map(
new HttpResultFuncCcche
>()); setSubscribe(observableCahce,observer); }
//獲取app首頁配置信息 banner 最新 最熱 public
void getHomeInfo(boolean isload,Observer
observer){ Observable observable=service.getHomeInfo().map(
new HttpResultFunc
());; Observable observableCache=providers.getHomeInfo(observable,
new DynamicKey(
"首頁配置"),
new EvictDynamicKey(isload)).map(
new HttpResultFuncCcche
()); setSubscribe(observableCache,observer); }
//得到搜索熱門標籤 public
void getSearchLable(Observer
<
String>> observer){ Observable observable=service.getHotLable().map(new HttpResultFunc
<
String>>());; Observable observableCache=providers.getHotLable(observable,new DynamicKey("搜索熱門標籤"), new EvictDynamicKey(false)).map(new HttpResultFuncCcche
<
String>>()); setSubscribe(observableCache,observer); } //根據類型獲取書籍集合 public void getBookList(int bookType, int pageIndex, Observer
> observer) { Observable observable = service.getBookList(bookType,pageIndex).map(
new HttpResultFunc
>()); Observable observableCache=providers.getBookList(observable,
new DynamicKey(
"getStackTypeHtml"+bookType+pageIndex),
new EvictDynamicKey(
false)).map(
new HttpResultFuncCcche
>()); setSubscribe(observableCache, observer); }
//根據關鍵字搜索書籍 public
void getSearchList(
String key,Observer
> observer){
try {
//中文記得轉碼 否則會亂碼 搜索不出想要的效果 key = URLEncoder.encode(key,
"utf-8"); }
catch (UnsupportedEncodingException e) { e.printStackTrace(); } Observable observable=service.getSearchList(key).map(
new HttpResultFunc
>()); Observable observableCache=providers.getSearchList(observable,
new DynamicKey(
"getSearchList&"+key),
new EvictDynamicKey(
false)).map(
new HttpResultFuncCcche
>()); setSubscribe(observableCache, observer); }
//獲取書籍詳情 public
void getBookInfo(int id, Observer
observer){ Observable observable=service.getBookInfo(id).map(
new HttpResultFunc
()); Observable observableCache=providers.getBookInfo(observable,
new DynamicKey(
"getBookInfo&"+id),
new EvictDynamicKey(
false)).map(
new HttpResultFuncCcche
()); setSubscribe(observableCache, observer); }
/** * 插入觀察者 * * @param observable * @param observer * @param
*/
public
static
void setSubscribe(Observable
observable, Observer
observer) { observable.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.newThread())
//子線程訪問網絡 .observeOn(AndroidSchedulers.mainThread())
//回調到主線程 .subscribe(observer); }
/** * 用來統一處理Http的resultCode,並將HttpResult的Data部分剝離出來返回給subscriber * * @param
Subscriber真正須要的數據類型,也就是Data部分的數據類型 */
private
class HttpResultFunc<T> implements Func1<HttpResult<T>, T> { @Override public T call(HttpResult
httpResult) {
if (httpResult.getCode() !=
1 ) {
throw
new ApiException(httpResult); }
return httpResult.getData(); } }
/** * 用來統一處理RxCacha的結果 */ private
class HttpResultFuncCcche<T> implements Func1<Reply<T>, T> { @Override public T call(Reply
httpResult) {
return httpResult.getData(); } } }
<
<
<
複製代碼
到這裏呢網絡框架的搭建和使用介紹完了,這裏若是是複製粘貼只須要修改api相關資料的仍是不須要多少時間的,這裏咱們算1小時吧
以前的項目結構咱們也看到了,其中有一個文件夾就叫MVP
這一個查看書籍詳情的功能
首先咱們須要肯定BookInfoActivity有一些什麼樣的交互,好比說在加載的時候顯示加載頁面 網絡異常時顯示異常頁面等等
當咱們清這個壓面的交互和視圖的顯示是,咱們就能夠編寫BookInfoView.class (其實不是蠻清楚也能夠的 你先把知道的加上,後面想起來了在添加就行了)
public interface BookInfoView {
//顯示加載頁
void showProgress();
//關閉加載頁
void hideProgress();
//數據加載成功
void newData(BookInfoDto data);
//顯示加載失敗
void showLoadFailMsg();
}複製代碼
而後呢咱們就要開始去編寫BookInfoModel.class 網絡請求咱們寫到這個裏面
/** * 獲取書籍詳情數據 */
public class BookInfoModel {
public void loadData(int id, final OnLoadDataListListener listener){
HttpData.getInstance().getBookInfo(id, new Observer
() { @Override public
void onCompleted() { } @Override public
void onError(Throwable e) { listener.onFailure(e); } @Override public
void onNext(BookInfoDto bookInfoDto) { listener.onSuccess(bookInfoDto); } }); } }
複製代碼
最後就是BookInfoPresenter
public class BookInfoPresenter implements OnLoadDataListListener<BookInfoDto>{
private BookInfoView mView;
private BookInfoModel mModel;
public BookInfoPresenter(BookInfoView mView) {
this.mView = mView;
mModel=new BookInfoModel();
}
public void loadData(int id){
mModel.loadData(id,this);
mView.showProgress();
}
@Override
public void onSuccess(BookInfoDto data) {
if(data.getBookName().equals("")){
mView.showLoadFailMsg();
}else{
mView.newData(data);
mView.hideProgress();
}
}
@Override
public void onFailure(Throwable e) {
mView.showLoadFailMsg();
}
}複製代碼
最後就是BookInfoActivity對這些進行使用了,仔細看代碼,Activity裏面將不會出現任何數據邏輯
public class BookInfoActivity extends BaseActivity implements BookInfoView {
@BindView(R.id.book_info_toolbar_textview_title)
TextView bookInfoToolbarTextviewTitle;
.....
private int bookid;
private BookInfoPresenter presenter;
@Override
protected void loadViewLayout() {
setContentView(R.layout.activity_book_info);
}
@Override
protected void findViewById() {
Intent intent = getIntent();
bookid = intent.getIntExtra("bookid",0);
}
public void initview(BookInfoDto data) {
bookInfoTextviewName.setText(data.getBookName());
.....數據顯示
}
@Override
protected void setListener() {
}
@Override
protected void processLogic() {
presenter = new BookInfoPresenter(this);
presenter.loadData(bookid);
}
@Override
protected Context getActivityContext() {
return this;
}
/* 如下是BookInfoView定義的相關接口 activity是須要實現就行了 */
@Override
public void showProgress() {//顯示加載頁
bookInfoProgress.showLoading();
}
@Override
public void hideProgress() {//顯示數據頁
bookInfoProgress.showContent();
}
@Override
public void newData(BookInfoDto data) {//
initview(data);
}
@Override
public void showLoadFailMsg() {
toError();
}
public void toError() {
bookInfoProgress.showError(getResources().getDrawable(R.mipmap.load_error), Constant.ERROR_TITLE, Constant.ERROR_CONTEXT, Constant.ERROR_BUTTON, new View.OnClickListener() {
@Override
public void onClick(View v) {
bookInfoProgress.showLoading();
//重試
presenter.loadData(bookid);
}
});
}
}複製代碼
MVP的使用大概就是這樣,新司機能夠先按照我這種比較簡單易理解的方式先實現,當你實現了再去看深度比較深的MVP相關文章是,你就不會以爲很難理解了,這裏話的把app的功能都實現差很少要話2個小時左右
首頁介紹一下咱們這個app都用到了哪些第三方框架
每一個框架我都提供了連接,感興趣的直接點進去查看,畢竟人家寫的好詳細的,我就很少嘴了, 這些框架使用到項目裏主要是BGABanner和FileDownloader 加上頁面的編寫以及適配器的編寫,差不哦兩小時左右
從你開始構建這個項目開始,到這個項目結束,半天時間足以先把東西先玩起來再去細緻的瞭解,會比你先詳細瞭解在開發要輕鬆的多我沒有太多的耐心去寫的很細緻,可是大家有任何疑問能夠發郵件給我mychinalance@gmail.comapi你們能夠隨意使用 可是用的是美國服務器,會比較的慢,api是用spring mvc寫,須要源碼的能夠聯繫我