解決的問題
在開發(fā)階段魄藕,后臺經(jīng)常會發(fā)布典徊,或者接口還未寫好杭煎,但是格式已經(jīng)定好,其實這時候完全可以自己先寫一個json文件放在assets文件夾中卒落,然后自己使用羡铲,不需要一直等待。后臺經(jīng)常時不時的發(fā)布儡毕,重啟也切。前端只能一直等待。
我自己本身會使用rap來mock數(shù)據(jù),但是在發(fā)布的時候必須要改回來雷恃,也就是需要改動代碼疆股。記得某一次,上線褂萧,發(fā)布。代碼中有一處mock數(shù)據(jù)還是用的rap上的url葵萎。這就很操蛋了导犹。這里可以統(tǒng)一配置。
retrofit.createMocker(ServiceApi::class.java,BuildConfig.DEBUG)
最后一個參數(shù)可以配置成BuildConfig.DEBUG
就再也不會出錯了羡忘。記得還有一種mock方式是直接在代碼里面一個個創(chuàng)建對象谎痢,然后塞入不同數(shù)據(jù),相對來說會比較麻煩卷雕,而且也是存在
2
的問題的节猿。
如何使用
- 引入依賴
Add it in your root build.gradle at the end of repositories:
allprojects {
repositories {
...
maven { url 'https://www.jitpack.io' }
}
}
Step 2. Add the dependency
dependencies {
implementation 'com.github.javalong:RetrofitMocker:1.0.0'
}
- 定義接口時使用@MOCK注釋
//不使用MOCK注解
@GET(" ")
fun test1():Call<String>
//使用MOCK注解,mock本地數(shù)據(jù)漫雕,指定assets文件夾中的test_1.json文件為mock數(shù)據(jù)
@MOCK("test_1.json")
@GET(" ")
fun test2():Call<String>
//使用MOCK注解滨嘱,mock遠程數(shù)據(jù),重新指定url地址
@MOCK("https://api.github.com/users")
@GET(" ")
fun test3():Call<String>
//支持RxJava+Retrofit的方式
@MOCK("test_1.json")
@GET(" ")
fun test4():Observable<String>
這里不管你使用Call
,還是使用Observable
,返回的是 String
還是對象浸间,都可以太雨,這個框架不會對你的原來的使用造成任何影響。
- 使用createMocker來獲取代理對象
var retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
serviceApi = retrofit.createMocker(ServiceApi::class.java)
//建議這樣寫魁蒜,發(fā)布就不會忘了開關(guān) serviceApi = retrofit.createMocker(ServiceApi::class.java,BuildConfig.DEBUG)
//如果沒有使用 kotlin 可以這么寫 serviceApi = MockerHelper.createMocker(retrofit,ServiceApi::class.java,BuildConfig.DEBUG)
提供了2種方式獲取代理對象
使用了kotlin囊扳,我在框架中添加了
Retrofit
對象的擴展方法,原本使用retrofit.create
的地方替換為retrofit.createMocker
即可兜看。不使用kotlin锥咸,直接調(diào)用
MockerHelper.createMocker
獲取
注意:這里最后使用BuildConfig.DEBUG當作參數(shù)傳入,作為是否啟用mock數(shù)據(jù)的開關(guān)细移,防止發(fā)布時遺忘搏予。
demo
- mock本地json文件
//接口定義,需要mock數(shù)據(jù)的方法上添加MOCK注解弧轧,然后指定assets文件夾中的文件名
@MOCK("test_1.json")
@GET(" ")
fun test2():Call<String>
....
//使用的時候和以前的時候方法一致缔刹,不需要做任何改變
val call = serviceApi.test2()
call.enqueue(object :Callback<String>{
override fun onResponse(call: Call<String>?, response: Response<String>?) {
response.let {
tvHttpContent.text = response!!.body()
Log.e(TAG,response!!.body())
}
}
override fun onFailure(call: Call<String>?, t: Throwable?) {
}
})
點擊 MOCKER1
按鈕,即不添加MOCK
注解的情況劣针,調(diào)用github api
獲取數(shù)據(jù)如圖
點擊
MOCKER2
按鈕校镐,即添加了MOCK
注解,并指定為test_1.json
,返回如圖- mock遠程url
其實這種方式也是很常用的捺典,比如我使用了rap來mock數(shù)據(jù)鸟廓,就可以直接使用MOCK
注解,然后指定遠程的url。
@MOCK("https://api.github.com/users")
@GET(" ")
fun test3():Call<String>
我這里是通過是否以http開頭來判斷是遠程的mock還是本地的mock引谜。
點擊MOCKER3
按鈕牍陌,調(diào)用后,獲取數(shù)據(jù)如圖
因為他把本身的
https://api.github.com
請求地址替換成了https://api.github.com/users
员咽。
實現(xiàn)的原理
大家都知道Retrofit
的主要的一段代碼就是
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
使用了動態(tài)代理模式毒涧,當我們調(diào)用**Api
方法時,其實就是進入了這里贝室,然后它會進行解析契讲。
為了不影響Retrofit
之前的使用,我這里也決定采用動態(tài)代理的方式滑频。也就是在這一層動態(tài)代理的外面再包一層動態(tài)代理捡偏。
既然使用到了動態(tài)代理,那么也會使用到反射峡迷。
當調(diào)用**Api
里面的方法時银伟,首先會進入我的動態(tài)代理,反射獲取這個方法是否有@MOCK
注釋绘搞,有就自己進行處理彤避,沒有就傳遞給Retrofit
的動態(tài)代理。
總結(jié):使用到了反射
夯辖,動態(tài)代理
忠藤。