那些年,被我们遗忘的技术<Android 基于URL界面跳转>

  对于安卓界面跳转主要大家常用的可能都是显示的调用方式,我记得曾经有次面试的时候还被问到,确实显示的跳转狠简单并且很暴力,同时也深受大众喜爱。但是既然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
 * 邮箱:[email protected]
 */
@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"); 
时间: 2024-08-28 10:01:40

那些年,被我们遗忘的技术<Android 基于URL界面跳转>的相关文章

Android Intent实现界面跳转切换,随时记录一下

好久不用Android,今天突然用到了Intent实现界面跳转. Intent方法:Intent itent = new Intent(); intent.setClass(**Activity1.this,**Activity2.class); **Activity1.this.startActivity(intent); 不要忘记在AndroidManifest.xml中需要注册**Activity2. <activity android:name=".**Activity2"

Android 用户登录界面

黑色10分钟,winxp和QQ账号全军覆灭 最近一朋友忘记QQ密码,听说可以自己找回,就上网去找,看到有QQ密码破解工具,就下载,浏览器提示软件不安全,但是为了找回密码,我按了运行,然后下载了QQ账号密码破解工具,运行,提示说账号没找到.我又下载一个破解工具,还是没有用. 于是我用另一个QQ账号登陆,提示密码错误.我觉得奇怪,这个QQ账号密码没有忘记的.跑到另一台机器登陆也是提示不能登陆,查找原因是密码被改动了.我想,难道运行破解工具盗取了QQ账号然后就修改密码?于是用另外一个QQ账号登陆,谢天

【转】Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果--不错

原文网址:http://blog.csdn.net/xiaanming/article/details/10163203 转载请注明出处:http://blog.csdn.net/xiaanming/article/details/10163203 了解二维码这个东西还是从微信中,当时微信推出二维码扫描功能,自己感觉挺新颖的,从一张图片中扫一下竟然能直接加好友,不可思议啊,那时候还不了解二维码,呵呵,然后做项目的时候,老板说要加上二维码扫描功能,然后自己的屁颠屁颠的去百度,google啥的,发现

Android之Activity之间跳转

本人自学Android,想到什么就写点什么.主要是怕忘了,哈哈~请观者不要建议~ 今天写点Android窗口之间的跳转以及自己理解: 1.Android中窗口之间的跳转,就是Activity之间的跳转. 2.Activity之间的跳转,通过Intent实现. 3.AndroidManifest.xml要记得添加新创建Activity. 4.Activity类都有相应的layout文件.(xml文件) 5.每个layout文件中配置的控件,都能在R.java文件中找到相应的Id,在Activity

开发一个新的android界面、界面跳转 看图学Android---Android 开发实例教程三、四

Android实例图解教程目录 http://blog.csdn.net/wyx100/article/details/45061407 一.课程功能 本课程讲述建立一个新界面和界面切换(从界面一切换到界面二). 二.课程界面 界面一(启动界面) 界面二(主界面) 三.工作流程 完成页面切换需要2个过程: 1.建立一个工程,见第二节. http://blog.csdn.net/wyx100/article/details/45248209 可以在该项目基础继续开发. 2.建立开机界面 先引入资源

Android笔记二十三.Android基于事件监听器处理机制

转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 一.Android的事件处理 Android事件处理包括两个部分:Android事件处理机制(基本)和Android消息传递机制(进阶).前者包含三种处理方式,即基于监听的事件处理.基于回调的事件处理.直接绑定到标签;后者包含两种处理方式,即Handler消息传递.异步任务处理. 1.Android的事件处理机制 (1)基于监听的事件处理方式 通常做法是为Android界面组件绑定特定的事件监听

Android实现界面跳转

实现界面跳转的代码如下: 第一种: Intent mIntent =  new Intent();mIntent.setClassName(mcureeActivity.this, nextActivity.class);startActivity(mIntent) 第二种: Intent mIntent =  new Intent();mIntent.setClassName("com.android.test", "com.android.test.next")

Android基于cordova3.3的插件开发

最近工作的项目,需要用到cordova进行插件开发,具体Cordova的作用,就不再赘述,大家可以自行的去百度就OK了,直接开始.具体的流程,我将已一个小的Demo进行推进讲解.也是刚刚接触,太理论的基础东西我也说不好,还是先跑起来一个Demo,才有继续学下去的动力-大家多多指教- Step1.准备工作: 首先将我提供的Demo实例包中的HelloWorld-CordovaLib引入到工作空间中,我是使用的Eclipse,接着创建工程MultiImageChooser,同时将HelloWorld

Android开发之Intent跳转到系统应用中的拨号界面、联系人界面、短信界面

现在开发中的功能需要直接跳转到拨号.联系人.短信界面等等,查找了很多资料,自己整理了一下.1.跳转到拨号界面,代码如下: 1)直接拨打 Intent intentPhone = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + phoneNumber)); startActivity(intentPhone); 2)跳转到拨号界面 Intent intent = newIntent(Intent.ACTION_DIAL,Uri.pars