接下来说下项目中用的一些网络数据的获取。采用的是Rxjava + Retrofit2 这种方式来进行网络请求的。
Rxjava: 一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库
Retrofit : Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装
关于Rxjava与Retrofit2的详解,可以参照下面这两篇博客进行学习
这是一份很详细的 Retrofit 2.0 使用教程(含实例讲解)
首先,列下获取数据的相关API接口,用的是网易新闻的API,如下:
头条:http://c.m.163.com//nc/article/headline/T1348647909107/0-20.html
科技:http://c.m.163.com//nc/article/list/T1348649580692/0-20.html
财经:http://c.m.163.com//nc/article/list/T1348648756099/0-20.html
军事:http://c.m.163.com//nc/article/list/T1348648141035/0-20.html
体育:http://c.m.163.com//nc/article/list/T1348649079062/0-20.html
另外,提醒下,需要在AndroidManifest.xml配置文件中加入对应访问网络的权限
<uses-permission android:name="android.permission.INTERNET" />
下面主要介绍下新闻页面下对应不同类型新闻信息获取具体功能实现。关于新闻详情和图片信息的获取不再详细介绍,思路都是一样的,后面直接贴出代码。
1. 创建对应数据模型
根据上面提供的网易新闻的API,直接在浏览器上去访问对应url,获取来对应json串,在线解析后,找出项目中需要用到的键值对,对应创建出来的新闻类如下:
package com.jkxy.leijx.app_myfirstproject.model; import java.util.List; /** * 新闻类 * Created by leijx on 2017/9/18. */ public class Newsbean { private String postid; //id private String title ; //标题 private String digest ; //摘要 private String ptime ; //发布时间 private String imgsrc; //图片地址 private List<adsBean> ads; //图片新闻 public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDigest() { return digest; } public void setDigest(String digest) { this.digest = digest; } public String getPtime() { return ptime; } public void setPtime(String ptime) { this.ptime = ptime; } public String getImgsrc() { return imgsrc; } public void setImgsrc(String imgsrc) { this.imgsrc = imgsrc; } public String getPostid() { return postid; } public void setPostid(String postid) { this.postid = postid; } public List<adsBean> getAds() { return ads; } public void setAds(List<adsBean> ads) { this.ads = ads; } public class adsBean{ private String title; //标题 private String imgsrc; //图片url public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getImgsrc() { return imgsrc; } public void setImgsrc(String imgsrc) { this.imgsrc = imgsrc; } } }这块需要注意,对应NewsBean中定义的变量名,必须要与json串中的键值保持一致!!!
2. 定义对应接口类
/** * Created by leijx on 2017/9/18. * Retrofit+Rxjava需要使用的接口类 */ public interface GetNewsInfoApi { /** * 获取新闻列表 * @param type 新闻类型 * @param id 新闻类型ID * @param page 开始时的新闻数目 * @return 返回对应的 被观察者 Observable<T> 对象 * 当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。 */ @GET("/nc/article/{type}/{id}/{startpage}-20.html") Observable<Map<String, List<Newsbean>>> getNewslist(@Path("type") String type, @Path("id") String id, @Path("startpage") int page); /** * 获取网络图片 * @param url * @return 返回对应的 被观察者 Observable<T> 对象 * 当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。 */ @GET Observable<ResponseBody> getimage(@Url String url); /** * 获取新闻详情 * @param postid * @return 返回对应的 被观察者 Observable<T> 对象 * 当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。 */ @GET("/nc/article/{postid}/full.html") Observable<Map<String, NewsDetailBean>> getNewsDetail(@Path("postid") String postid); }
3. 创建Retrofit对象,接着根据创建的Retrofit对象,去创建网络请求接口实例,也就是GetNewsInfoApi 对象。
/** * 获取新闻列表 * @return 返回GetNewsInfoApi接口对象 */ public GetNewsInfoApi getNewsinfo(){ Log.d(TAG,"getNewsinfo"); //创建Retrofit对象 Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .baseUrl(BaseUrl) .addConverterFactory(GsonConverterFactory.create()) //设置数据解析器 .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //设置网络请求适配器 .build(); return retrofit.create(GetNewsInfoApi.class); }
5. 接下来就是使用Rxjava中的各种运算符,对数据进行各种加工,得到最终需要的数据,以及线程控制等操作。
/** * 获取新闻列表 * @param context 上下文对象 * @param lodingtype 加载的类型(0:普通获取信息 1:上拉刷新 2:加载更多) * @param type 新闻类型 * @param id 新闻类型id * @param page 开始的新闻个数 * @param callback 获取来结果后的回调 * @return */ public static Subscription getNewsinfo(Context context, int lodingtype, String type, final String id, int page, final Callback_getNews callback){ Log.d(TAG,"getNewsinfo---type=="+type+"---id=="+id); return Network.getInstance(context) //获取Network实例 .getNewsinfo() //获取GetNewsInfoApi接口实例 .getNewslist(type,id,page) //请求数据,获取Observable被监听者对象 .flatMap(new Func1<Map<String, List<Newsbean>>, Observable<Newsbean>>() { //过滤出对应id的所有Newsbean对象 @Override public Observable<Newsbean> call(Map<String, List<Newsbean>> stringListMap) { Log.d(TAG,"flatMap---"+stringListMap.size()); return Observable.from(stringListMap.get(id)); } }) .distinct() //去重 .toSortedList(new Func2<Newsbean, Newsbean, Integer>() { //将当前过滤出的所有Newsbean根据ptime进行排序,组成新的list列表 @Override public Integer call(Newsbean newsbean, Newsbean newsbean2) { Log.d(TAG,"toSortedList---"); // return newsbean.getPtime().compareTo(newsbean2.getPtime()); //升序 return newsbean2.getPtime().compareTo(newsbean.getPtime()); //降序 } }) .subscribeOn(Schedulers.io()) //io线程中处理相关请求 .observeOn(AndroidSchedulers.mainThread()) //ui线程对获取来的数据进行对应处理 .unsubscribeOn(Schedulers.io()) .subscribe(new Observer<List<Newsbean>>() { //订阅 @Override public void onCompleted() { } @Override public void onError(Throwable e) { Log.d(TAG,"error==="+e.getMessage()); callback.faile(); } @Override public void onNext(List<Newsbean> list) { Log.d(TAG,"onNext"+list.size()); for(int i=0;i<list.size();i++){ Log.d(TAG,"titel"+list.get(i).getTitle()); Log.d(TAG,"diagest"+list.get(i).getDigest()); Log.d(TAG,"ptime"+list.get(i).getPtime()); } callback.sucess(list); //在重写的Callback_getNews中的sucess方法中进行对应处理 } }); }
下面贴下获取网络数据这块的完整的代码
1. Newsbean新闻类,上面已有展示,不再给出
2. NewsDetailBean 新闻详情类
package com.jkxy.leijx.app_myfirstproject.model; import java.util.List; /** * Created by leijx on 2017/9/21. * 新闻详情类 */ public class NewsDetailBean { private String body; //内容 private String title; //标题 private List<ImageBean> img; //图片 public String getBody() { return body; } public void setBody(String body) { this.body = body; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public List<ImageBean> getImg() { return img; } public void setImg(List<ImageBean> img) { this.img = img; } public class ImageBean{ private String src; //图片url public String getSrc() { return src; } public void setSrc(String src) { this.src = src; } } }
3.Rxjava工具类
package com.jkxy.leijx.app_myfirstproject.rxjava; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.Log; import com.jkxy.leijx.app_myfirstproject.model.NewsDetailBean; import com.jkxy.leijx.app_myfirstproject.model.Newsbean; import com.jkxy.leijx.app_myfirstproject.network.Network; import com.jkxy.leijx.app_myfirstproject.util.FileioUtil; import java.util.List; import java.util.Map; import okhttp3.ResponseBody; import rx.Observable; import rx.Observer; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Func1; import rx.functions.Func2; import rx.schedulers.Schedulers; /** * RxJava 工具类 * Created by leijx on 2017/9/18. */ public class RxJavaUtil { private static final String TAG = "RxJavaUtil_leijx"; /** * 获取新闻列表 * @param context 上下文对象 * @param lodingtype 加载的类型(0:普通获取信息 1:上拉刷新 2:加载更多) * @param type 新闻类型 * @param id 新闻类型id * @param page 开始的新闻个数 * @param callback 获取来结果后的回调 * @return */ public static Subscription getNewsinfo(Context context, int lodingtype, String type, final String id, int page, final Callback_getNews callback){ Log.d(TAG,"getNewsinfo---type=="+type+"---id=="+id); return Network.getInstance(context) //获取Network实例 .getNewsinfo() //获取GetNewsInfoApi接口实例 .getNewslist(type,id,page) //请求数据,获取Observable被监听者对象 .flatMap(new Func1<Map<String, List<Newsbean>>, Observable<Newsbean>>() { //过滤出对应id的所有Newsbean对象 @Override public Observable<Newsbean> call(Map<String, List<Newsbean>> stringListMap) { Log.d(TAG,"flatMap---"+stringListMap.size()); return Observable.from(stringListMap.get(id)); } }) .distinct() //去重 .toSortedList(new Func2<Newsbean, Newsbean, Integer>() { //将当前过滤出的所有Newsbean根据ptime进行排序,组成新的list列表 @Override public Integer call(Newsbean newsbean, Newsbean newsbean2) { Log.d(TAG,"toSortedList---"); // return newsbean.getPtime().compareTo(newsbean2.getPtime()); //升序 return newsbean2.getPtime().compareTo(newsbean.getPtime()); //降序 } }) .subscribeOn(Schedulers.io()) //io线程中处理相关请求 .observeOn(AndroidSchedulers.mainThread()) //ui线程对获取来的数据进行对应处理 .unsubscribeOn(Schedulers.io()) .subscribe(new Observer<List<Newsbean>>() { //订阅 @Override public void onCompleted() { } @Override public void onError(Throwable e) { Log.d(TAG,"error==="+e.getMessage()); callback.faile(); } @Override public void onNext(List<Newsbean> list) { Log.d(TAG,"onNext"+list.size()); for(int i=0;i<list.size();i++){ Log.d(TAG,"titel"+list.get(i).getTitle()); Log.d(TAG,"diagest"+list.get(i).getDigest()); Log.d(TAG,"ptime"+list.get(i).getPtime()); } callback.sucess(list); //在重写的Callback_getNews中的sucess方法中进行对应处理 } }); } /** * 获取新闻详情 * @param context 上下文对象 * @param postid 新闻ID * @param callback_getNewsDetail 获取来结果后的回调 * @return */ public static Subscription getNewsDetailInfo(Context context, final String postid, final Callback_getNewsDetail callback_getNewsDetail){ return Network.getInstance(context).getNewsDetail().getNewsDetail(postid) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .map(new Func1<Map<String,NewsDetailBean>, NewsDetailBean>() { @Override public NewsDetailBean call(Map<String, NewsDetailBean> stringNewsDetailBeanMap) { NewsDetailBean newsDetailBean = stringNewsDetailBeanMap.get(postid); List<NewsDetailBean.ImageBean> imagelist = newsDetailBean.getImg(); Log.d(TAG,"imagelist.size==="+imagelist.size()); if(imagelist!=null && imagelist.size()>1){ changeNewDetailBody(newsDetailBean, imagelist); } return newsDetailBean; } }) .subscribe(new Observer<NewsDetailBean>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { Log.d(TAG,"error==="+e.getMessage()); callback_getNewsDetail.faile(); } @Override public void onNext(NewsDetailBean bean) { callback_getNewsDetail.sucess(bean); } }); } /** * 将获取来的内容详情中的<!--IMG#" + i + "--> 换成对应图片的url,方便后面进行显示 * @param newsDetailBean * @param list */ private static void changeNewDetailBody(NewsDetailBean newsDetailBean, List<NewsDetailBean.ImageBean> list) { String body = newsDetailBean.getBody(); Log.d("","oldbody=="+body); for(int i=0; i<list.size(); i++){ String oldchar = "<!--IMG#" + i + "-->"; String newchar; if(i==0){ newchar = ""; }else{ newchar = "<img src=\""+list.get(i).getSrc()+"\" >"; } body = body.replace(oldchar,newchar); Log.d(TAG,"replacebody=="+body); } Log.d(TAG,"newbody=="+body); newsDetailBean.setBody(body); } public static void getimage(Context context, final String url, final Callback_getImage callback){ Network.getInstance(context) .getimage() .getimage(url) .subscribeOn(Schedulers.io()) .map(new Func1<ResponseBody, Drawable>() { //转换为Drawable对象 @Override public Drawable call(ResponseBody responseBody) { return FileioUtil.getDrawable(responseBody); } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Drawable>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { callback.faile(); } @Override public void onNext(Drawable drawable) { callback.sucess(drawable); callback.sucess(url, drawable); } }); } public interface Callback_getNews { abstract void sucess(List<Newsbean> list); abstract void faile(); } public interface Callback_getImage { abstract void sucess(Drawable bitmap); abstract void sucess(String src, Drawable bitmap); abstract void faile(); } public interface Callback_getNewsDetail { abstract void sucess(NewsDetailBean bean); abstract void faile(); } }
4.
package com.jkxy.leijx.app_myfirstproject.network; import android.content.Context; import android.util.Log; import com.jkxy.leijx.app_myfirstproject.network.Api.GetNewsInfoApi; import okhttp3.OkHttpClient; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; import retrofit2.converter.gson.GsonConverterFactory; import rx.Observable; import static com.jkxy.leijx.app_myfirstproject.ConstantsUtil.BaseUrl; /** * Created by leijx on 2017/9/18. */ public class Network { private static Network network; private Context context; private Observable subscription; private static OkHttpClient okHttpClient; private static String TAG="Network_leijx"; public static Network getInstance(Context context){ Log.d(TAG,"getInstance"); if(network==null){ network = new Network(); } okHttpClient = new OkHttpClient(); return network; } /** * 获取新闻列表 * @return 返回GetNewsInfoApi接口对象 */ public GetNewsInfoApi getNewsinfo(){ Log.d(TAG,"getNewsinfo"); //创建Retrofit对象 Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .baseUrl(BaseUrl) .addConverterFactory(GsonConverterFactory.create()) //设置数据解析器 .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) //设置网络请求适配器 .build(); return retrofit.create(GetNewsInfoApi.class); } /** * 获取对应图片 * @return 返回GetNewsInfoApi接口对象 */ public GetNewsInfoApi getimage(){ Retrofit retrofit = new Retrofit.Builder() .baseUrl(BaseUrl) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); return retrofit.create(GetNewsInfoApi.class); } /** * 获取对应新闻详情 * @return 返回GetNewsInfoApi接口对象 */ public GetNewsInfoApi getNewsDetail(){ Retrofit retrofit = new Retrofit.Builder() .baseUrl(BaseUrl) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); return retrofit.create(GetNewsInfoApi.class); } }
5.
package com.jkxy.leijx.app_myfirstproject.network.Api; import com.jkxy.leijx.app_myfirstproject.model.NewsDetailBean; import com.jkxy.leijx.app_myfirstproject.model.Newsbean; import java.util.List; import java.util.Map; import okhttp3.ResponseBody; import retrofit2.http.GET; import retrofit2.http.Path; import retrofit2.http.Url; import rx.Observable; /** * Created by leijx on 2017/9/18. * Retrofit+Rxjava需要使用的接口类 */ public interface GetNewsInfoApi { /** * 获取新闻列表 * @param type 新闻类型 * @param id 新闻类型ID * @param page 开始时的新闻数目 * @return 返回对应的 被观察者 Observable<T> 对象 * 当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。 */ @GET("/nc/article/{type}/{id}/{startpage}-20.html") Observable<Map<String, List<Newsbean>>> getNewslist(@Path("type") String type, @Path("id") String id, @Path("startpage") int page); /** * 获取网络图片 * @param url * @return 返回对应的 被观察者 Observable<T> 对象 * 当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。 */ @GET Observable<ResponseBody> getimage(@Url String url); /** * 获取新闻详情 * @param postid * @return 返回对应的 被观察者 Observable<T> 对象 * 当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()。 */ @GET("/nc/article/{postid}/full.html") Observable<Map<String, NewsDetailBean>> getNewsDetail(@Path("postid") String postid); }
6.
package com.jkxy.leijx.app_myfirstproject.util; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import okhttp3.ResponseBody; /** * 文件工具类 * Created by leijx on 2017/9/20. */ public class FileioUtil { /** * 将获取来的网络请求结果转换为对应的Bitmap对象 * @param responseBody * @return */ public static Bitmap getBitmap(ResponseBody responseBody){ Bitmap bitmap; bitmap = BitmapFactory.decodeStream(responseBody.byteStream()); return bitmap; } /** * 将获取来的网络请求结果转换为对应的Drawable对象 * @param responseBody * @return */ public static Drawable getDrawable(ResponseBody responseBody){ Drawable drawable; drawable = Drawable.createFromStream(responseBody.byteStream(),""); return drawable; } }