## Android-记录阿里的ARouter的使用以及遇到的坑

之前一直没有写文章的习惯,但是随着开发时间的增长,你会发现你之前遇到好多已经解决过的问题,因为没有记录只是单纯的为了解决问题而解决,结果就导致下次在遇到同样的问题还要费时间去百度,Google的一顿查询,白白浪费时间。正所谓好记性不如烂笔头。好了废话不多说了。

一.ARouter支持哪些功能

1.支持直接解析标准URL进行跳转,并自动注入参数到目标页面中

2.支持多模块工程使用

3.支持添加多个拦截器,自定义拦截顺序

4.支持依赖注入,可单独作为依赖注入框架使用

5.支持InstantRun

6.支持MultiDex(Google方案)

7.映射关系按组分类、多级管理,按需初始化

8.支持用户指定全局降级与局部降级策略

9.页面、拦截器、服务等组件均自动注册到框架

10.支持多种方式配置转场动画

11.支持获取Fragment

12.完全支持Kotlin以及混编(配置见文末 其他#5)

13.支持第三方 App 加固(使用 arouter-register 实现自动注册)

好了介绍完支持的哪些功能,接下来就开始使用下吧!
一.前期准备
首先在App的build.gradle的defaultConfig里面添加如下:

//使用阿里的ARouter配置
        javaCompileOptions {

            annotationProcessorOptions {

                arguments = [moduleName: project.getName()]

            }
        }

然后在App的build.gradle的dependencies里面添加ARouter的依赖

    //ARouter(进行activity,fragment的跳转以及传值)
    implementation 'com.alibaba:arouter-api:1.3.1'
    annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'

** 二.使用配置**
自定义一个Application并继承自Application在onCreate()方法中初始化Arouter

  //是否进行ARouter调试(可以通过AppConfig.isDebug=true/false来判断日志的是否开启)
        if (AppConfig.isDebug) {
            //下面两句话必须放到init前面,否则配置无效
            ARouter.openLog();  //打印ARouter日志
            ARouter.openDebug();  //开启ARouter的调试模式(如果在InstantRun模式下运行,必须开启调试模式,线上版本需要关闭,否则有安全风险),
        }
        //官方建议在Application里面进行初始化(使用该注解路径至少两级)
        ARouter.init(this);

最 后 不 要 忘 记 在 A n d r o i d M a n i f e s t . x m l 文 件 中 加 入 自 定 义 的 A p p l i c a t i o n \color{red}{最后不要忘记在AndroidManifest.xml文件中加入自定义的Application} AndroidManifest.xmlApplication
到这里ARouter就可以使用了,下面就简单的用代码实战下吧!
分别创建了2个Activity,1个fragment,1个IInterceptor(拦截器),1个ARouter的服务,1个携带数据时用到的转换器,下面是代码每一步都已经测试过了并且已经加上个人理解的注释了,有需要的可以复制试下
主 界 面 M a i n A c t i v i t y \color{red}{主界面MainActivity} MainActivity

package com.zd.myarouterdemo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatButton;
import androidx.core.app.ActivityOptionsCompat;
import androidx.fragment.app.Fragment;

import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.facade.callback.NavigationCallback;
import com.alibaba.android.arouter.launcher.ARouter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private AppCompatButton btn, btn1, btn2, btn3, btn4, btn5, btn6, btn7;

    /**
     * 推荐使用获取ARouter服务
     */
    @Autowired(name = AppConfig.SERVICE)
    ARouterService aRouterService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ARouter.getInstance().inject(this);
        btn = (AppCompatButton) findViewById(R.id.btn);
        btn1 = (AppCompatButton) findViewById(R.id.btn1);
        btn2 = (AppCompatButton) findViewById(R.id.btn2);
        btn3 = (AppCompatButton) findViewById(R.id.btn3);
        btn4 = (AppCompatButton) findViewById(R.id.btn4);
        btn5 = (AppCompatButton) findViewById(R.id.btn5);
        btn6 = (AppCompatButton) findViewById(R.id.btn6);
        btn7 = (AppCompatButton) findViewById(R.id.btn7);
        btn.setOnClickListener(this);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
        btn3.setOnClickListener(this);
        btn4.setOnClickListener(this);
        btn5.setOnClickListener(this);
        btn6.setOnClickListener(this);
        btn7.setOnClickListener(this);
    }

    /**
     * 都使用了一个拦截器TestInterceptor
     *
     * @param view
     */
    @Override
    public void onClick(View view) {
        switch (view.getId()) {
//            页面之间跳转使用阿里巴巴ARouter跳转,其中注意带返回值启动的时候请求吗不能为0否则没有回调onActivityResult
//            使用ARouter传递参数的时候如果有传递Bundle跟其他的属性Bundle必须写在前面否则其他的属性注入不了
            case R.id.btn:
                //直接跳转
                ARouter.getInstance().build(AppConfig.TEST).navigation();
                break;
            case R.id.btn1:
                //带进入动画的跳转(注意这里动画一定要在build()之后不然会找不到方法)一下两种方法都可以,可以自行选择
                //1.在调用navigation()时,若设置的效果未起效,则在navigation()添加参数,如navigation(this)即可。
//                ARouter.getInstance()
//                        .build(AppConfig.TEST)
//                        //进入动画(注意这里动画一定要在build()之后不然会找不到方法),若设置的效果未起效,则在navigation()添加参数,如navigation(this)即可。
//                        //参数1为打开的Activity的进入动画,参数2为当前的Activity的退出动画
//                        .withTransition(R.anim.top_in, R.anim.top_out).navigation(this);

                //2.新版本跳转动画
                if (Build.VERSION.SDK_INT >= 16) {
                    ActivityOptionsCompat compat = ActivityOptionsCompat.
                            makeScaleUpAnimation(view, view.getWidth() / 2, view.getHeight() / 2, 0, 0);

                    ARouter.getInstance()
                            .build(AppConfig.TEST)
                            .withOptionsCompat(compat)
                            .navigation();
                } else {
                    Toast.makeText(this, "API < 16,不支持新版本动画", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.btn2:
                //带动画如果不起作用,请看btn1事件注释
                //这里传递基础参数跟原生的一样(key-value)
                Bundle bundle = new Bundle();        //得到bundle对象
                bundle.putInt("index", 1);
                bundle.putString("name", "张三");
                bundle.putLong("num", 0x555555L);
                ARouter.getInstance()
                        .build(AppConfig.TEST)
                        .withInt("index", 1)
                        .withString("name", "张三")
                        .withLong("num", 0x555555L)
                        .withBundle("bundle", bundle)
                        .withTransition(R.anim.slide_in_left, R.anim.slide_out_right).navigation(this);
                break;
            case R.id.btn3:
                //带动画如果不起作用,请看btn1事件注释
                //这里传递对象参数跟原生的一样(key-value)
                Person person = new Person("张三", "25");
                ARouter.getInstance()
                        .build(AppConfig.TEST)
                        .withTransition(R.anim.bottom_in, R.anim.bottom_out)
                        .withInt("index", 2)
                        .withObject("person", person)
                        .navigation(this);
                break;
            case R.id.btn4:
                //带动画如果不起作用,请看btn1事件注释
                //这里传递集合参数跟原生的一样(key-value)
                List personList = new ArrayList<>();
                personList.add(new Person("张三", "25"));
                personList.add(new Person("里斯", "23"));
                Map map = new HashMap<>();
                map.put("person", new Person("王二麻子", "26"));
                map.put("list", personList);
                ARouter.getInstance()
                        .build(AppConfig.TEST)
                        .withTransition(R.anim.bottom_in, R.anim.bottom_out)
                        .withInt("index", 3)
                        .withObject("personList", personList)
                        .withObject("map", map)
                        .navigation(this);
                break;
            case R.id.btn5:
                //拦截器的使用面向切面编程
                //跳转拦截,可以用于登录
                ARouter.getInstance().build(AppConfig.TEST).navigation(this, new NavigationCallback() {
                    @Override
                    public void onFound(Postcard postcard) {
                        Log.e("MainActivity", "找到了");
                    }

                    @Override
                    public void onLost(Postcard postcard) {
                        Log.e("MainActivity", "找不到了");
                    }

                    @Override
                    public void onArrival(Postcard postcard) {
                        Log.e("MainActivity", "目标activity打开完成");
                    }

                    @Override
                    public void onInterrupt(Postcard postcard) {
                        // 被登录拦截了下来了
                        // 需要调转到登录页面,把参数跟被登录拦截下来的路径传递给登录页面,登录成功后再进行跳转被拦截的页面
                        Log.e("MainActivity", "被拦截了");
                    }
                });
                break;
            case R.id.btn6:
                //获取Fragment的实例(重复嵌套的情况下可能获取不到,所以不推荐)
                Fragment fragment = (Fragment) ARouter.getInstance().build(AppConfig.FRAGMENT).navigation();
                Toast.makeText(this, fragment.toString(), Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn7:
                // 1. (推荐)使用依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取
                // 2. 使用依赖查找的方式发现服务,主动去发现服务并使用,下面两种方式分别是byName和byType
                aRouterService.toast("第一种推荐的ARouter服务使用方式");
                break;
        }
    }
}

要 跳 转 的 界 面 M a i n A c t i v i t y 的 布 局 文 件 \color{red}{要跳转的界面MainActivity的布局文件} MainActivity




    

    

    

    

    

    

    

    


要 跳 转 的 界 面 T e s t A c t i v i t y \color{red}{要跳转的界面TestActivity} TestActivity

package com.zd.myarouterdemo;

import android.os.Bundle;
import android.text.TextUtils;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.alibaba.android.arouter.facade.annotation.Autowired;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.launcher.ARouter;

import java.util.List;
import java.util.Map;

/**
 * @ClassName: TestActivity
 * @Description: 测试页面
 * @Author: ljl
 * @CreateDate: 2019/9/3 11:22
 * @Version: 1.0
 */
@Route(path = AppConfig.TEST)
public class TestActivity extends AppCompatActivity {
    private TextView textShow, textShow1, textShow2;

    /**
     * 注解方式使用(尽量加上name),类型一定要对上不然也会获取不到数据,为null或者0
     */
    @Autowired(name = "index")
    int id;
    @Autowired(name = "name")
    String name;
    @Autowired(name = "num")
    long num;
    @Autowired(name = "bundle")
    Bundle bundleStr;
    @Autowired(name = "person")
    Person person;
    @Autowired(name = "personList")
    List personList;
    @Autowired(name = "map")
    Map map;
    @Autowired(name = "sp")
    String sp;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        textShow = (TextView) findViewById(R.id.tv_show);
        textShow1 = (TextView) findViewById(R.id.tv_show1);
        textShow2 = (TextView) findViewById(R.id.tv_show2);
        //2.注解方式(使用这种方式一定要注意加上ARouter.getInstance().inject(this)这句话,会获取不到数据(int类型为0,String类型为null))
        ARouter.getInstance().inject(this);
        Toast.makeText(this, sp, Toast.LENGTH_SHORT).show();
        if (id != 0) {
            if (id == 1) {
                //获取传递过来参数分为两种(一种是原始的,另一种就是通过ARouter的注解方式)
                //1.原始方式
                Bundle bundle = getIntent().getExtras();
                //下面是获取传递的Bundle数据(没什么区别)
                Bundle bundle1 = getIntent().getBundleExtra("bundle");
                textShow.setText("基础参数原始方式:序号:" + bundle1.getInt("index") + ",姓名:" + bundle.getString("name") + ",号码:" + bundle.getLong("num"));
                textShow1.setText("基础参数注解方式;序号:" + id + ",姓名:" + name + ",号码:" + num + "bundle数据获取:" + bundleStr.getString("name"));
            } else if (id == 2) {
                //这里获取对象的时候一定要写实现SerializationService的类(随便写不用调用就行,但是必须要有,不然或获取不到,我这里有的JsonSerializationService类)
                textShow2.setText("对象参数:" + person.toString());
            } else if (id == 3) {
                //这里获取集合的时候一定要写实现SerializationService的类(随便写不用调用就行,但是必须要有,不然或获取不到,我这里有的JsonSerializationService类)
                textShow2.setText("集合参数:" + personList.toString() + "==============map参数:对象:" + map.get("person") + "集合:" + map.get("list"));
            }
        }
    }
}

T e s t A c t i v i t y 的 布 局 文 件 \color{red}{TestActivity的布局文件} TestActivity




    

    

    


携 带 对 象 , 结 合 , m a p 跳 转 时 用 到 的 解 析 J s o n S e r i a l i z a t i o n S e r v i c e \color{red}{携带对象,结合,map跳转时用到的解析JsonSerializationService} mapJsonSerializationService

package com.zd.myarouterdemo;

import android.content.Context;

import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.facade.service.SerializationService;
import com.alibaba.fastjson.JSON;

import java.lang.reflect.Type;

/**
 * @ClassName: JsonSerializationService
 * @Description: 跳转界面带参(传递Object)定义解析Obeject的SerializationService
 * @Author: ljl
 * @CreateDate: 2019/9/2 10:32
 * @Version: 1.0
 */
@Route(path = AppConfig.OBJECT_CUSTOM_JSON)
public class JsonSerializationService implements SerializationService {


    @Override
    public  T json2Object(String input, Class clazz) {
        return JSON.parseObject(input, clazz);
    }

    @Override
    public String object2Json(Object instance) {
        return JSON.toJSONString(instance);
    }

    @Override
    public  T parseObject(String input, Type clazz) {
        return JSON.parseObject(input, clazz);
    }

    @Override
    public void init(Context context) {
    }
}

获 取 A R o u t e r 的 服 务 用 到 的 A R o u t e r S e r v i c e I P r o v i d e r \color{red}{获取ARouter的服务用到的ARouterServiceIProvider} ARouterARouterServiceIProvider

package com.zd.myarouterdemo;

import com.alibaba.android.arouter.facade.template.IProvider;

/**
 * @ClassName: ARouterServiceIProvider
 * @Description: ARouter服务使用
 * @Author: ljl
 * @CreateDate: 2019/9/3 17:05
 * @Version: 1.0
 */
public interface ARouterServiceIProvider extends IProvider {
    String toast(String msg);
}

获 取 A R o u t e r 的 服 务 用 到 的 A R o u t e r S e r v i c e \color{red}{获取ARouter的服务用到的ARouterService} ARouterARouterService

package com.zd.myarouterdemo;

import android.content.Context;

import com.alibaba.android.arouter.facade.annotation.Route;

/**
 * @ClassName: ARouterService
 * @Description: ARouterService
 * @CreateDate: 2019/9/3 16:59
 * @Version: 1.0
 */
@Route(path = AppConfig.SERVICE, name = "测试服务")
public class ARouterService implements ARouterServiceIProvider {
    @Override
    public String toast(String msg) {
        return "ARouter服务消息, " + msg;
    }

    @Override
    public void init(Context context) {

    }
}

界 面 跳 转 时 用 的 的 拦 截 器 \color{red}{界面跳转时用的的拦截器}

package com.zd.myarouterdemo;

import android.content.Context;
import android.util.Log;

import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.annotation.Interceptor;
import com.alibaba.android.arouter.facade.callback.InterceptorCallback;
import com.alibaba.android.arouter.facade.template.IInterceptor;

/**
 * @ClassName: TestInterceptor
 * @Description: ARouter拦截器
 * // 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查
 * // 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行
 * @CreateDate: 2019/9/3 16:23
 * @Version: 1.0
 */
@Interceptor(priority = 1)
public class TestInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        String path = postcard.getPath();
        Log.e("路径", path);
        if (path == AppConfig.TEST) {
            //如果是跳到TestActivity,就加个数据,当然也可以做其他操作
            postcard.withString("sp", "我是在拦截器中附加的参数");
        }

        //继续跳转
        callback.onContinue(postcard);

        //终止跳转
        //callback.onInterrupt(null)

        //抛出异常
        // callback.onInterrupt(RuntimeException("我觉得有点异常"))

        // onContinue和onInterrupt至少需要调用其中一种,否则不会继续路由
    }

    @Override
    public void init(Context context) {
        Log.e("TestInterceptor", "路由登录拦截器初始化成功"); //只会走一次
    }
}

