智慧廚房重構-Retrofit和RxJava進行網路請求
上一篇部落格智慧廚房重構-MVP架構中我們簡單談了一下MVP的使用方法,使用MVP介面的方式讓我們的程式碼已經很簡單明瞭了,但是我告訴你還不夠,使用了本篇的Retrofit和RxJava後讓你的程式碼美如畫。對,就是美如畫。
1.Retrofit的簡單介紹
Retrofit是square公司推出的一種使用介面方式進行網路請求的網路請求庫,裡面實際上用的還是okhttp的網路請求模組,不過他進行了封裝,使用起來更加方便。
1.1 環境配置
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.0.1'
compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0'
compile 'com.google.code.gson:gson:2.4'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
compile 'com.squareup.retrofit2:converter-scalars:2.0.0'
首先將上面的依賴庫新增到你的appgradle中,上面還有RxJava我一併給出了。
1.2 基本語法
首先我們看一下Retrofit的配置,它使用了Builder建造者模式來進行配置自己。
/**
* 作者:GXL on 2016/8/3 0003
* 部落格: http://blog.csdn.net/u014316462
*/
public class RetrofitWrapper {
public static RetrofitWrapper instance;
public static Retrofit retrofit;
private RetrofitWrapper () {
retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
public static RetrofitWrapper getInstance() {
if (instance == null) {
synchronized (RetrofitWrapper.class) {
instance = new RetrofitWrapper();
}
}
return instance;
}
public <T> T create(Class<T> service) {
return retrofit.create(service);
}
public class Constant {
public static final String BASE_URL = "http://www.meishij.net/";
}
}
上面是我對Retrofit配置的一個簡單包裹,我們看一下需要配置的東西:
(1) baseUrl 這個使我們需要訪問的網路地址
(2) addConverterFactory()這個引數特別重要,在這裡你要選擇你需要的轉化器型別。square給我們提供了下面這幾種
Gson :用來解析Json格式的資料。假如你請求的資料是Json的,就用這個。
scalars:用來解析字串格式的資料,假如你請求的資料是字串的話,就用這個。
別的我目前還沒用過,上面這兩個實際使用的比較多,你選擇哪一個將上面哪一個的依賴新增到gradle裡面啊。
(3) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 這個一看就是知道使用了RxJava作為我們的回撥庫,RxJava就是將你從回撥中解放出來的神器。
重要的配置就是這麼多了。現在我們來看一下介面請求的地方的引數。
/**
* 作者:GXL on 2016/8/3 0003
* 部落格: http://blog.csdn.net/u014316462
* 作用:該介面用來請求美食傑網頁資料
*/
public interface FoodService {
/**
*
* @param sortby 最新update和最熱renqi兩種
* @param lm 食品的種類
* @param page 當前的頁碼
* @return 當前html頁面
*/
@GET("list.php")
Observable<String> getFoodList(@Query("sortby") String sortby, @Query("lm") int lm, @Query("page") int page);
/**
*
* @param foodname 食品名
* @return 當前html頁面
*/
@GET("zuofa/{foodname}.html")
Observable<String> getDetailFood(@Path("foodname") String foodname);
/**
* 獲取滾動展示的集合,假如引數為空,要在裡面加一個空格
* @return 當前html頁面
*/
@GET(" ")
Observable<String> getSliderShowFood();
}
是不是很簡捷啊,一個函式代表一種網路請求,還是來看一下里面的引數:
(1) @GET 這個對應我們網路請求中的GET請求,裡面的引數就是網路請求URL剩下的一部分,BASE_URL+list.php=http://www.meishij.net/list.php,這才是我們最終的請求網址。
假如BASE_URL就是最後的請求地址那就是直接在裡面打一個空格。
再來看一下里面的引數可以有哪些配置
@Path
@GET("zuofa/{foodname}.html")
Observable<String> getDetailFood(@Path("foodname") String foodname);
@Path variable substitution for the API endpoint (i.e. username will be swapped for {username} in the URL endpoint).
這是官網的描述,個人理解就是替代塊,在URL中用{}包含一個引數名未知需要在實際使用中傳入,注意名稱相同。
@Query
@GET("list.php")
Observable<String> getFoodList(@Query("sortby") String sortby, @Query("lm") int lm, @Query("page") int page);
@Query specifies the query key name with the value corresponding to the value of that annotated parameter.
(2) @POST 這個對應我們網路請求中的POST請求,Post請求相對於get請求來說更加安全,因為他的引數不會暴露在url中,並且post也沒有url長度的限制。可以檢視這篇博文http://blog.chinaunix.net/uid-25311424-id-3959591.html
@Field
@FormUrlEncoded
@POST("/newfind/index_ask")
Observable<Response> getDaJia(@Field("page") int page,
@Field("pageSize") int size,
@Field("tokenMark") long tokenMark,
@Field("token") String token
);
比如下面這一種,都將引數存放到了field裡面,用鍵值對形式去請求資料。
FieldMap
@FormUrlEncoded
@POST("FundPaperTrade/AppUserLogin")
Observable<Response> getTransData(@FieldMap Map<String,String> map);
假如資料過多,可以使用鍵值對錶的形式。
@FormUrlEncoded
註明是表單提交,使用field就需要使用該引數
@Headers()
請求頭設定
@Headers("Cache-Control: max-age=640000")
@GET("/widget/list")
List<Widget> widgetList();
例如上述程式碼就給http請求添加了請求頭。注意所有頭都不會覆蓋,同名的會一併提交。所以假如我們想在所有的請求中新增一樣的請求頭可以使用下面的辦法。
RequestInterceptor requestInterceptor = new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader("User-Agent", "Retrofit-Sample-App");
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.setRequestInterceptor(requestInterceptor)
.build();
大致常用的引數就這麼多,還有好多上傳檔案用到的,後面我在慢慢整理,看了這麼多是不是暈暈乎乎的啊,趕快看看RxJava放鬆一下。
2.RxJava的簡單介紹
RxJava之前我寫過相關的博文簡單介紹過,請看Rxjava操作步驟
3.例項
終於將前面的一些皮毛講解完了,下面我們就要用他們來乾點事情了。實戰出真知嘛。
還是我們上一篇的需求。
變成這樣。
想說一下思路:
1.想要把這個網頁的html檔案拿到手,就是字串格式的。好,來幹。
配置Retrofit
/**
* 作者:GXL on 2016/8/3 0003
* 部落格: http://blog.csdn.net/u014316462
*/
public class RetrofitWrapper {
public static RetrofitWrapper instance;
public static Retrofit retrofit;
private RetrofitWrapper() {
retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
}
public static RetrofitWrapper getInstance() {
if (instance == null) {
synchronized (RetrofitWrapper.class) {
instance = new RetrofitWrapper();
}
}
return instance;
}
public <T> T create(Class<T> service) {
return retrofit.create(service);
}
public class Constant {
public static final String BASE_URL = "http://www.meishij.net/";
}
}
定義請求介面
/**
* 作者:GXL on 2016/8/3 0003
* 部落格: http://blog.csdn.net/u014316462
* 作用:該介面用來請求美食傑網頁資料
*/
public interface FoodService {
/**
*
* @param sortby 最新update和最熱renqi兩種
* @param lm 食品的種類
* @param page 當前的頁碼
* @return 當前html頁面
*/
@GET("list.php")
Observable<String> getFoodList(@Query("sortby") String sortby, @Query("lm") int lm, @Query("page") int page);
}
2.經過retrofit的奮戰,String已經拿到了,但是現在還不能用,需要程式設計實體類,這時候RxJava也不能閒著了。
/**
* 作者:GXL on 2016/8/3 0003
* 部落格: http://blog.csdn.net/u014316462
* 作用:Food請求model
*/
public class FoodModel implements FoodModelImpl {
@Override
public void getGeneralFoodsItem(String sortby, int type, int page, final BaseListener listener) {
FoodService service = RetrofitWrapper.getInstance().create(FoodService.class);
service.getFoodList(sortby,type,page).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.map(new Func1<String, List<FoodGeneralItem>>(){
@Override
public List<FoodGeneralItem> call(String s) {
return HtmlParser.parserHtml(s);
}
}).subscribe(new Subscriber<List<FoodGeneralItem>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
listener.getFailure();
}
@Override
public void onNext(List<FoodGeneralItem> s) {
listener.getSuccess(s);
}
});
}
}
下面這段程式碼是解析的核心,將String物件轉化成了List<FoodGeneralItem>>
``` lasso
.map(new Func1<String, List<FoodGeneralItem>>(){
@Override
public List<FoodGeneralItem> call(String s) {
return HtmlParser.parserHtml(s);
}
})
<div class="se-preview-section-delimiter"></div>
HtmlParser.parserHtml(s); 這個函式使用Jsoup解析html的節點。下面給出程式碼,自己研究一下啊。
/**
* 作者:GXL on 2016/8/3 0003
* 部落格: http://blog.csdn.net/u014316462
* 作用:解析html獲得實體物件
*/
public class HtmlParser {
/**
* 獲得大概的美食集合
* @param msg
* @return
*/
public static List<FoodGeneralItem> parserHtml(String msg) {
String content_personame = null;
List<FoodGeneralItem> foodsItemList=new ArrayList<>();
org.jsoup.nodes.Document doc = Jsoup.parse(msg);
Elements units = doc.getElementsByClass("listtyle1");
Elements detail_units = doc.getElementsByClass("c1");
Elements date_units = doc.getElementsByClass("c2");
for (int i = 0; i < units.size(); i++) {
FoodGeneralItem foodsItem = new FoodGeneralItem();
Element detail_ele = units.get(i);
Elements links = detail_ele.getElementsByTag("a");
String link = links.get(0).attr("href");
String title = links.get(0).attr("title");
Element imagelink_ele = detail_ele.getElementsByTag("img").get(0);
String imagelink = imagelink_ele.attr("src");
Element content_ele = detail_units.get(i);
Elements content_pinglus = detail_ele.getElementsByTag("span");
String content_pinglun = content_pinglus.get(0).text();
Elements content_pername = detail_ele.getElementsByTag("em");
if(content_pername.size()!=0) {
content_personame = content_pername.get(0).text();
}
Element date_ele = date_units.get(i);
Elements date_shijian = detail_ele.getElementsByTag("ul");
String content_bushu = date_shijian.get(0)
.getElementsByClass("li1").text();
String content_weidao = date_shijian.get(0)
.getElementsByClass("li2").text();
String Personname = content_personame;
String Date = content_bushu;
foodsItem.setTitle(title);
foodsItem.setLink(link);
foodsItem.setWriter(Personname);
foodsItem.setTaste(content_weidao);
foodsItem.setDate(Date);
foodsItem.setDiscuss(content_pinglun);
foodsItem.setImgLink(imagelink);
foodsItemList.add(foodsItem);
}
return foodsItemList;
}
/**
* 解析手機端的Html獲得詳細的做菜步驟
* @param canshu
* @return
*/
public static FoodDetailTeachItem parserHtmlToDetailInMobile(String canshu) {
FoodDetailTeachItem detailTeachList=new FoodDetailTeachItem();
ArrayList<FoodAccessories> accessoriesList=new ArrayList<>();
ArrayList<FoodTeachStep> teachStepList=new ArrayList<>();
Document doc = Jsoup.parse(canshu);
String food_title = doc.getElementsByClass("fade_topbar").get(0)
.getElementsByTag("h2").get(0).text();
Log.i("food_title", food_title);
detailTeachList.setFoodTitle(food_title);
String showfood_text = doc.getElementsByClass("cp_main").attr("style");
Log.i("showfood_text", showfood_text);
detailTeachList.setFoodIntroduction(showfood_text);
String NewStr = showfood_text.substring(showfood_text.indexOf("(") + 1,
showfood_text.lastIndexOf(")"));
Log.i("showfood_text", NewStr);
detailTeachList.setFoodImage(NewStr);
String show_writer_name_text = doc.getElementsByClass("con_main")
.get(0).getElementsByTag("a").get(0).getElementsByTag("span")
.text();
detailTeachList.setWriteName(show_writer_name_text);
String show_writer_image_text = doc.getElementsByClass("con_main")
.get(0).getElementsByTag("a").get(0).getElementsByTag("img")
.attr("src");
detailTeachList.setWritePhoto(show_writer_image_text);
/*
* 下面解析出做該菜要用到的食材和輔料
*/
if (doc.select("div.material_coloum_type1").size() > 0) {
Elements units = doc.getElementsByClass("material_coloum_type1");
Elements units_zl = units.get(0).getElementsByClass(
"material_coloum");
for (int i = 0; i < units_zl.size(); i++) {
FoodAccessories accessories = new FoodAccessories();
Element content_zl_name = units_zl.get(i)
.getElementsByTag("span").get(0);
String content_name = content_zl_name.text();
Element content_zl_shuliang = units_zl.get(i)
.getElementsByTag("em").get(0);
String content_shuliang = content_zl_shuliang.text();
accessories.setName(content_name);
accessories.setNumber(content_shuliang);
Log.i("content_name", content_name);
Log.i("content_shuliang", content_shuliang);
accessoriesList.add(accessories);
}
}
if (doc.select("div.material_coloum_type2").size() > 0) {
Elements units_fuliao = doc
.getElementsByClass("material_coloum_type2");
Elements Units_fuliao = units_fuliao.get(0).getElementsByClass(
"material_coloum");
Log.i("content_fuliao_shuliang", Units_fuliao.size() + "");
for (int i = 0; i < Units_fuliao.size(); i++) {
FoodAccessories fuliao = new FoodAccessories();
Element content_fuliao = Units_fuliao.get(i)
.getElementsByTag("span").get(0);
String content_fuliao_name = content_fuliao.text();
Element content_zl_shuliang = Units_fuliao.get(i)
.getElementsByTag("em").get(0);
String content_fuliao_shuliang = content_zl_shuliang.text();
Log.i("content_fuliao_name", content_fuliao_name);
Log.i("content_fuliao_shuliang", content_fuliao_shuliang);
fuliao.setName(content_fuliao_name);
fuliao.setNumber(content_fuliao_shuliang);
accessoriesList.add(fuliao);
}
}
/**
* 下面解析出詳細的做菜方法
*/
if (doc.select("div.cp_step").size() > 0) {
Elements units_teach = doc.getElementsByClass("cp_step").get(0)
.getElementsByTag("h2");
String teachtext;
String imagelink;
for (int i = 0; i < units_teach.size() - 1; i++) {
FoodTeachStep foodcontent = new FoodTeachStep();
Log.i("teachtext", units_teach.get(i).text());
if (units_teach.get(i).nextElementSibling()
.getElementsByTag("img").size() > 0) {
imagelink = units_teach.get(i).nextElementSibling()
.getElementsByTag("img").get(0).attr("src");
teachtext = units_teach.get(i).nextElementSibling()
.nextElementSibling().text();
Log.i("imagelink", imagelink);
Log.i("teachtext", teachtext);
showfood_text = imagelink;
} else {
imagelink = "noimagelink";
teachtext = units_teach.get(i).nextElementSibling().text();
Log.i("imagelink", imagelink);
Log.i("teachtext", teachtext);
}
foodcontent.setNum(units_teach.get(i).text());
foodcontent.setImagelink(imagelink);
foodcontent.setTeachtext(teachtext);
teachStepList.add(foodcontent);
}
}
detailTeachList.setAccessoriesList(accessoriesList);
detailTeachList.setStepList(teachStepList);
return detailTeachList;
}
/**
* 解析電腦端的Html獲得詳細的做菜步驟
* @param canshu
* @return
*/
public static FoodDetailTeachItem parserHtmlToDetailInPc(String canshu) {
FoodDetailTeachItem detailTeachList=new FoodDetailTeachItem();
ArrayList<FoodAccessories> accessoriesList=new ArrayList<>();
ArrayList<FoodTeachStep> teachStepList=new ArrayList<>();
Document doc = Jsoup.parse(canshu);
String food_title = doc.getElementsByClass("info1").get(0)
.getElementsByTag("a").get(0).text();
Log.i("food_title",food_title);
detailTeachList.setFoodTitle(food_title);
String showfood_image = doc.getElementsByClass("cp_headerimg_w").get(0)
.getElementsByTag("img").get(0).attr("src");
detailTeachList.setFoodImage(showfood_image);
String showdetail_text = doc.getElementsByClass("materials").get(0)
.getElementsByTag("p").get(0).text();
detailTeachList.setFoodIntroduction(showdetail_text);
String show_writer_name_text = doc.getElementsByClass("user").get(0)
.getElementsByClass("info").get(0).getElementsByTag("h4")
.get(0).getElementsByTag("a").get(0).text();
detailTeachList.setWriteName(show_writer_name_text);
String show_writer_image_text = doc.getElementsByClass("user").get(0)
.getElementsByTag("a").get(0).getElementsByTag("img").get(0)
.attr("src");
detailTeachList.setWritePhoto(show_writer_image_text);
String show_writer_date_text = doc.getElementsByClass("user").get(0)
.getElementsByClass("info").get(0).getElementsByTag("strong")
.get(0).text();
show_writer_date_text="創建於"+show_writer_date_text.substring(0,show_writer_date_text.lastIndexOf('/')-1);
detailTeachList.setWriteDate(show_writer_date_text);
Log.i("tishi", food_title);
/*
* 下面解析出做該菜要用到的食材和輔料
*/
if (doc.select("div.zl").size() > 0) {
Elements units = doc.getElementsByClass("zl");
Elements units_zl = units.get(0).getElementsByTag("li");
for (int i = 0; i < units_zl.size(); i++) {
FoodAccessories fuliao = new FoodAccessories();
Element content_zl_name = units_zl.get(i)
.getElementsByClass("c").get(0).getElementsByTag("h4")
.get(0).getElementsByTag("a").get(0);
String content_name = content_zl_name.text();
Element content_zl_shuliang = units_zl.get(i)
.getElementsByClass("c").get(0).getElementsByTag("h4")
.get(0).getElementsByTag("span").get(0);
String content_shuliang = content_zl_shuliang.text();
fuliao.setName(content_name);
fuliao.setNumber(content_shuliang);
Log.i("content_name", content_name);
Log.i("content_shuliang", content_shuliang);
accessoriesList.add(fuliao);
}
}
if (doc.select("div.fuliao").size() > 0) {
Elements units_fuliao = doc.getElementsByClass("fuliao");
Elements Units_fuliao = units_fuliao.get(0).getElementsByTag("li");
Log.i("content_fuliao_shuliang", Units_fuliao.size() + "");
for (int i = 0; i < Units_fuliao.size(); i++) {
FoodAccessories fuliao = new FoodAccessories();
Element content_fuliao = Units_fuliao.get(i)
.getElementsByTag("h4").get(0).getElementsByTag("a")
.get(0);
String content_fuliao_name = content_fuliao.text();
Element content_zl_shuliang = Units_fuliao.get(i)
.getElementsByTag("span").get(0);
String content_fuliao_shuliang = content_zl_shuliang.text();
Log.i("content_fuliao_name", content_fuliao_name);
Log.i("content_fuliao_shuliang", content_fuliao_shuliang);
fuliao.setName(content_fuliao_name);
fuliao.setNumber(content_fuliao_shuliang);
accessoriesList.add(fuliao);
}
}
/**
* 下面解析出詳細的做菜方法
*/
if (doc.select("div.content").size() > 0) {
Log.i("1111", "1111");
Elements units_teach = doc.getElementsByClass("measure").get(0)
.getElementsByClass("edit").get(0)
.getElementsByClass("content");
Log.i("units_teach", units_teach.size() + "");
String teachtext;
String imagelink;
for (int i = 0; i < units_teach.size(); i++) {
FoodTeachStep foodcontent = new FoodTeachStep();
if (units_teach.get(i).getElementsByClass("c").get(0)
.getElementsByTag("p").size() == 2) {
teachtext = units_teach.get(i).getElementsByClass("c")
.get(0).getElementsByTag("p").get(0).text();
imagelink = units_teach.get(i).getElementsByClass("c")
.get(0).getElementsByTag("p").get(1)
.getElementsByTag("img").get(0).attr("src");
} else {
if (units_teach.get(i).getElementsByClass("c").get(0)
.getElementsByTag("p").get(0).hasAttr("src")) {
teachtext = "";
imagelink = units_teach.get(i).getElementsByClass("c")
.get(0).getElementsByTag("p").get(0)
.getElementsByTag("img").get(0).attr("src");
} else {
teachtext = units_teach.get(i).getElementsByClass("c")
.get(0).getElementsByTag("p").get(0).text();
imagelink = "noimagelink";
}
}
Log.i("teachtext", teachtext);
Log.i("imagelink", imagelink);
foodcontent.setNum(String.valueOf(i + 1));
foodcontent.setImagelink(imagelink);
foodcontent.setTeachtext(teachtext);
teachStepList.add(foodcontent);
}
} else {
Elements units_teach = doc.getElementsByClass("measure").get(0)
.getElementsByClass("edit").get(0).getElementsByTag("p");
Log.i("units_teach", units_teach.size() + "");
Log.i("units_teach_texy", units_teach.get(0).text());
int num = 0;
for (int i = 0, j = 0; i < units_teach.size() - 1; i++, j++) {
FoodTeachStep foodcontent = new FoodTeachStep();
String teachtext = null;
String imagelink = null;
if (units_teach.get(i).getElementsByTag("em").size() > 0) {
teachtext = units_teach.get(i).text();
int flag = i + 1;
if (units_teach.get(flag).getElementsByClass("conimg")
.size() > 0) {
imagelink = units_teach.get(flag)
.getElementsByClass("conimg").get(0)
.attr("src");
i++;
} else {
imagelink = "noimagelink";
}
} else if (units_teach.get(i).getElementsByTag("img").size() > 0) {
teachtext = "";
imagelink = units_teach.get(i).getElementsByClass("conimg")
.get(0).attr("src");
} else {
continue;
}
Log.i("teachtext", teachtext);
Log.i("imagelink", imagelink);
foodcontent.setNum(String.valueOf(++num));
foodcontent.setImagelink(imagelink);
foodcontent.setTeachtext(teachtext);
teachStepList.add(foodcontent);
}
}
detailTeachList.setAccessoriesList(accessoriesList);
detailTeachList.setStepList(teachStepList);
return detailTeachList;
}
public static List<SlideShowView.SliderShowViewItem> parserHtmlToSliderShow(String msg) {
ArrayList<SlideShowView.SliderShowViewItem> list=new ArrayList<>();
SlideShowView.SliderShowViewItem viewItem = null;
org.jsoup.nodes.Document doc = Jsoup.parse(msg);
Elements content = doc.getElementsByClass("zzw_item_2");
Elements links = content.get(0).getElementsByTag(