使用这个标题先表示对老王的尊敬
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写,须要源码的能够联系我