获 取 f r a g m e n t 实 例 时 用 到 的 B l a n k F r a g m e n t \color{red}{获取fragment实例时用到的BlankFragment} fragmentBlankFragment

package com.zd.myarouterdemo;

import android.content.Context;
import android.net.Uri;
import android.os.Bundle;

import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.alibaba.android.arouter.facade.annotation.Route;


/**
 * fragment测试
 */
@Route(path = AppConfig.FRAGMENT)
public class BlankFragment extends Fragment {


    private OnFragmentInteractionListener mListener;

    public BlankFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_blank, container, false);
    }

    public void onButtonPressed(Uri uri) {
        if (mListener != null) {
            mListener.onFragmentInteraction(uri);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    public interface OnFragmentInteractionListener {
        void onFragmentInteraction(Uri uri);
    }
}

A p p C o n f i g 配 置 \color{red}{AppConfig配置} AppConfig

package com.zd.myarouterdemo;

/**
 * @ClassName: AppConfig
 * @Description: ARouter跳转参数配置
 * @Author: ljl
 * @CreateDate: 2019/9/3 11:17
 * @Version: 1.0
 */
public class AppConfig {
    /**
     * 是否是测试模式(用于判断是否开启ARouter是否打印Log和openDebug)
     */
    public static boolean isDebug = true;

    /**
     * 这里用于统一管理跳转注解路径(这里要注意一定要至少两级并且前面一定要有斜杠,不要忘记,不然不会出错)
     * 1.前面缺少斜杠会出现错误,错误如下
     * Process: com.zd.myarouterdemo, PID: 12831
     * com.alibaba.android.arouter.exception.HandlerException: ARouter::Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!
     * 

*

* 2.如果注解路径只有一级时,build会在编译的时候直接报错,错误如下 * 错误: ARouter::Compiler An exception is encountered, [Failed to extract default group! String index out of range: -2] * 警告: ARouter::Compiler >>> Route meta verify error, group is <<< * 注: ARouter::Compiler >>> Generated provider map, name is ARouter$$Providers$$app <<< * 注: ARouter::Compiler >>> Generated root, name is ARouter$$Root$$app <<< * 1 个错误 * 1 个警告 */ public static final String TEST = "/app/TestActivity"; public static final String FRAGMENT = "/app/BlankFragment"; //传值的时候对象实体,集合,map使用 public static final String OBJECT_CUSTOM_JSON = "/custom/json"; //ARouter里的Service public static final String SERVICE = "/custom/service"; }

实 体 对 象 P e r s o n \color{red}{实体对象Person} Person

package com.zd.myarouterdemo;

import android.text.TextUtils;

/**
 * @ClassName: Person
 * @Description: java类作用描述
 * @CreateDate: 2019/9/3 14:24
 * @Version: 1.0
 */
public class Person {
    public String name;
    public String age;

    public Person() {
    }

    public Person(String name, String age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        if (TextUtils.isEmpty(name)) {
            return "";
        } else {
            return "name : " + name + "\n age : " + age;
        }
    }

}

M y A p p l i c a t i o n \color{red}{MyApplication} MyApplication

package com.zd.myarouterdemo;

import android.app.Application;
import android.content.Context;

import com.alibaba.android.arouter.launcher.ARouter;

/**
 * @ClassName: MyApplication
 * @Description: java类作用描述
 * @Author: ljl
 * @CreateDate: 2019/9/3 11:17
 * @Version: 1.0
 */
public class MyApplication extends Application {

    private static Context mContext;

    @Override
    public void onCreate() {
        super.onCreate();
        //是否进行ARouter调试
        if (AppConfig.isDebug) {
            //下面两句话必须放到init前面,否则配置无效
            ARouter.openLog();  //打印ARouter日志
            ARouter.openDebug();  //开启ARouter的调试模式(如果在InstantRun模式下运行,必须开启调试模式,线上版本需要关闭,否则有安全风险),
        }
        //官方建议在Application里面进行初始化(使用该注解路径至少两级)
        ARouter.init(this);
    }

    public static Context getContext() {
        return mContext;
    }
}

最 后 在 写 的 过 程 中 遇 到 过 找 不 到 布 局 文 件 里 的 I d ( 问 题 : C a n n o t r e s o l v e s y m b o l ′ b t n 7 ′ ) , 但 是 编 译 没 问 题 。 \color{red}{最后在写的过程中遇到过找不到布局文件里的Id(问题:Cannot resolve symbol 'btn7'),但是编译没问题。} IdCannotresolvesymbolbtn7,
解决方式:android studio报出一个提示,R类里出现了File size exceeds configured limit (2560000). Code insight features not available.
原因是Intellij IDEA对单个文件的大小默认限制为2500kb, android studio是基于Intellij IDEA的,而Windows系统和OS X系统对文件大小的计算方式不一致,前者是1024进制,后者是1000进制,所以暂时OS X系统上没有出现问题,后来将OS X 系统上的限制大小改小后,就出现一样的问题。
进入android studio的安装目录 ,将里面的idea.max.intellisense.filesize=2500数值更改为5000,重启下Android studio就可以了

你可能感兴趣的:(android,java,android,ARouter使用,ARouter跳转拦截,ARouter)