標籤:start 輸出 undle substr port 理解 來講 tostring on()
Android應用組件化各個組件頁面之間要實現跳轉使用路由是一個很好的選擇。本文將實現一個比較輕量級的路由群組件,主要涉及以下知識:
- Annotation (聲明路由目標資訊)
- AnnotationProcessor (處理註解)
- JavaPoet (產生Java檔案)
- UriMatcher (匹配Uri)
本文將使用Java註解來實現一個簡單的路由群組件,主要從這幾方面來講解:
- 註解定義與使用
- 註解跳轉服務
- 使用AnnotationProcessor處理註解、組建檔案
- Uri的匹配
- 安全參數
- 註解跳轉服務的開發
由於使用AnnotationProcessor,所以整個路由可分為以下模組:
- lib-component-router (Android工程)
- lib-component-router-annotation (存放註解)
- lib-component-router-compiler (註解處理)
註解定義
由於我們的路由群組件相對簡單,主要定義以下註解:
- UriDestination (聲明路由目標資訊)
- DestinationUri (定義Uri路徑)
- DestinationArgument (參數聲明)
聲明目標路由註解
@Retention(RetentionPolicy.SOURCE)@Target(ElementType.TYPE)public @interface UriDestination { String name(); DestinationUri uri(); DestinationArgument[] out() default {}; DestinationArgument[] in() default {};}
該註解主要用來註解Activity,聲明一個路由目標的資訊,各參數說明如下:
- authority (匹配Uri authority)
- scheme (匹配Uri scheme)
- path (匹配Uri路徑)
- out (輸出參數)
- in (輸入參數)
路由Uri註解
@Retention(RetentionPolicy.SOURCE)@Target(ElementType.ANNOTATION_TYPE)public @interface DestinationUri { String authority() default "imxingzhe.com"; String scheme() default "xingzhe"; String path() default "";}
該路由主要用於聲明路由目標的Uri資訊,各參數說明如下:
- authority (匹配android.net.Uri authority)
- scheme (匹配android.net.Uri scheme)
- path (匹配路由路徑資訊)
路由參數註解
@Retention(RetentionPolicy.SOURCE)@Target(ElementType.ANNOTATION_TYPE)public @interface DestinationArgument { String key(); boolean require() default false; Class<?> type();}
該註解主要用於聲明路由的輸入、輸出參數資訊,各參數說明如下:
- key (參數的名稱)
- require (是否是必要參數)
- type (參數的類型)
路由群組件功能實現目標Action
public interface DestinationAction { Context getContext(); int getFlags(); int getRequestCode(); boolean getUriOnly(); Bundle getArguments();}
Action可理解為一次跳轉動作,使用端通過Builder模式產生Action執行個體,然後再通過DestinationService執行給定的動作。
跳轉服務邏輯
public interface DestinationService { void start(DestinationAction destinationAction);}
此介面只包含一個start方法用於執行DestinationAction邏輯。主要實現跳轉方式是使用類式ContentProvider的UriMatcher方式來實現。首先聲明一個抽象Service:
public abstract class AbstractUriDestinationService implements DestinationService { private final static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); private static boolean isDestinationDefinitionResolved; @Override public void start(DestinationAction ){ ... } ... protected abstract List<DestinationDefinition> getDestinationDefinitions();}
方法getDestinationDefinitions由子類來實現,主要提供此路由目標的相關資訊, DestinationDefinition類如下:
public class DestinationDefinition { private final String name; private final Class<?> destination; private final List<DestinationArgumentDefinition> inArgumentDefinitions; private final List<DestinationArgumentDefinition> outArgumentDefinitions; public DestinationDefinition(String name, Class<?> destination, List<DestinationArgumentDefinition> inArgumentDefinitions, List<DestinationArgumentDefinition> outArgumentDefinitions) { this.name = name; this.destination = destination; this.inArgumentDefinitions = inArgumentDefinitions; this.outArgumentDefinitions = outArgumentDefinitions; } public String getName() { return name; } public Class<?> getDestination() { return destination; } public List<DestinationArgumentDefinition> getInArgumentDefinitions() { return inArgumentDefinitions; } public List<DestinationArgumentDefinition> getOutArgumentDefinitions() { return outArgumentDefinitions; }}
AbstractUriDestinationService類中的start方法實現真正的跳轉邏輯:
public abstract class AbstractUriDestinationService implements DestinationService { private final static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); private static boolean isDestinationDefinitionResolved; @Override public void start(DestinationAction destinationAction) { List<DestinationDefinition> destinationDefinitions = getDestinationDefinitions(); resolveDestinationDefinition(destinationDefinitions); Context context = destinationAction.getContext(); if (context == null) { throw new IllegalArgumentException("content == null"); } PackageManager packageManager = context.getPackageManager(); if (destinationAction instanceof UriDestinationAction) { Uri uri = ((UriDestinationAction) destinationAction).getUri(); int index = matcher.match(uri); if (UriMatcher.NO_MATCH == index || index >= destinationDefinitions.size()) { throw new IllegalStateException("Not found destination for : " + uri); } DestinationDefinition destinationDefinition = destinationDefinitions.get(index); List<DestinationArgumentDefinition> destinationArgumentDefinitions = destinationDefinition.getInArgumentDefinitions(); for (DestinationArgumentDefinition argumentDefinition : destinationArgumentDefinitions) { Bundle args = destinationAction.getArguments(); if (argumentDefinition.isRequire() && !args.containsKey(argumentDefinition.getKey())) { throw new IllegalArgumentException("No such key: " + argumentDefinition.getKey()); } } Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(uri); if (packageManager.resolveActivity(intent, 0) == null) { if (destinationAction.getUriOnly()) { throw new IllegalStateException("Not found activity for : " + uri); } else { intent = new Intent(context, destinationDefinition.getDestination()); if (packageManager.resolveActivity(intent, 0) == null) { throw new IllegalStateException("Not found activity for : " + uri); } } } intent.addFlags(destinationAction.getFlags()); Bundle args = destinationAction.getArguments(); if (args != null) { intent.putExtras(args); } if (context instanceof Activity) { ((Activity) context).startActivityForResult(intent, destinationAction.getRequestCode()); } else { context.startActivity(intent); } } else { throw new IllegalStateException("Not support operate"); } } private static void resolveDestinationDefinition(List<DestinationDefinition> destinationDefinitions) { if (isDestinationDefinitionResolved) { return; } int index = 0; for (DestinationDefinition destinationDefinition : destinationDefinitions) { if (destinationDefinition instanceof UriDestinationDefinition) { Uri uri = ((UriDestinationDefinition) destinationDefinition).getUri(); String stringForUri = uri.toString(); String path = uri.getPath(); int pathIndex = stringForUri.indexOf(path); if (pathIndex != -1) { path = stringForUri.substring( pathIndex, stringForUri.length() ); } matcher.addURI(uri.getAuthority(), path, index++); } } isDestinationDefinitionResolved = true; } protected abstract List<DestinationDefinition> getDestinationDefinitions();}
這樣通過實現AbstractUriDestinationService類,提供相應的DestinationDefinition就可以實現路由的跳轉功能,由於使用的註冊我們可以使用AnnotationProcessor來處理註解產生DestinationService的實作類別。
源碼下載: https://github.com/yjwfn/AndroidRouterSample
Android組件化路由實踐