项目Demo已经上传至github
链接
本人android小白一枚,在学习android的过程中,我感受很深,在做了这么多练手的项目之后,我觉得android就像一块画板,画板是白纸,作为android的开发人员,要做的就是运用一切可能,去在画板上画出让人看的懂并且美观的画。说起来,我们却也属于艺术家这一行了。。。
在我看来,android是为了展示数据的,这里就要分两个步骤了,第一就是怎样去展示,这部分是相当于UI的工作,,第二就是数据的来源,数据从哪来,怎么来,来的是什么,有什么规律,这些都需要我们去考虑,一般来说,就现在的开发模式来说,数据都是存储在数据库中,后台的工作人员通过各种语言去实现对数据库的增删改查,然后将数据库中的数据进行加工处理,部署在服务器上,android端就需要通过访问服务器去获取并解析数据,这里就涉及到获取并解析数据了。不涉及到网络的应用现如今基本上是不复存在的了,一款App,没有强大的后台作为其支撑,纵然你将界面做的再花哨华丽,终究只能算是“花瓶”,“鸡肋”。
所以在开始开发一个项目之前,选取一款优秀的网络框架是非常有必要的。RxJava和Retrofit出现也很久了,之前是一直使用okHttp去封装一个请求类,这样虽然能够正确的请求到数据,但是,代码不仅复杂繁多,遇到多请求问题就会让逻辑不清晰,思维很容易进入混乱,说再多都无法表达多请求问题的恐怖,亲身经历才能够感受它的复杂。
RxJava的概念其实很模糊,我对它的理解就是一个给你方便处理异步问题的框架,到底有多方便,体会过才知道。。。
Retrofit就是对okhttp做了一层封装。把网络请求都交给给了Okhttp,我们只需要通过简单的配置就能使用retrofit来进行网络请求了,Retrofit 除了提供了传统的 Callback 形式的 API,还有 RxJava 版本的 Observable 形式 API,在我看来,retrofit有点像javaee里的springboot框架,通过xml文件配置好一切后,直接就可以在方法头前添加注释来执行网络请求,实在是不能再方便了。
使用方法:build.gradle文件中引入几个jar
compile 'io.reactivex.rxjava2:rxjava:2.0.7' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.okhttp3:okhttp:3.5.0' compile 'com.squareup.okhttp3:logging-interceptor:3.6.0' compile 'com.jakewharton:butterknife:7.0.1' compile 'com.trello.rxlifecycle2:rxlifecycle:2.1.0' compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0'
说再多,不如一个例子来的简单,我们现在定义好一个场景,来练练手。
假设现在有这么一组json数据
results是一个json数据。它的请求url是http://gank.io/api/data/福利/10/1。因为本篇重点就是在讲运用网络框架去取数据,所以在这里,只要取到数据就可以了,至于怎样去展示,不是本篇重点,所以不做讨论。
1.首先观察json数据的规律,要自定义一个实体,负责将json数据解析成一个个实体类,方便去调用。
总体类:
public class BaseResult<T> { private int code; private String message; private T results; private boolean error; public T getResults() { return results; } public void setResults(T results) { this.results = results; } public boolean isError() { return error; } public void setError(boolean error) { this.error = error; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }results中的数据类:Meizi
public class MeiZi { /** * _id : 59cd9b53421aa9727fdb25eb * createdAt : 2017-09-29T09:01:07.894Z * desc : 9-29 * publishedAt : 2017-09-29T11:21:16.116Z * source : chrome * type : 福利 * url : https://ws1.sinaimg.cn/large/610dc034ly1fk05lf9f4cj20u011h423.jpg * used : true * who : daimajia */ private String _id; private String createdAt; private String desc; private String publishedAt; private String source; private String type; private String url; private boolean used; private String who; public String get_id() { return _id; } public void set_id(String _id) { this._id = _id; } public String getCreatedAt() { return createdAt; } public void setCreatedAt(String createdAt) { this.createdAt = createdAt; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getPublishedAt() { return publishedAt; } public void setPublishedAt(String publishedAt) { this.publishedAt = publishedAt; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public boolean isUsed() { return used; } public void setUsed(boolean used) { this.used = used; } public String getWho() { return who; } public void setWho(String who) { this.who = who; } }2.数据的容器准备好之后,就开始去获取数据了,使用RxJava2与Retrofit2框架去进行网络请求,我觉得定义Retrofit访问的接口
这个是最好想的,所以先编写这个接口。
public interface ApiService { /** * 网络请求超时时间毫秒 */ int DEFAULT_TIMEOUT = 20000; String HOST = "http://gank.io/"; String API_SERVER_URL = HOST + "api/data/"; @GET("福利/10/1") Observable3.就是对自己的Retrofit去进行封装设置>> getMezi(); /** * @param page * @param number * @return */ @Headers("Cache-Control: public, max-age=100")//设置缓存 缓存时间为100s @GET("everySay/selectAll.do") Observable >> lookBack(@Query("page") int page, @Query("rows") int number); @POST("upload/uploadFile.do") Observable uploadFiles(@Part("filename") String description, @Part("pic\"; filename=\"image1.png") RequestBody imgs1, @Part("pic\"; filename=\"image2.png") RequestBody imgs2); @POST("upload/uploadFile.do") Observable uploadFiles(@Part("filename") String description, @PartMap() Map , RequestBody> maps); }
(1)retrofit是基于okhttp的加强版,所以第一步去自定义一个okhttpclient,里面设置两个拦截器,一个是日志拦截器,用于对日志的筛选,还一个就是网络拦截器,用于对网络请求头的总体设置。
//日志拦截器 HttpLoggingInterceptor interceptor=new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { try { String text = URLDecoder.decode(message, "utf-8"); LogUtils.e(text); } catch (UnsupportedEncodingException e) { e.printStackTrace(); LogUtils.e(message); } } });
class HttpNetWorkInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); if (!NetUtil.checkNetWork(BaseApplication.getmContext())) { request = request.newBuilder() .cacheControl(CacheControl.FORCE_CACHE) .build(); LogUtils.d("no network"); } Response originalResponse = chain.proceed(request); if (NetUtil.checkNetWork(BaseApplication.getmContext())) { //有网的时候读接口上的@Headers里的配置,可以在这里进行统一的设置 String cacheControl = request.cacheControl().toString(); return originalResponse.newBuilder() .header("Cache-Control", cacheControl) .removeHeader("Pragma") .build(); } else { return originalResponse.newBuilder() .header("Cache-Control", "public, only-if-cached, max-stale=2419200") .removeHeader("Pragma") .build(); } } }(2)创建一个okhttpclient
//初始化一个OKhttpClient OkHttpClient okHttpClient=new OkHttpClient.Builder() .addInterceptor(interceptor) .readTimeout(8, TimeUnit.SECONDS) .connectTimeout(8,TimeUnit.SECONDS) .addNetworkInterceptor(new HttpNetWorkInterceptor()) .cache(cache) .build();(3)创建一个Retrofit
Gson gson=new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create(); Retrofit retrofit=new Retrofit.Builder() .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(ApiService.API_SERVER_URL) .build(); service=retrofit.create(ApiService.class);(4)创建一个观察者基类,在其中进行对请求错误的封装和对进度条显示与消失的封装。
public abstract class DefaultObserver<T extends BaseResult> implements Observer<T> { private Activity activity; // Activity 是否在执行onStop()时取消订阅 private boolean isAddInStop = false; private CommonDialogUtils dialogUtils; public DefaultObserver(Activity activity) { this.activity = activity; dialogUtils=new CommonDialogUtils(); dialogUtils.showProgress(activity); } public DefaultObserver(Activity activity, boolean isShowLoading) { this.activity = activity; dialogUtils=new CommonDialogUtils(); if (isShowLoading) { dialogUtils.showProgress(activity,"Loading..."); } } @Override public void onSubscribe(@NonNull Disposable d) { } @Override public void onNext(@NonNull T t) { dismissProgress(); if (!t.isError()) { onSuccess(t); } else { onFail(t); } } private void dismissProgress(){ if(dialogUtils!=null){ dialogUtils.dismissProgress(); } } @Override public void onError(@NonNull Throwable e) { LogUtils.e(e.getMessage()); dismissProgress(); if (e instanceof HttpException) { // HTTP错误 onException(ExceptionReason.BAD_NETWORK); } else if (e instanceof ConnectException || e instanceof UnknownHostException) { // 连接错误 onException(CONNECT_ERROR); } else if (e instanceof InterruptedIOException) { // 连接超时 onException(CONNECT_TIMEOUT); } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) { // 解析错误 onException(PARSE_ERROR); } else { onException(UNKNOWN_ERROR); } } @Override public void onComplete() { } /** * 请求成功 * * @param response 服务器返回的数据 */ abstract public void onSuccess(T response); /** * 服务器返回数据,但响应码不为200 * * @param response 服务器返回的数据 */ public void onFail(T response) { String message = response.getMessage(); if (TextUtils.isEmpty(message)) { ToastUtils.show(R.string.response_return_error); } else { ToastUtils.show(message); } } /** * 请求异常 * * @param reason */ public void onException(ExceptionReason reason) { switch (reason) { case CONNECT_ERROR: ToastUtils.show(R.string.connect_error, Toast.LENGTH_SHORT); break; case CONNECT_TIMEOUT: ToastUtils.show(R.string.connect_timeout, Toast.LENGTH_SHORT); break; case BAD_NETWORK: ToastUtils.show(R.string.bad_network, Toast.LENGTH_SHORT); break; case PARSE_ERROR: ToastUtils.show(R.string.parse_error, Toast.LENGTH_SHORT); break; case UNKNOWN_ERROR: default: ToastUtils.show(R.string.unknown_error, Toast.LENGTH_SHORT); break; } } /** * 请求网络失败原因 */ public enum ExceptionReason { /** * 解析数据失败 */ PARSE_ERROR, /** * 网络问题 */ BAD_NETWORK, /** * 连接错误 */ CONNECT_ERROR, /** * 连接超时 */ CONNECT_TIMEOUT, /** * 未知错误 */ UNKNOWN_ERROR, } }(5)对BaseActivity的封装,通过rxlifecycle对Rxjava的生命周期进行控制
public abstract class BaseActivity extends RxAppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutId()); init(savedInstanceState); } protected void showToast(String msg) { ToastUtils.show(msg); } protected abstract @LayoutRes int getLayoutId(); protected abstract void init(Bundle savedInstanceState); }(6)对BaseFragment的封装
public abstract class BaseFragment extends RxFragment { public View rootView; public LayoutInflater inflater; @Nullable @Override public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); this.inflater = inflater; if (rootView == null) { rootView = inflater.inflate(this.getLayoutId(), container, false); init(savedInstanceState); } ViewGroup parent = (ViewGroup) rootView.getParent(); if (parent != null) { parent.removeView(rootView); } return rootView; } protected abstract int getLayoutId(); protected abstract void init(Bundle savedInstanceState); protected void showToast(String msg) { ToastUtils.show(msg); } @Override public void onResume() { super.onResume(); } @Override public void onPause() { super.onPause(); } @Override public void onDestroyView() { super.onDestroyView(); } }(7)请求数据的方法
public void getData() { IdeaApi.getSingleHolder() .getMezi() .compose(this.>>bindToLifecycle()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DefaultObserver >>(this) { @Override public void onSuccess(BaseResult > response) { List
results = response.getResults(); showToast("请求成功,妹子个数为"+results.size()); } }); }