对于安卓界面跳转主要大家常用的可能都是显示的调用方式,我记得曾经有次面试的时候还被问到,确实显示的跳转狠简单并且很暴力,同时也深受大众喜爱。但是既然Google提供了另一种隐式的界面跳转,能一直存在下来必然是有意义的。那么问题来了,为什么这么说? 鞥横。
Google提供该功能的一个重要原因吧!可能在当前应用内部很少会有人用到这种调用方式,但是对于当下组件化开发盛行时代,我相信隐式调用完成界面跳转的春天来了。
^_^
好吧,这里不啰嗦了,直接进入主题。
一、介绍android标签<data>
标签使用语法:
<data android:scheme="string"
android:host="string"
android:port="string"
android:path="string"
android:pathPattern="string"
android:pathPrefix="string"
android:mimeType="string" />
这里我们简单介绍下我们今天要用到的几个属性。
scheme
http等。
host
scheme之后才有意义。
port
schemem和host之后才有意义。
path
intent对象的路径。
二、如何在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标签中的配置。
三、如何实现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;
}
四、通过注解实现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();
}
}
使用如下:
步骤一、定义接口类,声明需要的方法,并且通过自定义的注解完成参数传入。
Router的create方法创建一个对应的接口对象。
步骤三、调用接口中声明的方法。(完成界面跳转)
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");