阅读 1374

Android路由框架用法大全

一、什么是android路由?

主要是映射页面跳转关系,根据路由表页面请求分发到指定页面。

二、android路由使用场景

  1. App接收到一个通知,点击通知打开App的某个页面
  2. 浏览器App中点击某个链接打开App的某个页面
  3. 运营活动需求,动态把原生的页面替换成H5页面
  4. 打开页面需要某些条件,先验证完条件,再去打开那个页面
  5. 不合法的打开App的页面被屏蔽掉
  6. H5打开链接在所有平台都一样,方便统一跳转
  7. 打开某个APP,如果APP存在就直接打开,不存在就去下载页面下载

三、为什么需要路由框架?

Android原生系统已经给我们提供了AndroidManifest去管理页面跳转,比如startActivity,为什么还需要使用路由框架呢?我们来简单分析下路由框架存在的意义:

  1. 显示Intent:项目庞大以后,类依赖耦合太大,不适合组件化拆分
  2. 隐式Intent:协作开发困难,调用的时候不知道调什么参数
  3. 每个注册了Scheme的Activity都可以直接打开,有安全风险
  4. AndroidMainfest集中式管理比较臃肿
  5. 无法动态修改路由,如果页面出错,无法动态降级
  6. 无法动态拦截跳转,例如未登录的情况下,打开登录页面,登录成功后接着打开刚才想打开的页面
  7. H5、Android、iOS地址不一样,不利于统一跳转
  8. 在一些复杂的业务场景下(比如电商),灵活性比较强,很多功能都是运营人员动态配置的,比如下发一个活动页面,我们事先并不知道具体的目标页面,但如果事先做了约定,提前做好页面映射,便可以自由配置。
  9. 简化代码,数行跳转代码精简成一行代码,这个大家应该都很容易理解,无非就是对代码块的封装。

四、选择怎么样的路由框架比较好?

路由说到底还是为了解决开发者遇到的各种奇葩需求,选择路由框架要求使用简单、侵入性低、维护方便是首要条件,不影响你原来的代码,写入代码越少越好,这里推荐使用ARouter路由框架。

ARouter是阿里巴巴开源的Android平台中对页面、服务提供路由功能的中间件,一个用于帮助 Android App 进行组件化改造的框架,支持模块间的路由、通信、解耦,提倡的是简单且够用。

GitHub地址:github.com/alibaba/ARo…

