效果圖
網(wǎng)站效果:
手機(jī)展示:
概述
Fruit是一個(gè)解析html的框架∥鸸可以將html文件解析成本地實(shí)體文件。
Fruit地址:https://github.com/ghuiii/Fruit
解析的網(wǎng)址為:http://www.wyl.cc/tag/xinlingjitang/page/1
配合Rxjava、okhttp爽彤、retrofit使用
導(dǎo)包
compile 'io.reactivex.rxjava2:rxjava:2.1.3'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.okhttp3:okhttp:3.9.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'
/*解析html*/
compile 'me.ghui:fruit-converter-retrofit:1.0.5'
compile 'me.ghui:global-retrofit-converter:1.0.1'
/*converter-gson 用于將請(qǐng)求結(jié)果轉(zhuǎn)換為實(shí)體類型*/
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
/*converter-scalars 用于將請(qǐng)求結(jié)果轉(zhuǎn)換為基本類型,一般為String*/
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
注意
剛開始因?yàn)橛昧?converter:1.0.4導(dǎo)致一直報(bào)錯(cuò):
Error:Execution failed for task ':app:transformClassesWithDexBuilderForDebug'.
應(yīng)該是和后面導(dǎo)入的converter-gson
或者是converter-scalars
不兼容缚陷。當(dāng)改成fruit-converter-retrofit:1.0.5
后問題解決适篙。
初始化 OkHttp Retrofit Fruit
基本配置
public class ApiStrategy {
//讀超時(shí)長(zhǎng),單位:毫秒
public static final int READ_TIME_OUT = 7676;
//連接時(shí)長(zhǎng)箫爷,單位:毫秒
public static final int CONNECT_TIME_OUT = 7676;
/**
* 設(shè)緩存有效期為兩天
*/
private static final long CACHE_STALE_SEC = 60 * 60 * 24 * 2;
public static QuotaionsApis apiService;
private Fruit mFruit;
public static QuotaionsApis getApiService() {
if (apiService == null) {
synchronized (Api.class) {
if (apiService == null) {
new ApiStrategy();
}
}
}
return apiService;
}
private ApiStrategy() {
HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor();
logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//緩存
File cacheFile = new File(MyApplication.getContextObject().getCacheDir(), "cache");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb
//增加頭部信息
Interceptor headerInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request build = chain.request().newBuilder()
//.addHeader("Content-Type", "application/json")//設(shè)置允許請(qǐng)求json數(shù)據(jù)
.build();
return chain.proceed(build);
}
};
//創(chuàng)建一個(gè)OkHttpClient并設(shè)置超時(shí)時(shí)間
OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(READ_TIME_OUT, TimeUnit.MILLISECONDS)
.connectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS)
.addInterceptor(mRewriteCacheControlInterceptor)
.addNetworkInterceptor(mRewriteCacheControlInterceptor)
.addInterceptor(headerInterceptor)
.addInterceptor(logInterceptor)
.cache(cache)
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(Constant.BASE_URL)
// .addConverterFactory(GsonConverterFactory.create())//請(qǐng)求的結(jié)果轉(zhuǎn)為實(shí)體類
//適配RxJava2.0,RxJava1.x則為RxJavaCallAdapterFactory.create()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GlobalConverterFactory.create().add(FruitConverterFactory.create(getFruit()), Html.class))
.build();
apiService = retrofit.create(QuotaionsApis.class);
}
/**
* 云端響應(yīng)頭攔截器嚷节,用來配置緩存策略
* Dangerous interceptor that rewrites the server's cache-control header.
*/
private final Interceptor mRewriteCacheControlInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
Log.i("http", url);
String cacheControl = request.cacheControl().toString();
if (!NetworkUtil.isNetworkConnected(MyApplication.getContextObject())) {
request = request.newBuilder()
.cacheControl(TextUtils.isEmpty(cacheControl) ? CacheControl
.FORCE_NETWORK : CacheControl.FORCE_CACHE)
.build();
}
Response originalResponse = chain.proceed(request);
if (NetworkUtil.isNetworkConnected(MyApplication.getContextObject())) {
return originalResponse.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma")
.build();
} else {
return originalResponse.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" +
CACHE_STALE_SEC)
.removeHeader("Pragma")
.build();
}
}
};
private Fruit getFruit() {
if (mFruit == null) {
mFruit = new Fruit();
}
return mFruit;
}
}
配置請(qǐng)求參數(shù)
和基本的retrofit參數(shù)類似
public interface QuotaionsApis {
//使用動(dòng)態(tài) url 匹配
@GET
@Html
Observable<QuotationsBean> getQuotationsBean(@Url String url);
}
根據(jù)網(wǎng)頁結(jié)構(gòu)得到相應(yīng)的字段
對(duì)應(yīng)的實(shí)體如下:
public class QuotationsBean {
@Pick(value = "article")
private List<QuotationsBeanItem> quotationsBeanItemList;
...
public static class QuotationsBeanItem {
//圖片
@Pick(value = "img", attr = Attrs.SRC)
private String img;
@Pick(value = "h2.entry-title > a")
private String title;
@Pick(value = "div.archive-content")
private String content;
@Pick(value = "span.entry-meta > span")
private String time;
@Pick(value = "h2.entry-title > a", attr = Attrs.HREF)
private String link;
@Pick(value = "span.cat > a")
private String tag;
...
}
根據(jù)視圖結(jié)構(gòu)便可以得到對(duì)應(yīng)的數(shù)據(jù)。
完成響應(yīng) 進(jìn)行請(qǐng)求
ApiMethods.getQuotations(new MyObserver<QuotationsBean>(MainActivity.this, new ObserverOnNextListener<QuotationsBean>() {
@Override
public void onNext(QuotationsBean quotationsBean) {
List<QuotationsBean.QuotationsBeanItem> listData = quotationsBean.getQuotationsBeanItemList();
if (page == 1) {
mList.clear();
mList.addAll(listData);
refreshLayout.finishRefresh(1000);
} else if (page > 0) {
mList.addAll(listData);
refreshLayout.finishLoadMore(1000/*,false*/);
}
mAdapter.notifyDataSetChanged();
}
}), Constant.QUOTATIONS_URL + String.valueOf(page));