blackList;
@Override
public void init(Context context) {
classCache = new LruCache<>(50);
blackList = new ArrayList<>();
}
@Override
public void autowire(Object instance) {
doInject(instance, null);
}
/**
* Recursive injection
*
* @param instance who call me.
* @param parent parent of me.
*/
private void doInject(Object instance, Class> parent) {
Class> clazz = null == parent ? instance.getClass() : parent;
ISyringe syringe = getSyringe(clazz);
if (null != syringe) {
syringe.inject(instance);
}
Class> superClazz = clazz.getSuperclass();
// has parent and its not the class of framework.
if (null != superClazz && !superClazz.getName().startsWith("android")) {
doInject(instance, superClazz);
}
}
private ISyringe getSyringe(Class> clazz) {
String className = clazz.getName();
try {
if (!blackList.contains(className)) {
ISyringe syringeHelper = classCache.get(className);
if (null == syringeHelper) { // No cache.
syringeHelper = (ISyringe) Class.forName(clazz.getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
}
classCache.put(className, syringeHelper);
return syringeHelper;
}
} catch (Exception e) {
blackList.add(className); // This instance need not autowired.
}
return null;
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/core/InstrumentationHook.java
================================================
package com.alibaba.android.arouter.core;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import com.alibaba.android.arouter.launcher.ARouter;
import com.alibaba.android.arouter.utils.Consts;
import com.alibaba.android.arouter.utils.TextUtils;
import java.lang.reflect.Field;
/**
* Use ARouter.getInstance().inject(this) now!
*
* Hook the instrumentation, inject values for activity's field.
* Support normal activity only, not contain unit test.
*
* @author Alex Contact me.
* @version 1.0
* @since 2016/11/24 16:42
*/
@Deprecated
public class InstrumentationHook extends Instrumentation {
/**
* Hook the instrumentation's newActivity, inject
*
* Perform instantiation of the process's {@link Activity} object. The
* default implementation provides the normal system behavior.
*
* @param cl The ClassLoader with which to instantiate the object.
* @param className The name of the class implementing the Activity
* object.
* @param intent The Intent object that specified the activity class being
* instantiated.
* @return The newly instantiated Activity object.
*/
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
// return (Activity)cl.loadClass(className).newInstance();
Class> targetActivity = cl.loadClass(className);
Object instanceOfTarget = targetActivity.newInstance();
if (ARouter.canAutoInject()) {
String[] autoInjectParams = intent.getStringArrayExtra(ARouter.AUTO_INJECT);
if (null != autoInjectParams && autoInjectParams.length > 0) {
for (String paramsName : autoInjectParams) {
Object value = intent.getExtras().get(TextUtils.getLeft(paramsName));
if (null != value) {
try {
Field injectField = targetActivity.getDeclaredField(TextUtils.getLeft(paramsName));
injectField.setAccessible(true);
injectField.set(instanceOfTarget, value);
} catch (Exception e) {
ARouter.logger.error(Consts.TAG, "Inject values for activity error! [" + e.getMessage() + "]");
}
}
}
}
}
return (Activity) instanceOfTarget;
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/core/InterceptorServiceImpl.java
================================================
package com.alibaba.android.arouter.core;
import android.content.Context;
import com.alibaba.android.arouter.exception.HandlerException;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.facade.callback.InterceptorCallback;
import com.alibaba.android.arouter.facade.service.InterceptorService;
import com.alibaba.android.arouter.facade.template.IInterceptor;
import com.alibaba.android.arouter.thread.CancelableCountDownLatch;
import com.alibaba.android.arouter.utils.MapUtils;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static com.alibaba.android.arouter.launcher.ARouter.logger;
import static com.alibaba.android.arouter.utils.Consts.TAG;
/**
* All of interceptors
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/23 下午2:09
*/
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
checkInterceptorsInitStatus();
if (!interceptorHasInit) {
callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
return;
}
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_execute(0, interceptorCounter, postcard);
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // Maybe some exception in the tag.
callback.onInterrupt((Throwable) postcard.getTag());
} else {
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
callback.onContinue(postcard);
}
}
/**
* Excute interceptor
*
* @param index current interceptor index
* @param counter interceptor counter
* @param postcard routeMeta
*/
private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
if (index < Warehouse.interceptors.size()) {
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// Last interceptor excute over with no exception.
counter.countDown();
_execute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
}
@Override
public void onInterrupt(Throwable exception) {
// Last interceptor execute over with fatal exception.
postcard.setTag(null == exception ? new HandlerException("No message.") : exception); // save the exception message for backup.
counter.cancel();
// Be attention, maybe the thread in callback has been changed,
// then the catch block(L207) will be invalid.
// The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
// if (!Looper.getMainLooper().equals(Looper.myLooper())) { // You shouldn't throw the exception if the thread is main thread.
// throw new HandlerException(exception.getMessage());
// }
}
});
}
}
@Override
public void init(final Context context) {
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
for (Map.Entry> entry : Warehouse.interceptorsIndex.entrySet()) {
Class extends IInterceptor> interceptorClass = entry.getValue();
try {
IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
iInterceptor.init(context);
Warehouse.interceptors.add(iInterceptor);
} catch (Exception ex) {
throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
}
}
interceptorHasInit = true;
logger.info(TAG, "ARouter interceptors init over.");
synchronized (interceptorInitLock) {
interceptorInitLock.notifyAll();
}
}
}
});
}
private static void checkInterceptorsInitStatus() {
synchronized (interceptorInitLock) {
while (!interceptorHasInit) {
try {
interceptorInitLock.wait(10 * 1000);
} catch (InterruptedException e) {
throw new HandlerException(TAG + "Interceptor init cost too much time error! reason = [" + e.getMessage() + "]");
}
}
}
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/core/LogisticsCenter.java
================================================
package com.alibaba.android.arouter.core;
import android.content.Context;
import android.net.Uri;
import com.alibaba.android.arouter.exception.HandlerException;
import com.alibaba.android.arouter.exception.NoRouteFoundException;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.enums.TypeKind;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.android.arouter.facade.template.IInterceptorGroup;
import com.alibaba.android.arouter.facade.template.IProvider;
import com.alibaba.android.arouter.facade.template.IProviderGroup;
import com.alibaba.android.arouter.facade.template.IRouteGroup;
import com.alibaba.android.arouter.facade.template.IRouteRoot;
import com.alibaba.android.arouter.launcher.ARouter;
import com.alibaba.android.arouter.utils.ClassUtils;
import com.alibaba.android.arouter.utils.Consts;
import com.alibaba.android.arouter.utils.MapUtils;
import com.alibaba.android.arouter.utils.PackageUtils;
import com.alibaba.android.arouter.utils.TextUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
import static com.alibaba.android.arouter.launcher.ARouter.logger;
import static com.alibaba.android.arouter.utils.Consts.AROUTER_SP_CACHE_KEY;
import static com.alibaba.android.arouter.utils.Consts.AROUTER_SP_KEY_MAP;
import static com.alibaba.android.arouter.utils.Consts.DOT;
import static com.alibaba.android.arouter.utils.Consts.ROUTE_ROOT_PAKCAGE;
import static com.alibaba.android.arouter.utils.Consts.SDK_NAME;
import static com.alibaba.android.arouter.utils.Consts.SEPARATOR;
import static com.alibaba.android.arouter.utils.Consts.SUFFIX_INTERCEPTORS;
import static com.alibaba.android.arouter.utils.Consts.SUFFIX_PROVIDERS;
import static com.alibaba.android.arouter.utils.Consts.SUFFIX_ROOT;
import static com.alibaba.android.arouter.utils.Consts.TAG;
/**
* LogisticsCenter contains all of the map.
*
* 1. Creates instance when it is first used.
* 2. Handler Multi-Module relationship map(*)
* 3. Complex logic to solve duplicate group definition
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 15:02
*/
public class LogisticsCenter {
private static Context mContext;
static ThreadPoolExecutor executor;
private static boolean registerByPlugin;
/**
* arouter-auto-register plugin will generate code inside this method
* call this method to register all Routers, Interceptors and Providers
*/
private static void loadRouterMap() {
registerByPlugin = false;
// auto generate register code by gradle plugin: arouter-auto-register
// looks like below:
// registerRouteRoot(new ARouter..Root..modulejava());
// registerRouteRoot(new ARouter..Root..modulekotlin());
}
/**
* register by class name
* Sacrificing a bit of efficiency to solve
* the problem that the main dex file size is too large
*/
private static void register(String className) {
if (!TextUtils.isEmpty(className)) {
try {
Class> clazz = Class.forName(className);
Object obj = clazz.getConstructor().newInstance();
if (obj instanceof IRouteRoot) {
registerRouteRoot((IRouteRoot) obj);
} else if (obj instanceof IProviderGroup) {
registerProvider((IProviderGroup) obj);
} else if (obj instanceof IInterceptorGroup) {
registerInterceptor((IInterceptorGroup) obj);
} else {
logger.info(TAG, "register failed, class name: " + className
+ " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.");
}
} catch (Exception e) {
logger.error(TAG,"register class error:" + className, e);
}
}
}
/**
* method for arouter-auto-register plugin to register Routers
* @param routeRoot IRouteRoot implementation class in the package: com.alibaba.android.arouter.core.routers
*/
private static void registerRouteRoot(IRouteRoot routeRoot) {
markRegisteredByPlugin();
if (routeRoot != null) {
routeRoot.loadInto(Warehouse.groupsIndex);
}
}
/**
* method for arouter-auto-register plugin to register Interceptors
* @param interceptorGroup IInterceptorGroup implementation class in the package: com.alibaba.android.arouter.core.routers
*/
private static void registerInterceptor(IInterceptorGroup interceptorGroup) {
markRegisteredByPlugin();
if (interceptorGroup != null) {
interceptorGroup.loadInto(Warehouse.interceptorsIndex);
}
}
/**
* method for arouter-auto-register plugin to register Providers
* @param providerGroup IProviderGroup implementation class in the package: com.alibaba.android.arouter.core.routers
*/
private static void registerProvider(IProviderGroup providerGroup) {
markRegisteredByPlugin();
if (providerGroup != null) {
providerGroup.loadInto(Warehouse.providersIndex);
}
}
/**
* mark already registered by arouter-auto-register plugin
*/
private static void markRegisteredByPlugin() {
if (!registerByPlugin) {
registerByPlugin = true;
}
}
/**
* LogisticsCenter init, load all metas in memory. Demand initialization
*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set routerMap;
// It will rebuild router map every times when debuggable.
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// These class was generated by arouter-compiler.
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
logger.info(TAG, "Load router map from cache.");
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, "No mapping files were found, check your configuration please!");
}
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
/**
* Build postcard by serviceName
*
* @param serviceName interfaceName
* @return postcard
*/
public static Postcard buildProvider(String serviceName) {
RouteMeta meta = Warehouse.providersIndex.get(serviceName);
if (null == meta) {
return null;
} else {
return new Postcard(meta.getPath(), meta.getGroup());
}
}
/**
* Completion the postcard by route metas
*
* @param postcard Incomplete postcard, should complete by this method.
*/
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
// Load route and cache it into memory, then delete from metas.
try {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
addRouteGroupDynamic(postcard.getGroup(), null);
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
completion(postcard); // Reload
}
} else {
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) { // Try to set params into bundle.
Map resultMap = TextUtils.splitQueryParameters(rawUri);
Map paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
Class extends IProvider> providerMeta = (Class extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
logger.error(TAG, "Init provider failed!", e);
throw new HandlerException("Init provider failed!");
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
/**
* Set value by known type
*
* @param postcard postcard
* @param typeDef type
* @param key key
* @param value value
*/
private static void setValue(Postcard postcard, Integer typeDef, String key, String value) {
if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {
return;
}
try {
if (null != typeDef) {
if (typeDef == TypeKind.BOOLEAN.ordinal()) {
postcard.withBoolean(key, Boolean.parseBoolean(value));
} else if (typeDef == TypeKind.BYTE.ordinal()) {
postcard.withByte(key, Byte.parseByte(value));
} else if (typeDef == TypeKind.SHORT.ordinal()) {
postcard.withShort(key, Short.parseShort(value));
} else if (typeDef == TypeKind.INT.ordinal()) {
postcard.withInt(key, Integer.parseInt(value));
} else if (typeDef == TypeKind.LONG.ordinal()) {
postcard.withLong(key, Long.parseLong(value));
} else if (typeDef == TypeKind.FLOAT.ordinal()) {
postcard.withFloat(key, Float.parseFloat(value));
} else if (typeDef == TypeKind.DOUBLE.ordinal()) {
postcard.withDouble(key, Double.parseDouble(value));
} else if (typeDef == TypeKind.STRING.ordinal()) {
postcard.withString(key, value);
} else if (typeDef == TypeKind.PARCELABLE.ordinal()) {
// TODO : How to description parcelable value with string?
} else if (typeDef == TypeKind.OBJECT.ordinal()) {
postcard.withString(key, value);
} else { // Compatible compiler sdk 1.0.3, in that version, the string type = 18
postcard.withString(key, value);
}
} else {
postcard.withString(key, value);
}
} catch (Throwable ex) {
logger.warning(Consts.TAG, "LogisticsCenter setValue failed! " + ex.getMessage());
}
}
/**
* Suspend business, clear cache.
*/
public static void suspend() {
Warehouse.clear();
}
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (Warehouse.groupsIndex.containsKey(groupName)){
// If this group is included, but it has not been loaded
// load this group first, because dynamic route has high priority.
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/core/Warehouse.java
================================================
package com.alibaba.android.arouter.core;
import com.alibaba.android.arouter.base.UniqueKeyTreeMap;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.android.arouter.facade.template.IInterceptor;
import com.alibaba.android.arouter.facade.template.IProvider;
import com.alibaba.android.arouter.facade.template.IRouteGroup;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Storage of route meta and other data.
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/23 下午1:39
*/
class Warehouse {
// Cache route and metas
static Map> groupsIndex = new HashMap<>();
static Map routes = new HashMap<>();
// Cache provider
static Map providers = new HashMap<>();
static Map providersIndex = new HashMap<>();
// Cache interceptor
static Map> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/exception/HandlerException.java
================================================
package com.alibaba.android.arouter.exception;
/**
* 主流程的处理异常
*
* @author zhilong Contact me.
* @version 1.0
* @since 15/12/7 上午10:30
*/
public class HandlerException extends RuntimeException {
public HandlerException(String detailMessage) {
super(detailMessage);
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/exception/InitException.java
================================================
package com.alibaba.android.arouter.exception;
/**
* 初始化相关异常
*
* @author zhilong Contact me.
* @version 1.0
* @since 2015-12-07 14:17:30
*/
public class InitException extends RuntimeException {
public InitException(String detailMessage) {
super(detailMessage);
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/exception/NoRouteFoundException.java
================================================
package com.alibaba.android.arouter.exception;
/**
* As its name
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/24 10:43
*/
public class NoRouteFoundException extends RuntimeException {
/**
* Constructs a new {@code RuntimeException} with the current stack trace
* and the specified detail message.
*
* @param detailMessage the detail message for this exception.
*/
public NoRouteFoundException(String detailMessage) {
super(detailMessage);
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/Postcard.java
================================================
package com.alibaba.android.arouter.facade;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.app.ActivityOptionsCompat;
import android.util.SparseArray;
import com.alibaba.android.arouter.facade.callback.NavigationCallback;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.android.arouter.facade.service.SerializationService;
import com.alibaba.android.arouter.facade.template.IProvider;
import com.alibaba.android.arouter.launcher.ARouter;
import java.io.Serializable;
import java.util.ArrayList;
/**
* A container that contains the roadmap.
*
* @author Alex Contact me.
* @version 1.1.0
* @since 16/8/22 19:16
*/
public final class Postcard extends RouteMeta {
// Base
private Uri uri;
private Object tag; // A tag prepare for some thing wrong. inner params, DO NOT USE!
private Bundle mBundle; // Data to transform
private int flags = 0; // Flags of route
private int timeout = 300; // Navigation timeout, TimeUnit.Second
private IProvider provider; // It will be set value, if this postcard was provider.
private boolean greenChannel;
private SerializationService serializationService;
private Context context; // May application or activity, check instance type before use it.
private String action;
// Animation
private Bundle optionsCompat; // The transition animation of activity
private int enterAnim = -1;
private int exitAnim = -1;
public Bundle getOptionsBundle() {
return optionsCompat;
}
public int getEnterAnim() {
return enterAnim;
}
public int getExitAnim() {
return exitAnim;
}
public IProvider getProvider() {
return provider;
}
public Postcard setProvider(IProvider provider) {
this.provider = provider;
return this;
}
public Postcard() {
this(null, null);
}
public Postcard(String path, String group) {
this(path, group, null, null);
}
public Postcard(String path, String group, Uri uri, Bundle bundle) {
setPath(path);
setGroup(group);
setUri(uri);
this.mBundle = (null == bundle ? new Bundle() : bundle);
}
public boolean isGreenChannel() {
return greenChannel;
}
public Object getTag() {
return tag;
}
public Postcard setTag(Object tag) {
this.tag = tag;
return this;
}
public Bundle getExtras() {
return mBundle;
}
public int getTimeout() {
return timeout;
}
/**
* Set timeout of navigation this time.
*
* @param timeout timeout
* @return this
*/
public Postcard setTimeout(int timeout) {
this.timeout = timeout;
return this;
}
public Uri getUri() {
return uri;
}
public Postcard setUri(Uri uri) {
this.uri = uri;
return this;
}
/**
* Navigation to the route with path in postcard.
* No param, will be use application context.
*/
public Object navigation() {
return navigation(null);
}
/**
* Navigation to the route with path in postcard.
*
* @param context Activity and so on.
*/
public Object navigation(Context context) {
return navigation(context, null);
}
/**
* Navigation to the route with path in postcard.
*
* @param context Activity and so on.
*/
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
/**
* Navigation to the route with path in postcard.
*
* @param mContext Activity and so on.
* @param requestCode startActivityForResult's param
*/
public void navigation(Activity mContext, int requestCode) {
navigation(mContext, requestCode, null);
}
/**
* Navigation to the route with path in postcard.
*
* @param mContext Activity and so on.
* @param requestCode startActivityForResult's param
*/
public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
ARouter.getInstance().navigation(mContext, this, requestCode, callback);
}
/**
* Green channel, it will skip all of interceptors.
*
* @return this
*/
public Postcard greenChannel() {
this.greenChannel = true;
return this;
}
/**
* BE ATTENTION TO THIS METHOD WAS SET, NOT ADD!
*/
public Postcard with(Bundle bundle) {
if (null != bundle) {
mBundle = bundle;
}
return this;
}
/**
* Set special flags controlling how this intent is handled. Most values
* here depend on the type of component being executed by the Intent,
* specifically the FLAG_ACTIVITY_* flags are all for use with
* {@link Context#startActivity Context.startActivity()} and the
* FLAG_RECEIVER_* flags are all for use with
* {@link Context#sendBroadcast(Intent) Context.sendBroadcast()}.
*/
public Postcard withFlags(int flag) {
this.flags = flag;
return this;
}
/**
* Add additional flags to the intent (or with existing flags
* value).
*
* @param flags The new flags to set.
* @return Returns the same Intent object, for chaining multiple calls
* into a single statement.
* @see #withFlags
*/
public Postcard addFlags(int flags) {
this.flags |= flags;
return this;
}
public int getFlags() {
return flags;
}
/**
* Set object value, the value will be convert to string by 'Fastjson'
*
* @param key a String, or null
* @param value a Object, or null
* @return current
*/
public Postcard withObject(@Nullable String key, @Nullable Object value) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
mBundle.putString(key, serializationService.object2Json(value));
return this;
}
// Follow api copy from #{Bundle}
/**
* Inserts a String value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a String, or null
* @return current
*/
public Postcard withString(@Nullable String key, @Nullable String value) {
mBundle.putString(key, value);
return this;
}
/**
* Inserts a Boolean value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a boolean
* @return current
*/
public Postcard withBoolean(@Nullable String key, boolean value) {
mBundle.putBoolean(key, value);
return this;
}
/**
* Inserts a short value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a short
* @return current
*/
public Postcard withShort(@Nullable String key, short value) {
mBundle.putShort(key, value);
return this;
}
/**
* Inserts an int value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value an int
* @return current
*/
public Postcard withInt(@Nullable String key, int value) {
mBundle.putInt(key, value);
return this;
}
/**
* Inserts a long value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a long
* @return current
*/
public Postcard withLong(@Nullable String key, long value) {
mBundle.putLong(key, value);
return this;
}
/**
* Inserts a double value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a double
* @return current
*/
public Postcard withDouble(@Nullable String key, double value) {
mBundle.putDouble(key, value);
return this;
}
/**
* Inserts a byte value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a byte
* @return current
*/
public Postcard withByte(@Nullable String key, byte value) {
mBundle.putByte(key, value);
return this;
}
/**
* Inserts a char value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a char
* @return current
*/
public Postcard withChar(@Nullable String key, char value) {
mBundle.putChar(key, value);
return this;
}
/**
* Inserts a float value into the mapping of this Bundle, replacing
* any existing value for the given key.
*
* @param key a String, or null
* @param value a float
* @return current
*/
public Postcard withFloat(@Nullable String key, float value) {
mBundle.putFloat(key, value);
return this;
}
/**
* Inserts a CharSequence value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a CharSequence, or null
* @return current
*/
public Postcard withCharSequence(@Nullable String key, @Nullable CharSequence value) {
mBundle.putCharSequence(key, value);
return this;
}
/**
* Inserts a Parcelable value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Parcelable object, or null
* @return current
*/
public Postcard withParcelable(@Nullable String key, @Nullable Parcelable value) {
mBundle.putParcelable(key, value);
return this;
}
/**
* Inserts an array of Parcelable values into the mapping of this Bundle,
* replacing any existing value for the given key. Either key or value may
* be null.
*
* @param key a String, or null
* @param value an array of Parcelable objects, or null
* @return current
*/
public Postcard withParcelableArray(@Nullable String key, @Nullable Parcelable[] value) {
mBundle.putParcelableArray(key, value);
return this;
}
/**
* Inserts a List of Parcelable values into the mapping of this Bundle,
* replacing any existing value for the given key. Either key or value may
* be null.
*
* @param key a String, or null
* @param value an ArrayList of Parcelable objects, or null
* @return current
*/
public Postcard withParcelableArrayList(@Nullable String key, @Nullable ArrayList extends Parcelable> value) {
mBundle.putParcelableArrayList(key, value);
return this;
}
/**
* Inserts a SparceArray of Parcelable values into the mapping of this
* Bundle, replacing any existing value for the given key. Either key
* or value may be null.
*
* @param key a String, or null
* @param value a SparseArray of Parcelable objects, or null
* @return current
*/
public Postcard withSparseParcelableArray(@Nullable String key, @Nullable SparseArray extends Parcelable> value) {
mBundle.putSparseParcelableArray(key, value);
return this;
}
/**
* Inserts an ArrayList value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an ArrayList object, or null
* @return current
*/
public Postcard withIntegerArrayList(@Nullable String key, @Nullable ArrayList value) {
mBundle.putIntegerArrayList(key, value);
return this;
}
/**
* Inserts an ArrayList value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an ArrayList object, or null
* @return current
*/
public Postcard withStringArrayList(@Nullable String key, @Nullable ArrayList value) {
mBundle.putStringArrayList(key, value);
return this;
}
/**
* Inserts an ArrayList value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value an ArrayList object, or null
* @return current
*/
public Postcard withCharSequenceArrayList(@Nullable String key, @Nullable ArrayList value) {
mBundle.putCharSequenceArrayList(key, value);
return this;
}
/**
* Inserts a Serializable value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Serializable object, or null
* @return current
*/
public Postcard withSerializable(@Nullable String key, @Nullable Serializable value) {
mBundle.putSerializable(key, value);
return this;
}
/**
* Inserts a byte array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a byte array object, or null
* @return current
*/
public Postcard withByteArray(@Nullable String key, @Nullable byte[] value) {
mBundle.putByteArray(key, value);
return this;
}
/**
* Inserts a short array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a short array object, or null
* @return current
*/
public Postcard withShortArray(@Nullable String key, @Nullable short[] value) {
mBundle.putShortArray(key, value);
return this;
}
/**
* Inserts a char array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a char array object, or null
* @return current
*/
public Postcard withCharArray(@Nullable String key, @Nullable char[] value) {
mBundle.putCharArray(key, value);
return this;
}
/**
* Inserts a float array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a float array object, or null
* @return current
*/
public Postcard withFloatArray(@Nullable String key, @Nullable float[] value) {
mBundle.putFloatArray(key, value);
return this;
}
/**
* Inserts a CharSequence array value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a CharSequence array object, or null
* @return current
*/
public Postcard withCharSequenceArray(@Nullable String key, @Nullable CharSequence[] value) {
mBundle.putCharSequenceArray(key, value);
return this;
}
/**
* Inserts a Bundle value into the mapping of this Bundle, replacing
* any existing value for the given key. Either key or value may be null.
*
* @param key a String, or null
* @param value a Bundle object, or null
* @return current
*/
public Postcard withBundle(@Nullable String key, @Nullable Bundle value) {
mBundle.putBundle(key, value);
return this;
}
/**
* Set normal transition anim
*
* @param enterAnim enter
* @param exitAnim exit
* @return current
*/
public Postcard withTransition(int enterAnim, int exitAnim) {
this.enterAnim = enterAnim;
this.exitAnim = exitAnim;
return this;
}
/**
* Set options compat
*
* @param compat compat
* @return this
*/
@RequiresApi(16)
public Postcard withOptionsCompat(ActivityOptionsCompat compat) {
if (null != compat) {
this.optionsCompat = compat.toBundle();
}
return this;
}
@Override
public String toString() {
return "Postcard{" +
"uri=" + uri +
", tag=" + tag +
", mBundle=" + mBundle +
", flags=" + flags +
", timeout=" + timeout +
", provider=" + provider +
", greenChannel=" + greenChannel +
", optionsCompat=" + optionsCompat +
", enterAnim=" + enterAnim +
", exitAnim=" + exitAnim +
"}\n" +
super.toString();
}
public String getAction() {
return action;
}
public Postcard withAction(String action) {
this.action = action;
return this;
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/callback/InterceptorCallback.java
================================================
package com.alibaba.android.arouter.facade.callback;
import com.alibaba.android.arouter.facade.Postcard;
/**
* The callback of interceptor.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/4 17:36
*/
public interface InterceptorCallback {
/**
* Continue process
*
* @param postcard route meta
*/
void onContinue(Postcard postcard);
/**
* Interrupt process, pipeline will be destroy when this method called.
*
* @param exception Reson of interrupt.
*/
void onInterrupt(Throwable exception);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/callback/NavCallback.java
================================================
package com.alibaba.android.arouter.facade.callback;
import com.alibaba.android.arouter.facade.Postcard;
/**
* Easy to use navigation callback.
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/4/10 下午12:59
*/
public abstract class NavCallback implements NavigationCallback {
@Override
public void onFound(Postcard postcard) {
// Do nothing
}
@Override
public void onLost(Postcard postcard) {
// Do nothing
}
@Override
public abstract void onArrival(Postcard postcard);
@Override
public void onInterrupt(Postcard postcard) {
// Do nothing
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/callback/NavigationCallback.java
================================================
package com.alibaba.android.arouter.facade.callback;
import com.alibaba.android.arouter.facade.Postcard;
/**
* Callback after navigation.
*
* @author Alex Contact me.
* @version 1.0
* @since 2016/9/22 14:15
*/
public interface NavigationCallback {
/**
* Callback when find the destination.
*
* @param postcard meta
*/
void onFound(Postcard postcard);
/**
* Callback after lose your way.
*
* @param postcard meta
*/
void onLost(Postcard postcard);
/**
* Callback after navigation.
*
* @param postcard meta
*/
void onArrival(Postcard postcard);
/**
* Callback on interrupt.
*
* @param postcard meta
*/
void onInterrupt(Postcard postcard);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/AutowiredService.java
================================================
package com.alibaba.android.arouter.facade.service;
import com.alibaba.android.arouter.facade.template.IProvider;
/**
* Service for autowired.
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/28 下午6:06
*/
public interface AutowiredService extends IProvider {
/**
* Autowired core.
* @param instance the instance who need autowired.
*/
void autowire(Object instance);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/ClassLoaderService.java
================================================
package com.alibaba.android.arouter.facade.service;
import com.alibaba.android.arouter.facade.template.IProvider;
/**
* Get class by user, maybe someone use InstantRun and other tech will move dex files.
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/23 下午12:16
*/
public interface ClassLoaderService extends IProvider {
Class> forName();
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/DegradeService.java
================================================
package com.alibaba.android.arouter.facade.service;
import android.content.Context;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.template.IProvider;
/**
* Provide degrade service for router, you can do something when route has lost.
*
* @author Alex Contact me.
* @version 1.0
* @since 2016/9/22 14:51
*/
public interface DegradeService extends IProvider {
/**
* Router has lost.
*
* @param postcard meta
*/
void onLost(Context context, Postcard postcard);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/InterceptorService.java
================================================
package com.alibaba.android.arouter.facade.service;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.callback.InterceptorCallback;
import com.alibaba.android.arouter.facade.template.IProvider;
/**
* Interceptor service
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/23 下午2:06
*/
public interface InterceptorService extends IProvider {
/**
* Do interceptions
*/
void doInterceptions(Postcard postcard, InterceptorCallback callback);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/PathReplaceService.java
================================================
package com.alibaba.android.arouter.facade.service;
import android.net.Uri;
import com.alibaba.android.arouter.facade.template.IProvider;
/**
* Preprocess your path
*
* @author Alex Contact me.
* @version 1.0
* @since 2016/12/9 16:48
*/
public interface PathReplaceService extends IProvider {
/**
* For normal path.
*
* @param path raw path
*/
String forString(String path);
/**
* For uri type.
*
* @param uri raw uri
*/
Uri forUri(Uri uri);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/PretreatmentService.java
================================================
package com.alibaba.android.arouter.facade.service;
import android.content.Context;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.template.IProvider;
/**
* Pretreatment service used for check if need navigation.
*
* @author zhilong [Contact me.](mailto:zhilong.liu@aliyun.com)
* @version 1.0
* @since 2019-05-08 11:53
*/
public interface PretreatmentService extends IProvider {
/**
* Do something before navigation.
*
* @param context context
* @param postcard meta
* @return if need navigation.
*/
boolean onPretreatment(Context context, Postcard postcard);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/service/SerializationService.java
================================================
package com.alibaba.android.arouter.facade.service;
import com.alibaba.android.arouter.facade.template.IProvider;
import java.lang.reflect.Type;
/**
* Used for parse json string.
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/4/10 下午1:43
*/
public interface SerializationService extends IProvider {
/**
* Parse json to object
*
* USE @parseObject PLEASE
*
* @param input json string
* @param clazz object type
* @return instance of object
*/
@Deprecated
T json2Object(String input, Class clazz);
/**
* Object to json
*
* @param instance obj
* @return json string
*/
String object2Json(Object instance);
/**
* Parse json to object
*
* @param input json string
* @param clazz object type
* @return instance of object
*/
T parseObject(String input, Type clazz);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IInterceptor.java
================================================
package com.alibaba.android.arouter.facade.template;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.callback.InterceptorCallback;
/**
* Used for inject custom logic when navigation.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 13:56
*/
public interface IInterceptor extends IProvider {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
void process(Postcard postcard, InterceptorCallback callback);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IInterceptorGroup.java
================================================
package com.alibaba.android.arouter.facade.template;
import java.util.Map;
/**
* Template of interceptor group.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/29 09:51
*/
public interface IInterceptorGroup {
/**
* Load interceptor to input
*
* @param interceptor input
*/
void loadInto(Map> interceptor);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/ILogger.java
================================================
package com.alibaba.android.arouter.facade.template;
import com.alibaba.android.arouter.utils.Consts;
/**
* Logger
*
* @author 正纬 Contact me.
* @version 1.0
* @since 16/5/16 下午5:39
*/
public interface ILogger {
boolean isShowLog = false;
boolean isShowStackTrace = false;
String defaultTag = Consts.TAG;
void showLog(boolean isShowLog);
void showStackTrace(boolean isShowStackTrace);
void debug(String tag, String message);
void info(String tag, String message);
void warning(String tag, String message);
void error(String tag, String message);
void error(String tag, String message, Throwable e);
void monitor(String message);
boolean isMonitorMode();
String getDefaultTag();
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IPolicy.java
================================================
package com.alibaba.android.arouter.facade.template;
/**
* Store policy.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/24 10:15
*/
@Deprecated
public interface IPolicy {
int getFlag();
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IProvider.java
================================================
package com.alibaba.android.arouter.facade.template;
import android.content.Context;
/**
* Provider interface, base of other interface.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 23:08
*/
public interface IProvider {
/**
* Do your init work in this method, it well be call when processor has been load.
*
* @param context ctx
*/
void init(Context context);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IProviderGroup.java
================================================
package com.alibaba.android.arouter.facade.template;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import java.util.Map;
/**
* Template of provider group.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/08/30 12:42
*/
public interface IProviderGroup {
/**
* Load providers map to input
*
* @param providers input
*/
void loadInto(Map providers);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IRouteGroup.java
================================================
package com.alibaba.android.arouter.facade.template;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import java.util.Map;
/**
* Group element.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 16:37
*/
public interface IRouteGroup {
/**
* Fill the atlas with routes in group.
*/
void loadInto(Map atlas);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/IRouteRoot.java
================================================
package com.alibaba.android.arouter.facade.template;
import java.util.Map;
/**
* Root element.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 16:36
*/
public interface IRouteRoot {
/**
* Load routes to input
* @param routes input
*/
void loadInto(Map> routes);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/facade/template/ISyringe.java
================================================
package com.alibaba.android.arouter.facade.template;
/**
* Template of syringe
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/20 下午4:41
*/
public interface ISyringe {
void inject(Object target);
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/launcher/ARouter.java
================================================
package com.alibaba.android.arouter.launcher;
import android.app.Application;
import android.content.Context;
import android.net.Uri;
import com.alibaba.android.arouter.exception.InitException;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.callback.NavigationCallback;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.android.arouter.facade.template.ILogger;
import com.alibaba.android.arouter.facade.template.IRouteGroup;
import com.alibaba.android.arouter.utils.Consts;
import java.util.concurrent.ThreadPoolExecutor;
/**
* ARouter facade
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/16 14:36
*/
public final class ARouter {
// Key of raw uri
public static final String RAW_URI = "NTeRQWvye18AkPd6G";
public static final String AUTO_INJECT = "wmHzgD4lOj5o4241";
private volatile static ARouter instance = null;
private volatile static boolean hasInit = false;
public static ILogger logger;
private ARouter() {
}
/**
* Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
/**
* Get instance of router. A
* All feature U use, will be starts here.
*/
public static ARouter getInstance() {
if (!hasInit) {
throw new InitException("ARouter::Init::Invoke init(context) first!");
} else {
if (instance == null) {
synchronized (ARouter.class) {
if (instance == null) {
instance = new ARouter();
}
}
}
return instance;
}
}
public static synchronized void openDebug() {
_ARouter.openDebug();
}
public static boolean debuggable() {
return _ARouter.debuggable();
}
public static synchronized void openLog() {
_ARouter.openLog();
}
public static synchronized void printStackTrace() {
_ARouter.printStackTrace();
}
public static synchronized void setExecutor(ThreadPoolExecutor tpe) {
_ARouter.setExecutor(tpe);
}
public synchronized void destroy() {
_ARouter.destroy();
hasInit = false;
}
/**
* The interface is not stable enough, use 'ARouter.inject();';
*/
@Deprecated
public static synchronized void enableAutoInject() {
_ARouter.enableAutoInject();
}
@Deprecated
public static boolean canAutoInject() {
return _ARouter.canAutoInject();
}
/**
* The interface is not stable enough, use 'ARouter.inject();';
*/
@Deprecated
public static void attachBaseContext() {
_ARouter.attachBaseContext();
}
public static synchronized void monitorMode() {
_ARouter.monitorMode();
}
public static boolean isMonitorMode() {
return _ARouter.isMonitorMode();
}
public static void setLogger(ILogger userLogger) {
_ARouter.setLogger(userLogger);
}
/**
* Inject params and services.
*/
public void inject(Object thiz) {
_ARouter.inject(thiz);
}
/**
* Build the roadmap, draw a postcard.
*
* @param path Where you go.
*/
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
/**
* Build the roadmap, draw a postcard.
*
* @param path Where you go.
* @param group The group of path.
*/
@Deprecated
public Postcard build(String path, String group) {
return _ARouter.getInstance().build(path, group, false);
}
/**
* Build the roadmap, draw a postcard.
*
* @param url the path
*/
public Postcard build(Uri url) {
return _ARouter.getInstance().build(url);
}
/**
* Launch the navigation by type
*
* @param service interface of service
* @param return type
* @return instance of service
*/
public T navigation(Class extends T> service) {
return _ARouter.getInstance().navigation(service);
}
/**
* Launch the navigation.
*
* @param mContext .
* @param postcard .
* @param requestCode Set for startActivityForResult
* @param callback cb
*/
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
/**
* Add route group dynamic.
* @param group route group.
* @return add result.
*/
public boolean addRouteGroup(IRouteGroup group) {
return _ARouter.getInstance().addRouteGroup(group);
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/launcher/_ARouter.java
================================================
package com.alibaba.android.arouter.launcher;
import android.app.Activity;
import android.app.Application;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.widget.Toast;
import com.alibaba.android.arouter.core.InstrumentationHook;
import com.alibaba.android.arouter.core.LogisticsCenter;
import com.alibaba.android.arouter.exception.HandlerException;
import com.alibaba.android.arouter.exception.InitException;
import com.alibaba.android.arouter.exception.NoRouteFoundException;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.callback.InterceptorCallback;
import com.alibaba.android.arouter.facade.callback.NavigationCallback;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.android.arouter.facade.service.*;
import com.alibaba.android.arouter.facade.template.ILogger;
import com.alibaba.android.arouter.facade.template.IRouteGroup;
import com.alibaba.android.arouter.thread.DefaultPoolExecutor;
import com.alibaba.android.arouter.utils.Consts;
import com.alibaba.android.arouter.utils.DefaultLogger;
import com.alibaba.android.arouter.utils.TextUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
/**
* ARouter core (Facade patten)
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/16 14:39
*/
final class _ARouter {
static ILogger logger = new DefaultLogger(Consts.TAG);
private volatile static boolean monitorMode = false;
private volatile static boolean debuggable = false;
private volatile static boolean autoInject = false;
private volatile static _ARouter instance = null;
private volatile static boolean hasInit = false;
private volatile static ThreadPoolExecutor executor = DefaultPoolExecutor.getInstance();
private static Handler mHandler;
private static Context mContext;
private static InterceptorService interceptorService;
private _ARouter() {
}
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
/**
* Destroy arouter, it can be used only in debug mode.
*/
static synchronized void destroy() {
if (debuggable()) {
hasInit = false;
LogisticsCenter.suspend();
logger.info(Consts.TAG, "ARouter destroy success!");
} else {
logger.error(Consts.TAG, "Destroy can be used in debug mode only!");
}
}
protected static _ARouter getInstance() {
if (!hasInit) {
throw new InitException("ARouterCore::Init::Invoke init(context) first!");
} else {
if (instance == null) {
synchronized (_ARouter.class) {
if (instance == null) {
instance = new _ARouter();
}
}
}
return instance;
}
}
static synchronized void openDebug() {
debuggable = true;
logger.info(Consts.TAG, "ARouter openDebug");
}
static synchronized void openLog() {
logger.showLog(true);
logger.info(Consts.TAG, "ARouter openLog");
}
@Deprecated
static synchronized void enableAutoInject() {
autoInject = true;
}
@Deprecated
static boolean canAutoInject() {
return autoInject;
}
@Deprecated
static void attachBaseContext() {
Log.i(Consts.TAG, "ARouter start attachBaseContext");
try {
Class> mMainThreadClass = Class.forName("android.app.ActivityThread");
// Get current main thread.
Method getMainThread = mMainThreadClass.getDeclaredMethod("currentActivityThread");
getMainThread.setAccessible(true);
Object currentActivityThread = getMainThread.invoke(null);
// The field contain instrumentation.
Field mInstrumentationField = mMainThreadClass.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
// Hook current instrumentation
mInstrumentationField.set(currentActivityThread, new InstrumentationHook());
Log.i(Consts.TAG, "ARouter hook instrumentation success!");
} catch (Exception ex) {
Log.e(Consts.TAG, "ARouter hook instrumentation failed! [" + ex.getMessage() + "]");
}
}
static synchronized void printStackTrace() {
logger.showStackTrace(true);
logger.info(Consts.TAG, "ARouter printStackTrace");
}
static synchronized void setExecutor(ThreadPoolExecutor tpe) {
executor = tpe;
}
static synchronized void monitorMode() {
monitorMode = true;
logger.info(Consts.TAG, "ARouter monitorMode on");
}
static boolean isMonitorMode() {
return monitorMode;
}
static boolean debuggable() {
return debuggable;
}
static void setLogger(ILogger userLogger) {
if (null != userLogger) {
logger = userLogger;
}
}
static void inject(Object thiz) {
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
if (null != autowiredService) {
autowiredService.autowire(thiz);
}
}
/**
* Build postcard by path and default group
*/
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path), true);
}
}
/**
* Build postcard by uri
*/
protected Postcard build(Uri uri) {
if (null == uri || TextUtils.isEmpty(uri.toString())) {
throw new HandlerException(Consts.TAG + "Parameter invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
uri = pService.forUri(uri);
}
return new Postcard(uri.getPath(), extractGroup(uri.getPath()), uri, null);
}
}
/**
* Build postcard by path and group
*/
protected Postcard build(String path, String group, Boolean afterReplace) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
if (!afterReplace) {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
/**
* Extract the default group from path.
*/
private String extractGroup(String path) {
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
}
try {
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (TextUtils.isEmpty(defaultGroup)) {
throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");
} else {
return defaultGroup;
}
} catch (Exception e) {
logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());
return null;
}
}
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
protected T navigation(Class extends T> service) {
try {
Postcard postcard = LogisticsCenter.buildProvider(service.getName());
// Compatible 1.0.5 compiler sdk.
// Earlier versions did not use the fully qualified name to get the service
if (null == postcard) {
// No service, or this service in old version.
postcard = LogisticsCenter.buildProvider(service.getSimpleName());
}
if (null == postcard) {
return null;
}
// Set application to postcard.
postcard.setContext(mContext);
LogisticsCenter.completion(postcard);
return (T) postcard.getProvider();
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
return null;
}
}
/**
* Use router navigation.
*
* @param context Activity or null.
* @param postcard Route metas
* @param requestCode RequestCode
* @param callback cb
*/
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
if (debuggable()) {
// Show friendly tips for user.
runInMainThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
});
}
if (null != callback) {
callback.onLost(postcard);
} else {
// No callback for this invoke, then we use the global degrade service.
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
return _navigation(postcard, requestCode, callback);
}
return null;
}
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (0 != flags) {
intent.setFlags(flags);
}
// Non activity, need FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class> fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
/**
* Be sure execute in main thread.
*
* @param runnable code
*/
private void runInMainThread(Runnable runnable) {
if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
mHandler.post(runnable);
} else {
runnable.run();
}
}
/**
* Start activity
*
* @see ActivityCompat
*/
private void startActivity(int requestCode, Context currentContext, Intent intent, Postcard postcard, NavigationCallback callback) {
if (requestCode >= 0) { // Need start for result
if (currentContext instanceof Activity) {
ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
} else {
logger.warning(Consts.TAG, "Must use [navigation(activity, ...)] to support [startActivityForResult]");
}
} else {
ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
}
if ((-1 != postcard.getEnterAnim() && -1 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version.
((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
}
if (null != callback) { // Navigation over.
callback.onArrival(postcard);
}
}
boolean addRouteGroup(IRouteGroup group) {
if (null == group) {
return false;
}
String groupName = null;
try {
// Extract route meta.
Map dynamicRoute = new HashMap<>();
group.loadInto(dynamicRoute);
// Check route meta.
for (Map.Entry route : dynamicRoute.entrySet()) {
String path = route.getKey();
String groupByExtract = extractGroup(path);
RouteMeta meta = route.getValue();
if (null == groupName) {
groupName = groupByExtract;
}
if (null == groupName || !groupName.equals(groupByExtract) || !groupName.equals(meta.getGroup())) {
// Group name not consistent
return false;
}
}
LogisticsCenter.addRouteGroupDynamic(groupName, group);
logger.info(Consts.TAG, "Add route group [" + groupName + "] finish, " + dynamicRoute.size() + " new route meta.");
return true;
} catch (Exception exception) {
logger.error(Consts.TAG, "Add route group dynamic exception!", exception);
}
return false;
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/thread/CancelableCountDownLatch.java
================================================
package com.alibaba.android.arouter.thread;
import java.util.concurrent.CountDownLatch;
/**
* As its name.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/29 15:48
*/
public class CancelableCountDownLatch extends CountDownLatch {
/**
* Constructs a {@code CountDownLatch} initialized with the given count.
*
* @param count the number of times {@link #countDown} must be invoked
* before threads can pass through {@link #await}
* @throws IllegalArgumentException if {@code count} is negative
*/
public CancelableCountDownLatch(int count) {
super(count);
}
public void cancel() {
while (getCount() > 0) {
countDown();
}
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/thread/DefaultPoolExecutor.java
================================================
package com.alibaba.android.arouter.thread;
import com.alibaba.android.arouter.launcher.ARouter;
import com.alibaba.android.arouter.utils.Consts;
import com.alibaba.android.arouter.utils.TextUtils;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Executors
*
* @author 正纬 Contact me.
* @version 1.0
* @since 16/4/28 下午4:07
*/
public class DefaultPoolExecutor extends ThreadPoolExecutor {
// Thread args
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int INIT_THREAD_COUNT = CPU_COUNT + 1;
private static final int MAX_THREAD_COUNT = INIT_THREAD_COUNT;
private static final long SURPLUS_THREAD_LIFE = 30L;
private static volatile DefaultPoolExecutor instance;
public static DefaultPoolExecutor getInstance() {
if (null == instance) {
synchronized (DefaultPoolExecutor.class) {
if (null == instance) {
instance = new DefaultPoolExecutor(
INIT_THREAD_COUNT,
MAX_THREAD_COUNT,
SURPLUS_THREAD_LIFE,
TimeUnit.SECONDS,
new ArrayBlockingQueue(64),
new DefaultThreadFactory());
}
}
}
return instance;
}
private DefaultPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
ARouter.logger.error(Consts.TAG, "Task rejected, too many task!");
}
});
}
/*
* 线程执行结束,顺便看一下有么有什么乱七八糟的异常
*
* @param r the runnable that has completed
* @param t the exception that caused termination, or null if
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future>) {
try {
((Future>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // ignore/reset
}
}
if (t != null) {
ARouter.logger.warning(Consts.TAG, "Running task appeared exception! Thread [" + Thread.currentThread().getName() + "], because [" + t.getMessage() + "]\n" + TextUtils.formatStackTrace(t.getStackTrace()));
}
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/thread/DefaultThreadFactory.java
================================================
package com.alibaba.android.arouter.thread;
import android.support.annotation.NonNull;
import com.alibaba.android.arouter.launcher.ARouter;
import com.alibaba.android.arouter.utils.Consts;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 线程池工厂类
*
* @author zhilong Contact me.
* @version 1.0
* @since 15/12/25 上午10:51
*/
public class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final String namePrefix;
public DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "ARouter task pool No." + poolNumber.getAndIncrement() + ", thread No.";
}
public Thread newThread(@NonNull Runnable runnable) {
String threadName = namePrefix + threadNumber.getAndIncrement();
ARouter.logger.info(Consts.TAG, "Thread production, name is [" + threadName + "]");
Thread thread = new Thread(group, runnable, threadName, 0);
if (thread.isDaemon()) { //设为非后台线程
thread.setDaemon(false);
}
if (thread.getPriority() != Thread.NORM_PRIORITY) { //优先级为normal
thread.setPriority(Thread.NORM_PRIORITY);
}
// 捕获多线程处理中的异常
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
ARouter.logger.info(Consts.TAG, "Running task appeared exception! Thread [" + thread.getName() + "], because [" + ex.getMessage() + "]");
}
});
return thread;
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/utils/ClassUtils.java
================================================
package com.alibaba.android.arouter.utils;
// Copy from galaxy sdk ${com.alibaba.android.galaxy.utils.ClassUtils}
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import com.alibaba.android.arouter.launcher.ARouter;
import com.alibaba.android.arouter.thread.DefaultPoolExecutor;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import dalvik.system.DexFile;
/**
* Scanner, find out class with any conditions, copy from google source code.
*
* @author 正纬 Contact me.
* @version 1.0
* @since 16/6/27 下午10:58
*/
public class ClassUtils {
private static final String EXTRACTED_NAME_EXT = ".classes";
private static final String EXTRACTED_SUFFIX = ".zip";
private static final String SECONDARY_FOLDER_NAME = "code_cache" + File.separator + "secondary-dexes";
private static final String PREFS_FILE = "multidex.version";
private static final String KEY_DEX_NUMBER = "dex.number";
private static final int VM_WITH_MULTIDEX_VERSION_MAJOR = 2;
private static final int VM_WITH_MULTIDEX_VERSION_MINOR = 1;
private static SharedPreferences getMultiDexPreferences(Context context) {
return context.getSharedPreferences(PREFS_FILE, Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ? Context.MODE_PRIVATE : Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
}
/**
* 通过指定包名,扫描包下面包含的所有的ClassName
*
* @param context U know
* @param packageName 包名
* @return 所有class的集合
*/
public static Set getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set classNames = new HashSet<>();
List paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try {
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable ignore) {
Log.e("ARouter", "Scan map file in dex files made error.", ignore);
} finally {
if (null != dexfile) {
try {
dexfile.close();
} catch (Throwable ignore) {
}
}
parserCtl.countDown();
}
}
});
}
parserCtl.await();
Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
/**
* get all the dex path
*
* @param context the application context
* @return all the dex path
* @throws PackageManager.NameNotFoundException
* @throws IOException
*/
public static List getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
List sourcePaths = new ArrayList<>();
sourcePaths.add(applicationInfo.sourceDir); //add the default apk path
//the prefix of extracted file, ie: test.classes
String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
// 如果VM已经支持了MultiDex,就不要去Secondary Folder加载 Classesx.zip了,那里已经么有了
// 通过是否存在sp中的multidex.version是不准确的,因为从低版本升级上来的用户,是包含这个sp配置的
if (!isVMMultidexCapable()) {
//the total dex numbers
int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
//for each dex file, ie: test.classes2.zip, test.classes3.zip...
String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile()) {
sourcePaths.add(extractedFile.getAbsolutePath());
//we ignore the verify zip part
} else {
throw new IOException("Missing extracted secondary dex file '" + extractedFile.getPath() + "'");
}
}
}
if (ARouter.debuggable()) { // Search instant run support only debuggable
sourcePaths.addAll(tryLoadInstantRunDexFile(applicationInfo));
}
return sourcePaths;
}
/**
* Get instant run dex path, used to catch the branch usingApkSplits=false.
*/
private static List tryLoadInstantRunDexFile(ApplicationInfo applicationInfo) {
List instantRunSourcePaths = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && null != applicationInfo.splitSourceDirs) {
// add the split apk, normally for InstantRun, and newest version.
instantRunSourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));
Log.d(Consts.TAG, "Found InstantRun support");
} else {
try {
// This man is reflection from Google instant run sdk, he will tell me where the dex files go.
Class pathsByInstantRun = Class.forName("com.android.tools.fd.runtime.Paths");
Method getDexFileDirectory = pathsByInstantRun.getMethod("getDexFileDirectory", String.class);
String instantRunDexPath = (String) getDexFileDirectory.invoke(null, applicationInfo.packageName);
File instantRunFilePath = new File(instantRunDexPath);
if (instantRunFilePath.exists() && instantRunFilePath.isDirectory()) {
File[] dexFile = instantRunFilePath.listFiles();
for (File file : dexFile) {
if (null != file && file.exists() && file.isFile() && file.getName().endsWith(".dex")) {
instantRunSourcePaths.add(file.getAbsolutePath());
}
}
Log.d(Consts.TAG, "Found InstantRun support");
}
} catch (Exception e) {
Log.e(Consts.TAG, "InstantRun support error, " + e.getMessage());
}
}
return instantRunSourcePaths;
}
/**
* Identifies if the current VM has a native support for multidex, meaning there is no need for
* additional installation by this library.
*
* @return true if the VM handles multidex
*/
private static boolean isVMMultidexCapable() {
boolean isMultidexCapable = false;
String vmName = null;
try {
if (isYunOS()) { // YunOS需要特殊判断
vmName = "'YunOS'";
isMultidexCapable = Integer.valueOf(System.getProperty("ro.build.version.sdk")) >= 21;
} else { // 非YunOS原生Android
vmName = "'Android'";
String versionString = System.getProperty("java.vm.version");
if (versionString != null) {
Matcher matcher = Pattern.compile("(\\d+)\\.(\\d+)(\\.\\d+)?").matcher(versionString);
if (matcher.matches()) {
try {
int major = Integer.parseInt(matcher.group(1));
int minor = Integer.parseInt(matcher.group(2));
isMultidexCapable = (major > VM_WITH_MULTIDEX_VERSION_MAJOR)
|| ((major == VM_WITH_MULTIDEX_VERSION_MAJOR)
&& (minor >= VM_WITH_MULTIDEX_VERSION_MINOR));
} catch (NumberFormatException ignore) {
// let isMultidexCapable be false
}
}
}
}
} catch (Exception ignore) {
}
Log.i(Consts.TAG, "VM with name " + vmName + (isMultidexCapable ? " has multidex support" : " does not have multidex support"));
return isMultidexCapable;
}
/**
* 判断系统是否为YunOS系统
*/
private static boolean isYunOS() {
try {
String version = System.getProperty("ro.yunos.version");
String vmName = System.getProperty("java.vm.name");
return (vmName != null && vmName.toLowerCase().contains("lemur"))
|| (version != null && version.trim().length() > 0);
} catch (Exception ignore) {
return false;
}
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/utils/Consts.java
================================================
package com.alibaba.android.arouter.utils;
/**
* ARouter constants.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 9:38
*/
public final class Consts {
public static final String SDK_NAME = "ARouter";
public static final String TAG = SDK_NAME + "::";
public static final String SEPARATOR = "$$";
public static final String SUFFIX_ROOT = "Root";
public static final String SUFFIX_INTERCEPTORS = "Interceptors";
public static final String SUFFIX_PROVIDERS = "Providers";
public static final String SUFFIX_AUTOWIRED = SEPARATOR + SDK_NAME + SEPARATOR + "Autowired";
public static final String DOT = ".";
public static final String ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes";
public static final String AROUTER_SP_CACHE_KEY = "SP_AROUTER_CACHE";
public static final String AROUTER_SP_KEY_MAP = "ROUTER_MAP";
public static final String LAST_VERSION_NAME = "LAST_VERSION_NAME";
public static final String LAST_VERSION_CODE = "LAST_VERSION_CODE";
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/utils/DefaultLogger.java
================================================
package com.alibaba.android.arouter.utils;
import android.text.TextUtils;
import android.util.Log;
import com.alibaba.android.arouter.facade.template.ILogger;
/**
* Default logger
*
* @author zhilong Contact me.
* @version 1.0
* @since 2015-12-08 21:44:10
*/
public class DefaultLogger implements ILogger {
private static boolean isShowLog = false;
private static boolean isShowStackTrace = false;
private static boolean isMonitorMode = false;
private String defaultTag = "ARouter";
public void showLog(boolean showLog) {
isShowLog = showLog;
}
public void showStackTrace(boolean showStackTrace) {
isShowStackTrace = showStackTrace;
}
public void showMonitor(boolean showMonitor) {
isMonitorMode = showMonitor;
}
public DefaultLogger() {
}
public DefaultLogger(String defaultTag) {
this.defaultTag = defaultTag;
}
@Override
public void debug(String tag, String message) {
if (isShowLog) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.d(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));
}
}
@Override
public void info(String tag, String message) {
if (isShowLog) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.i(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));
}
}
@Override
public void warning(String tag, String message) {
if (isShowLog) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.w(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));
}
}
@Override
public void error(String tag, String message) {
if (isShowLog) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.e(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message + getExtInfo(stackTraceElement));
}
}
@Override
public void error(String tag, String message, Throwable e) {
if (isShowLog) {
Log.e(TextUtils.isEmpty(tag) ? getDefaultTag() : tag, message, e);
}
}
@Override
public void monitor(String message) {
if (isShowLog && isMonitorMode()) {
StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[3];
Log.d(defaultTag + "::monitor", message + getExtInfo(stackTraceElement));
}
}
@Override
public boolean isMonitorMode() {
return isMonitorMode;
}
@Override
public String getDefaultTag() {
return defaultTag;
}
public static String getExtInfo(StackTraceElement stackTraceElement) {
if (isShowStackTrace) {
String separator = " & ";
StringBuilder sb = new StringBuilder("[");
String threadName = Thread.currentThread().getName();
String fileName = stackTraceElement.getFileName();
String className = stackTraceElement.getClassName();
String methodName = stackTraceElement.getMethodName();
long threadID = Thread.currentThread().getId();
int lineNumber = stackTraceElement.getLineNumber();
sb.append("ThreadId=").append(threadID).append(separator);
sb.append("ThreadName=").append(threadName).append(separator);
sb.append("FileName=").append(fileName).append(separator);
sb.append("ClassName=").append(className).append(separator);
sb.append("MethodName=").append(methodName).append(separator);
sb.append("LineNumber=").append(lineNumber);
sb.append(" ] ");
return sb.toString();
}
return "";
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/utils/MapUtils.java
================================================
package com.alibaba.android.arouter.utils;
import java.util.Map;
/**
* Copy from apache common.
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/3/10 下午3:26
*/
public class MapUtils {
/**
* Null-safe check if the specified map is not empty.
*
* Null returns false.
*
* @param map the map to check, may be null
* @return true if non-null and non-empty
* @since 3.2
*/
public static boolean isNotEmpty(final Map,?> map) {
return !isEmpty(map);
}
/**
* Null-safe check if the specified map is empty.
*
* Null returns true.
*
* @param map the map to check, may be null
* @return true if empty or null
* @since 3.2
*/
public static boolean isEmpty(final Map,?> map) {
return map == null || map.isEmpty();
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/utils/PackageUtils.java
================================================
package com.alibaba.android.arouter.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import static com.alibaba.android.arouter.launcher.ARouter.logger;
import static com.alibaba.android.arouter.utils.Consts.AROUTER_SP_CACHE_KEY;
import static com.alibaba.android.arouter.utils.Consts.LAST_VERSION_CODE;
import static com.alibaba.android.arouter.utils.Consts.LAST_VERSION_NAME;
/**
* Android package utils
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/8/8 下午8:19
*/
public class PackageUtils {
private static String NEW_VERSION_NAME;
private static int NEW_VERSION_CODE;
public static boolean isNewVersion(Context context) {
PackageInfo packageInfo = getPackageInfo(context);
if (null != packageInfo) {
String versionName = packageInfo.versionName;
int versionCode = packageInfo.versionCode;
SharedPreferences sp = context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE);
if (!versionName.equals(sp.getString(LAST_VERSION_NAME, null)) || versionCode != sp.getInt(LAST_VERSION_CODE, -1)) {
// new version
NEW_VERSION_NAME = versionName;
NEW_VERSION_CODE = versionCode;
return true;
} else {
return false;
}
} else {
return true;
}
}
public static void updateVersion(Context context) {
if (!android.text.TextUtils.isEmpty(NEW_VERSION_NAME) && NEW_VERSION_CODE != 0) {
SharedPreferences sp = context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE);
sp.edit().putString(LAST_VERSION_NAME, NEW_VERSION_NAME).putInt(LAST_VERSION_CODE, NEW_VERSION_CODE).apply();
}
}
private static PackageInfo getPackageInfo(Context context) {
PackageInfo packageInfo = null;
try {
packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_CONFIGURATIONS);
} catch (Exception ex) {
logger.error(Consts.TAG, "Get package info error.");
}
return packageInfo;
}
}
================================================
FILE: arouter-api/src/main/java/com/alibaba/android/arouter/utils/TextUtils.java
================================================
package com.alibaba.android.arouter.utils;
import android.net.Uri;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Text utils
*
* @author Alex Contact me.
* @version 1.0
* @since 16/9/9 14:40
*/
public class TextUtils {
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
/**
* Print thread stack
*/
public static String formatStackTrace(StackTraceElement[] stackTrace) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : stackTrace) {
sb.append(" at ").append(element.toString());
sb.append("\n");
}
return sb.toString();
}
/**
* Split query parameters
* @param rawUri raw uri
* @return map with params
*/
public static Map splitQueryParameters(Uri rawUri) {
String query = rawUri.getEncodedQuery();
if (query == null) {
return Collections.emptyMap();
}
Map paramMap = new LinkedHashMap<>();
int start = 0;
do {
int next = query.indexOf('&', start);
int end = (next == -1) ? query.length() : next;
int separator = query.indexOf('=', start);
if (separator > end || separator == -1) {
separator = end;
}
String name = query.substring(start, separator);
if (!android.text.TextUtils.isEmpty(name)) {
String value = (separator == end ? "" : query.substring(separator + 1, end));
paramMap.put(Uri.decode(name), Uri.decode(value));
}
// Move start to end of name.
start = end + 1;
} while (start < query.length());
return Collections.unmodifiableMap(paramMap);
}
/**
* Split key with |
*
* @param key raw key
* @return left key
*/
public static String getLeft(String key) {
if (key.contains("|") && !key.endsWith("|")) {
return key.substring(0, key.indexOf("|"));
} else {
return key;
}
}
/**
* Split key with |
*
* @param key raw key
* @return right key
*/
public static String getRight(String key) {
if (key.contains("|") && !key.startsWith("|")) {
return key.substring(key.indexOf("|") + 1);
} else {
return key;
}
}
}
================================================
FILE: arouter-compiler/build.gradle
================================================
apply plugin: 'java'
compileJava {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
dependencies {
implementation 'com.alibaba:arouter-annotation:1.0.6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
compileOnly 'com.google.auto.service:auto-service-annotations:1.0-rc7'
implementation 'com.squareup:javapoet:1.8.0'
implementation 'org.apache.commons:commons-lang3:3.5'
implementation 'org.apache.commons:commons-collections4:4.1'
implementation 'com.alibaba:fastjson:1.2.69'
}
apply from: rootProject.file('gradle/publish.gradle')
================================================
FILE: arouter-compiler/gradle.properties
================================================
POM_NAME=ARouter Compiler
POM_ARTIFACT_ID=arouter-compiler
POM_PACKAGING=jar
POM_DESCRIPTION=Compiler for ARouter to find route
VERSION_NAME=1.5.2
================================================
FILE: arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/entity/RouteDoc.java
================================================
package com.alibaba.android.arouter.compiler.entity;
import com.alibaba.fastjson.annotation.JSONField;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
/**
* Description route info, used for generate router map
*
* @author zhilong Contact me.
* @version 1.0
* @since 2018/8/9 11:59 AM
*/
public class RouteDoc {
@JSONField(ordinal = 1)
private String group;
@JSONField(ordinal = 2)
private String path;
@JSONField(ordinal = 3)
private String description;
@JSONField(ordinal = 4)
private String prototype;
@JSONField(ordinal = 5)
private String className;
@JSONField(ordinal = 6)
private String type;
@JSONField(ordinal = 7)
private int mark;
@JSONField(ordinal = 8)
private List params;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getPrototype() {
return prototype;
}
public void setPrototype(String prototype) {
this.prototype = prototype;
}
public void addPrototype(String prototype) {
if (StringUtils.isNotEmpty(getPrototype())) {
setPrototype(prototype);
} else {
setPrototype(getPrototype() + ", " + prototype);
}
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
if (StringUtils.isNotEmpty(description)) {
this.description = description;
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
public List getParams() {
return params;
}
public void setParams(List params) {
this.params = params;
}
public static class Param {
@JSONField(ordinal = 1)
private String key;
@JSONField(ordinal = 2)
private String type;
@JSONField(ordinal = 3)
private String description;
@JSONField(ordinal = 4)
private boolean required;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
if (StringUtils.isNotEmpty(description)) {
this.description = description;
}
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
}
}
================================================
FILE: arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/processor/AutowiredProcessor.java
================================================
package com.alibaba.android.arouter.compiler.processor;
import com.alibaba.android.arouter.compiler.utils.Consts;
import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.facade.enums.TypeKind;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import static com.alibaba.android.arouter.compiler.utils.Consts.*;
import static javax.lang.model.element.Modifier.PUBLIC;
/**
* Processor used to create autowired helper
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/20 下午5:56
*/
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})
public class AutowiredProcessor extends BaseProcessor {
private Map> parentAndChild = new HashMap<>(); // Contain field need autowired and his super class.
private static final ClassName ARouterClass = ClassName.get("com.alibaba.android.arouter.launcher", "ARouter");
private static final ClassName AndroidLog = ClassName.get("android.util", "Log");
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
logger.info(">>> AutowiredProcessor init. <<<");
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (CollectionUtils.isNotEmpty(set)) {
try {
logger.info(">>> Found autowired field, start... <<<");
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
generateHelper();
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void generateHelper() throws IOException, IllegalAccessException {
TypeElement type_ISyringe = elementUtils.getTypeElement(ISYRINGE);
TypeElement type_JsonService = elementUtils.getTypeElement(JSON_SERVICE);
TypeMirror iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();
TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// Build input param name.
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
if (MapUtils.isNotEmpty(parentAndChild)) {
for (Map.Entry> entry : parentAndChild.entrySet()) {
// Build method : 'inject'
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(objectParamSpec);
TypeElement parent = entry.getKey();
List childs = entry.getValue();
String qualifiedName = parent.getQualifiedName().toString();
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;
logger.info(">>> Start process " + childs.size() + " field in " + parent.getSimpleName() + " ... <<<");
TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_ISyringe))
.addModifiers(PUBLIC);
FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
helper.addField(jsonServiceField);
injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class)", ARouterClass, ClassName.get(type_JsonService));
injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));
// Generate method body, start inject.
for (Element element : childs) {
Autowired fieldConfig = element.getAnnotation(Autowired.class);
String fieldName = element.getSimpleName().toString();
if (types.isSubtype(element.asType(), iProvider)) { // It's provider
if ("".equals(fieldConfig.name())) { // User has not set service path, then use byType.
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
ARouterClass,
ClassName.get(element.asType())
);
} else { // use byName
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation()",
ClassName.get(element.asType()),
ARouterClass,
fieldConfig.name()
);
}
// Validator
if (fieldConfig.required()) {
injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
injectMethodBuilder.addStatement(
"throw new RuntimeException(\"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
} else { // It's normal intent value
String originalValue = "substitute." + fieldName;
String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
boolean isActivity = false;
if (types.isSubtype(parent.asType(), activityTm)) { // Activity, then use getIntent()
isActivity = true;
statement += "getIntent().";
} else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) { // Fragment, then use getArguments()
statement += "getArguments().";
} else {
throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
}
statement = buildStatement(originalValue, statement, typeUtils.typeExchange(element), isActivity, isKtClass(parent));
if (statement.startsWith("serializationService.")) { // Not mortals
injectMethodBuilder.beginControlFlow("if (null != serializationService)");
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = " + statement,
(StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
ClassName.get(element.asType())
);
injectMethodBuilder.nextControlFlow("else");
injectMethodBuilder.addStatement(
"$T.e(\"" + Consts.TAG + "\", \"You want automatic inject the field '" + fieldName + "' in class '$T' , then you should implement 'SerializationService' to support object auto inject!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
} else {
injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
}
// Validator
if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) { // Primitive wont be check.
injectMethodBuilder.beginControlFlow("if (null == substitute." + fieldName + ")");
injectMethodBuilder.addStatement(
"$T.e(\"" + Consts.TAG + "\", \"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
}
}
helper.addMethod(injectMethodBuilder.build());
// Generate autowire helper
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);
logger.info(">>> " + parent.getSimpleName() + " has been processed, " + fileName + " has been generated. <<<");
}
logger.info(">>> Autowired processor stop. <<<");
}
}
private boolean isKtClass(Element element) {
for (AnnotationMirror annotationMirror : elementUtils.getAllAnnotationMirrors(element)) {
if (annotationMirror.getAnnotationType().toString().contains("kotlin")) {
return true;
}
}
return false;
}
private String buildCastCode(Element element) {
if (typeUtils.typeExchange(element) == TypeKind.SERIALIZABLE.ordinal()) {
return CodeBlock.builder().add("($T) ", ClassName.get(element.asType())).build().toString();
}
return "";
}
/**
* Build param inject statement
*/
private String buildStatement(String originalValue, String statement, int type, boolean isActivity, boolean isKt) {
switch (TypeKind.values()[type]) {
case BOOLEAN:
statement += "getBoolean" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
break;
case BYTE:
statement += "getByte" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
break;
case SHORT:
statement += "getShort" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
break;
case INT:
statement += "getInt" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
break;
case LONG:
statement += "getLong" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
break;
case CHAR:
statement += "getChar" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
break;
case FLOAT:
statement += "getFloat" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
break;
case DOUBLE:
statement += "getDouble" + (isActivity ? "Extra" : "") + "($S, " + originalValue + ")";
break;
case STRING:
statement += (isActivity ? ("getExtras() == null ? " + originalValue + " : substitute.getIntent().getExtras().getString($S") : ("getString($S")) + ", " + originalValue + ")";
break;
case SERIALIZABLE:
statement += (isActivity ? ("getSerializableExtra($S)") : ("getSerializable($S)"));
break;
case PARCELABLE:
statement += (isActivity ? ("getParcelableExtra($S)") : ("getParcelable($S)"));
break;
case OBJECT:
statement = "serializationService.parseObject(substitute." + (isActivity ? "getIntent()." : "getArguments().") + (isActivity ? "getStringExtra($S)" : "getString($S)") + ", new " + TYPE_WRAPPER + "<$T>(){}.getType())";
break;
}
return statement;
}
/**
* Categories field, find his papa.
*
* @param elements Field need autowired
*/
private void categories(Set extends Element> elements) throws IllegalAccessException {
if (CollectionUtils.isNotEmpty(elements)) {
for (Element element : elements) {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
if (element.getModifiers().contains(Modifier.PRIVATE)) {
throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
+ element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
}
if (parentAndChild.containsKey(enclosingElement)) { // Has categries
parentAndChild.get(enclosingElement).add(element);
} else {
List childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
}
}
logger.info("categories finished.");
}
}
}
================================================
FILE: arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/processor/BaseProcessor.java
================================================
package com.alibaba.android.arouter.compiler.processor;
import com.alibaba.android.arouter.compiler.utils.Logger;
import com.alibaba.android.arouter.compiler.utils.TypeUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static com.alibaba.android.arouter.compiler.utils.Consts.*;
import static com.alibaba.android.arouter.compiler.utils.Consts.NO_MODULE_NAME_TIPS;
/**
* Base Processor
*
* @author zhilong [Contact me.](mailto:zhilong.lzl@alibaba-inc.com)
* @version 1.0
* @since 2019-03-01 12:31
*/
public abstract class BaseProcessor extends AbstractProcessor {
Filer mFiler;
Logger logger;
Types types;
Elements elementUtils;
TypeUtils typeUtils;
// Module name, maybe its 'app' or others
String moduleName = null;
// If need generate router doc
boolean generateDoc;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
types = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
typeUtils = new TypeUtils(types, elementUtils);
logger = new Logger(processingEnv.getMessager());
// Attempt to get user configuration [moduleName]
Map options = processingEnv.getOptions();
if (MapUtils.isNotEmpty(options)) {
moduleName = options.get(KEY_MODULE_NAME);
generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_NAME));
}
if (StringUtils.isNotEmpty(moduleName)) {
moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");
logger.info("The user has configuration the module name, it was [" + moduleName + "]");
} else {
logger.error(NO_MODULE_NAME_TIPS);
throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
}
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set getSupportedOptions() {
return new HashSet() {{
this.add(KEY_MODULE_NAME);
this.add(KEY_GENERATE_DOC_NAME);
}};
}
}
================================================
FILE: arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/processor/InterceptorProcessor.java
================================================
package com.alibaba.android.arouter.compiler.processor;
import com.alibaba.android.arouter.compiler.utils.Consts;
import com.alibaba.android.arouter.facade.annotation.Interceptor;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import org.apache.commons.collections4.CollectionUtils;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import static com.alibaba.android.arouter.compiler.utils.Consts.*;
import static javax.lang.model.element.Modifier.PUBLIC;
/**
* Process the annotation of #{@link Interceptor}
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/23 14:11
*/
@AutoService(Processor.class)
@SupportedAnnotationTypes(ANNOTATION_TYPE_INTECEPTOR)
public class InterceptorProcessor extends BaseProcessor {
private Map interceptors = new TreeMap<>();
private TypeMirror iInterceptor = null;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
iInterceptor = elementUtils.getTypeElement(Consts.IINTERCEPTOR).asType();
logger.info(">>> InterceptorProcessor init. <<<");
}
/**
* {@inheritDoc}
*
* @param annotations
* @param roundEnv
*/
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set extends Element> elements = roundEnv.getElementsAnnotatedWith(Interceptor.class);
try {
parseInterceptors(elements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
/**
* Parse interceptor.
*
* @param elements elements of interceptor.
*/
private void parseInterceptors(Set extends Element> elements) throws IOException {
if (CollectionUtils.isNotEmpty(elements)) {
logger.info(">>> Found interceptors, size is " + elements.size() + " <<<");
// Verify and cache, sort incidentally.
for (Element element : elements) {
if (verify(element)) { // Check the interceptor meta
logger.info("A interceptor verify over, its " + element.asType());
Interceptor interceptor = element.getAnnotation(Interceptor.class);
Element lastInterceptor = interceptors.get(interceptor.priority());
if (null != lastInterceptor) { // Added, throw exceptions
throw new IllegalArgumentException(
String.format(Locale.getDefault(), "More than one interceptors use same priority [%d], They are [%s] and [%s].",
interceptor.priority(),
lastInterceptor.getSimpleName(),
element.getSimpleName())
);
}
interceptors.put(interceptor.priority(), element);
} else {
logger.error("A interceptor verify failed, its " + element.asType());
}
}
// Interface of ARouter.
TypeElement type_IInterceptor = elementUtils.getTypeElement(IINTERCEPTOR);
TypeElement type_IInterceptorGroup = elementUtils.getTypeElement(IINTERCEPTOR_GROUP);
/**
* Build input type, format as :
*
* ```Map>```
*/
ParameterizedTypeName inputMapTypeOfTollgate = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(Integer.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IInterceptor))
)
);
// Build input param name.
ParameterSpec tollgateParamSpec = ParameterSpec.builder(inputMapTypeOfTollgate, "interceptors").build();
// Build method : 'loadInto'
MethodSpec.Builder loadIntoMethodOfTollgateBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(tollgateParamSpec);
// Generate
if (null != interceptors && interceptors.size() > 0) {
// Build method body
for (Map.Entry entry : interceptors.entrySet()) {
loadIntoMethodOfTollgateBuilder.addStatement("interceptors.put(" + entry.getKey() + ", $T.class)", ClassName.get((TypeElement) entry.getValue()));
}
}
// Write to disk(Write file even interceptors is empty.)
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(NAME_OF_INTERCEPTOR + SEPARATOR + moduleName)
.addModifiers(PUBLIC)
.addJavadoc(WARNING_TIPS)
.addMethod(loadIntoMethodOfTollgateBuilder.build())
.addSuperinterface(ClassName.get(type_IInterceptorGroup))
.build()
).build().writeTo(mFiler);
logger.info(">>> Interceptor group write over. <<<");
}
}
/**
* Verify inteceptor meta
*
* @param element Interceptor taw type
* @return verify result
*/
private boolean verify(Element element) {
Interceptor interceptor = element.getAnnotation(Interceptor.class);
// It must be implement the interface IInterceptor and marked with annotation Interceptor.
return null != interceptor && ((TypeElement) element).getInterfaces().contains(iInterceptor);
}
}
================================================
FILE: arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/processor/RouteProcessor.java
================================================
package com.alibaba.android.arouter.compiler.processor;
import com.alibaba.android.arouter.compiler.entity.RouteDoc;
import com.alibaba.android.arouter.compiler.utils.Consts;
import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.facade.enums.RouteType;
import com.alibaba.android.arouter.facade.enums.TypeKind;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.tools.StandardLocation;
import static com.alibaba.android.arouter.compiler.utils.Consts.ACTIVITY;
import static com.alibaba.android.arouter.compiler.utils.Consts.ANNOTATION_TYPE_AUTOWIRED;
import static com.alibaba.android.arouter.compiler.utils.Consts.ANNOTATION_TYPE_ROUTE;
import static com.alibaba.android.arouter.compiler.utils.Consts.FRAGMENT;
import static com.alibaba.android.arouter.compiler.utils.Consts.IPROVIDER_GROUP;
import static com.alibaba.android.arouter.compiler.utils.Consts.IROUTE_GROUP;
import static com.alibaba.android.arouter.compiler.utils.Consts.ITROUTE_ROOT;
import static com.alibaba.android.arouter.compiler.utils.Consts.METHOD_LOAD_INTO;
import static com.alibaba.android.arouter.compiler.utils.Consts.NAME_OF_GROUP;
import static com.alibaba.android.arouter.compiler.utils.Consts.NAME_OF_PROVIDER;
import static com.alibaba.android.arouter.compiler.utils.Consts.NAME_OF_ROOT;
import static com.alibaba.android.arouter.compiler.utils.Consts.PACKAGE_OF_GENERATE_DOCS;
import static com.alibaba.android.arouter.compiler.utils.Consts.PACKAGE_OF_GENERATE_FILE;
import static com.alibaba.android.arouter.compiler.utils.Consts.SEPARATOR;
import static com.alibaba.android.arouter.compiler.utils.Consts.SERVICE;
import static com.alibaba.android.arouter.compiler.utils.Consts.WARNING_TIPS;
import static javax.lang.model.element.Modifier.PUBLIC;
/**
* A processor used for find route.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/15 下午10:08
*/
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor {
private Map> groupMap = new HashMap<>(); // ModuleName and routeMeta.
private Map rootMap = new TreeMap<>(); // Map of root metas, used for generate class file in order.
private TypeMirror iProvider = null;
private Writer docWriter; // Writer used for write doc
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
if (generateDoc) {
try {
docWriter = mFiler.createResource(
StandardLocation.SOURCE_OUTPUT,
PACKAGE_OF_GENERATE_DOCS,
"arouter-map-of-" + moduleName + ".json"
).openWriter();
} catch (IOException e) {
logger.error("Create doc writer failed, because " + e.getMessage());
}
}
iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
logger.info(">>> RouteProcessor init. <<<");
}
/**
* {@inheritDoc}
*
* @param annotations
* @param roundEnv
*/
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
try {
logger.info(">>> Found routes, start... <<<");
this.parseRoutes(routeElements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void parseRoutes(Set extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
// prepare the type an so on.
logger.info(">>> Found routes, size is " + routeElements.size() + " <<<");
rootMap.clear();
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// Interface of ARouter
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
ClassName routeTypeCn = ClassName.get(RouteType.class);
/*
Build input type, format as :
```Map>```
*/
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*
```Map```
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
/*
Build input param name.
*/
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!
/*
Build method : 'loadInto'
*/
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
// Follow a sequence, find out metas of group first, generate java file, then statistics them as root.
for (Element element : routeElements) {
TypeMirror tm = element.asType();
Route route = element.getAnnotation(Route.class);
RouteMeta routeMeta;
// Activity or Fragment
if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
// Get all fields annotation by @Autowired
Map paramsType = new HashMap<>();
Map injectConfig = new HashMap<>();
injectParamCollector(element, paramsType, injectConfig);
if (types.isSubtype(tm, type_Activity)) {
// Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
} else {
// Fragment
logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
}
routeMeta.setInjectConfig(injectConfig);
} else if (types.isSubtype(tm, iProvider)) { // IProvider
logger.info(">>> Found provider route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_Service)) { // Service
logger.info(">>> Found service route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
} else {
throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");
}
categories(routeMeta);
}
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
Map> docSource = new HashMap<>();
// Start generate java source, structure is divided into upper and lower levels, used for demand initialization.
for (Map.Entry> entry : groupMap.entrySet()) {
String groupName = entry.getKey();
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
List routeDocList = new ArrayList<>();
// Build group method body
Set groupData = entry.getValue();
for (RouteMeta routeMeta : groupData) {
RouteDoc routeDoc = extractDocInfo(routeMeta);
ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
switch (routeMeta.getType()) {
case PROVIDER: // Need cache provider's super class
List extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces) {
routeDoc.addPrototype(tm.toString());
if (types.isSameType(tm, iProvider)) { // Its implements iProvider interface himself.
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
}
}
break;
default:
break;
}
// Make map body for paramsType
StringBuilder mapBodyBuilder = new StringBuilder();
Map paramsType = routeMeta.getParamsType();
Map injectConfigs = routeMeta.getInjectConfig();
if (MapUtils.isNotEmpty(paramsType)) {
List paramList = new ArrayList<>();
for (Map.Entry types : paramsType.entrySet()) {
mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); ");
RouteDoc.Param param = new RouteDoc.Param();
Autowired injectConfig = injectConfigs.get(types.getKey());
param.setKey(types.getKey());
param.setType(TypeKind.values()[types.getValue()].name().toLowerCase());
param.setDescription(injectConfig.desc());
param.setRequired(injectConfig.required());
paramList.add(param);
}
routeDoc.setParams(paramList);
}
String mapBody = mapBodyBuilder.toString();
loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
routeMeta.getPath(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath().toLowerCase(),
routeMeta.getGroup().toLowerCase());
routeDoc.setClassName(className.toString());
routeDocList.add(routeDoc);
}
// Generate groups
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated group: " + groupName + "<<<");
rootMap.put(groupName, groupFileName);
docSource.put(groupName, routeDocList);
}
if (MapUtils.isNotEmpty(rootMap)) {
// Generate root meta by group name, it must be generated before root, then I can find out the class of group.
for (Map.Entry entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
// Output route doc
if (generateDoc) {
docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
docWriter.flush();
docWriter.close();
}
// Write provider into disk
String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(providerMapFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IProviderGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfProviderBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated provider map, name is " + providerMapFileName + " <<<");
// Write root meta into disk.
String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated root, name is " + rootFileName + " <<<");
}
}
/**
* Recursive inject config collector.
*
* @param element current element.
*/
private void injectParamCollector(Element element, Map paramsType, Map injectConfig) {
for (Element field : element.getEnclosedElements()) {
if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !types.isSubtype(field.asType(), iProvider)) {
// It must be field, then it has annotation, but it not be provider.
Autowired paramConfig = field.getAnnotation(Autowired.class);
String injectName = StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name();
paramsType.put(injectName, typeUtils.typeExchange(field));
injectConfig.put(injectName, paramConfig);
}
}
// if has parent?
TypeMirror parent = ((TypeElement) element).getSuperclass();
if (parent instanceof DeclaredType) {
Element parentElement = ((DeclaredType) parent).asElement();
if (parentElement instanceof TypeElement && !((TypeElement) parentElement).getQualifiedName().toString().startsWith("android")) {
injectParamCollector(parentElement, paramsType, injectConfig);
}
}
}
/**
* Extra doc info from route meta
*
* @param routeMeta meta
* @return doc
*/
private RouteDoc extractDocInfo(RouteMeta routeMeta) {
RouteDoc routeDoc = new RouteDoc();
routeDoc.setGroup(routeMeta.getGroup());
routeDoc.setPath(routeMeta.getPath());
routeDoc.setDescription(routeMeta.getName());
routeDoc.setType(routeMeta.getType().name().toLowerCase());
routeDoc.setMark(routeMeta.getExtra());
return routeDoc;
}
/**
* Sort metas in group.
*
* @param routeMete metas.
*/
private void categories(RouteMeta routeMete) {
if (routeVerify(routeMete)) {
logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");
Set routeMetas = groupMap.get(routeMete.getGroup());
if (CollectionUtils.isEmpty(routeMetas)) {
Set routeMetaSet = new TreeSet<>(new Comparator() {
@Override
public int compare(RouteMeta r1, RouteMeta r2) {
try {
return r1.getPath().compareTo(r2.getPath());
} catch (NullPointerException npe) {
logger.error(npe.getMessage());
return 0;
}
}
});
routeMetaSet.add(routeMete);
groupMap.put(routeMete.getGroup(), routeMetaSet);
} else {
routeMetas.add(routeMete);
}
} else {
logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<<");
}
}
/**
* Verify the route meta
*
* @param meta raw meta
*/
private boolean routeVerify(RouteMeta meta) {
String path = meta.getPath();
if (StringUtils.isEmpty(path) || !path.startsWith("/")) { // The path must be start with '/' and not empty!
return false;
}
if (StringUtils.isEmpty(meta.getGroup())) { // Use default group(the first word in path)
try {
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (StringUtils.isEmpty(defaultGroup)) {
return false;
}
meta.setGroup(defaultGroup);
return true;
} catch (Exception e) {
logger.error("Failed to extract default group! " + e.getMessage());
return false;
}
}
return true;
}
}
================================================
FILE: arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/utils/Consts.java
================================================
package com.alibaba.android.arouter.compiler.utils;
/**
* Some consts used in processors
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/24 20:18
*/
public class Consts {
// Generate
public static final String SEPARATOR = "$$";
public static final String PROJECT = "ARouter";
public static final String TAG = PROJECT + "::";
public static final String WARNING_TIPS = "DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER.";
public static final String METHOD_LOAD_INTO = "loadInto";
public static final String METHOD_INJECT = "inject";
public static final String NAME_OF_ROOT = PROJECT + SEPARATOR + "Root";
public static final String NAME_OF_PROVIDER = PROJECT + SEPARATOR + "Providers";
public static final String NAME_OF_GROUP = PROJECT + SEPARATOR + "Group" + SEPARATOR;
public static final String NAME_OF_INTERCEPTOR = PROJECT + SEPARATOR + "Interceptors";
public static final String NAME_OF_AUTOWIRED = SEPARATOR + PROJECT + SEPARATOR + "Autowired";
public static final String PACKAGE_OF_GENERATE_FILE = "com.alibaba.android.arouter.routes";
public static final String PACKAGE_OF_GENERATE_DOCS = "com.alibaba.android.arouter.docs";
// System interface
public static final String ACTIVITY = "android.app.Activity";
public static final String FRAGMENT = "android.app.Fragment";
public static final String FRAGMENT_V4 = "android.support.v4.app.Fragment";
public static final String SERVICE = "android.app.Service";
public static final String PARCELABLE = "android.os.Parcelable";
// Java type
private static final String LANG = "java.lang";
public static final String BYTE = LANG + ".Byte";
public static final String SHORT = LANG + ".Short";
public static final String INTEGER = LANG + ".Integer";
public static final String LONG = LANG + ".Long";
public static final String FLOAT = LANG + ".Float";
public static final String DOUBEL = LANG + ".Double";
public static final String BOOLEAN = LANG + ".Boolean";
public static final String CHAR = LANG + ".Character";
public static final String STRING = LANG + ".String";
public static final String SERIALIZABLE = "java.io.Serializable";
// Custom interface
private static final String FACADE_PACKAGE = "com.alibaba.android.arouter.facade";
private static final String TEMPLATE_PACKAGE = ".template";
private static final String SERVICE_PACKAGE = ".service";
private static final String MODEL_PACKAGE = ".model";
public static final String IPROVIDER = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IProvider";
public static final String IPROVIDER_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IProviderGroup";
public static final String IINTERCEPTOR = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IInterceptor";
public static final String IINTERCEPTOR_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IInterceptorGroup";
public static final String ITROUTE_ROOT = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IRouteRoot";
public static final String IROUTE_GROUP = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".IRouteGroup";
public static final String ISYRINGE = FACADE_PACKAGE + TEMPLATE_PACKAGE + ".ISyringe";
public static final String JSON_SERVICE = FACADE_PACKAGE + SERVICE_PACKAGE + ".SerializationService";
public static final String TYPE_WRAPPER = FACADE_PACKAGE + MODEL_PACKAGE + ".TypeWrapper";
// Log
static final String PREFIX_OF_LOGGER = PROJECT + "::Compiler ";
public static final String NO_MODULE_NAME_TIPS = "These no module name, at 'build.gradle', like :\n" +
"android {\n" +
" defaultConfig {\n" +
" ...\n" +
" javaCompileOptions {\n" +
" annotationProcessorOptions {\n" +
" arguments = [AROUTER_MODULE_NAME: project.getName()]\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n";
// Options of processor
public static final String KEY_MODULE_NAME = "AROUTER_MODULE_NAME";
public static final String KEY_GENERATE_DOC_NAME = "AROUTER_GENERATE_DOC";
public static final String VALUE_ENABLE = "enable";
// Annotation type
public static final String ANNOTATION_TYPE_INTECEPTOR = FACADE_PACKAGE + ".annotation.Interceptor";
public static final String ANNOTATION_TYPE_ROUTE = FACADE_PACKAGE + ".annotation.Route";
public static final String ANNOTATION_TYPE_AUTOWIRED = FACADE_PACKAGE + ".annotation.Autowired";
}
================================================
FILE: arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/utils/Logger.java
================================================
package com.alibaba.android.arouter.compiler.utils;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.processing.Messager;
import javax.tools.Diagnostic;
/**
* Simplify the message print.
*
* @author Alex Contact me.
* @version 1.0
* @since 16/8/22 上午11:48
*/
public class Logger {
private Messager msg;
public Logger(Messager messager) {
msg = messager;
}
/**
* Print info log.
*/
public void info(CharSequence info) {
if (StringUtils.isNotEmpty(info)) {
msg.printMessage(Diagnostic.Kind.NOTE, Consts.PREFIX_OF_LOGGER + info);
}
}
public void error(CharSequence error) {
if (StringUtils.isNotEmpty(error)) {
msg.printMessage(Diagnostic.Kind.ERROR, Consts.PREFIX_OF_LOGGER + "An exception is encountered, [" + error + "]");
}
}
public void error(Throwable error) {
if (null != error) {
msg.printMessage(Diagnostic.Kind.ERROR, Consts.PREFIX_OF_LOGGER + "An exception is encountered, [" + error.getMessage() + "]" + "\n" + formatStackTrace(error.getStackTrace()));
}
}
public void warning(CharSequence warning) {
if (StringUtils.isNotEmpty(warning)) {
msg.printMessage(Diagnostic.Kind.WARNING, Consts.PREFIX_OF_LOGGER + warning);
}
}
private String formatStackTrace(StackTraceElement[] stackTrace) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : stackTrace) {
sb.append(" at ").append(element.toString());
sb.append("\n");
}
return sb.toString();
}
}
================================================
FILE: arouter-compiler/src/main/java/com/alibaba/android/arouter/compiler/utils/TypeUtils.java
================================================
package com.alibaba.android.arouter.compiler.utils;
import com.alibaba.android.arouter.facade.enums.TypeKind;
import javax.lang.model.element.Element;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import static com.alibaba.android.arouter.compiler.utils.Consts.BOOLEAN;
import static com.alibaba.android.arouter.compiler.utils.Consts.BYTE;
import static com.alibaba.android.arouter.compiler.utils.Consts.DOUBEL;
import static com.alibaba.android.arouter.compiler.utils.Consts.FLOAT;
import static com.alibaba.android.arouter.compiler.utils.Consts.INTEGER;
import static com.alibaba.android.arouter.compiler.utils.Consts.LONG;
import static com.alibaba.android.arouter.compiler.utils.Consts.PARCELABLE;
import static com.alibaba.android.arouter.compiler.utils.Consts.SERIALIZABLE;
import static com.alibaba.android.arouter.compiler.utils.Consts.SHORT;
import static com.alibaba.android.arouter.compiler.utils.Consts.STRING;
import static com.alibaba.android.arouter.compiler.utils.Consts.CHAR;
/**
* Utils for type exchange
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/2/21 下午1:06
*/
public class TypeUtils {
private Types types;
private TypeMirror parcelableType;
private TypeMirror serializableType;
public TypeUtils(Types types, Elements elements) {
this.types = types;
parcelableType = elements.getTypeElement(PARCELABLE).asType();
serializableType = elements.getTypeElement(SERIALIZABLE).asType();
}
/**
* Diagnostics out the true java type
*
* @param element Raw type
* @return Type class of java
*/
public int typeExchange(Element element) {
TypeMirror typeMirror = element.asType();
// Primitive
if (typeMirror.getKind().isPrimitive()) {
return element.asType().getKind().ordinal();
}
switch (typeMirror.toString()) {
case BYTE:
return TypeKind.BYTE.ordinal();
case SHORT:
return TypeKind.SHORT.ordinal();
case INTEGER:
return TypeKind.INT.ordinal();
case LONG:
return TypeKind.LONG.ordinal();
case FLOAT:
return TypeKind.FLOAT.ordinal();
case DOUBEL:
return TypeKind.DOUBLE.ordinal();
case BOOLEAN:
return TypeKind.BOOLEAN.ordinal();
case CHAR:
return TypeKind.CHAR.ordinal();
case STRING:
return TypeKind.STRING.ordinal();
default:
// Other side, maybe the PARCELABLE or SERIALIZABLE or OBJECT.
if (types.isSubtype(typeMirror, parcelableType)) {
// PARCELABLE
return TypeKind.PARCELABLE.ordinal();
} else if (types.isSubtype(typeMirror, serializableType)) {
// SERIALIZABLE
return TypeKind.SERIALIZABLE.ordinal();
} else {
return TypeKind.OBJECT.ordinal();
}
}
}
}
================================================
FILE: arouter-compiler/src/main/resources/META-INF/gradle/incremental.annotation.processors
================================================
com.alibaba.android.arouter.compiler.processor.RouteProcessor,aggregating
com.alibaba.android.arouter.compiler.processor.AutowiredProcessor,aggregating
com.alibaba.android.arouter.compiler.processor.InterceptorProcessor,aggregating
================================================
FILE: arouter-gradle-plugin/build.gradle
================================================
apply plugin: 'groovy'
dependencies {
compile gradleApi()
compile localGroovy()
}
dependencies {
compile 'com.android.tools.build:gradle:2.1.3'
}
apply from: rootProject.file('gradle/publish.gradle')
================================================
FILE: arouter-gradle-plugin/gradle.properties
================================================
POM_NAME=ARouter Register
POM_ARTIFACT_ID=arouter-register
POM_PACKAGING=jar
POM_DESCRIPTION=Gradle plugin used for arouter route map register
VERSION_NAME=1.0.2
================================================
FILE: arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/core/RegisterCodeGenerator.groovy
================================================
package com.alibaba.android.arouter.register.core
import com.alibaba.android.arouter.register.utils.Logger
import com.alibaba.android.arouter.register.utils.ScanSetting
import org.apache.commons.io.IOUtils
import org.objectweb.asm.*
import java.util.jar.JarEntry
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry
/**
* generate register code into LogisticsCenter.class
* @author billy.qi email: qiyilike@163.com
*/
class RegisterCodeGenerator {
ScanSetting extension
private RegisterCodeGenerator(ScanSetting extension) {
this.extension = extension
}
static void insertInitCodeTo(ScanSetting registerSetting) {
if (registerSetting != null && !registerSetting.classList.isEmpty()) {
RegisterCodeGenerator processor = new RegisterCodeGenerator(registerSetting)
File file = RegisterTransform.fileContainsInitClass
if (file.getName().endsWith('.jar'))
processor.insertInitCodeIntoJarFile(file)
}
}
/**
* generate code into jar file
* @param jarFile the jar file which contains LogisticsCenter.class
* @return
*/
private File insertInitCodeIntoJarFile(File jarFile) {
if (jarFile) {
def optJar = new File(jarFile.getParent(), jarFile.name + ".opt")
if (optJar.exists())
optJar.delete()
def file = new JarFile(jarFile)
Enumeration enumeration = file.entries()
JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(optJar))
while (enumeration.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) enumeration.nextElement()
String entryName = jarEntry.getName()
ZipEntry zipEntry = new ZipEntry(entryName)
InputStream inputStream = file.getInputStream(jarEntry)
jarOutputStream.putNextEntry(zipEntry)
if (ScanSetting.GENERATE_TO_CLASS_FILE_NAME == entryName) {
Logger.i('Insert init code to class >> ' + entryName)
def bytes = referHackWhenInit(inputStream)
jarOutputStream.write(bytes)
} else {
jarOutputStream.write(IOUtils.toByteArray(inputStream))
}
inputStream.close()
jarOutputStream.closeEntry()
}
jarOutputStream.close()
file.close()
if (jarFile.exists()) {
jarFile.delete()
}
optJar.renameTo(jarFile)
}
return jarFile
}
//refer hack class when object init
private byte[] referHackWhenInit(InputStream inputStream) {
ClassReader cr = new ClassReader(inputStream)
ClassWriter cw = new ClassWriter(cr, 0)
ClassVisitor cv = new MyClassVisitor(Opcodes.ASM5, cw)
cr.accept(cv, ClassReader.EXPAND_FRAMES)
return cw.toByteArray()
}
class MyClassVisitor extends ClassVisitor {
MyClassVisitor(int api, ClassVisitor cv) {
super(api, cv)
}
void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces)
}
@Override
MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions)
//generate code into this method
if (name == ScanSetting.GENERATE_TO_METHOD_NAME) {
mv = new RouteMethodVisitor(Opcodes.ASM5, mv)
}
return mv
}
}
class RouteMethodVisitor extends MethodVisitor {
RouteMethodVisitor(int api, MethodVisitor mv) {
super(api, mv)
}
@Override
void visitInsn(int opcode) {
//generate code before return
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
extension.classList.each { name ->
name = name.replaceAll("/", ".")
mv.visitLdcInsn(name)//类名
// generate invoke register method into LogisticsCenter.loadRouterMap()
mv.visitMethodInsn(Opcodes.INVOKESTATIC
, ScanSetting.GENERATE_TO_CLASS_NAME
, ScanSetting.REGISTER_METHOD_NAME
, "(Ljava/lang/String;)V"
, false)
}
}
super.visitInsn(opcode)
}
@Override
void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(maxStack + 4, maxLocals)
}
}
}
================================================
FILE: arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/core/RegisterTransform.groovy
================================================
package com.alibaba.android.arouter.register.core
import com.alibaba.android.arouter.register.utils.Logger
import com.alibaba.android.arouter.register.utils.ScanSetting
import com.alibaba.android.arouter.register.utils.ScanUtil
import com.android.build.api.transform.*
import com.android.build.gradle.internal.pipeline.TransformManager
import org.apache.commons.codec.digest.DigestUtils
import org.apache.commons.io.FileUtils
import org.gradle.api.Project
/**
* transform api
*
* 1. Scan all classes to find which classes implement the specified interface
* 2. Generate register code into class file: {@link ScanSetting#GENERATE_TO_CLASS_FILE_NAME}
* @author billy.qi email: qiyilike@163.com
* @since 17/3/21 11:48
*/
class RegisterTransform extends Transform {
Project project
static ArrayList registerList
static File fileContainsInitClass;
RegisterTransform(Project project) {
this.project = project
}
/**
* name of this transform
* @return
*/
@Override
String getName() {
return ScanSetting.PLUGIN_NAME
}
@Override
Set getInputTypes() {
return TransformManager.CONTENT_CLASS
}
/**
* The plugin will scan all classes in the project
* @return
*/
@Override
Set getScopes() {
return TransformManager.SCOPE_FULL_PROJECT
}
@Override
boolean isIncremental() {
return false
}
@Override
void transform(Context context, Collection inputs
, Collection referencedInputs
, TransformOutputProvider outputProvider
, boolean isIncremental) throws IOException, TransformException, InterruptedException {
Logger.i('Start scan register info in jar file.')
long startTime = System.currentTimeMillis()
boolean leftSlash = File.separator == '/'
if (!isIncremental){
outputProvider.deleteAll()
}
inputs.each { TransformInput input ->
// scan all jars
input.jarInputs.each { JarInput jarInput ->
String destName = jarInput.name
// rename jar files
def hexName = DigestUtils.md5Hex(jarInput.file.absolutePath)
if (destName.endsWith(".jar")) {
destName = destName.substring(0, destName.length() - 4)
}
// input file
File src = jarInput.file
// output file
File dest = outputProvider.getContentLocation(destName + "_" + hexName, jarInput.contentTypes, jarInput.scopes, Format.JAR)
//scan jar file to find classes
if (ScanUtil.shouldProcessPreDexJar(src.absolutePath)) {
ScanUtil.scanJar(src, dest)
}
FileUtils.copyFile(src, dest)
}
// scan class files
input.directoryInputs.each { DirectoryInput directoryInput ->
File dest = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY)
String root = directoryInput.file.absolutePath
if (!root.endsWith(File.separator))
root += File.separator
directoryInput.file.eachFileRecurse { File file ->
def path = file.absolutePath.replace(root, '')
if (!leftSlash) {
path = path.replaceAll("\\\\", "/")
}
if(file.isFile() && ScanUtil.shouldProcessClass(path)){
ScanUtil.scanClass(file)
}
}
// copy to dest
FileUtils.copyDirectory(directoryInput.file, dest)
}
}
Logger.i('Scan finish, current cost time ' + (System.currentTimeMillis() - startTime) + "ms")
if (fileContainsInitClass) {
registerList.each { ext ->
Logger.i('Insert register code to file ' + fileContainsInitClass.absolutePath)
if (ext.classList.isEmpty()) {
Logger.e("No class implements found for interface:" + ext.interfaceName)
} else {
ext.classList.each {
Logger.i(it)
}
RegisterCodeGenerator.insertInitCodeTo(ext)
}
}
}
Logger.i("Generate code finish, current cost time: " + (System.currentTimeMillis() - startTime) + "ms")
}
}
================================================
FILE: arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/launch/PluginLaunch.groovy
================================================
package com.alibaba.android.arouter.register.launch
import com.alibaba.android.arouter.register.utils.Logger
import com.android.build.gradle.AppExtension
import com.android.build.gradle.AppPlugin
import com.alibaba.android.arouter.register.utils.ScanSetting
import com.alibaba.android.arouter.register.core.RegisterTransform
import org.gradle.api.Plugin
import org.gradle.api.Project
/**
* Simple version of AutoRegister plugin for ARouter
* @author billy.qi email: qiyilike@163.com
* @since 17/12/06 15:35
*/
public class PluginLaunch implements Plugin {
@Override
public void apply(Project project) {
def isApp = project.plugins.hasPlugin(AppPlugin)
//only application module needs this plugin to generate register code
if (isApp) {
Logger.make(project)
Logger.i('Project enable arouter-register plugin')
def android = project.extensions.getByType(AppExtension)
def transformImpl = new RegisterTransform(project)
//init arouter-auto-register settings
ArrayList list = new ArrayList<>(3)
list.add(new ScanSetting('IRouteRoot'))
list.add(new ScanSetting('IInterceptorGroup'))
list.add(new ScanSetting('IProviderGroup'))
RegisterTransform.registerList = list
//register this plugin
android.registerTransform(transformImpl)
}
}
}
================================================
FILE: arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/utils/Logger.groovy
================================================
package com.alibaba.android.arouter.register.utils
import org.gradle.api.Project
/**
* Format log
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/12/18 下午2:43
*/
class Logger {
static org.gradle.api.logging.Logger logger
static void make(Project project) {
logger = project.getLogger()
}
static void i(String info) {
if (null != info && null != logger) {
logger.info("ARouter::Register >>> " + info)
}
}
static void e(String error) {
if (null != error && null != logger) {
logger.error("ARouter::Register >>> " + error)
}
}
static void w(String warning) {
if (null != warning && null != logger) {
logger.warn("ARouter::Register >>> " + warning)
}
}
}
================================================
FILE: arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/utils/ScanSetting.groovy
================================================
package com.alibaba.android.arouter.register.utils
/**
* register setting
* @author billy.qi email: qiyilike@163.com
* @since 17/3/28 11:48
*/
class ScanSetting {
static final String PLUGIN_NAME = "com.alibaba.arouter"
/**
* The register code is generated into this class
*/
static final String GENERATE_TO_CLASS_NAME = 'com/alibaba/android/arouter/core/LogisticsCenter'
/**
* you know. this is the class file(or entry in jar file) name
*/
static final String GENERATE_TO_CLASS_FILE_NAME = GENERATE_TO_CLASS_NAME + '.class'
/**
* The register code is generated into this method
*/
static final String GENERATE_TO_METHOD_NAME = 'loadRouterMap'
/**
* The package name of the class generated by the annotationProcessor
*/
static final String ROUTER_CLASS_PACKAGE_NAME = 'com/alibaba/android/arouter/routes/'
/**
* The package name of the interfaces
*/
private static final INTERFACE_PACKAGE_NAME = 'com/alibaba/android/arouter/facade/template/'
/**
* register method name in class: {@link #GENERATE_TO_CLASS_NAME}
*/
static final String REGISTER_METHOD_NAME = 'register'
/**
* scan for classes which implements this interface
*/
String interfaceName = ''
/**
* jar file which contains class: {@link #GENERATE_TO_CLASS_NAME}
*/
File fileContainsInitClass
/**
* scan result for {@link #interfaceName}
* class names in this list
*/
ArrayList classList = new ArrayList<>()
/**
* constructor for arouter-auto-register settings
* @param interfaceName interface to scan
*/
ScanSetting(String interfaceName){
this.interfaceName = INTERFACE_PACKAGE_NAME + interfaceName
}
}
================================================
FILE: arouter-gradle-plugin/src/main/groovy/com/alibaba/android/arouter/register/utils/ScanUtil.groovy
================================================
package com.alibaba.android.arouter.register.utils
import com.alibaba.android.arouter.register.core.RegisterTransform
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes
import java.util.jar.JarEntry
import java.util.jar.JarFile
/**
* Scan all class in the package: com/alibaba/android/arouter/
* find out all routers,interceptors and providers
* @author billy.qi email: qiyilike@163.com
* @since 17/3/20 11:48
*/
class ScanUtil {
/**
* scan jar file
* @param jarFile All jar files that are compiled into apk
* @param destFile dest file after this transform
*/
static void scanJar(File jarFile, File destFile) {
if (jarFile) {
def file = new JarFile(jarFile)
Enumeration enumeration = file.entries()
while (enumeration.hasMoreElements()) {
JarEntry jarEntry = (JarEntry) enumeration.nextElement()
String entryName = jarEntry.getName()
if (entryName.startsWith(ScanSetting.ROUTER_CLASS_PACKAGE_NAME)) {
InputStream inputStream = file.getInputStream(jarEntry)
scanClass(inputStream)
inputStream.close()
} else if (ScanSetting.GENERATE_TO_CLASS_FILE_NAME == entryName) {
// mark this jar file contains LogisticsCenter.class
// After the scan is complete, we will generate register code into this file
RegisterTransform.fileContainsInitClass = destFile
}
}
file.close()
}
}
static boolean shouldProcessPreDexJar(String path) {
return !path.contains("com.android.support") && !path.contains("/android/m2repository")
}
static boolean shouldProcessClass(String entryName) {
return entryName != null && entryName.startsWith(ScanSetting.ROUTER_CLASS_PACKAGE_NAME)
}
/**
* scan class file
* @param class file
*/
static void scanClass(File file) {
scanClass(new FileInputStream(file))
}
static void scanClass(InputStream inputStream) {
ClassReader cr = new ClassReader(inputStream)
ClassWriter cw = new ClassWriter(cr, 0)
ScanClassVisitor cv = new ScanClassVisitor(Opcodes.ASM5, cw)
cr.accept(cv, ClassReader.EXPAND_FRAMES)
inputStream.close()
}
static class ScanClassVisitor extends ClassVisitor {
ScanClassVisitor(int api, ClassVisitor cv) {
super(api, cv)
}
void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces)
RegisterTransform.registerList.each { ext ->
if (ext.interfaceName && interfaces != null) {
interfaces.each { itName ->
if (itName == ext.interfaceName) {
//fix repeated inject init code when Multi-channel packaging
if (!ext.classList.contains(name)) {
ext.classList.add(name)
}
}
}
}
}
}
}
}
================================================
FILE: arouter-gradle-plugin/src/main/resources/META-INF/gradle-plugins/com.alibaba.arouter.properties
================================================
implementation-class=com.alibaba.android.arouter.register.launch.PluginLaunch
================================================
FILE: arouter-idea-plugin/build.gradle
================================================
plugins {
id 'org.jetbrains.kotlin.jvm'
id "org.jetbrains.intellij" version '0.3.12'
}
intellij {
version support_idea_version
updateSinceUntilBuild false
plugins 'coverage'
}
patchPluginXml {
changeNotes """
1.0.0
First Release
"""
}
publishPlugin {
username intellijPublishUsername
password intellijPublishPassword
channels "beta"
}
dependencies {
compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
}
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "1.8"
}
}
================================================
FILE: arouter-idea-plugin/src/main/kotlin/com/alibaba/android/arouter/idea/extensions/NavigationLineMarker.kt
================================================
package com.alibaba.android.arouter.idea.extensions
import com.intellij.codeHighlighting.Pass
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler
import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.LineMarkerProviderDescriptor
import com.intellij.navigation.NavigationItem
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.notification.Notifications
import com.intellij.openapi.editor.markup.GutterIconRenderer
import com.intellij.openapi.util.IconLoader
import com.intellij.psi.*
import com.intellij.psi.impl.source.tree.java.PsiMethodCallExpressionImpl
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.AnnotatedMembersSearch
import java.awt.event.MouseEvent
/**
* Mark navigation target.
*
* @author zhilong Contact me.
* @version 1.0
* @since 2018/12/13 12:30 PM
*/
class NavigationLineMarker : LineMarkerProviderDescriptor(), GutterIconNavigationHandler {
override fun getName(): String? {
return "ARouter Location"
}
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {
return if (isNavigationCall(element)) {
LineMarkerInfo(element, element.textRange, navigationOnIcon,
Pass.UPDATE_ALL, null, this,
GutterIconRenderer.Alignment.LEFT)
} else {
null
}
}
override fun navigate(e: MouseEvent?, psiElement: PsiElement?) {
if (psiElement is PsiMethodCallExpression) {
val psiExpressionList = (psiElement as PsiMethodCallExpressionImpl).argumentList
if (psiExpressionList.expressions.size == 1) {
// Support `build(path)` only now.
val targetPath = psiExpressionList.expressions[0].text.replace("\"", "")
val fullScope = GlobalSearchScope.allScope(psiElement.project)
val routeAnnotationWrapper = AnnotatedMembersSearch.search(getAnnotationWrapper(psiElement, fullScope)
?: return, fullScope).findAll()
val target = routeAnnotationWrapper.find {
it.modifierList?.annotations?.map { it.findAttributeValue("path")?.text?.replace("\"", "") }?.contains(targetPath)
?: false
}
if (null != target) {
// Redirect to target.
NavigationItem::class.java.cast(target).navigate(true)
return
}
}
}
notifyNotFound()
}
private fun notifyNotFound() {
Notifications.Bus.notify(Notification(NOTIFY_SERVICE_NAME, NOTIFY_TITLE, NOTIFY_NO_TARGET_TIPS, NotificationType.WARNING))
}
private fun getAnnotationWrapper(psiElement: PsiElement?, scope: GlobalSearchScope): PsiClass? {
if (null == routeAnnotationWrapper) {
routeAnnotationWrapper = JavaPsiFacade.getInstance(psiElement?.project).findClass(ROUTE_ANNOTATION_NAME, scope)
}
return routeAnnotationWrapper
}
override fun collectSlowLineMarkers(elements: MutableList, result: MutableCollection>) {}
/**
* Judge whether the code used for navigation.
*/
private fun isNavigationCall(psiElement: PsiElement): Boolean {
if (psiElement is PsiCallExpression) {
val method = psiElement.resolveMethod() ?: return false
val parent = method.parent
if (method.name == "build" && parent is PsiClass) {
if (isClassOfARouter(parent)) {
return true
}
}
}
return false
}
/**
* Judge whether the caller was ARouter
*/
private fun isClassOfARouter(psiClass: PsiClass): Boolean {
// It was ARouter
if (psiClass.name.equals(SDK_NAME)) {
return true
}
// It super class was ARouter
psiClass.supers.find { it.name == SDK_NAME } ?: return false
return true
}
companion object {
const val ROUTE_ANNOTATION_NAME = "com.alibaba.android.arouter.facade.annotation.Route"
const val SDK_NAME = "ARouter"
// Notify
const val NOTIFY_SERVICE_NAME = "ARouter Plugin Tips"
const val NOTIFY_TITLE = "Road Sign"
const val NOTIFY_NO_TARGET_TIPS = "No destination found or unsupported type."
val navigationOnIcon = IconLoader.getIcon("/icon/outline_my_location_black_18dp.png")
}
// I'm 100% sure this point can not made memory leak.
private var routeAnnotationWrapper: PsiClass? = null
}
================================================
FILE: arouter-idea-plugin/src/main/resources/META-INF/plugin.xml
================================================
arouter-roadsign
ARouter Helper
1.0.0
Alibaba
================================================
FILE: build.gradle
================================================
buildscript {
ext.kotlin_version = '1.4.10'
ext.arouter_register_version = '1.0.2'
repositories {
mavenCentral()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.alibaba:arouter-register:$arouter_register_version"
}
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
google()
}
}
================================================
FILE: gradle/publish.gradle
================================================
/*
* Copyright 2013 Chris Banes
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply plugin: 'maven'
apply plugin: 'signing'
version = VERSION_NAME
group = GROUP
def isReleaseBuild() {
return VERSION_NAME.contains("SNAPSHOT") == false
}
def getReleaseRepositoryUrl() {
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
: "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
}
def getSnapshotRepositoryUrl() {
return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
: "https://s01.oss.sonatype.org/content/repositories/snapshots/"
}
def getRepositoryUsername() {
return hasProperty('SONATYPE_NEXUS_USERNAME') ? SONATYPE_NEXUS_USERNAME : ""
}
def getRepositoryPassword() {
return hasProperty('SONATYPE_NEXUS_PASSWORD') ? SONATYPE_NEXUS_PASSWORD : ""
}
def configurePom(pom) {
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: getReleaseRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
snapshotRepository(url: getSnapshotRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
configurePom(pom)
}
}
}
tasks.create("installLocally", Upload) {
configuration = configurations.archives
repositories {
mavenDeployer {
repository(url: "file://${rootProject.buildDir}/localMaven")
configurePom(pom)
}
}
}
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
if (project.getPlugins().hasPlugin('com.android.application') ||
project.getPlugins().hasPlugin('com.android.library')) {
task install(type: Upload, dependsOn: assemble) {
repositories.mavenInstaller {
configuration = configurations.archives
configurePom(pom)
}
}
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.source
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.source
}
} else {
install {
repositories.mavenInstaller {
configurePom(pom)
}
}
task sourcesJar(type: Jar, dependsOn:classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn:javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
}
if (JavaVersion.current().isJava8Compatible()) {
allprojects {
tasks.withType(Javadoc) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
}
artifacts {
if (project.getPlugins().hasPlugin('com.android.application') ||
project.getPlugins().hasPlugin('com.android.library')) {
archives androidSourcesJar
archives androidJavadocsJar
} else {
archives sourcesJar
archives javadocJar
}
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Jun 12 21:40:29 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
================================================
FILE: gradle.properties
================================================
## Project-wide Gradle settings.
#
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Fri Oct 09 19:03:24 CST 2015
# JVM
org.gradle.daemon=true
#org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
COMPILE_SDK_VERSION=29
BUILDTOOLS_VERSION=29.0.2
SUPPORT_LIB_VERSION=28.0.0
MIN_SDK_VERSION=14
TARGET_SDK_VERSION=28
GROUP=com.alibaba
POM_URL=https://github.com/Alibaba/ARouter/
POM_SCM_URL=https://github.com/Alibaba/ARouter/
POM_SCM_CONNECTION=scm:git:git://github.com/Alibaba/ARouter.git
POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/Alibaba/ARouter.git
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=zhi1ong
POM_DEVELOPER_NAME=ZhiLong Liu
arouter_idea_plugin_version=1.0.0
support_idea_version=2016.2.5
arouter_idea_plugin_name=ARouter Road Sign
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: module-java/build.gradle
================================================
apply plugin: 'com.android.library'
dependencies {
implementation project(':arouter-annotation')
implementation project(':arouter-api')
annotationProcessor project(':arouter-compiler')
implementation project(':module-java-export')
implementation "com.android.support:appcompat-v7:${SUPPORT_LIB_VERSION}"
implementation 'com.alibaba:fastjson:1.2.48'
}
android {
compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)
buildToolsVersion BUILDTOOLS_VERSION
defaultConfig {
minSdkVersion Integer.parseInt(MIN_SDK_VERSION)
targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
debuggable false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
lintOptions { abortOnError false }
}
}
================================================
FILE: module-java/src/main/AndroidManifest.xml
================================================
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/BlankFragment.java
================================================
package com.alibaba.android.arouter.demo.module1;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.alibaba.android.arouter.demo.service.model.TestObj;
import com.alibaba.android.arouter.demo.service.model.TestParcelable;
import com.alibaba.android.arouter.demo.service.model.TestSerializable;
import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.facade.annotation.Route;
import java.util.List;
import java.util.Map;
/**
* A simple {@link Fragment} subclass.
*/
@Route(path = "/test/fragment")
public class BlankFragment extends Fragment {
@Autowired
String name;
@Autowired(required = true)
TestObj obj;
@Autowired
int age = 10;
@Autowired
int height = 175;
@Autowired(name = "boy", required = true)
boolean girl;
@Autowired
char ch = 'A';
@Autowired
float fl = 12.00f;
@Autowired
double dou = 12.01d;
@Autowired
TestSerializable ser;
@Autowired
TestParcelable pac;
@Autowired
List objList;
@Autowired
Map> map;
public BlankFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
TextView textView = new TextView(getActivity());
return textView;
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/MainLooper.java
================================================
package com.alibaba.android.arouter.demo.module1;
import android.os.Handler;
import android.os.Looper;
public class MainLooper extends Handler {
private static MainLooper instance = new MainLooper(Looper.getMainLooper());
protected MainLooper(Looper looper) {
super(looper);
}
public static MainLooper getInstance() {
return instance;
}
public static void runOnUiThread(Runnable runnable) {
if(Looper.getMainLooper().equals(Looper.myLooper())) {
runnable.run();
} else {
instance.post(runnable);
}
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/TestInterceptor90.java
================================================
package com.alibaba.android.arouter.demo.module1;
import android.content.Context;
import android.util.Log;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.annotation.Interceptor;
import com.alibaba.android.arouter.facade.callback.InterceptorCallback;
import com.alibaba.android.arouter.facade.template.IInterceptor;
/**
* TODO feature
*
* @author Alex Contact me.
* @version 1.0
* @since 16/9/9 14:34
*/
@Interceptor(priority = 90)
public class TestInterceptor90 implements IInterceptor {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
callback.onContinue(postcard);
}
/**
* Do your init work in this method, it well be call when processor has been load.
*
* @param context ctx
*/
@Override
public void init(Context context) {
Log.e("test", "位于moudle1中的拦截器初始化了");
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/TestModule2Activity.java
================================================
package com.alibaba.android.arouter.demo.module1;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.alibaba.android.arouter.facade.annotation.Route;
@Route(path = "/module/2", group = "m2")
public class TestModule2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_module2);
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/TestModuleActivity.java
================================================
package com.alibaba.android.arouter.demo.module1;
import android.app.Activity;
import android.os.Bundle;
import com.alibaba.android.arouter.facade.annotation.Route;
@Route(path = "/module/1")
public class TestModuleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_module);
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/TestWebview.java
================================================
package com.alibaba.android.arouter.demo.module1;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import com.alibaba.android.arouter.facade.annotation.Route;
@Route(path = "/test/webview")
public class TestWebview extends Activity {
WebView webview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_webview);
webview = (WebView) findViewById(R.id.webview);
webview.loadUrl(getIntent().getStringExtra("url"));
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/BaseActivity.java
================================================
package com.alibaba.android.arouter.demo.module1.testactivity;
import android.support.v7.app.AppCompatActivity;
import com.alibaba.android.arouter.facade.annotation.Autowired;
/**
* Base Activity (Used for test inject)
*/
public class BaseActivity extends AppCompatActivity {
@Autowired(desc = "姓名")
String name = "jack";
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/Test1Activity.java
================================================
package com.alibaba.android.arouter.demo.module1.testactivity;
import android.os.Bundle;
import android.widget.TextView;
import com.alibaba.android.arouter.demo.module1.R;
import com.alibaba.android.arouter.demo.service.HelloService;
import com.alibaba.android.arouter.demo.service.model.TestObj;
import com.alibaba.android.arouter.demo.service.model.TestParcelable;
import com.alibaba.android.arouter.demo.service.model.TestSerializable;
import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.launcher.ARouter;
import java.util.List;
import java.util.Map;
/**
* https://m.aliyun.com/test/activity1?name=老王&age=23&boy=true&high=180
*/
@Route(path = "/test/activity1", name = "测试用 Activity")
public class Test1Activity extends BaseActivity {
@Autowired
int age = 10;
@Autowired
int height = 175;
@Autowired(name = "boy", required = true)
boolean girl;
@Autowired
char ch = 'A';
@Autowired
float fl = 12.00f;
@Autowired
double dou = 12.01d;
@Autowired
TestSerializable ser;
@Autowired
TestParcelable pac;
@Autowired
TestObj obj;
@Autowired
List objList;
@Autowired
Map> map;
private long high;
@Autowired
String url;
@Autowired
HelloService helloService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);
ARouter.getInstance().inject(this);
// No more getter ...
// name = getIntent().getStringExtra("name");
// age = getIntent().getIntExtra("age", 0);
// girl = getIntent().getBooleanExtra("girl", false);
// high = getIntent().getLongExtra("high", 0);
// url = getIntent().getStringExtra("url");
String params = String.format(
"name=%s,\n age=%s, \n height=%s,\n girl=%s,\n high=%s,\n url=%s,\n ser=%s,\n pac=%s,\n obj=%s \n ch=%s \n fl = %s, \n dou = %s, \n objList=%s, \n map=%s",
name,
age,
height,
girl,
high,
url,
ser,
pac,
obj,
ch,
fl,
dou,
objList,
map
);
helloService.sayHello("Hello moto.");
((TextView) findViewById(R.id.test)).setText("I am " + Test1Activity.class.getName());
((TextView) findViewById(R.id.test2)).setText(params);
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/Test2Activity.java
================================================
package com.alibaba.android.arouter.demo.module1.testactivity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.widget.Toast;
import com.alibaba.android.arouter.demo.module1.R;
import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.facade.annotation.Route;
@Route(path = "/test/activity2")
public class Test2Activity extends AppCompatActivity {
@Autowired
String key1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test2);
String value = getIntent().getStringExtra("key1");
if (!TextUtils.isEmpty(value)) {
Toast.makeText(this, "exist param :" + value, Toast.LENGTH_LONG).show();
}
setResult(999);
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/Test3Activity.java
================================================
package com.alibaba.android.arouter.demo.module1.testactivity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import com.alibaba.android.arouter.demo.module1.R;
import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.launcher.ARouter;
/**
* 自动注入的测试用例
*/
@Route(path = "/test/activity3")
public class Test3Activity extends AppCompatActivity {
@Autowired
String name;
@Autowired
int age;
@Autowired(name = "boy")
boolean girl;
// 这个字段没有注解,是不会自动注入的
private long high;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);
ARouter.getInstance().inject(this);
String params = String.format("name=%s, age=%s, girl=%s, high=%s", name, age, girl, high);
((TextView)findViewById(R.id.test)).setText("I am " + Test3Activity.class.getName());
((TextView)findViewById(R.id.test2)).setText(params);
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/Test4Activity.java
================================================
package com.alibaba.android.arouter.demo.module1.testactivity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.widget.TextView;
import com.alibaba.android.arouter.demo.module1.R;
import com.alibaba.android.arouter.facade.annotation.Route;
@Route(path = "/test/activity4")
public class Test4Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);
((TextView)findViewById(R.id.test)).setText("I am " + Test4Activity.class.getName());
String extra = getIntent().getStringExtra("extra");
if (!TextUtils.isEmpty(extra)) {
((TextView)findViewById(R.id.test2)).setText(extra);
}
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testactivity/TestDynamicActivity.java
================================================
package com.alibaba.android.arouter.demo.module1.testactivity;
import android.os.Bundle;
// 用于测试不标注 Route 的情况下,动态增加路由
//@Route(path="/dynamic/activity")
public class TestDynamicActivity extends Test1Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testinterceptor/Test1Interceptor.java
================================================
package com.alibaba.android.arouter.demo.module1.testinterceptor;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.util.Log;
import com.alibaba.android.arouter.demo.module1.MainLooper;
import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.annotation.Interceptor;
import com.alibaba.android.arouter.facade.callback.InterceptorCallback;
import com.alibaba.android.arouter.facade.template.IInterceptor;
/**
* 一个拦截器的例子
*
* @author Alex Contact me.
* @version 1.0
* @since 2017/1/3 11:20
*/
@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
@Override
public void process(final Postcard postcard, final InterceptorCallback callback) {
if ("/test/activity4".equals(postcard.getPath())) {
// 这里的弹窗仅做举例,代码写法不具有可参考价值
final AlertDialog.Builder ab = new AlertDialog.Builder(postcard.getContext());
ab.setCancelable(false);
ab.setTitle("温馨提醒");
ab.setMessage("想要跳转到Test4Activity么?(触发了\"/inter/test1\"拦截器,拦截了本次跳转)");
ab.setNegativeButton("继续", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onContinue(postcard);
}
});
ab.setNeutralButton("算了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onInterrupt(null);
}
});
ab.setPositiveButton("加点料", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
postcard.withString("extra", "我是在拦截器中附加的参数");
callback.onContinue(postcard);
}
});
MainLooper.runOnUiThread(new Runnable() {
@Override
public void run() {
ab.create().show();
}
});
} else {
callback.onContinue(postcard);
}
}
/**
* Do your init work in this method, it well be call when processor has been load.
*
* @param context ctx
*/
@Override
public void init(Context context) {
Log.e("testService", Test1Interceptor.class.getName() + " has init.");
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testservice/HelloServiceImpl.java
================================================
package com.alibaba.android.arouter.demo.module1.testservice;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import com.alibaba.android.arouter.demo.service.HelloService;
import com.alibaba.android.arouter.facade.annotation.Route;
/**
* TODO feature
*
* @author Alex Contact me.
* @version 1.0
* @since 2017/1/3 10:26
*/
@Route(path = "/yourservicegroupname/hello")
public class HelloServiceImpl implements HelloService {
Context mContext;
@Override
public void sayHello(String name) {
Toast.makeText(mContext, "Hello " + name, Toast.LENGTH_SHORT).show();
}
/**
* Do your init work in this method, it well be call when processor has been load.
*
* @param context ctx
*/
@Override
public void init(Context context) {
mContext = context;
Log.e("testService", HelloService.class.getName() + " has init.");
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testservice/JsonServiceImpl.java
================================================
package com.alibaba.android.arouter.demo.module1.testservice;
import android.content.Context;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.facade.service.SerializationService;
import com.alibaba.fastjson.JSON;
import java.lang.reflect.Type;
/**
* Used for json converter
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/4/10 下午2:10
*/
@Route(path = "/yourservicegroupname/json")
public class JsonServiceImpl implements SerializationService {
@Override
public void init(Context context) {
}
@Override
public T json2Object(String text, Class clazz) {
return JSON.parseObject(text, clazz);
}
@Override
public String object2Json(Object instance) {
return JSON.toJSONString(instance);
}
@Override
public T parseObject(String input, Type clazz) {
return JSON.parseObject(input, clazz);
}
}
================================================
FILE: module-java/src/main/java/com/alibaba/android/arouter/demo/module1/testservice/SingleService.java
================================================
package com.alibaba.android.arouter.demo.module1.testservice;
import android.content.Context;
import android.widget.Toast;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.facade.template.IProvider;
/**
* 测试单类注入
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/4/24 下午9:04
*/
@Route(path = "/yourservicegroupname/single")
public class SingleService implements IProvider {
Context mContext;
public void sayHello(String name) {
Toast.makeText(mContext, "Hello " + name, Toast.LENGTH_SHORT).show();
}
@Override
public void init(Context context) {
mContext = context;
}
}
================================================
FILE: module-java/src/main/res/layout/activity_test1.xml
================================================
================================================
FILE: module-java/src/main/res/layout/activity_test2.xml
================================================
================================================
FILE: module-java/src/main/res/layout/activity_test4.xml
================================================
================================================
FILE: module-java/src/main/res/layout/activity_test_module.xml
================================================
================================================
FILE: module-java/src/main/res/layout/activity_test_module2.xml
================================================
================================================
FILE: module-java/src/main/res/layout/activity_test_webview.xml
================================================
================================================
FILE: module-java/src/main/res/values/dimens.xml
================================================
16dp
16dp
================================================
FILE: module-java/src/main/res/values/strings.xml
================================================
================================================
FILE: module-java-export/build.gradle
================================================
apply plugin: 'com.android.library'
dependencies {
implementation project(':arouter-annotation')
implementation project(':arouter-api')
implementation "com.android.support:appcompat-v7:${SUPPORT_LIB_VERSION}"
annotationProcessor project(':arouter-compiler')
implementation 'com.alibaba:fastjson:1.2.48'
}
android {
compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)
buildToolsVersion BUILDTOOLS_VERSION
defaultConfig {
minSdkVersion Integer.parseInt(MIN_SDK_VERSION)
targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)
javaCompileOptions {
annotationProcessorOptions {
arguments = [ AROUTER_MODULE_NAME : project.getName() ]
}
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
debuggable false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
lintOptions { abortOnError false }
}
}
================================================
FILE: module-java-export/src/main/AndroidManifest.xml
================================================
================================================
FILE: module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/Entrance.java
================================================
package com.alibaba.android.arouter.demo.service;
import android.content.Context;
import com.alibaba.android.arouter.launcher.ARouter;
public class Entrance {
/**
* 跳转到 Test1 Activity,
*
* @param name 姓名
* @param age 年龄
* @param context ctx
*/
public static void redirect2Test1Activity(String name, int age, Context context) {
ARouter.getInstance().build("/test/activity1")
.withString("name", name)
.withInt("age", age)
.navigation(context);
}
}
================================================
FILE: module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/HelloService.java
================================================
package com.alibaba.android.arouter.demo.service;
import com.alibaba.android.arouter.facade.template.IProvider;
/**
* 通过 service module 提供给使用方依赖,使用方可以不依赖具体实现,只需要保证最终打包在 app 中即可
*
* @author Alex Contact me.
* @version 1.0
* @since 2017/1/3 10:26
*/
public interface HelloService extends IProvider {
void sayHello(String name);
}
================================================
FILE: module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/model/TestObj.java
================================================
package com.alibaba.android.arouter.demo.service.model;
/**
* TODO:Feature
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/3/16 下午4:42
*/
public class TestObj {
public String name;
public int id;
public TestObj() {
}
public TestObj(String name, int id) {
this.name = name;
this.id = id;
}
}
================================================
FILE: module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/model/TestParcelable.java
================================================
package com.alibaba.android.arouter.demo.service.model;
import android.os.Parcel;
import android.os.Parcelable;
/**
* TODO:Feature
*
* @author zhilong Contact me.
* @version 1.0
* @since 2017/3/16 下午4:42
*/
public class TestParcelable implements Parcelable {
public String name;
public int id;
public TestParcelable() {
}
public TestParcelable(String name, int id) {
this.name = name;
this.id = id;
}
protected TestParcelable(Parcel in) {
name = in.readString();
id = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(id);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator CREATOR = new Creator() {
@Override
public TestParcelable createFromParcel(Parcel in) {
return new TestParcelable(in);
}
@Override
public TestParcelable[] newArray(int size) {
return new TestParcelable[size];
}
};
}
================================================
FILE: module-java-export/src/main/java/com/alibaba/android/arouter/demo/service/model/TestSerializable.java
================================================
package com.alibaba.android.arouter.demo.service.model;
import java.io.Serializable;
/**
* Created by @author joker on 2018/7/10.
*/
public class TestSerializable implements Serializable {
public String name;
public int id;
public TestSerializable() {
}
public TestSerializable(String name, int id) {
this.name = name;
this.id = id;
}
}
================================================
FILE: module-kotlin/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
dependencies {
implementation project(':arouter-api')
implementation project(':arouter-annotation')
kapt project(':arouter-compiler')
implementation "com.android.support:appcompat-v7:${SUPPORT_LIB_VERSION}"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
}
android {
compileSdkVersion Integer.parseInt(COMPILE_SDK_VERSION)
buildToolsVersion BUILDTOOLS_VERSION
defaultConfig {
minSdkVersion Integer.parseInt(MIN_SDK_VERSION)
targetSdkVersion Integer.parseInt(TARGET_SDK_VERSION)
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
debuggable false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
lintOptions { abortOnError false }
}
}
================================================
FILE: module-kotlin/src/main/AndroidManifest.xml
================================================
================================================
FILE: module-kotlin/src/main/java/com/alibaba/android/arouter/demo/kotlin/KotlinTestActivity.kt
================================================
package com.alibaba.android.arouter.demo.kotlin
import android.app.Activity
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import kotlinx.android.synthetic.main.activity_kotlin_test.*
@Route(path = "/kotlin/test")
class KotlinTestActivity : Activity() {
@Autowired
@JvmField var name: String? = null
@Autowired
@JvmField var age: Int? = 0
override fun onCreate(savedInstanceState: Bundle?) {
ARouter.getInstance().inject(this) // Start auto inject.
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kotlin_test)
content.text = "name = $name, age = $age"
}
}
================================================
FILE: module-kotlin/src/main/java/com/alibaba/android/arouter/demo/kotlin/TestNormalActivity.java
================================================
package com.alibaba.android.arouter.demo.kotlin;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.alibaba.android.arouter.facade.annotation.Route;
@Route(path = "/kotlin/java")
public class TestNormalActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_normal);
}
}
================================================
FILE: module-kotlin/src/main/res/layout/activity_kotlin_test.xml
================================================
================================================
FILE: module-kotlin/src/main/res/layout/activity_test_normal.xml
================================================
================================================
FILE: module-kotlin/src/main/res/values/dimens.xml
================================================
16dp
16dp
================================================
FILE: module-kotlin/src/main/res/values/strings.xml
================================================
================================================
FILE: settings.gradle
================================================
include ':app'
include ':arouter-api'
include ':arouter-compiler'
include ':arouter-annotation'
include ':module-java'
include ':module-java-export'
include ':module-kotlin'
include ':arouter-gradle-plugin'
//include ':arouter-idea-plugin'