ARouter的优势:

  1. 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
  2. 支持多模块工程使用
  3. 支持添加多个拦截器,自定义拦截顺序
  4. 支持依赖注入,可单独作为依赖注入框架使用
  5. 支持InstantRun
  6. 支持MultiDex(Google方案)
  7. 映射关系按组分类、多级管理,按需初始化
  8. 支持用户指定全局降级与局部降级策略
  9. 页面、拦截器、服务等组件均自动注册到框架
  10. 支持多种方式配置转场动画
  11. 支持获取Fragment
  12. 完全支持Kotlin以及混编(配置见文末 其他#5)
  13. 支持第三方 App 加固(使用 arouter-register 实现自动注册)
  14. 支持生成路由文档
  15. 提供 IDE 插件便捷的关联路径和目标类
  16. 支持增量编译(开启文档生成后无法增量编译)

ARouter的典型应用:

  1. 从外部URL映射到内部页面,以及参数传递与解析
  2. 跨模块页面跳转,模块间解耦
  3. 拦截跳转过程,处理登陆、埋点等逻辑
  4. 跨模块API调用,通过控制反转来做组件解耦

五、ARouter的使用示例:

第一步:ARouter配置(添加依赖,引入类库)

打开build.gradle文件,在dependencies {}配置项里引入ARouter的远程类库(不懂的可以参考github上的官方说明),代码如下:

android {
    defaultConfig {
       
         //前面的代码省略...
        
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

dependencies {
    implementation 'com.alibaba:arouter-api:1.5.1'
    annotationProcessor 'com.alibaba:arouter-compiler:1.5.1'
}
复制代码

 注意:

1、如果是组件化开发,那么所有的module组件(包括主项目和library项目 的build.gradle都要添加如上的依赖配置,如果只是加在公共的common组件里,是完全不起作用的。

2、api 的版本和 compiler 的版本号需要用最新的,最新的版本在官方的Github上可以看到,如下图:

第二步:添加ARouter路由注解(可选)

**
 * 在支持路由的页面上添加注解(必选)
 * 注意:这里的路径需至少需要有两级,/xx/xx
 */
@Route(path = "/test/RouteActivity")
public class RouteDemoActivity extends AppCompatActivity {
    //省略这里的代码....
}
复制代码

上面的代码就是在目标Activity类上面声明Route path 注解,以此对应,跳转如果不对应路径,框架会Toast说路径不匹配。

第三步:ARouter初始化

方建议我们在Application里面进行ARouter初始化,因此需要创建一个自定义的Application,看下面的步骤:

1、在Activity同级包里,创建一个新的类RouteApplication.java继承Application,代码如下:

import android.app.Application;

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

/**
 * @author 安阳 QQ:15577969
 * @version 1.0
 * @team 美奇软件开发工作室
 * @date 2020/11/14 14:56
 */
public class RouteApplication extends Application {
    //ARouter调用开关
    private boolean isDebugARouter=true;

    @Override
    public void onCreate() {
        super.onCreate();
        if (isDebugARouter) {
            // 打印日志
            ARouter.openLog();
            // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
            ARouter.openDebug();
        }
        ARouter.init(this);
    }
}
复制代码

2、在AndroidManifest.xml清单配置文件,<application>中加入android:name="RouteApplication"

<application
    android:name="RouteApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
复制代码

第四步:发起路由操作

 //应用内简单的跳转(通过URL跳转在'进阶用法'中)
 ARouter.getInstance().build("/test/RouteActivity").navigation();
复制代码

六、监听路由过程

 ARouter.getInstance().build("/test/RouteActivity").navigation(this, new NavCallback() {
            @Override
            public void onFound(Postcard postcard) {
                Log.e("监听路由过程", "onArrival: 找到了路由");
            }

            @Override
            public void onLost(Postcard postcard) {
                Log.e("监听路由过程", "onArrival: 找不到路由");
            }

            @Override
            public void onArrival(Postcard postcard) {
                Log.e("监听路由过程", "onArrival: 路由跳转完成");
            }

            @Override
            public void onInterrupt(Postcard postcard) {
                Log.e("监听路由过程", "onArrival: 路由被拦截了");
            }
});
复制代码

七、发起路由并且传递参数

// 跳转并携带参数
ARouter.getInstance().build("/test/RouteActivity")
                .withLong("id", 1)
                .withString("name", "张三")
                .withObject("obj", this)
                .navigation();
复制代码

ARouter 提供了大量的参数类型,如下所示:

//基础类型
.withString( String key, String value )
.withBoolean( String key, boolean value)
.withChar( String key, char value )
.withShort( String key, short value)
.withInt( String key, int value)
.withLong( String key, long value)
.withDouble( String key, double value)
.withByte( String key, byte value)
.withFloat( String key, float value)
.withCharSequence( String key,  CharSequence value)

//数组类型
.withParcelableArrayList( String key, ArrayList<? extends Parcelable > value)
.withStringArrayList( String key,  ArrayList<String> value)
.withIntegerArrayList( String key, ArrayList<Integer> value)
.withSparseParcelableArray( String key, SparseArray<? extends Parcelable> value)
.withCharSequenceArrayList( String key, ArrayList<CharSequence> value)
.withShortArray( String key,  short[] value)
.withCharArray( String key, char[] value)
.withFloatArray( String key, float[] value)
.withCharSequenceArray( String key,  CharSequence[] value)

//Bundle 类型
.with( Bundle bundle )

//Activity 跳转动画
.withTransition(int enterAnim, int exitAnim)

//其他类型
.withParcelable( String key, Parcelable value)
.withParcelableArray( String key,  Parcelable[] value)
.withSerializable( String key, Serializable value)
.withByteArray( String key, byte[] value)
.withTransition(int enterAnim, int exitAnim)
复制代码

八、路由分组

什么是路由分组?

ARouter框架是分组管理,按需加载。在编译期框架扫描了所有的注册页面/服务/字段/拦截器等,那么很明显运行期不可能一股脑全部加载进来,这样就太不和谐了。所以就分组来管理,ARouter在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,这样就会极大地降低初始化时加载结点的数量。比如某些Activity分成一组,组名就叫demo,然后在第一次需要加载组内的某个页面时再将demo这个组加载进来。 

@Route(path = "/test/RouteActivity")
复制代码

上面这行代码,就是我们前面添加的路由注释,最前面的demo就是路由分组。我们还可以在监控路由过程里,获取所在的分组,代码如下:

 ARouter.getInstance().build("/test/RouteActivity").navigation(this, new NavCallback() {
            @Override
            public void onArrival(Postcard postcard) {
                String group = postcard.getGroup();
                Log.e("监听路由过程", "当前路由分组是: " + group);
            }

});
复制代码

运行结果如下:

九、自定义路由分组

 1、在原来的注解上添加group字段

**
 * 添加路由和分组
 */
@Route(path = "/test/RouteActivity", group = "testGroup")
public class RouteDemoActivity extends AppCompatActivity {
    //省略这里的代码....
}
复制代码

2、发起路由时,第二个参数填写路由的分组

build(String path, String group)
复制代码

具体示例代码如下:

 // 应用内简单的跳转(通过URL跳转在'进阶用法'中)
ARouter.getInstance().build("/test/RouteActivity","testGroup").navigation();
复制代码

3、添加自定义分组后,运行结果如下:

十、Fragment路由

创建 Fragment 类,并且添加路由注解,如下:

/**
 * 添加Fragment碎片路由
 */
@Route(path = "/test/ModuleFragment")
public class ModuleCFragment extends Fragment {
    //省略这里的代码....
}
复制代码

然后我们可以通过路由,获取Fragment实例:

//通过路由,获取Fragment实例
Fragment moduleFragment = (Fragment) ARouter.getInstance().build( "/test/ModuleFragment" ).navigation();
复制代码

十一、android和html交互:通过URL跳转(进阶用法)

先来看一下web的URL链接跳转流程图:

1、创建一个URL中间跳转页,可以在公共common组件里创建一个URLReceiveActivity活动,具体代码如下:

/**
 * URL中间跳转页
 * 原理:新建一个Activity用于监听Scheme事件,之后直接把url传递给ARouter即可
 */
public class URLReceiveActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_urlreceive);
        //对URI数据进行分发
        Uri uri = getIntent().getData();
        ARouter.getInstance().build(uri).navigation();
        finish();
    }
}
复制代码

2、URLReceiveActivity需要在AndroidManifest.xml清单文件里,添加如下注册:

<activity android:name=".URLReceiveActivity">
            <intent-filter>
                <data
                    android:host="m.zy13.net"
                    android:scheme="arouter" />
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
            </intent-filter>
</activity>
复制代码

注意:这里面的 host 、scheme 字段很重要。点击 url 会根据这两个字段调起本地的Activity 。

3、将android studio目录模式切换至Project,然后在main目录下创建一个assets目录:

4、在 assets目录下创建一个demo.html文件,编写如下HTML代码:

<!doctype html>
<html>
<head>
    <!-- 设置当前HTML文件的字符编码 -->
    <meta charset="utf-8">
    <!-- 设置浏览器的兼容模式版本 (让IE使用最新的渲染引擎工作) -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- 声明当前网页在移动端浏览器中展示的相关设置 -->
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

</head>
<body>
<h2>1、URL普通跳转</h2>
<p>
    <a href="arouter://m.zy13.net/test/RouteActivity">URL普通跳转</a>
</p>
<h2>2、URL普通跳转携带参数</h2>
<p>
    <a href="arouter://m.zy13.net/test/RouteActivity?name=xiao7&age=18">URL普通跳转携带参数</a>
</p>
</body>
</html>
复制代码

注明: a标签里面的arouter://m.zy13.net分别代表着scheme和host,/demo/RouteActivity就是目标Activity的注解。

5、在需要与html交互的活动页面xml布局里,添加WebView控件:

    <WebView
        android:id="@+id/webview"
        android:layout_marginTop="50dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
复制代码

6、然后在Activity活动的onCreate()里加载html页面,如下:

private WebView contentWebView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 从assets目录加载html文件
        contentWebView = (WebView) findViewById(R.id.webview);
        contentWebView.loadUrl("file:///android_asset/demo.html");
}
复制代码

