在項目的開發過程中,我們可能會遇到一些很重的App,涉及到很多業務線,N個團隊的共同開發。這個時候,如果還是單app的開發方式的話,可能會導致開發中的編譯時間很長,每個團隊之間的代碼互相存在干擾。這個時候,可以考慮組件化開發。
所謂組件化開發,其實主要就是將一個module拆分成多個module,每個團隊負責自己的module,在發布時再將所有的module合并到一起發布。
這種分module的方式比起普通的分模塊的方式的好處就在于:不同的module之間是互相隔離的,只能通過接口訪問,能更好的保證:高內聚低耦合。開發時,每個module都可以獨立打包安裝,編譯時間短。各個module的res資源是隔離的,不像以前都是混在一起,一旦哪個module不需要了,很方便將代碼和資源文件一起干掉,避免代碼腐爛。
上面說了組件化開發,那路由框架和組件化開發有什么關系呢?上面我們知道,不同的組件之間是互相隔離的,那當需要從一個組件的頁面跳轉到另一個組件的頁面時,該怎么辦呢?這個時候就需要路由框架了,組件之間不直接打交道,而是通過路由來轉發。這樣方便我們實行動態跳轉、攔截、統一轉發等額外操作。
下面是我模仿Retrofit的代理模式來實現的一套簡單的Android路由框架。主要是參考的Android輕量級路由框架LiteRouter,不過個別地方根據自己的習慣做了點改動。雖然是模仿,但在自己敲的過程中,對Retrofit的代理模式又有了更深的理解,同時,LiteRouter的攔截器很簡單,只有一個,我這里又模仿OkHttp的責任鏈模式對它的攔截器做了簡單的拓展,支持多個攔截器,在實現的過程中,又過了一遍OkHttp的源碼,受益匪淺。不得不說,多看源碼真的挺有好處的,大家不妨也去看一看,第一次看不懂很正常,多看幾次就有感覺了,碼讀百遍,其意自現。
使用方法
1、定義接口
/**
* 定義Intent之間跳轉的協議
* <p>
* 作者:余天然 on 2017/5/25 上午11:21
*/
public interface IntentService {
@ClassName("com.soubu.routerdemo.Demo1Activity")
void gotoDemo1(Context context);
@ClassName("com.soubu.routerdemo2.Demo2Activity")
void gotoDemo2(Context context, @Key("request") String request, @Key("response") String response);
}
2、創建Router實例,可以按需要添加攔截器
package com.soubu.routerhost;
import com.soubu.router.IntentRouter;
/**
* 作者:余天然 on 2017/5/25 下午5:43
*/
public class IntentClient {
public static IntentClient instance;
private IntentRouter intentRouter;
/**
* 單例
*/
public static IntentClient getInstance() {
if (instance == null) {
instance = new IntentClient();
}
return instance;
}
/**
* 創建頁面跳轉輔助類
*/
private IntentClient() {
//通過動態代理,獲得具體的跳轉協議實現類,可自定義攔截器
intentRouter = new IntentRouter.Builder()
.addInterceptor(new Interceptor1())
.addInterceptor(new Interceptor2())
.addInterceptor(new Interceptor3())
.build();
}
/**
* 創建各個模塊的頁面接口
*/
public IntentService intentService() {
return intentRouter.create(IntentService.class);
}
}
攔截器,可以統一對Intent做一些額外的處理
/**
* Intent攔截器
*
* 作者:余天然 on 2017/5/25 下午4:05
*/
public class Interceptor1 implements Interceptor {
@Override
public IntentWrapper intercept(Chain chain) {
IntentWrapper request = chain.request();
LogUtil.print("處理請求-1");
Bundle reqExtras = request.getIntent().getExtras();
reqExtras.putString("request", "1");
request.getIntent().putExtras(reqExtras);
IntentWrapper response = chain.process(request);
LogUtil.print("處理響應-1");
Bundle respExtras = response.getIntent().getExtras();
respExtras.putString("response", "1");
response.getIntent().putExtras(respExtras);
return response;
}
}
3、在每個module中,通過Router進行跳轉
IntentClient.getInstance()
.intentService()
.gotoDemo1(activity);
源碼流程
在創建Router實例時,通過動態代理創建了我們定義的跳轉接口的實現類。
/**
* 作者:余天然 on 2017/5/25 上午11:06
*/
public class IntentRouter {
private List<Interceptor> interceptors;
IntentRouter(List<Interceptor> interceptors) {
this.interceptors = interceptors;
}
/**
* create router class service
*
* @param service router class
* @param <T>
* @return
*/
public <T> T create(final Class<T> service) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, final Method method, final Object... args) throws Throwable {
//責任鏈模式,方便定制攔截器
Parser parser = new Parser();
IntentWrapper request = parser.parse(method, args);
Interceptor.Chain chain = new RealInterceptorChain(request, interceptors, 0);
IntentWrapper response = chain.process(request);
response.start();
//設置返回值,不過感覺沒卵用
Class returnTYpe = method.getReturnType();
if (returnTYpe == void.class) {
return null;
} else if (returnTYpe == IntentWrapper.class) {
return response;
}
throw new RuntimeException("method return type only support 'void' or 'IntentWrapper'");
}
});
}
public static final class Builder {
private List<Interceptor> interceptors;
public Builder() {
this.interceptors = new ArrayList<>();
}
public Builder addInterceptor(Interceptor interceptor) {
this.interceptors.add(interceptor);
return this;
}
public IntentRouter build() {
interceptors.add(new DefaultInterceptor());
return new IntentRouter(interceptors);
}
}
}
在動態代理的invoke方法中,通過一個反射解析器來獲取接口的方法的注解信息,根據這些注解自動幫其創建Intent,避免重復的體力活。
/**
* 作者:余天然 on 2017/5/25 上午11:56
*/
public class Parser {
private int requestCode;
private String className;
private int mFlags;
private Bundle bundleExtra;
private Context context;
public Parser() {
}
public Parser addFlags(int flags) {
mFlags |= flags;
return this;
}
public IntentWrapper parse(Method method, Object... args) {
// 解析方法注解
parseMethodAnnotations(method);
// 解析參數注解
parseParameterAnnotations(method, args);
//創建Intent
Intent intent = new Intent();
intent.setClassName(context, className);
intent.putExtras(bundleExtra);
intent.addFlags(mFlags);
requestCode = method.isAnnotationPresent(RequestCode.class) ? requestCode : -1;
return new IntentWrapper(context, intent, requestCode);
}
/**
* 解析參數注解
*/
private void parseParameterAnnotations(Method method, Object[] args) {
//參數類型
Type[] types = method.getGenericParameterTypes();
// 參數名稱
Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();
bundleExtra = new Bundle();
for (int i = 0; i < types.length; i++) {
// key
String key = null;
Annotation[] parameterAnnotations = parameterAnnotationsArray[i];
for (Annotation annotation : parameterAnnotations) {
if (annotation instanceof Key) {
key = ((Key) annotation).value();
break;
}
}
parseParameter(bundleExtra, types[i], key, args[i]);
}
if (context == null) {
throw new RuntimeException("Context不能為空");
}
}
/**
* 解析方法注解
*/
void parseMethodAnnotations(Method method) {
Annotation[] methodAnnotations = method.getAnnotations();
for (Annotation annotation : methodAnnotations) {
if (annotation instanceof ClassName) {
ClassName className = (ClassName) annotation;
this.className = className.value();
} else if (annotation instanceof RequestCode) {
RequestCode requestCode = (RequestCode) annotation;
this.requestCode = requestCode.value();
}
}
if (className == null) {
throw new RuntimeException("JumpTo annotation is required.");
}
}
/**
* 解析參數注解
*
* @param bundleExtra 存儲的Bundle
* @param type 參數類型
* @param key 參數名稱
* @param arg 參數值
*/
void parseParameter(Bundle bundleExtra, Type type, String key, Object arg) {
Class<?> rawParameterType = getRawType(type);
if (rawParameterType == Context.class) {
context = (Context) arg;
}
if (rawParameterType == String.class) {
bundleExtra.putString(key, arg.toString());
} else if (rawParameterType == String[].class) {
bundleExtra.putStringArray(key, (String[]) arg);
} else if (rawParameterType == int.class || rawParameterType == Integer.class) {
bundleExtra.putInt(key, Integer.parseInt(arg.toString()));
} else if (rawParameterType == int[].class || rawParameterType == Integer[].class) {
bundleExtra.putIntArray(key, (int[]) arg);
} else if (rawParameterType == short.class || rawParameterType == Short.class) {
bundleExtra.putShort(key, Short.parseShort(arg.toString()));
} else if (rawParameterType == short[].class || rawParameterType == Short[].class) {
bundleExtra.putShortArray(key, (short[]) arg);
} else if (rawParameterType == long.class || rawParameterType == Long.class) {
bundleExtra.putLong(key, Long.parseLong(arg.toString()));
} else if (rawParameterType == long[].class || rawParameterType == Long[].class) {
bundleExtra.putLongArray(key, (long[]) arg);
} else if (rawParameterType == char.class) {
bundleExtra.putChar(key, arg.toString().toCharArray()[0]);
} else if (rawParameterType == char[].class) {
bundleExtra.putCharArray(key, arg.toString().toCharArray());
} else if (rawParameterType == double.class || rawParameterType == Double.class) {
bundleExtra.putDouble(key, Double.parseDouble(arg.toString()));
} else if (rawParameterType == double[].class || rawParameterType == Double[].class) {
bundleExtra.putDoubleArray(key, (double[]) arg);
} else if (rawParameterType == float.class || rawParameterType == Float.class) {
bundleExtra.putFloat(key, Float.parseFloat(arg.toString()));
} else if (rawParameterType == float[].class || rawParameterType == Float[].class) {
bundleExtra.putFloatArray(key, (float[]) arg);
} else if (rawParameterType == byte.class || rawParameterType == Byte.class) {
bundleExtra.putByte(key, Byte.parseByte(arg.toString()));
} else if (rawParameterType == byte[].class || rawParameterType == Byte[].class) {
bundleExtra.putByteArray(key, (byte[]) arg);
} else if (rawParameterType == boolean.class || rawParameterType == Boolean.class) {
bundleExtra.putBoolean(key, Boolean.parseBoolean(arg.toString()));
} else if (rawParameterType == boolean[].class || rawParameterType == Boolean[].class) {
bundleExtra.putBooleanArray(key, (boolean[]) arg);
} else if (rawParameterType == Bundle.class) {
if (TextUtils.isEmpty(key)) {
bundleExtra.putAll((Bundle) arg);
} else {
bundleExtra.putBundle(key, (Bundle) arg);
}
} else if (rawParameterType == SparseArray.class) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Type actualTypeArgument = actualTypeArguments[0];
if (actualTypeArgument instanceof Class) {
Class<?>[] interfaces = ((Class) actualTypeArgument).getInterfaces();
for (Class<?> interfaceClass : interfaces) {
if (interfaceClass == Parcelable.class) {
bundleExtra.putSparseParcelableArray(key, (SparseArray<Parcelable>) arg);
return;
}
}
throw new RuntimeException("SparseArray的泛型必須實現Parcelable接口");
}
} else {
throw new RuntimeException("SparseArray的泛型必須實現Parcelable接口");
}
} else if (rawParameterType == ArrayList.class) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); // 泛型類型數組
if (actualTypeArguments == null || actualTypeArguments.length != 1) {
throw new RuntimeException("ArrayList的泛型必須實現Parcelable接口");
}
Type actualTypeArgument = actualTypeArguments[0]; // 獲取第一個泛型類型
if (actualTypeArgument == String.class) {
bundleExtra.putStringArrayList(key, (ArrayList<String>) arg);
} else if (actualTypeArgument == Integer.class) {
bundleExtra.putIntegerArrayList(key, (ArrayList<Integer>) arg);
} else if (actualTypeArgument == CharSequence.class) {
bundleExtra.putCharSequenceArrayList(key, (ArrayList<CharSequence>) arg);
} else if (actualTypeArgument instanceof Class) {
Class<?>[] interfaces = ((Class) actualTypeArgument).getInterfaces();
for (Class<?> interfaceClass : interfaces) {
if (interfaceClass == Parcelable.class) {
bundleExtra.putParcelableArrayList(key, (ArrayList<Parcelable>) arg);
return;
}
}
throw new RuntimeException("ArrayList的泛型必須實現Parcelable接口");
}
} else {
throw new RuntimeException("ArrayList的泛型必須實現Parcelable接口");
}
} else {
if (rawParameterType.isArray()) // Parcelable[]
{
Class<?>[] interfaces = rawParameterType.getComponentType().getInterfaces();
for (Class<?> interfaceClass : interfaces) {
if (interfaceClass == Parcelable.class) {
bundleExtra.putParcelableArray(key, (Parcelable[]) arg);
return;
}
}
throw new RuntimeException("Object[]數組中的對象必須全部實現了Parcelable接口");
} else // 其他接口
{
Class<?>[] interfaces = rawParameterType.getInterfaces();
for (Class<?> interfaceClass : interfaces) {
if (interfaceClass == Serializable.class) {
bundleExtra.putSerializable(key, (Serializable) arg);
} else if (interfaceClass == Parcelable.class) {
bundleExtra.putParcelable(key, (Parcelable) arg);
} else {
throw new RuntimeException("Bundle不支持的類型, 參數: " + key);
}
}
}
}
}
/**
* 獲取返回類型
*
* @param type
* @return
*/
Class<?> getRawType(Type type) {
if (type == null) throw new NullPointerException("type == null");
if (type instanceof Class<?>) {
// Type is a normal class.
return (Class<?>) type;
}
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but
// suspects some pathological case related to nested classes exists.
Type rawType = parameterizedType.getRawType();
if (!(rawType instanceof Class)) throw new IllegalArgumentException();
return (Class<?>) rawType;
}
if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
return Array.newInstance(getRawType(componentType), 0).getClass();
}
if (type instanceof TypeVariable) {
// We could use the variable's bounds, but that won't work if there are multiple. Having a raw
// type that's more general than necessary is okay.
return Object.class;
}
if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
}
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
+ "GenericArrayType, but <" + type + "> is of type " + type.getClass().getName());
}
}
這里的Intent我是用一個IntentWrapper包裝了一下,主要就是傳入了context對象,方便startActivity等方法的統一調用
public class IntentWrapper {
private Context context;
private Intent intent;
private int requestCode = -1;
public IntentWrapper(Context context, Intent intent, int requestCode) {
this.context = context;
this.intent = intent;
this.requestCode = requestCode;
}
public Intent getIntent() {
return intent;
}
public Context getContext() {
return context;
}
public int getRequestCode() {
return requestCode;
}
public void start() {
if (requestCode == -1) {
startActivity();
} else {
startActivityForResult(requestCode);
}
}
public void startActivity() {
if (!(context instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
public void startActivityForResult(int requestCode) {
if (!(context instanceof Activity)) {
throw new RuntimeException("startActivityForResult only works for activity context");
}
((Activity) context).startActivityForResult(intent, requestCode);
}
}
其實不要攔截器,也是完全可以的。但是為了擁抱變化(其實是為了裝逼),我們對Parser創建的IntenWrapper又做了一點點加工,在中間添加了一道責任鏈,雖然這里輸入的是IntenWrapper,輸出的也是IntenWrapper,但是,這中間我們是可以自己做一點額外的處理的,比如給每個Intent添加一個額外的參數、統一修改Intent的信息。
為了大家更對OkHttp中的責任鏈有一個更直觀的感受,我這里插播一張圖
看上圖,我們可以定義多個攔截器:RetryAndFollowupInterceptor來負責失敗重試和重定向、BridgeInterceptor來處理request和response的header里面的信息、CacheInterceptor來實現各種緩存策略、ConnectInterceptor來建立與服務器的連接、CallServerInterceptor來與服務器交互數據。這幾個其實是OkHttp自帶的攔截器,其實我們在開發時,通常會額外的加幾個自定義的攔截器,比如對token的統一處理啊、網絡日志的打印啊等等。
攔截器的設計,可以像工廠流水線一樣,傳遞用戶發起的請求 Request,每一個攔截器完成相應的功能,從失敗重試和重定向實現、請求頭的修改和Cookie 的處理,緩存的處理,建立 TCP 和 SSH 連接,發送 Request 和讀取 Response,每一個環節由專門的 Interceptor 負責。
定義Interceptor和Chain的接口。
public interface Interceptor {
IntentWrapper intercept(Chain chain);
interface Chain {
IntentWrapper request();
IntentWrapper process(IntentWrapper request);
}
}
自定義RealInterceptorChain實現Chain接口,其實就是內部維護了一個List<Interceptor>,源碼比較簡單,主要功能就是將多個Interceptor合并成一串。
/**
* 作者:余天然 on 2017/5/25 下午4:35
*/
public class RealInterceptorChain implements Interceptor.Chain {
private IntentWrapper originalRequest;
private List<Interceptor> interceptors;
private int index;
public RealInterceptorChain(IntentWrapper request, List<Interceptor> interceptors, int index) {
this.originalRequest = request;
this.interceptors = interceptors;
this.index = index;
}
@Override
public IntentWrapper request() {
return originalRequest;
}
@Override
public IntentWrapper process(IntentWrapper request) {
Interceptor interceptor = interceptors.get(index);
RealInterceptorChain next = new RealInterceptorChain(request, interceptors, index + 1);
IntentWrapper response = interceptor.intercept(next);
return response;
}
}
默認的攔截器,其實啥都沒做,主要就是方便RealInterceptorChain中至少有一個攔截器可以傳輸數據,但是總覺得這里有點怪,大家要是有更好的方法的話可以省掉這個類的話,歡迎提出來。
public class DefaultInterceptor implements Interceptor {
@Override
public IntentWrapper intercept(Chain chain) {
return chain.request();
}
}
在我們定義接口的時候,其實還用到了幾個注解:@ClassName、@Key、@RequestCode。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ClassName {
String value();
}
這里只發一個吧,另外兩個和它除了名字不一樣都是一樣的,就不發了,免得大家說我湊字數。
以上就是這個路由框架的實現思路了,其實這個框架的實用性不是很大,因為現在阿里巴巴已經開源了一個ARouter,這個比我們這個更好一點,它沒有反射,也沒有接口定義類,而是在每個Activity上添加注解,然后通過apt在編譯時獲取這些注解信息,動態創建一個輔助類來實現路由的。我們這個框架是用的運行時注解+反射,性能上比不上編譯時注解,并且,我們的接口定義類不知道放在哪個module比較好,它依賴了router框架,同時又被所有的module都依賴。