安卓静态代理、动态代理以及仿retorfit实现


什么是代理?

代理模式其实就是我实现你的方法代替你去做某件事

为啥要这么麻烦呢!!!其实是为了不去修改原有的代码,通过代理也可以访问这个对象而且可以进行扩展

代理分为静态代理和动态代理,主要用于我们不想修改原本的实现逻辑,却想再原本实现逻辑上做拓展


静态代理:实现被代理类的接口,重新需要代理的方法 使用场景:比如我们想对所有的某些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)
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值