在实际开发中,通常是直接调用网址链接与html进行交互的,这里为了简化,我们直接加载了html文件,效果如下:

7、如果需要接收 UR 中的参数,需要在Activity中调用自动注入初始化的方法,代码如下:

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

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

/**
 * 在支持路由的页面上添加注解(必选)
 * 注意:这里的路径需至少需要有两级,/xx/xx
 */
@Route(path = "/test/RouteActivity")
public class RouteDemoActivity extends AppCompatActivity {
    private TextView textView;

    //为每一个参数声明一个字段,并使用@Autowired标注
    @Autowired
    String name;

    // 通过name来映射URL中的不同参数
    @Autowired(name = "age")
    int age;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //自动注入初始化,ARouter会自动对字段进行赋值,无需主动获取
        ARouter.getInstance().inject(this);
        setContentView(R.layout.activity_route_demo);
        //文本
        textView = (TextView) findViewById(R.id.urlTextView);

        textView.setText("URL参数是: " + "name: " + name + "  age: " + age);

    }
}
复制代码

8、如果不使用自动注入,那么可以不写ARouter.getInstance().inject(this),但是需要取值的字段仍然需要标上 @Autowired 注解,因为 只有标上@Autowired注解之后,ARouter才能知道以哪一种数据类型提取URL中的参数并放入Intent中,这样才能在intent中获取到对应的参数,具体的代码如下:

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

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

