对于安卓界面跳转主要你们经常使用的可能都是显示的调用方式,我记得曾经有次面试的时候还被问到,确实显示的跳转狠简单而且很暴力,同时也深受大众喜好。可是既然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");