什么是代理?
代理模式其实就是我实现你的方法代替你去做某件事
为啥要这么麻烦呢!!!其实是为了不去修改原有的代码,通过代理也可以访问这个对象而且可以进行扩展代理分为静态代理和动态代理,主要用于我们不想修改原本的实现逻辑,却想再原本实现逻辑上做拓展
静态代理:实现被代理类的接口,重新需要代理的方法 使用场景:比如我们想对所有的某些click统计点击事件耗时,就可以使用静态代理方式实现。retrofit通过动态代理获取到方法上的注解值,使用更加灵活方便
动态代理:动态代理的特点是不需要提前创建代理对象,而是利用反射机制在运行时创建代理类,主要便是利用了java提供的Proxy类和InvocationHandler 使用场景:动态代理我们听过最多的就是retrofit中使用,retrofit调用create方法时候会使用动态代理解析注解中的方法
一、静态代理
示例1,通用场景
import android.util.Log;
/**
* 静态代理
*/
public class StaticProxy implements ISubject {
private ISubject iSubject;
public StaticProxy(ISubject iSubject) {
this.iSubject = iSubject;
}
@Override
public void doSomeThing() {
iSubject.doSomeThing();
Log.i("king", "我是代理类做的事");
}
}
public interface ISubject {
void doSomeThing();
}
调用方式:
var real = RealSubject()
var proxy = StaticProxy(real)
proxy.doSomeThing()
示例2,为了click增加点击时间统计
import android.util.Log;
import android.view.View;
public class StaticProxyClickListener implements View.OnClickListener {
public static final String TAG = "king";
private View.OnClickListener listener;
public StaticProxyClickListener(View.OnClickListener listener) {
this.listener = listener;
}
@Override
public void onClick(View v) {
long startTime = System.currentTimeMillis();
Log.i(TAG, "开始点击事件");
if (listener != null)
listener.onClick(v);
long endTime = System.currentTimeMillis();
Log.i(TAG, "结束点击事件,共耗时:"+(endTime-startTime)+"毫秒");
}
}
textView.setOnClickListener(new StaticProxyClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "onClick: 点击了textView");
}
}));
# 二、动态代理 既然已经有了静态代理,为什么又要来一个动态代理呢? 静态代理的拓展性比较差,对于那些实现了之后不怎么改变的功能还可以使用,如果频繁改动,代理类也需要跟着改动
代码如下(示例):
package com.example.mystudy_kotlin.proxy;
import android.util.Log;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getDeclaringClass().getName();
Log.i("king", "执行了方法:" + name);
return method.invoke(object, args);
}
}
var dynamic = DynamicProxy(real)
var dynamicSubject = Proxy.newProxyInstance(ISubject::class.java.classLoader,arrayOf(ISubject::class.java),dynamic) as ISubject
dynamicSubject.doSomeThing()
三、仿retorfit实现动态代理
package com.hzp.hi.library.restful
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.util.concurrent.ConcurrentHashMap
class HiRestful constructor(val baseUrl: String, callFactory: HiCall.Factory) {
private var interceptors: MutableList<HiInterceptor> = mutableListOf()
//缓存方法,多次调用可以复用
private var methodService: ConcurrentHashMap<Method, MethodParser> = ConcurrentHashMap()
private var scheduler: Scheduler
fun addInterceptor(interceptor: HiInterceptor) {
interceptors.add(interceptor)
}
init {
scheduler = Scheduler(callFactory,interceptors)
}
/**
* interface ApiService {
* @Headers("auth-token:token", "accountId:123456")
* @BaseUrl("https://api.devio.org/as/")
* @POST("/cities/{province}")
* @GET("/cities")
* fun listCities(@Path("province") province: Int,@Filed("page") page: Int): HiCall<JsonObject>
* }
*/
fun <T> create(service: Class<T>): T {
return Proxy.newProxyInstance(
service.classLoader,
arrayOf<Class<*>>(service),
object :InvocationHandler{
//bugFix:此处需要考虑 空参数
override fun invoke(proxy: Any?, method: Method, args: Array<Any>?): Any {
var methodParser=methodService.get(method)
if(methodParser==null){
methodParser=MethodParser.parse(baseUrl,method)
methodService[method]=methodParser
}
//bugFix:此处 应当考虑到 methodParser复用,每次调用都应当解析入参
val request = methodParser.newRequest(method,args)
return scheduler.newCall(request)
}
}
) as T
}
}
package com.hzp.hi.library.restful
import com.hzp.hi.library.restful.annotation.*
import java.lang.IllegalStateException
import java.lang.reflect.*
/**
* 方法解析器
*/
class MethodParser(private val baseUrl: String, method: Method) {
private var replaceRelativeUrl: String? = null
private var cacheStrategy: Int = CacheStrategy.NET_ONLY
private var domainUrl: String? = null
private var formPost: Boolean = true
private var httpMethod: Int = -1
private lateinit var relativeUrl: String
private lateinit var returnType: Type
private var headers: MutableMap<String, String> = mutableMapOf()
private var parameters: MutableMap<String, String> = mutableMapOf()
init {
//解析方法注解@GET,@POST,@Headers,@BaseUrl
//parse method annotations such as get,headers,post baseUrl
parseMethodAnnotations(method)
//解析方法返回值类型
//parse method generic return type
parseMethodReturnType(method)
//bug 存在以方法名为key的复用,当方法相同,参数不同时不会解析。
//解析方法参数注册@Path,@Filed
//parse method parameters such as path,filed
// parseMethodParameters(method, args)
}
/**
* interface ApiService {
* @Headers("auth-token:token", "accountId:123456")
* @BaseUrl("https://api.devio.org/as/")
* @POST("/cities/{province}")
* @GET("/cities")
* fun listCities(@Path("province") province: Int,@Filed("page") page: Int): HiCall<JsonObject>
* }
*/
private fun parseMethodReturnType(method: Method) {
if (method.returnType != HiCall::class.java) {
throw IllegalStateException(
String.format("method %s must be type of HiCall.class", method.name)
)
}
val genericReturnType = method.genericReturnType
if (genericReturnType is ParameterizedType) {
val actualTypeArguments = genericReturnType.actualTypeArguments
require(actualTypeArguments.size == 1) { "method %s can only has one generic return type" }
val argument = actualTypeArguments[0]
require(validateGenericType(argument)) {
String.format("method %s generic return type must not be an unknown type. " + method.name)
}
returnType = argument
} else {
throw IllegalStateException(
String.format("method %s must has one gerneric return type", method.name)
)
}
}
private fun parseMethodParameters(method: Method, args: Array<Any>) {
//每次调用api接口时 应该把上一次解析到的参数清理掉,因为methodParser存在复用
parameters.clear()
//@Path("province") province: Int,@Filed("page") page: Int
val parameterAnnotations = method.parameterAnnotations
//参数注解和参数个数是否相同
val equals = parameterAnnotations.size == args.size
require(equals) {
String.format(
"arguments annotations count %s don't match expect count %s",
parameterAnnotations.size,
args.size
)
}
//args
for (index in args.indices) {
val annotations = parameterAnnotations[index]
//只允许一个参数一个注解
//@Path("province") province: Int,@Filed("page")@Filed("page")@Filed("page") page: Int
require(annotations.size <= 1) { "filed can only has one annotation :index =$index" }
val value = args[index]
//是否是基本数据类型
require(isPrimitive(value)) { "8 basic types are supported for now,index=$index" }
val annotation = annotations[0]
if (annotation is Filed) {
val key = annotation.value
val value = args[index]
parameters[key] = value.toString()
} else if (annotation is Path) {
val replaceName = annotation.value
val replacement = value.toString()
if (replaceName != null && replacement != null) {
//relativeUrl=home/{categoryId}
replaceRelativeUrl = relativeUrl.replace("{$replaceName}", replacement)
}
} else if (annotation is CacheStrategy) {
cacheStrategy = value as Int
} else {
throw IllegalStateException("cannot handle parameter annotation :" + annotation.javaClass.toString())
}
}
}
private fun parseMethodAnnotations(method: Method) {
val annotations = method.annotations
for (annotation in annotations) {
if (annotation is GET) {
relativeUrl = annotation.value
httpMethod = HiRequest.METHOD.GET
} else if (annotation is POST) {
relativeUrl = annotation.value
httpMethod = HiRequest.METHOD.POST
formPost = annotation.formPost
} else if (annotation is PUT) {
formPost = annotation.formPost
httpMethod = HiRequest.METHOD.PUT
relativeUrl = annotation.value
} else if (annotation is DELETE) {
httpMethod = HiRequest.METHOD.DELETE
relativeUrl = annotation.value
} else if (annotation is Headers) {
val headersArray = annotation.value
//@Headers("auth-token:token", "accountId:123456")
for (header in headersArray) {
val colon = header.indexOf(":")
check(!(colon == 0 || colon == -1)) {
String.format(
"@headers value must be in the form [name:value] ,but found [%s]",
header
)
}
val name = header.substring(0, colon)
val value = header.substring(colon + 1).trim()
headers[name] = value
}
} else if (annotation is BaseUrl) {
domainUrl = annotation.value
} else if (annotation is CacheStrategy) {
cacheStrategy = annotation.value
} else {
throw IllegalStateException("cannot handle method annotation:" + annotation.javaClass.toString())
}
}
require((httpMethod == HiRequest.METHOD.GET) || (httpMethod == HiRequest.METHOD.POST)
|| (httpMethod == HiRequest.METHOD.PUT)|| (httpMethod == HiRequest.METHOD.DELETE)) {
String.format("method %s must has one of GET,POST,PUT,DELETE ", method.name)
}
if (domainUrl == null) {
domainUrl = baseUrl
}
}
private fun validateGenericType(type: Type): Boolean {
/**
*wrong
* fun test():HiCall<Any>
* fun test():HiCall<List<*>>
* fun test():HiCall<ApiInterface>
*expect
* fun test():HiCall<User>
*/
//如果指定的泛型是集合类型的,那还检查集合的泛型参数
if (type is GenericArrayType) {
return validateGenericType(type.genericComponentType)
}
//如果指定的泛型是一个接口 也不行
if (type is TypeVariable<*>) {
return false
}
//如果指定的泛型是一个通配符 ?extends Number 也不行
if (type is WildcardType) {
return false
}
return true
}
//是否是基本数据类型
private fun isPrimitive(value: Any): Boolean {
//String
if (value.javaClass == String::class.java) {
return true
}
try {
//int byte short long boolean char double float
val field = value.javaClass.getField("TYPE")
val clazz = field[null] as Class<*>
if (clazz.isPrimitive) {
return true
}
} catch (e: IllegalAccessException) {
e.printStackTrace()
} catch (e: NoSuchFieldException) {
e.printStackTrace()
}
return false
}
fun newRequest(method: Method, args: Array<out Any>?): HiRequest {
val arguments: Array<Any> = args as Array<Any>? ?: arrayOf()
parseMethodParameters(method, arguments)
val request = HiRequest()
request.domainUrl = domainUrl
request.returnType = returnType
request.relativeUrl = replaceRelativeUrl ?: relativeUrl
request.parameters = parameters
request.headers = headers
request.httpMethod = httpMethod
request.formPost = formPost
request.cacheStrategy = cacheStrategy
return request
}
companion object {
fun parse(baseUrl: String, method: Method): MethodParser {
return MethodParser(baseUrl, method)
}
}
}