Android基础之--Navigation + ARouter

Navigation的概念

Navigation是一套用于实现应用内部页面跳转和参数传递的组件,旨在简化Android应用的导航逻辑并推荐使用单Activity架构。

Navigation组件主要由3个部分组成:

  1. NavGraph导航图:包含所有导航相关信息的 XML 资源,其中包含所有目的地和操作。该图表会显示应用的所有导航路径(跳转关系);
  2. NavHostFragment导航宿主:导航宿主是一个空容器,用户在应用中导航时,目的地会在该容器中交换进出(可以理解为存放fragment的view容器);
  3. NavController:是用来编程式或声明式地控制应用内的导航流程(可以理解为用作fragment跳转的工具类);

Navigation的基本使用

    1. 首先导入Navigation依赖;
    implementation 'androidx.navigation:navigation-runtime-ktx:2.3.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'

    1. 创建显示在Activity中的多个Fragment(我创建了A、B 两个Fragment);
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".AFragment">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="this is A fragment"
        android:textSize="40dp"/>

    <Button
        android:id="@+id/a_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="a-btn"/>
    
</LinearLayout>

    1. 创建导航图;
      在res中新建navigation的资源文件夹
    1. 在NavGraph中添加Fragment,连接跳转关系;
      只需在右侧添加并连接Fragment即可,左侧代码会根据右侧的状态自动生成。
    1. 在需要添加Fragment的Activity中添加fragment控件;
<fragment
    android:id="@+id/fragment_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    // 指定了要使用的Fragment类是NavHostFragment
    android:name="androidx.navigation.fragment.NavHostFragment"
    // 指定了要使用的导航图(Navigation Graph)
    app:navGraph="@navigation/navigation_main"
    app:defaultNavHost="true"/>

    1. 给Fragment的Button控件添加点击事件,实现点击按钮跳转Fragment;
a_btn.setOnClickListener {
    // 使用Navigation.findNavController()方法来获取当前活动的NavController实例
    // 然后调用navigate()方法来执行导航操作
    Navigation.findNavController(requireActivity(), R.id.fragment_view).navigate(R.id.BFragment)
}

Navigation的基本使用完成,运行后点击按钮即可完成主Activity中两个Fragment之间相互切换。

ARouter概述

项目开发使用的原生路由方案一般是通过显示Intent或隐式Intent方式实现Activity 和 Fragment的跳转,随着项目的组件化、模块化开发,业务解耦,各个module不会进行相互依赖,如果moduleA和moduleB中的Activity需要相互跳转,使用传统的startActivity(intent)来进行通信是不能实现的。当然也可以通过其他方法去实现,比如隐式跳转或者反射机制实现跳转,但隐式跳转需要在Manifest中进行大量的过滤配置,不利于维护;通过反射跳转可能会对性能造成影响。因此我们来学习下路由框架ARouter。

ARouter的大致原理

ARouter使用@Route注解,在编译时期通过APT技术(Annotation Processing Tool)生成类文件用于存储path和activityClass的映射关系。在app进程启动的时候会拿到这些类文件,把里面存储的映射关系数据读到内存里,保存在路由表map中。 在进行路由跳转时,通过ARouter的build()方法传入要到达页面的路由地址,ARouter在路由表中找到路由地址对应的activityClass,然后new Intent(),通过ARouter的withString()方法传入携带参数,内部调用intent.putExtra(),通过ARouter的navigation()跳转,内部调用startActivity(intent)。

ARouter的基本使用

1. 添加依赖

在project下的<build.gradle>中添加ARouter的依赖

android {
    ...

    defaultConfig {
        ... 

        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }

    ...
}

dependencies {

    implementation 'androidx.annotation:annotation:1.3.0'
    implementation 'com.alibaba:arouter-api:1.5.2'
    annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'

    ...
}

  • 导入依赖遇到的问题

Q1: 导入ARouter依赖同步成功,编译时会报错;

    Manifest merger failed with multiple errors, see logs

A1: 在project中的<gradle.properties>中添加配置,将项目迁移到AndroidX中;

    android.useAndroidX=true    //在编译时自动使用AndroidX库替代旧的Support库
    android.enableJetifier=true    //自动将项目中使用的第三方库迁移到AndroidX

Q2: 添加javaCompileOptions配置会报错;

    ARouter::Compiler >>> No module name, for more information, look at gradle log.

A2: arguments中键名不对,将moduleName 改为AROUTER_MODULE_NAME就可以编译成功了;

原:
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [moduleName : project.getName()]
            }
        }

改:
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }

2. 初始化ARouter

官方建议在Application里面进行ARouter的初始化,并将初始化ARouter的Application配置到Androidmanifest.xml文件中;

  • 创建初始化ARouter的Application类<InitApplication.kt>;
class InitApplication : Application() {

    private var isDebugARouter = true

    override fun onCreate() {
        super.onCreate()

        if (isDebugARouter){
            // 写在init之前才会生效
            ARouter.openLog()
            ARouter.openDebug()
        }

        //初始化ARouter
        ARouter.init(this)
    }

}

  • 在Androidmanifest中配置InitApplication ;
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.arouterdemo">

    <application
        android:name="com.example.arouterdemo.InitApplication"
        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/Theme.ARouterDemo">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

3. ARouter的使用

这里先简要介绍下ARouter的使用流程,由于ARouter主要用于解决组件间和模块间的界面跳转问题,在接下来的4. ARouter模块跳转中会以模块为单位进行跳转。

1. 在即将跳转到的Activity注解中指明该Activity的路径
  【 @Route(path = "/xxx/xxx") 】:

    @Route(path = "/main/first")
    class FirstActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
           setContentView(R.layout.activity_first)
        }
    }


