代理可以看成一种包装器,通过调用自己的方法来传递函数的调用,在传递过程中,可能会增加一些新的功能。而动态代理,可以使用一个类的一个方法为任意一个类提供多个方法的调用,可以看做是任何接口的一个实现。它将所有的方法调用都路由到一个handler中——invoke()方法。
动态代理可以用于这种情况下,当有多个逻辑相似的代码时,我们可以通过使用动态代理。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类以及事后处理消息等。
如何使用
实现InvocationHandler
在使用动态代理是,所有的方法调用会被转发到一个handler当中,通过实现InvocationHandler接口来实现这种转发。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("invoke methods" + method.getName());
return null;
}
}
上面就是一个简单的代理,当方法被调用时,会打印出调用被代理的方法的名字。
创建Proxy实例
import java.lang.reflect.Proxy;
import java.util.Map;
public class Main {
public static void main(String [] args){
Map proxyInstance = (Map) Proxy.newProxyInstance(Main.class.getClassLoader(),new Class[]{Map.class},new DynamicInvocationHandler());
proxyInstance.put("hello","world");
}
}
当方法被调用时,此时代理方法会打印出log,即方法的名字“put”。
动态代理在安卓中的使用
假如我们模拟一个简单的网络请求,当我们业务场景需要调用网络请求执行登陆操作的时候,会这样写:
public class UserApi {
private static final String API_LOGIN = "http://***.***.***";
private static final Gson sGson = new Gson();
public static User login(String username, String password) {
Map<String, Object> params = new HashMap<>();
params.put("username", username);
params.put("password", password);
String response = VirtualHelper.request(API_LOGIN, params);
//注,这里只是为了举例说明一下,就假设此时的数据结构就是跟User一致的
return sGson.fromJson(response, User.class);
}
}
我们使用login方法去进行网络请求,此时,UserApi中又多了register方法,query方法,getToken方法,validate方法…..甚至接下来一个UserApi已经满足不了我们了,我们开始有GoodsApi,OrderApi等等,试想如果有N个Api的类,每个Api类都有N个类似于上面login的这种方法,而实际情况下我们request的入参还远远不止username,password两个这么简单。当我们业务场景扩大的时候,这些都是我们势必要面对的。这是一个问题,能不能去以一种更优雅的方式去解决从而简化业务方的代码量并且降低使用成本呢。这个时候,可以使用动态代理。
动态代理方式去解决
1、先定义两个支持配置的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Inherited
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface URL {
String value();
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Inherited
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Param {
String value();
}
上面就是两个可配字符串的注解。
2、定义一套接口
这一步不是必须的,我们完全可以在动态代理中直接显示的调用VirtualHelper类,但既然抽象出来网络Api这块,那就干脆定义一套几口来解耦具体实现
import java.util.Map;
public interface IRequest {
String url();
Map<String,Object> params();
Class<?> responseCls();
}
import java.util.Map;
public class Request implements IRequest{
String url;
Map<String,Object> params;
Class<?> responseCls;
public Request(String url, Map<String, Object> params, Class<?> responseCls) {
this.url = url;
this.params = params;
this.responseCls = responseCls;
}
@Override
public String url() {
return url;
}
@Override
public Map<String, Object> params() {
return params;
}
@Override
public Class<?> responseCls() {
return responseCls;
}
}
上面这是请求接口和实现类。
public interface INetExecutor {
<T> T execute(IRequest request);
}
import com.google.gson.Gson;
public class DefaultNetExecutor implements INetExecutor {
private static final Gson sGson = new Gson();
@Override
public <T> T execute(IRequest request) {
String response = VirtualHelper.request(request.url(),request.params());
return (T) sGson.fromJson(response,request.responseCls());
}
}
这个是网络执行器已经默认的实现方式,主要是抽献出接口可以让也无妨自主定制真正的执行操作。
3、动态代理器
import android.text.TextUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings("unchecked")
public class ApiGenerator {
private static final Map<Class,Object> sApiCache = new HashMap<>();
private static INetExecutor sNetExecutor;
private static class Handler<T> implements InvocationHandler{
private Class<T> apiInterface;
public Handler(Class<T> apiInterface){
this.apiInterface = apiInterface;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
IRequest request = resolveRequest(method,objects,apiInterface);
if (sNetExecutor == null){
sNetExecutor = defaultNetExecutor();
}
return sNetExecutor.execute(request);
}
}
private static <T> IRequest resolveRequest(Method method,Object[] args,Class<T> apiInterface){
StringBuilder urlBuilder = new StringBuilder();
Map<String,Object> params = null;
if (apiInterface.isAnnotationPresent(URL.class)){
String baseUrl = apiInterface.getAnnotation(URL.class).value();
if (!TextUtils.isEmpty(baseUrl)){
urlBuilder.append(baseUrl);
}
}
if (method.isAnnotationPresent(URL.class)){
String subUrl = method.getAnnotation(URL.class).value();
if (!TextUtils.isEmpty(subUrl)){
urlBuilder.append(subUrl);
}
}
int index = 0;
for (Annotation[] annotations : method.getParameterAnnotations()){
for (Annotation annotation : annotations){
if (annotation instanceof Param){
String key = ((Param) annotation).value();
if (!TextUtils.isEmpty(key)){
if (params == null){
params = new HashMap<>();
}
params.put(key,args[index]);
}
break;
}
}
index++;
}
return new Request(urlBuilder.toString(),params,method.getReturnType());
}
private static INetExecutor defaultNetExecutor(){
return new DefaultNetExecutor();
}
public static <T> T generateApi(Class<T> apiInterface){
if (apiInterface == null || !apiInterface.isInterface()){
throw new RuntimeException("the apiInterface is null or isn`t interface.");
}
synchronized (ApiGenerator.class){
Object api = sApiCache.get(apiInterface);
if (api == null){
api = Proxy.newProxyInstance(apiInterface.getClassLoader(),
new Class[]{apiInterface},
new Handler<>(apiInterface));
sApiCache.put(apiInterface,api);
}
return (T) api;
}
}
public static void setsNetExecutor(INetExecutor sNetExecutor) {
ApiGenerator.sNetExecutor = sNetExecutor;
}
}
我们可以看一下ApiGenerator这个类有个sApiCache的静态变量,他缓存了动态代理生成的对象,这里这样做还是很有必要的,防止重复创建Api的代理类造成额外的性能消耗。
4、如何使用
@URL("http://balabala")
public interface LoginApi {
User login(@Param("username") String username, @Param("passwrod") String password);
}
这个是业务方要写的接口,以及对应的一些注解配置。
5、模拟网络请求
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
LoginApi loginApi = ApiGenerator.generateApi(LoginApi.class);
User user = loginApi.login("123","456");
Toast.makeText(MainActivity.this,user.toString(),Toast.LENGTH_SHORT).show();
}
});
}
}