/**
 * 在支持路由的页面上添加注解(必选)
 * 注意:这里的路径需至少需要有两级,/xx/xx
 */
@Route(path = "/test/RouteActivity")
public class RouteDemoActivity extends AppCompatActivity {
    private TextView textView;

    //为每一个参数声明一个字段,并使用@Autowired标注
    @Autowired
    String name;

    // 通过name来映射URL中的不同参数
    @Autowired(name = "age")
    int age;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_route_demo);
        //文本
        textView = (TextView) findViewById(R.id.urlTextView);

         /**
         * 解析URL参数,手动获取
         */
        Bundle bundle = getIntent().getExtras();
        name = bundle.getString("name");
        age = bundle.getInt("age");

        textView.setText("URL参数是: " + "name: " + name + "  age: " + age);

    }
}
复制代码

获取URL参数效果如下:

十二、对外暴露接口服务 

这里所指的服务不是Android四大组件中的Service,而是接口开发的概念,就是将一部分功能和组件封装起来成为接口,以接口的形式对外提供能力,我们可以将需要对外提供的功能作为一个服务,而服务的实现就是具体的业务功能。

1、先自定义一个接口 IService 并且继承 IProvider ,IService 接口里面有一个 getData() 方法,具体代码如下:

public interface IService extends IProvider {
    void getData(Context context );
}
复制代码

2、然后定义一个 IService 的实现类 MyService 并且添加注解,代码如下:

import android.content.Context;
import android.widget.Toast;

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

import net.zy13.module.common.service.IService;

/**
 * @author 安阳 QQ:15577969
 * @version 1.0
 * @team 美奇软件开发工作室
 * @date 2020/11/21 14:14
 */
@Route(path = "/service/demo", name = "测试服务")
public class MyService implements IService {
    @Override
    public void demo(Context context) {
        Toast.makeText(  context , "这是一个对外暴露的接口服务!", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void init(Context context) {

    }
}
复制代码

十三、发现接口服务

1、定义服务对象, 通过@Autowired注解依赖注入接口服务,我们不需要知道接口的具体实现类,如下:

//使用依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取
@Autowired(name = "/service/demo")
IService service;
复制代码
Autowired注解中标注name之后,将会使用byName的方式注入对应的字段,不设置name属性,会默认使用byType的方式发现服务(注意:当同一接口有多个实现的时候,必须使用byName的方式发现服务)
复制代码

2、然后添加注解初始化,自动赋值

ARouter.getInstance().inject(this);
复制代码

3、这样就可以调用 service接口服务里面的 demo() 方法了

service.demo(this);
复制代码

效果如下: 

文章分类
Android
文章标签