2. 在主动跳转的Activity中指明要跳转去的Activity的路径:
  【 ARouter.getInstance().build("/xxx/xxx").navigation()class SecondActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_second)

            initView()
        }

        fun initView(){
            btn.setOnClickListener {
                ARouter.getInstance().build("/main/first").navigation()
            }
        }
    }

  • 遇到的问题

**Q:**ARouter简单使用的apk点击跳转按钮提示没有匹配路径;

ARouter::: ARouter::There is no route match the path [/xxx/xxx], in group [xxx][ ] 

**A:**由于Android开发使用的是kotlin语言编写的,因此需要使用kapt依赖,而ARouter使用的是annotationProcessor;

原:

android {
    ...

    defaultConfig {
        ... 

        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }

    ...
}

dependencies {

    implementation 'androidx.annotation:annotation:1.3.0'
    implementation 'com.alibaba:arouter-api:1.5.2'
    annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'

    ...
}


改:

apply plugin: 'kotlin-kapt'

android {
    ...

    defaultConfig {
        ... 

        kapt {
            arguments {
                arg("AROUTER_MODULE_NAME", project.getName())
            }
        }
    }

    ...
}

dependencies {

    implementation 'androidx.annotation:annotation:1.3.0'
    implementation 'com.alibaba:arouter-api:1.5.2'
    kapt 'com.alibaba:arouter-compiler:1.5.2'

    ...
}


4. ARouter模块跳转
  • 创建模块
    首先创建一个Base模块用来存放项目模块的路由路径;

project根目录 —> new —> module —> Android Library —> 设置module名称 —> Next —> Finish

  • 导入ARouter依赖
    只要是使用ARouter的所有模块,都需要在module的build.gradle中添加ARouter依赖;

  • 创建路由路径对象
    根据实际项目创建的模块来编写路由路径,路径至少2级,不同模块的1级路径不相同,写成一样的可能会出问题;

object RouterPath {

    // 主页面
    const val MAIN = "/app/main"

    // 设置主页
    const val SETTING = "/setting/main"

    // 消息页面
    const val MESSAGE = "/message/main"

}

  • 根据路由路径封装跳转api
object Router {

    fun navMain(){
        ARouter.getInstance().build(RouterPath.MAIN).navigation()
    }

    fun navSetting(){
        ARouter.getInstance().build(RouterPath.SETTING).navigation()
    }

    fun navMessage(){
        ARouter.getInstance().build(RouterPath.MESSAGE).navigation()
    }

}

  • 模块间依赖
    由于module-base模块中有整个项目的路由路径及跳转api,属于项目公用的模块,所以其他所有模块都需要在build.gradle中依赖module-base模块;
以app模块为例:

dependencies {
    ...

    implementation project(path: ':module-base')
    implementation project(path: ':module-setting')
    implementation project(path: ':module-message')
}


以上仅为公用Base模块的创建,除此之外还创建了module-message和module-setting模块用来测试模块间跳转,流程相同,①创建模块,②导入ARouter依赖,③创建主Activity。

  • module-message模块的代码

在页面上添加两个按钮,分别用来点击跳转到另外两个模块主页面,在点击监听中添加ARouter跳转逻辑即可(三个模块的代码几乎一样,区别在于主模块app导入其他所有模块的依赖,而其他模块只需要导入module-base模块的依赖);

activity_message.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MessageActivity">


    <Button
        android:id="@+id/to_setting"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="setting"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <Button
        android:id="@+id/to_main"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="main"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Message Activity"
        android:textSize="50dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MessageActivity.kt

@Route(path = RouterPath.MESSAGE)
class MessageActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_message)

        initView()
    }

    private fun initView(){
        to_setting.setOnClickListener {
            Router.navSetting()
        }
        to_main.setOnClickListener {
            Router.navMain()
        }
    }
}

使用ARouter实现Fragment跳转

部分项目中使用的是该方法,但我没有写demo,大致流程如下:

  1. 创建Fragment的Route<MainRoute.kt>
object MainRoute {
    const val FRAGMENT_A = "/app/afragment"
    const val FRAGMENT_B = "/app/bfragment"
    const val FRAGMENT_C = "/app/cfragment"
}

  1. 创建导航图<MainNavGraph.kt>
    这段代码有问题,不知道是否因为导入的navigation版本问题,这里先讲实现思路;
object MainNavGraph {

    fun create(navController: NavController, startDest : String = MainRoute.FRAGMENT_A){
        navController.apply{
            graph = createGraph(
                route = "app",
                startDestination = startDest
            ){
                // 创建Fragment实例,并将其添加到MainRoute路由中
                fragment<AFragment>(route = MainRoute.FRAGMENT_A)

                fragment<BFragment>(route = MainRoute.FRAGMENT_B)

                fragment<CFragment>(route = MainRoute.FRAGMENT_C)
            }
        }
    }

}

导航图主要是把fragment和route路由路径对应起来,当根据路由路径进行跳转时,实际上是跳转到对应的Fragment中,而Fragment就不需要添加route注释@Route来绑定路由路径了。

  1. Fragment跳转实现

在Fragment所在的Activity中需要绑定NavHostFragment和navController,然后通过navController和route路径来实现跳转;

class MainActivity : AppCompatActivity() {

    private lateinit var navController : NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment_view) as NavHostFragment
        navController = navHostFragment.navController
        MainNavGraph.create(navController)

        to_setting.setOnClickListener {
            navController.navigate(MainRoute.FRAGMENT_B)
        }
    }
}

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值