那些年,被咱們遺忘的技術

  對於安卓界面跳轉主要你們經常使用的可能都是顯示的調用方式,我記得曾經有次面試的時候還被問到,確實顯示的跳轉狠簡單而且很暴力,同時也深受大衆喜好。可是既然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

  端口,端口號,只有匹配了schememhost以後纔有意義。

  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-filterdata標籤中的配置。

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、經過類Routercreate方法建立一個對應的接口對象。

  步驟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");
相關文章
相關標籤/搜索