上一節咱們講了一些Retrofit的概覽,這一節咱們主要來講一下代理模式。有同窗可能要問,這不是Retrofit的源碼分析嗎,怎麼都第二節了還不分析源碼呢?其實Retrofit這個框架中應用了不少的設計模式,其中最重要的就是動態代理模式。若是咱們要理解並掌握Retrofit,那麼就必須先掌握代理模式。代理模式主要分爲兩種,靜態代理和動態代理,下面咱們來細細的說明一下。java
package com.blackflagbin.frameanalysis.staticproxy;
//抽象日誌類
public abstract class AbstractLogger {
abstract public void log();
}
複製代碼
package com.blackflagbin.frameanalysis.staticproxy;
//真實操做日誌類,繼承AbstractLogger
public class RealLogger extends AbstractLogger {
@Override
public void log() {
System.out.println("show some log");
}
}
複製代碼
package com.blackflagbin.frameanalysis.staticproxy;
//代理日誌類,包含一個真實日誌類的實例,在打印日誌以前校驗權限
public class ProxyLogger extends AbstractLogger {
private AbstractLogger mLogger;
public ProxyLogger(AbstractLogger logger) {
mLogger = logger;
}
private boolean checkPermission() {
return true;
}
@Override
public void log() {
if (checkPermission()) {
mLogger.log();
} else {
System.out.println("you have no access");
}
}
}
複製代碼
上面三個類分別是抽象日誌類、真實日誌類、代理日誌類。抽象日誌類定義了一個打印日誌的接口,真實日誌類繼承了抽象日誌,並實現的這個打印日誌的方法。這個時候,咱們想要在打印日誌前加上權限校驗,又不想直接修改咱們的真實日誌類,那麼就須要使用的靜態代理模式。爲了實如今打印日誌前校驗權限的功能,咱們建立了一個新類,代理日誌類,這個類一樣繼承了抽象日誌類,關鍵的是包含了一個真實日誌類的引用對象。在這個代理類中的log方法中,經過在調用真實日誌引用對象的log方法以前加入權限校驗,從而實現了上述的功能。react
動態代理與靜態代理最大的區別在於動態。那麼問題來了,這個動態體如今哪裏,又該如何去理解? 這個動態關鍵在於代理類的建立時機。靜態代理中的代理類在咱們運行程序以前是必需要存在的,而動態代理中的代理類則是在程序運行時建立的。前半句很好理解,代理類的代碼確定是先存在,而後才能運行,這個邏輯很符合咱們日常的開發模式。問題就在於後半句,代理類在運行時建立,運行時如何建立代理類?這是否是很反邏輯?代碼都沒有,怎麼來根據咱們的需求來代理真實的被代理的對象?諸位稍安勿躁,咱們接下來會對動態代理進行詳細的解釋。 動態代理有兩種實現方式:設計模式
package com.blackflagbin.frameanalysis.dynamicproxy;
//日誌接口(動態代理不一樣於靜態代理,只能使用接口)
public interface ILogger {
void log();
}
複製代碼
package com.blackflagbin.frameanalysis.dynamicproxy;
//真實日誌類,實現日誌接口
public class RealLogger implements ILogger {
@Override
public void log() {
System.out.println("show some log");
}
}
複製代碼
package com.blackflagbin.frameanalysis.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyHandler implements InvocationHandler {
//被代理對象(即目標對象)的實例,在打印日誌這個例子中對應RealLogger的實例
private final Object mTarget;
public ProxyHandler(Object target) {
mTarget = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (checkPermission()) {
System.out.println("you have access");
//被代理對象(即目標對象)方法的調用
return method.invoke(mTarget, args);
} else {
System.out.println("you have no access");
return null;
}
}
//建立實際的代理對象
public Object getProxyInstance() {
return Proxy.newProxyInstance(mTarget.getClass().getClassLoader(), mTarget.getClass().getInterfaces(), this);
}
private boolean checkPermission() {
return true;
}
}
複製代碼
package com.blackflagbin.frameanalysis.dynamicproxy;
//測試類
public class Test {
public static void main(String[] args) {
ProxyHandler proxyHandler = new ProxyHandler(new RealLogger());
ILogger proxy = (ILogger) proxyHandler.getProxyInstance();
proxy.log();
}
}
/*
最終打印結果:
you have access
show some log
*/
複製代碼
日誌接口和真實日誌類沒什麼可說的,跟靜態代理同樣,只不過由於動態代理必需要使用接口因此把抽象類換成了接口。 動態代理實現打印日誌這個例子的關鍵在於ProxyHandler這個類。咱們知道,在靜態代理中,對原有功能進行擴展或修改的代碼實現是在靜態代理類中定義的。也就是在ProxyLogger中添加額外的權限校驗方法,並修改打印日誌的流程。那麼問題來了,在動態代理中,動態代理類是在程序運行時生成的,咱們並無事先聲明一個動態代理類,這個對原有功能進行擴展或修改的代碼實現究竟要放在哪裏? 問題的答案在這個打印日誌的例子裏已經很明確了,對原有功能進行擴展或修改的代碼實現放在了一個類中,這個類實現了InvocationHandler這個接口。咱們經過對invoke這個方法的修改來修改被代理對象的方法實現,經過在invoke方法中的method.invoke(mTarget, args)以前或以後插入咱們想要的邏輯來增對原有功能進行擴展。 在這個ProxyHandler類中,咱們還添加了一個getProxyInstance()方法來建立代理類對象。經過Proxy.newProxyInstance(mTarget.getClass().getClassLoader(), mTarget.getClass().getInterfaces(), this)來建立代理類的實例是固定的寫法,newProxyInstance須要傳入三個參數,分別是類加載器、接口數組和實現InvocationHandler的類的實例對象。 咱們再來總結一下。不管是靜態代理仍是動態代理,它們的本質都是代理對象包含一個被代理對象的實例,從而對被代理對象的原有功能進行擴展或修改。最大的區別是代理類的建立時機不一樣,靜態代理必須在程序運行前寫好代理類;而動態代理的代理類則不須要咱們手動提早寫好,它會在運行時建立相應的代理類。 值得再次強調的是,雖然動態代理不須要咱們在代碼中實現代理類,可是對原有功能進行擴展或修改的代碼實現是必須提早寫好的。這個很好理解,若是開發人員都不寫清楚要如何對原有功能進行擴展或修改,計算機又怎麼知道呢?因此對原有功能進行擴展或修改的代碼實現就必須提早寫好,問題是這些代碼要放在那裏,爲了解決這個問題,Java提供了一個InvocationHandler的接口,咱們只要把相應的代碼放到這個接口的實現類中便可。生成的代理對象在調用相應的方法時,實際上調用的是invoke這個方法,從而實現了對被代理對象的原有功能進行擴展或修改。 最後,我再貼上一些代碼。api
package com.zhidian.cloudforpolice.common.http
import com.zhidian.cloudforpolice.BuildConfig
import com.zhidian.cloudforpolice.common.entity.http.*
import com.zhidian.cloudforpolice.common.entity.http.Unit
import io.reactivex.Observable
import retrofit2.http.*
/**
* Created by blackflagbin on 2018/1/27.
*/
interface ApiService {
//登陸
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}account/login.do")
fun login(@Field("username") userName: String, @Field("password") pwd: String, @Field("clientType") clientType: Int): Observable<HttpResultEntity<UserEntity>>
//登出
@POST("${BuildConfig.EXTRA_URL}account/logout.do")
fun logout(): Observable<HttpResultEntity<Any>>
//獲取小區列表
@GET("${BuildConfig.EXTRA_URL}community/name/list")
fun getCommunityList(): Observable<HttpResultEntity<List<CommunityEntity>>>
//獲取首頁信息
@GET("${BuildConfig.EXTRA_URL}count/communityStatistic/{communityId}.do")
fun getMainData(@Path("communityId") communityId: Int): Observable<HttpResultEntity<MainEntity>>
//根據小區id獲取小區樓幢列表
@GET("${BuildConfig.EXTRA_URL}community/block/name/list/{communityId}")
fun getBuildingList(@Path("communityId") communityId: Int): Observable<HttpResultEntity<List<BuildingEntity>>>
//根據樓幢id獲取樓棟下的房間列表
@GET("${BuildConfig.EXTRA_URL}community/block/detail/{blockId}")
fun getRoomList(@Path("blockId") blockId: Int): Observable<HttpResultEntity<BuildingInfoEntity>>
//根據單元id獲取單元下樓層列表
@GET("${BuildConfig.EXTRA_URL}community/unit/query/{unitId}")
fun getFloorList(@Path("unitId") unitId: Int): Observable<HttpResultEntity<Unit>>
//根據房間id獲取房間詳情
@GET("${BuildConfig.EXTRA_URL}community/room/detail/{roomId}")
fun getRoomInfo(@Path("roomId") roomId: Int): Observable<HttpResultEntity<RoomInfoEntity>>
//根據條件查詢,獲取符合條件的居民列表
@GET("${BuildConfig.EXTRA_URL}community/resident/list/{pageNo}/{limit}")
fun getSearchedPersonList(
@Path("pageNo") pageNo: Int, @Path(
"limit") limit: Int, @QueryMap map: Map<String, String>): Observable<HttpResultEntity<PersonEntity>>
//根據用戶id獲取關聯房屋列表
@GET("${BuildConfig.EXTRA_URL}community/resident/room/list/{userId}")
fun getRelatedRoomList(@Path("userId") userId: Int): Observable<HttpResultEntity<List<RelatedRoomEntity>>>
//獲取樓幢詳情
@GET("${BuildConfig.EXTRA_URL}count/blockStatistic/{buildingId}.do")
fun getBuildingDetail(@Path("buildingId") buildingId: Int): Observable<HttpResultEntity<BuildingDetailEntity>>
//獲取一級標籤
@GET("${BuildConfig.EXTRA_URL}user/label/parent/list")
fun getFirstLabel(): Observable<HttpResultEntity<List<LabelItemEntity>>>
//獲取二級標籤
@GET("${BuildConfig.EXTRA_URL}user/label/child/list/{parentId}")
fun getSecondLabel(@Path("parentId") parentId: Int): Observable<HttpResultEntity<List<LabelItemEntity>>>
//修改密碼
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}account/password/update")
fun changePwd(@Field("password") oldPwd: String, @Field("newPsw") newPwd: String): Observable<HttpResultEntity<Any>>
//門禁在線
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}device/query")
fun getDeviceList(@Field("communityId") communityId: Int, @Field("pageNo") pageNo: Int, @Field("limit") limit: Int): Observable<HttpResultEntity<DoorEntity>>
//獲取門禁狀態
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}device/selectDeviceStatusCount")
fun getDeviceStatusList(@Field("communityId") communityId: Int): Observable<HttpResultEntity<List<DeviceStatusItem>>>
//根據條件查詢,獲取警情處理規範列表
@GET("${BuildConfig.EXTRA_URL}police/handle/norm/list/{pageNo}/{limit}")
fun getPoliceHandleList(
@Path("pageNo") pageNo: Int, @Path(
"limit") limit: Int, @QueryMap map: Map<String, String>): Observable<HttpResultEntity<PoliceHandleEntity>>
//根據條件查詢,獲取重點人員預警列表
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/person/query")
fun getPersonPreWarningList(
@FieldMap map: Map<String, String>): Observable<HttpResultEntity<PersonPreWarningEntity>>
//根據條件查詢,獲取觸警人員預警列表
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/person/queryContactPolice")
fun getAttackPreWarningList(
@FieldMap map: Map<String, String>): Observable<HttpResultEntity<PersonPreWarningEntity>>
//根據條件查詢,獲取重點房屋預警列表
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/room/query")
fun getRoomPreWarningList(
@FieldMap map: Map<String, String>): Observable<HttpResultEntity<RoomPreWarningEntity>>
//獲取行爲軌跡
@GET("${BuildConfig.EXTRA_URL}user/info/live/history/{userId}")
fun getMovePath(
@Path("userId") userId: String): Observable<HttpResultEntity<List<MovePathItemEntity>>>
//開門
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}device/operate/1")
fun openLock(
@Field("devicdId") deviceId: String): Observable<HttpResultEntity<Any>>
//更新人員預警狀態
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/person/updStatu")
fun updatePersonPreWaning(
@Field("id") id: String, @Field("statu") status: Int, @Field("processContent") processContent: String): Observable<HttpResultEntity<Any>>
//更新房屋預警狀態
@FormUrlEncoded
@POST("${BuildConfig.EXTRA_URL}police/room/updStatu")
fun updateRoomPreWaning(
@Field("id") id: String, @Field("statu") status: Int, @Field("processContent") processContent: String): Observable<HttpResultEntity<Any>>
//獲取二維碼返回結果
@FormUrlEncoded
@POST("api/qrcode/parsingcode")
fun getQrCodeResult(
@Field("code") qrcode: String): Observable<HttpResultEntity<String>>
}
複製代碼
這是個Kotlin寫的接口類,用過Retrofit的同窗應該很清楚,使用Retrofit進行網絡請求,首先就是要建立一個網絡請求接口類。數組
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://......../")
.build();
ApiService service = retrofit.create(ApiService.class);
複製代碼
看到**ApiService service = retrofit.create(ApiService.class)**有沒有很熟悉的感受?聰明的小夥伴看到這裏應該就會明白了,沒錯,這其實就是經過動態代理建立了一個代理對象。咱們只是寫了一個網絡接口類,裏面什麼都沒實現,爲何就能夠正確的請求網絡幷包裝返回的數據結果?若是你從上到下把這篇文章看完了,即便你如今還並不清楚裏面具體的代碼細節,但有一點你會很是明確:**咱們寫的ApiService這個接口類並不具備訪問網絡幷包裝返回數據結果的功能,是Retrofit經過動態代理的方式爲咱們生成了一個代理對象,爲咱們的接口方法擴展了網絡訪問的功能。**理解這一點,對咱們後續的源碼分析很是重要。bash