對於安卓界面跳轉主要你們經常使用的可能都是顯示的調用方式,我記得曾經有次面試的時候還被問到,確實顯示的跳轉狠簡單而且很暴力,同時也深受大衆喜好。可是既然Google提供了另外一種隱式的界面跳轉,能一直存在下來必然是有意義的。那麼問題來了,爲何這麼說? 鞥橫。html
對於系統級應用的調用我想應該不少人用到,例如調用系統打電話功能,系統相冊,系統相機等等。對於這些調用其實咱們都是隱式調用。這也許是Google提供該功能的一個重要緣由吧!可能在當前應用內部不多會有人用到這種調用方式,可是對於當下組件化開發盛行時代,我相信隱式調用完成界面跳轉的春天來了。java
^_^android
好吧,這裏不囉嗦了,直接進入主題。面試
1、介紹android標籤<data>
瀏覽器
標籤使用語法:app
<data android:scheme="string" android:host="string" android:port="string" android:path="string" android:pathPattern="string" android:pathPrefix="string" android:mimeType="string" />
這裏咱們簡單介紹下咱們今天要用到的幾個屬性。ide
scheme組件化
協議,能夠本身定義,例如http等。測試
hostui
主機,主機名,只有先匹配scheme以後纔有意義。
port
端口,端口號,只有匹配了schemem和host以後纔有意義。
path
路徑,匹配intent對象的路徑。
2、如何在html中直接打開Activity?
這裏是經過html中的a標籤中的屬性href完成界面跳轉,不須要在經過js調用android中的java代碼,而後在去經過android代碼完成界面跳轉。\(≧▽≦)/
須要打開的Activity的規則定義,清單文件intent-filter定義以下:
<activity android:configChanges="keyboardHidden|orientation|screenSize" android:screenOrientation="portrait" android:theme="@style/ActAnimTheme"> <intent-filter> <!--協議部分,隨便設置--> <data android:scheme="http" android:host="jump" android:path="/jumpDetailActivity" android:port="6666"/> <!--下面這幾行也必須得設置--> <category android:name="android.intent.category.DEFAULT"/> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.BROWSABLE"/> </intent-filter> </activity>
如上代碼定義action規則是Intent.VIEW,category有一個默認的設置,另外一個是指向瀏覽器端的category,當作默認設置設置便可。
data標籤分別自定義協議和主機號,端口,路徑(必須項)。
<a href="http://jump:8888/jumpDetailActivity?key=1367">點擊測試跳轉</a>
核心代碼一行,拼接以下協議,主機,端口,路徑,參數,都一一對應Intent-filter中data標籤中的配置。
3、如何實現java代碼中界面跳轉?
對於java代碼中的實現和html實現是一致的,不一樣在於java代碼中沒有a標籤,可是咱們有URI,直接去解析拼接的URL。
實現代碼以下:
public boolean toRoute(){ PackageManager packageManager = builder.applicationContext.getPackageManager(); Intent intent = new Intent(mAction, Uri.parse(url)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0); boolean isValid = !activities.isEmpty(); if (isValid) { builder.applicationContext.startActivity(intent); } return isValid; }
4、經過註解實現URL跳轉\(≧▽≦)/
因爲每次都須要本身拼接URL感受有些過於繁瑣,有沒有什麼更加有效果方法簡化咱們的開發。
註解:
自定義註解,不知道你們看過整個url以後有沒有注意到整個url的組織結構相似GET請求。咱們可否相似Retrofit的解析方式實現咱們本身的跳轉(路由)功能。
自定義註解
協議類註解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Scheme { String value() default ""; }
主機類註解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Host { String value() default ""; }
端口類註解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Port { String value() default ""; }
路徑類註解
/** * 做者:liemng on 2017/12/14 * 郵箱:859686819@qq.com */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Path { String value() default ""; }
參數類註解
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface RouteParam { String value() default ""; }
如上是自定義的註解,那麼咱們當下須要如何解析註解而且拼裝成一個URL,最後打開一個Activity。
定義路由類。
public class Router { private final Builder builder; private Map<Method, ServiceMethod> serviceMethodCache = new HashMap<>(); private Router(Builder builder) { this.builder = builder; } /** * 實例化對應的接口類對象 * @param clazz * @param <T> * @return */ public <T> T create(Class<T> clazz) { validateServiceInterface(clazz); return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*若是調用的是Object類中的方法,則直接調用*/ if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } ServiceMethod serviceMethod = loadServiceMethod(method, args); return serviceMethod.toRoute(); } }); } /** * 檢查註解是否完成了解析 * @param method * @param args * @return */ ServiceMethod loadServiceMethod(Method method, Object[] args) { ServiceMethod serviceMethod = serviceMethodCache.get(method); if (null != serviceMethod) return serviceMethod; synchronized (serviceMethodCache) { serviceMethod = new ServiceMethod(builder); serviceMethodCache.put(method, serviceMethod); } serviceMethod.parseAnnotation(method, args); return serviceMethod; } /** * 校驗接口是否合法 * @param clazz 接口類的字節碼 * @param <T> */ <T> void validateServiceInterface(Class<T> clazz) { if (!clazz.isInterface()) throw new IllegalArgumentException("clazz must be a interface."); if (clazz.getInterfaces().length > 0) throw new IllegalArgumentException("clazz must be not extent other interface."); } /** * 路由參數構建 */ public static class Builder { Context applicationContext; /*如下參數僅僅是默認值*/ String scheme; String host; String port; String path; /** * 爲了不內存泄露 * @param context */ public Builder(@NonNull Context context) { this.applicationContext = context.getApplicationContext(); } public Builder scheme(String scheme) { this.scheme = scheme; return this; } public Builder host(String host) { this.host = host; return this; } public Builder port(String port) { this.port = port; return this; } public Builder path(String path) { this.path = path; return this; } public Router build() { return new Router(this); } } }
Builder類主要是傳遞配置參數,對於默認其餘值最爲默認值,在找不到對應的註解參數會使用該值。
使用,能夠傳入一個接口類,而後返回指定的接口對象(動態代理),代理做用是解析接口中方法上的註解,而後拼接參數,而後打開指定的Activity。
註解解析類
public class ServiceMethod { private String url = ""; private Builder builder; private String mAction = Intent.ACTION_VIEW; public ServiceMethod(Builder builder) { this.builder = builder; } public void parseAnnotation(Method method, Object[] args) { /*解析方法註解*/ parseMethodAnnotation(method); /*解析方法參數註解*/ parseParamsAnnotation(method, args); } /** * 執行路由跳轉 * @return */ public boolean toRoute(){ PackageManager packageManager = builder.applicationContext.getPackageManager(); Intent intent = new Intent(mAction, Uri.parse(url)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0); boolean isValid = !activities.isEmpty(); if (isValid) { builder.applicationContext.startActivity(intent); } return isValid; } /** * 解析方法註解 * * @param method */ public void parseMethodAnnotation(Method method) { /*解析Action*/ Action action = method.getAnnotation(Action.class); if(null != action){ mAction = action.value(); } /*RouteUri: Scheme + Host + Port + Path*/ RouteUri routeUri = method.getAnnotation(RouteUri.class); if (null != routeUri) { url += routeUri.routeUri(); return; } /*拼接協議參數*/ Scheme scheme = method.getAnnotation(Scheme.class); if (null != scheme) { String value = scheme.value(); url += (TextUtils.isEmpty(value) ? builder.scheme : value); } /*拼接主機參數*/ Host host = method.getAnnotation(Host.class); if (null != host){ String value = host.value(); url += "://"; url += (TextUtils.isEmpty(value) ? builder.host : value); } /*拼接端口參數*/ Port port = method.getAnnotation(Port.class); if (null != port){ String value = port.value(); url += ":"; url += (TextUtils.isEmpty(value) ? builder.port : value); } /*拼接路徑參數*/ Path path = method.getAnnotation(Path.class); if (null != path){ String value = path.value(); url += (TextUtils.isEmpty(value) ? builder.path : value); } } /** * 解析方法參數註解 * * @param method */ public void parseParamsAnnotation(Method method, Object[] args) { /**/ Annotation[][] annotations = method.getParameterAnnotations(); StringBuilder reqParamsBuilder = new StringBuilder(); for (int i = 0; i < annotations.length; i++) { Annotation[] annotationsArrays = annotations[i]; if (annotationsArrays.length > 0) { Annotation annotationsItem = annotationsArrays[0]; if (!(annotationsItem instanceof RouteParam)) break; if (i == 0) { reqParamsBuilder.append("?"); } else { reqParamsBuilder.append("&"); } /*添加Key*/ reqParamsBuilder.append(((RouteParam) annotationsItem).value()); reqParamsBuilder.append("="); /*添加Value*/ reqParamsBuilder.append(args[i]); } } url += reqParamsBuilder.toString(); } }
使用以下:
步驟1、定義接口類,聲明須要的方法,而且經過自定義的註解完成參數傳入。
步驟2、經過類Router的create方法建立一個對應的接口對象。
步驟3、調用接口中聲明的方法。(完成界面跳轉)
public interface IRoute { @Scheme("http") @Host("jump") @Port("6666") @Path("/jumpDetailActivity")
@Action(Intent.ACTION_VIEW)
void skip(@RouteParam("key") String value); }
建立對應的接口對象,而且調用聲明方法,完成界面跳轉。
Router build = new Router.Builder(getActivity()).build(); IRoute iRoute = build.create(IRoute.class); iRoute.skip("ArMn123");