├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hd123 │ │ └── httpframe │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── hd123 │ │ │ └── httpframe │ │ │ ├── App.java │ │ │ ├── config │ │ │ └── Constant.java │ │ │ ├── net │ │ │ ├── cases │ │ │ │ ├── GetCitiesCase.java │ │ │ │ └── base │ │ │ │ │ └── UseCase.java │ │ │ ├── exception │ │ │ │ └── ApiException.java │ │ │ ├── extension │ │ │ │ ├── BaseNetProvider.java │ │ │ │ ├── BaseSubscriber.java │ │ │ │ └── IPaging.java │ │ │ ├── models │ │ │ │ ├── City.java │ │ │ │ └── PagingReq.java │ │ │ └── retrofit │ │ │ │ ├── NetInterceptor.java │ │ │ │ ├── NetMgr.java │ │ │ │ ├── NetProvider.java │ │ │ │ └── RequestHandler.java │ │ │ └── views │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── hd123 │ └── httpframe │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.jar ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 参考github项目[XDroidMvp](https://github.com/limedroid/XDroidMvp) 2 | 3 | 想必Retrifit+Rxjava的使用,如今已经非常的普及了吧。在此介绍一种比较优雅的有关Retrifit+Rxjava封装的方法。 4 | 5 | 原本的步骤应该是这样,首先要创建OKHttpClient ,在其中添加一些拦截和超时处理,然后创建Retrofit对象并注入OKHttpClient对象,再获取接口实例Observable对象,然后绑定生命周期(防止内存泄漏)并订阅观察者Subscriber处理返回信息。 6 | 7 | 那现在应该如何封装,才能比较优雅,并且能够尽量的解耦呢? 8 | 9 | * **1、提出Retrofits实现类,提供设置超时时间、添加拦截等处理的接口** 10 | 11 | 首先应该将Retrofit这一块提出来,而创建Retrofit需要注入OKHttpClient,其中有很多与业务相关的处理,比如需要设置超时时间,拦截头部添加Header等等。那么这一块就可以写一个接口回调,在外部实现后注入。看一下这一块的代码吧 12 | 13 | ```java 14 | public class NetMgr { 15 | private final long connectTimeoutMills = 10 * 1000L; 16 | private final long readTimeoutMills = 10 * 1000L; 17 | private NetProvider sProvider = null; 18 | private static NetMgr instance; 19 | private Map providerMap = new HashMap<>(); 20 | private Map retrofitMap = new HashMap<>(); 21 | private Map clientMap = new HashMap<>(); 22 | 23 | 24 | public static NetMgr getInstance() { 25 | if (instance == null) { 26 | synchronized (NetMgr.class) { 27 | if (instance == null) { 28 | instance = new NetMgr(); 29 | } 30 | } 31 | } 32 | return instance; 33 | } 34 | 35 | 36 | public S get(String baseUrl, Class service) { 37 | return getInstance().getRetrofit(baseUrl).create(service); 38 | } 39 | 40 | public void registerProvider(NetProvider provider) { 41 | this.sProvider = provider; 42 | } 43 | 44 | public void registerProvider(String baseUrl, NetProvider provider) { 45 | getInstance().providerMap.put(baseUrl, provider); 46 | } 47 | 48 | public NetProvider getCommonProvider() { 49 | return sProvider; 50 | } 51 | 52 | public void clearCache() { 53 | getInstance().retrofitMap.clear(); 54 | getInstance().clientMap.clear(); 55 | } 56 | 57 | public Retrofit getRetrofit(String baseUrl) { 58 | return getRetrofit(baseUrl, null); 59 | } 60 | 61 | public Retrofit getRetrofit(String baseUrl, NetProvider provider) { 62 | if (empty(baseUrl)) { 63 | throw new IllegalStateException("baseUrl can not be null"); 64 | } 65 | if (retrofitMap.get(baseUrl) != null) { 66 | return retrofitMap.get(baseUrl); 67 | } 68 | 69 | if (provider == null) { 70 | provider = providerMap.get(baseUrl); 71 | if (provider == null) { 72 | provider = sProvider; 73 | } 74 | } 75 | checkProvider(provider); 76 | 77 | Gson gson = new GsonBuilder() 78 | .setDateFormat("yyyy-MM-dd HH:mm:ss") 79 | .create(); 80 | 81 | Retrofit.Builder builder = new Retrofit.Builder() 82 | .baseUrl(baseUrl) 83 | .client(getClient(baseUrl, provider)) 84 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 85 | .addConverterFactory(GsonConverterFactory.create(gson)); 86 | 87 | Retrofit retrofit = builder.build(); 88 | retrofitMap.put(baseUrl, retrofit); 89 | providerMap.put(baseUrl, provider); 90 | 91 | return retrofit; 92 | } 93 | 94 | private boolean empty(String baseUrl) { 95 | return baseUrl == null || baseUrl.isEmpty(); 96 | } 97 | 98 | private OkHttpClient getClient(String baseUrl, NetProvider provider) { 99 | if (empty(baseUrl)) { 100 | throw new IllegalStateException("baseUrl can not be null"); 101 | } 102 | if (clientMap.get(baseUrl) != null) { 103 | return clientMap.get(baseUrl); 104 | } 105 | 106 | checkProvider(provider); 107 | 108 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 109 | 110 | builder.connectTimeout(provider.configConnectTimeoutSecs() != 0 111 | ? provider.configConnectTimeoutSecs() 112 | : connectTimeoutMills, TimeUnit.SECONDS); 113 | builder.readTimeout(provider.configReadTimeoutSecs() != 0 114 | ? provider.configReadTimeoutSecs() : readTimeoutMills, TimeUnit.SECONDS); 115 | 116 | builder.writeTimeout(provider.configWriteTimeoutSecs() != 0 117 | ? provider.configReadTimeoutSecs() : readTimeoutMills, TimeUnit.SECONDS); 118 | CookieJar cookieJar = provider.configCookie(); 119 | if (cookieJar != null) { 120 | builder.cookieJar(cookieJar); 121 | } 122 | provider.configHttps(builder); 123 | 124 | RequestHandler handler = provider.configHandler(); 125 | if (handler != null) { 126 | builder.addInterceptor(new NetInterceptor(handler)); 127 | } 128 | 129 | Interceptor[] interceptors = provider.configInterceptors(); 130 | if (!empty(interceptors)) { 131 | for (Interceptor interceptor : interceptors) { 132 | builder.addInterceptor(interceptor); 133 | } 134 | } 135 | 136 | if (provider.configLogEnable()) { 137 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); 138 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 139 | builder.addInterceptor(loggingInterceptor); 140 | } 141 | 142 | OkHttpClient client = builder.build(); 143 | clientMap.put(baseUrl, client); 144 | providerMap.put(baseUrl, provider); 145 | 146 | return client; 147 | } 148 | 149 | private boolean empty(Interceptor[] interceptors) { 150 | return interceptors == null || interceptors.length == 0; 151 | } 152 | 153 | private void checkProvider(NetProvider provider) { 154 | if (provider == null) { 155 | throw new IllegalStateException("must register provider first"); 156 | } 157 | } 158 | 159 | public Map getRetrofitMap() { 160 | return retrofitMap; 161 | } 162 | 163 | public Map getClientMap() { 164 | return clientMap; 165 | } 166 | 167 | } 168 | ``` 169 | 170 | * **2、实现NetProvider接口并注入** 171 | 172 | NetMgr就是一个Retrofit的实现类,然后NetProvider是一个接口,需要在外部去实现,然后注入。再看一下NetProvider的实现类BaseNetProvider 173 | 174 | ```java 175 | public class BaseNetProvider implements NetProvider { 176 | 177 | private static final long CONNECT_TIME_OUT = 30; 178 | private static final long READ_TIME_OUT = 180; 179 | private static final long WRITE_TIME_OUT = 30; 180 | 181 | 182 | @Override 183 | public Interceptor[] configInterceptors() { 184 | return null; 185 | } 186 | 187 | @Override 188 | public void configHttps(OkHttpClient.Builder builder) { 189 | 190 | } 191 | 192 | @Override 193 | public CookieJar configCookie() { 194 | return null; 195 | } 196 | 197 | @Override 198 | public RequestHandler configHandler() { 199 | 200 | return new HeaderHandler(); 201 | } 202 | 203 | @Override 204 | public long configConnectTimeoutSecs() { 205 | return CONNECT_TIME_OUT; 206 | } 207 | 208 | @Override 209 | public long configReadTimeoutSecs() { 210 | return READ_TIME_OUT; 211 | } 212 | 213 | @Override 214 | public long configWriteTimeoutSecs() { 215 | return WRITE_TIME_OUT; 216 | } 217 | 218 | @Override 219 | public boolean configLogEnable() { 220 | return BuildConfig.DEBUG; 221 | } 222 | 223 | 224 | private class HeaderHandler implements RequestHandler { 225 | 226 | @Override 227 | public Request onBeforeRequest(Request request, Interceptor.Chain chain) { 228 | return chain.request().newBuilder() 229 | .addHeader("X-Auth-Token", Constant.accessToken) 230 | .addHeader("Authorization", "") 231 | .build(); 232 | } 233 | 234 | @Override 235 | public Response onAfterRequest(Response response, Interceptor.Chain chain) 236 | throws IOException { 237 | ApiException e = null; 238 | if (401 == response.code()) { 239 | throw new ApiException("登录已过期,请重新登录!"); 240 | } else if (403 == response.code()) { 241 | throw new ApiException("禁止访问!"); 242 | } else if (404 == response.code()) { 243 | throw new ApiException("链接错误"); 244 | } else if (503 == response.code()) { 245 | throw new ApiException("服务器升级中!"); 246 | } else if (500 == response.code()) { 247 | throw new ApiException("服务器内部错误!"); 248 | } 249 | return response; 250 | } 251 | } 252 | 253 | ``` 254 | 255 | 在BaseNetProvider中实现了连接、读、写超时的时间处理,与请求和返回数据的请求头部处理。然后需要在Application中去注入BaseNetProvider 256 | 257 | ``` 258 | NetMgr.getInstance().registerProvider(new BaseNetProvider()); 259 | ``` 260 | 261 | * **3、Observable实现** 262 | 263 | 首先实现一个UseCase的基类,处理公共的使用方法。通过调用NetMgr.getInstance().getRetrofit(BuildConfig.BaseUrl).create(getType())来获取ApiService的实例,然后提供了指定线程的基类方法。至于PagingReq是一个分页模型,方便分页接口的使用。 264 | 265 | ```java 266 | public abstract class UseCase { 267 | //用于分页请求 268 | protected PagingReq pagingReq = new PagingReq(); 269 | 270 | protected T ApiClient() { 271 | return NetMgr.getInstance().getRetrofit(BuildConfig.BaseUrl).create(getType()); 272 | } 273 | 274 | //指定观察者与被观察者线程 275 | protected Observable.Transformer normalSchedulers() { 276 | return new Observable.Transformer() { 277 | @Override 278 | public Observable call(Observable source) { 279 | return source.onTerminateDetach().subscribeOn(Schedulers.io()) 280 | .observeOn(AndroidSchedulers.mainThread()); 281 | } 282 | }; 283 | } 284 | 285 | private Class getType() { 286 | Class entityClass = null; 287 | Type t = getClass().getGenericSuperclass(); 288 | Type[] p = ((ParameterizedType) t).getActualTypeArguments(); 289 | entityClass = (Class) p[0]; 290 | return entityClass; 291 | } 292 | } 293 | ``` 294 | 295 | 此处实现一个简单的获取城市信息的接口。 首先定义接口ApiService,然后实现获取Observable的方法 296 | 297 | ```java 298 | public class GetCitiesCase extends UseCase { 299 | interface Api { 300 | @GET("api/china/") 301 | Observable> getCitiesCase(); 302 | } 303 | 304 | 305 | public Observable> getCities() { 306 | return ApiClient().getCitiesCase() 307 | .compose(this.>normalSchedulers()); 308 | } 309 | 310 | } 311 | ``` 312 | 313 | * 使用时调用 314 | 315 | ```java 316 | new GetCitiesCase().getCities() 317 | .compose(this.>bindToLifecycle()) 318 | .subscribe(new BaseSubscriber>() { 319 | @Override 320 | public void onError(Throwable e) { 321 | Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); 322 | } 323 | 324 | @Override 325 | public void onNext(List o) { 326 | getCitiesTv.setText(""); 327 | if (o != null && o.size() != 0) { 328 | for (City city : o) { 329 | getCitiesTv.setText(getCitiesTv.getText().toString() + city.id.intValue() + " " + city.name + "\n"); 330 | } 331 | } 332 | } 333 | }); 334 | ``` 335 | 336 | 调用就很简单了,只需绑定生命周期(防止内存泄漏),然后订阅Subscriber,处理成功或失败后的返回。 337 | 338 | 339 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion "26.0.1" 6 | defaultConfig { 7 | applicationId "com.hd123.httpframe" 8 | minSdkVersion 15 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | buildConfigField "String", "BaseUrl", "\"" + (System.getenv()['BaseUrl'] ?: "http://guolin.tech/") + "\"" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | configurations.all { 23 | resolutionStrategy.force 'com.google.code.findbugs:jsr305:2.0.1' 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | compile 'com.android.support:appcompat-v7:26.+' 33 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 34 | testCompile 'junit:junit:4.12' 35 | 36 | //net 37 | compile 'com.squareup.retrofit2:retrofit:2.3.0' 38 | compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0' 39 | compile 'com.squareup.retrofit2:converter-gson:2.3.0' 40 | compile 'com.squareup.okhttp3:logging-interceptor:3.8.1' 41 | compile 'com.google.code.gson:gson:2.7' 42 | //rx 43 | compile 'io.reactivex:rxandroid:1.2.1' 44 | compile 'io.reactivex:rxjava:1.1.6' 45 | //rxlifecycle 46 | compile 'com.trello:rxlifecycle-components:1.0' 47 | } 48 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/tianyang/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/hd123/httpframe/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.hd123.httpframe", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/App.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe; 2 | 3 | import android.app.Application; 4 | 5 | import com.hd123.httpframe.net.extension.BaseNetProvider; 6 | import com.hd123.httpframe.net.retrofit.NetMgr; 7 | 8 | /** 9 | * 界面描述: 10 | *

11 | * Created by tianyang on 2017/9/27. 12 | */ 13 | 14 | public class App extends Application { 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | 19 | NetMgr.getInstance().registerProvider(new BaseNetProvider()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/config/Constant.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.config; 2 | 3 | /** 4 | * Created by dzq on 16/7/12. 5 | */ 6 | public class Constant { 7 | public static String accessToken = ""; 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/cases/GetCitiesCase.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.cases; 2 | 3 | import com.hd123.httpframe.net.cases.base.UseCase; 4 | import com.hd123.httpframe.net.models.City; 5 | 6 | import java.util.List; 7 | 8 | import retrofit2.http.GET; 9 | import retrofit2.http.POST; 10 | import rx.Observable; 11 | 12 | /** 13 | * 界面描述: 14 | *

15 | * Created by tianyang on 2017/9/27. 16 | */ 17 | 18 | public class GetCitiesCase extends UseCase { 19 | interface Api { 20 | @GET("api/china/") 21 | Observable> getCitiesCase(); 22 | } 23 | 24 | 25 | public Observable> getCities() { 26 | return ApiClient().getCitiesCase() 27 | .compose(this.>normalSchedulers()); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/cases/base/UseCase.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.cases.base; 2 | 3 | import com.hd123.httpframe.BuildConfig; 4 | import com.hd123.httpframe.net.models.PagingReq; 5 | import com.hd123.httpframe.net.retrofit.NetMgr; 6 | 7 | import java.lang.reflect.ParameterizedType; 8 | import java.lang.reflect.Type; 9 | 10 | import rx.Observable; 11 | import rx.android.schedulers.AndroidSchedulers; 12 | import rx.schedulers.Schedulers; 13 | 14 | /** 15 | * 界面描述: 16 | *

17 | * Created by tianyang on 2017/9/27. 18 | */ 19 | 20 | public abstract class UseCase { 21 | //用于分页请求 22 | protected PagingReq pagingReq = new PagingReq(); 23 | 24 | 25 | protected T ApiClient() { 26 | return NetMgr.getInstance().getRetrofit(BuildConfig.BaseUrl).create(getType()); 27 | } 28 | 29 | //指定观察者与被观察者线程 30 | protected Observable.Transformer normalSchedulers() { 31 | return new Observable.Transformer() { 32 | @Override 33 | public Observable call(Observable source) { 34 | return source.onTerminateDetach().subscribeOn(Schedulers.io()) 35 | .observeOn(AndroidSchedulers.mainThread()); 36 | } 37 | }; 38 | } 39 | 40 | private Class getType() { 41 | Class entityClass = null; 42 | Type t = getClass().getGenericSuperclass(); 43 | Type[] p = ((ParameterizedType) t).getActualTypeArguments(); 44 | entityClass = (Class) p[0]; 45 | return entityClass; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/exception/ApiException.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.exception; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * 界面描述:异常类 7 | *

8 | * Created by tianyang on 2017/9/27. 9 | */ 10 | 11 | public class ApiException extends IOException { 12 | public ApiException(String message) { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/extension/BaseNetProvider.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.extension; 2 | 3 | 4 | import com.hd123.httpframe.BuildConfig; 5 | import com.hd123.httpframe.config.Constant; 6 | import com.hd123.httpframe.net.exception.ApiException; 7 | import com.hd123.httpframe.net.retrofit.NetProvider; 8 | import com.hd123.httpframe.net.retrofit.RequestHandler; 9 | 10 | import java.io.IOException; 11 | 12 | import okhttp3.CookieJar; 13 | import okhttp3.Interceptor; 14 | import okhttp3.OkHttpClient; 15 | import okhttp3.Request; 16 | import okhttp3.Response; 17 | 18 | /** 19 | * 界面描述:NetProvider实现类 20 | *

21 | * Created by tianyang on 2017/9/27. 22 | */ 23 | 24 | public class BaseNetProvider implements NetProvider { 25 | 26 | private static final long CONNECT_TIME_OUT = 30; 27 | private static final long READ_TIME_OUT = 180; 28 | private static final long WRITE_TIME_OUT = 30; 29 | 30 | 31 | @Override 32 | public Interceptor[] configInterceptors() { 33 | return null; 34 | } 35 | 36 | @Override 37 | public void configHttps(OkHttpClient.Builder builder) { 38 | 39 | } 40 | 41 | @Override 42 | public CookieJar configCookie() { 43 | return null; 44 | } 45 | 46 | @Override 47 | public RequestHandler configHandler() { 48 | 49 | return new HeaderHandler(); 50 | } 51 | 52 | @Override 53 | public long configConnectTimeoutSecs() { 54 | return CONNECT_TIME_OUT; 55 | } 56 | 57 | @Override 58 | public long configReadTimeoutSecs() { 59 | return READ_TIME_OUT; 60 | } 61 | 62 | @Override 63 | public long configWriteTimeoutSecs() { 64 | return WRITE_TIME_OUT; 65 | } 66 | 67 | @Override 68 | public boolean configLogEnable() { 69 | return BuildConfig.DEBUG; 70 | } 71 | 72 | 73 | private class HeaderHandler implements RequestHandler { 74 | 75 | @Override 76 | public Request onBeforeRequest(Request request, Interceptor.Chain chain) { 77 | return chain.request().newBuilder() 78 | .addHeader("X-Auth-Token", Constant.accessToken) 79 | .addHeader("Authorization", "") 80 | .build(); 81 | } 82 | 83 | @Override 84 | public Response onAfterRequest(Response response, Interceptor.Chain chain) 85 | throws IOException { 86 | ApiException e = null; 87 | if (401 == response.code()) { 88 | throw new ApiException("登录已过期,请重新登录!"); 89 | } else if (403 == response.code()) { 90 | throw new ApiException("禁止访问!"); 91 | } else if (404 == response.code()) { 92 | throw new ApiException("链接错误"); 93 | } else if (503 == response.code()) { 94 | throw new ApiException("服务器升级中!"); 95 | } else if (500 == response.code()) { 96 | throw new ApiException("服务器内部错误!"); 97 | } 98 | return response; 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/extension/BaseSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.extension; 2 | 3 | import rx.Subscriber; 4 | 5 | /** 6 | * 界面描述: BaseSubscriber基类,处理返回数据 7 | *

8 | * Created by tianyang on 2017/9/27. 9 | */ 10 | 11 | public abstract class BaseSubscriber extends Subscriber { 12 | 13 | @Override 14 | public void onCompleted() { 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/extension/IPaging.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.extension; 2 | 3 | import java.util.List; 4 | 5 | 6 | /** 7 | * 界面描述:提供给分页模型的接口,提供类型转换成UralPaging 8 | *

9 | * Created by tianyang on 2017/9/27. 10 | */ 11 | 12 | public interface IPaging { 13 | 14 | List getItems(); 15 | 16 | int getPage(); 17 | 18 | int getPageSize(); 19 | 20 | long getTotal(); 21 | 22 | int getPageCount(); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/models/City.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.models; 2 | 3 | import java.math.BigDecimal; 4 | 5 | /** 6 | * 界面描述: 7 | *

8 | * Created by tianyang on 2017/9/27. 9 | */ 10 | 11 | public class City { 12 | public BigDecimal id; 13 | public String name; 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/models/PagingReq.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.models; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * 界面描述: 10 | *

11 | * Created by tianyang on 2017/9/28. 12 | */ 13 | 14 | public class PagingReq { 15 | public int page = 0; 16 | public int pageSize = 15; 17 | public boolean desc = true; 18 | public String sort_key; 19 | 20 | 21 | //组合参数 22 | public Map generatePagingParameter() { 23 | Map map = new HashMap<>(); 24 | map.put("page", String.valueOf(page)); 25 | map.put("page_size", String.valueOf(pageSize)); 26 | if (!TextUtils.isEmpty(sort_key)) { 27 | map.put("sort_key", sort_key); 28 | } 29 | map.put("desc", String.valueOf(desc)); 30 | return map; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/retrofit/NetInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.retrofit; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import java.io.IOException; 6 | 7 | import okhttp3.Interceptor; 8 | import okhttp3.Request; 9 | import okhttp3.Response; 10 | 11 | 12 | /** 13 | * 界面描述: 14 | *

15 | * Created by tianyang on 2017/9/27. 16 | */ 17 | 18 | public class NetInterceptor implements Interceptor { 19 | private RequestHandler handler; 20 | 21 | public NetInterceptor(RequestHandler handler) { 22 | this.handler = handler; 23 | } 24 | 25 | @Override 26 | public Response intercept(@NonNull Chain chain) throws IOException { 27 | Request request = chain.request(); 28 | if (handler != null) { 29 | request = handler.onBeforeRequest(request, chain); 30 | } 31 | Response response = chain.proceed(request); 32 | if (handler != null) { 33 | Response tmp = handler.onAfterRequest(response, chain); 34 | if (tmp != null) { 35 | return tmp; 36 | } 37 | } 38 | return response; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/retrofit/NetMgr.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.retrofit; 2 | 3 | 4 | import com.google.gson.Gson; 5 | import com.google.gson.GsonBuilder; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import okhttp3.CookieJar; 12 | import okhttp3.Interceptor; 13 | import okhttp3.OkHttpClient; 14 | import okhttp3.logging.HttpLoggingInterceptor; 15 | import retrofit2.Retrofit; 16 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 17 | import retrofit2.converter.gson.GsonConverterFactory; 18 | 19 | /** 20 | * 界面描述:Retrofit实现类 21 | *

22 | * Created by tianyang on 2017/9/27. 23 | */ 24 | 25 | public class NetMgr { 26 | private final long connectTimeoutMills = 10 * 1000L; 27 | private final long readTimeoutMills = 10 * 1000L; 28 | private NetProvider sProvider = null; 29 | private static NetMgr instance; 30 | private Map providerMap = new HashMap<>(); 31 | private Map retrofitMap = new HashMap<>(); 32 | private Map clientMap = new HashMap<>(); 33 | 34 | 35 | public static NetMgr getInstance() { 36 | if (instance == null) { 37 | synchronized (NetMgr.class) { 38 | if (instance == null) { 39 | instance = new NetMgr(); 40 | } 41 | } 42 | } 43 | return instance; 44 | } 45 | 46 | 47 | public S get(String baseUrl, Class service) { 48 | return getInstance().getRetrofit(baseUrl).create(service); 49 | } 50 | 51 | public void registerProvider(NetProvider provider) { 52 | this.sProvider = provider; 53 | } 54 | 55 | public void registerProvider(String baseUrl, NetProvider provider) { 56 | getInstance().providerMap.put(baseUrl, provider); 57 | } 58 | 59 | public NetProvider getCommonProvider() { 60 | return sProvider; 61 | } 62 | 63 | public void clearCache() { 64 | getInstance().retrofitMap.clear(); 65 | getInstance().clientMap.clear(); 66 | } 67 | 68 | public Retrofit getRetrofit(String baseUrl) { 69 | return getRetrofit(baseUrl, null); 70 | } 71 | 72 | public Retrofit getRetrofit(String baseUrl, NetProvider provider) { 73 | if (empty(baseUrl)) { 74 | throw new IllegalStateException("baseUrl can not be null"); 75 | } 76 | if (retrofitMap.get(baseUrl) != null) { 77 | return retrofitMap.get(baseUrl); 78 | } 79 | 80 | if (provider == null) { 81 | provider = providerMap.get(baseUrl); 82 | if (provider == null) { 83 | provider = sProvider; 84 | } 85 | } 86 | checkProvider(provider); 87 | 88 | Gson gson = new GsonBuilder() 89 | .setDateFormat("yyyy-MM-dd HH:mm:ss") 90 | .create(); 91 | 92 | Retrofit.Builder builder = new Retrofit.Builder() 93 | .baseUrl(baseUrl) 94 | .client(getClient(baseUrl, provider)) 95 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 96 | .addConverterFactory(GsonConverterFactory.create(gson)); 97 | 98 | Retrofit retrofit = builder.build(); 99 | retrofitMap.put(baseUrl, retrofit); 100 | providerMap.put(baseUrl, provider); 101 | 102 | return retrofit; 103 | } 104 | 105 | private boolean empty(String baseUrl) { 106 | return baseUrl == null || baseUrl.isEmpty(); 107 | } 108 | 109 | private OkHttpClient getClient(String baseUrl, NetProvider provider) { 110 | if (empty(baseUrl)) { 111 | throw new IllegalStateException("baseUrl can not be null"); 112 | } 113 | if (clientMap.get(baseUrl) != null) { 114 | return clientMap.get(baseUrl); 115 | } 116 | 117 | checkProvider(provider); 118 | 119 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 120 | 121 | builder.connectTimeout(provider.configConnectTimeoutSecs() != 0 122 | ? provider.configConnectTimeoutSecs() 123 | : connectTimeoutMills, TimeUnit.SECONDS); 124 | builder.readTimeout(provider.configReadTimeoutSecs() != 0 125 | ? provider.configReadTimeoutSecs() : readTimeoutMills, TimeUnit.SECONDS); 126 | 127 | builder.writeTimeout(provider.configWriteTimeoutSecs() != 0 128 | ? provider.configReadTimeoutSecs() : readTimeoutMills, TimeUnit.SECONDS); 129 | CookieJar cookieJar = provider.configCookie(); 130 | if (cookieJar != null) { 131 | builder.cookieJar(cookieJar); 132 | } 133 | provider.configHttps(builder); 134 | 135 | RequestHandler handler = provider.configHandler(); 136 | if (handler != null) { 137 | builder.addInterceptor(new NetInterceptor(handler)); 138 | } 139 | 140 | Interceptor[] interceptors = provider.configInterceptors(); 141 | if (!empty(interceptors)) { 142 | for (Interceptor interceptor : interceptors) { 143 | builder.addInterceptor(interceptor); 144 | } 145 | } 146 | 147 | if (provider.configLogEnable()) { 148 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); 149 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 150 | builder.addInterceptor(loggingInterceptor); 151 | } 152 | 153 | OkHttpClient client = builder.build(); 154 | clientMap.put(baseUrl, client); 155 | providerMap.put(baseUrl, provider); 156 | 157 | return client; 158 | } 159 | 160 | private boolean empty(Interceptor[] interceptors) { 161 | return interceptors == null || interceptors.length == 0; 162 | } 163 | 164 | private void checkProvider(NetProvider provider) { 165 | if (provider == null) { 166 | throw new IllegalStateException("must register provider first"); 167 | } 168 | } 169 | 170 | public Map getRetrofitMap() { 171 | return retrofitMap; 172 | } 173 | 174 | public Map getClientMap() { 175 | return clientMap; 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/retrofit/NetProvider.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.retrofit; 2 | 3 | import okhttp3.CookieJar; 4 | import okhttp3.Interceptor; 5 | import okhttp3.OkHttpClient; 6 | 7 | /** 8 | * 界面描述:回调处理 9 | *

10 | * Created by tianyang on 2017/9/27. 11 | */ 12 | 13 | public interface NetProvider { 14 | 15 | Interceptor[] configInterceptors(); 16 | 17 | void configHttps(OkHttpClient.Builder builder); 18 | 19 | CookieJar configCookie(); 20 | 21 | RequestHandler configHandler(); 22 | 23 | long configConnectTimeoutSecs(); 24 | 25 | long configReadTimeoutSecs(); 26 | 27 | long configWriteTimeoutSecs(); 28 | 29 | boolean configLogEnable(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/net/retrofit/RequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.net.retrofit; 2 | 3 | import java.io.IOException; 4 | 5 | import okhttp3.Interceptor; 6 | import okhttp3.Request; 7 | import okhttp3.Response; 8 | 9 | 10 | /** 11 | * 界面描述: 12 | *

13 | * Created by tianyang on 2017/9/27. 14 | */ 15 | 16 | public interface RequestHandler { 17 | 18 | Request onBeforeRequest(Request request, Interceptor.Chain chain); 19 | 20 | Response onAfterRequest(Response response, Interceptor.Chain chain) throws IOException; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/hd123/httpframe/views/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.hd123.httpframe.views; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | import android.widget.Toast; 7 | 8 | import com.hd123.httpframe.R; 9 | import com.hd123.httpframe.net.cases.GetCitiesCase; 10 | import com.hd123.httpframe.net.extension.BaseSubscriber; 11 | import com.hd123.httpframe.net.models.City; 12 | import com.trello.rxlifecycle.components.support.RxAppCompatActivity; 13 | 14 | import java.util.List; 15 | 16 | public class MainActivity extends RxAppCompatActivity { 17 | private TextView getCitiesTv; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_main); 23 | getCitiesTv = (TextView) findViewById(R.id.cities_tv); 24 | } 25 | 26 | public void getCities(View view) { 27 | new GetCitiesCase().getCities() 28 | .compose(this.>bindToLifecycle()) 29 | .subscribe(new BaseSubscriber>() { 30 | @Override 31 | public void onError(Throwable e) { 32 | Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); 33 | } 34 | 35 | @Override 36 | public void onNext(List o) { 37 | getCitiesTv.setText(""); 38 | if (o != null && o.size() != 0) { 39 | for (City city : o) { 40 | getCitiesTv.setText(getCitiesTv.getText().toString() + city.id.intValue() + " " + city.name + "\n"); 41 | } 42 | } 43 | } 44 | }); 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 |