Android组件化路由,ARouter框架在Kotlin中的用法(跨模块跳转、登录拦截、错误重定向)

GitHub

https://github.com/alibaba/ARouter

导入

  • 添加依赖
id 'kotlin-kapt'

android {
    defaultConfig {
		...
        kapt {
            arguments {
            	//根据模块名来命名路由根节点
                arg("AROUTER_MODULE_NAME", project.getName())
                //生成Json文件
                arg("AROUTER_GENERATE_DOC", "enable")
            }
        }
    }
}

dependencies {
	...
    //ARouter的api
    implementation 'com.alibaba:arouter-api:1.5.1'
    //kotlin中使用kapt,ARouter的注解处理器
    kapt 'com.alibaba:arouter-compiler:1.2.2'
}
  • 其他用到ARouter注解的模块中,也需要加入
    //kotlin中使用kapt,ARouter的注解处理器
    kapt 'com.alibaba:arouter-compiler:1.2.2'
  • (可选)在项目的根build.gradle中,推荐导入该插件,可以在编译阶段完成初始化时的事情,可以加快app的启动
    dependencies {
    	...
        classpath "com.alibaba:arouter-register:1.0.2"
    }

Base模块下增加接口IIProvider

package com.zhangyu.router

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

interface IIProvider : IProvider {

    fun startLoginActivity(from: String)

    fun startMainActivity(from: String)

    fun startSearchActivity(from: String)

    fun startEditorActivity(from: String)

}

object RouteFlag {
    //为什么可以使用route注解的extra参数为目标页指定属性
    //因为Int数值在内存中占4个字节,每个字节占8位,所以利用extras字段我们可以为目标页指定32个开关
    const val FLAG_LOGIN: Int = 0x01
    const val FLAG_AUTHORITY = FLAG_LOGIN.shl(1)//等同于java中的FLAG_LOGIN<<1
    const val FLAG_VIP = FLAG_AUTHORITY.shl(1)
}

app模块中实现IIProvider 接口


@Route(path = JumpProvider.PATH)
class JumpProvider : IIProvider {

    companion object {
        const val PATH = "/service/provider"
    }

    override fun init(context: Context?) {

    }

    override fun startLoginActivity(from: String) {
        ARouter.getInstance().build(LoginActivity.PATH)
            .navigation()
    }

    override fun startMainActivity(from: String) {
        ARouter.getInstance().build(MainActivity.PATH)
            .navigation()
    }

    override fun startSearchActivity(from: String) {
        ARouter.getInstance().build(SearchActivity.PATH)
            .navigation()
    }

    override fun startEditorActivity(from: String) {
        ARouter.getInstance().build(EditActivity.PATH)
            .navigation()
    }

}

定义几个页面

  • MainActivity
private const val TAG = "MainActivity"

@Route(path = MainActivity.PATH)
class MainActivity : AppCompatActivity() {

    companion object {
        const val PATH = "/app/main"
    }

    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.btGotoLogin.setOnClickListener {
            JumpProvider().startLoginActivity("MainActivity")
        }
        binding.btGotoEditor.setOnClickListener {
            JumpProvider().startEditorActivity("MainActivity")
        }
        binding.btGotoSearch.setOnClickListener {
            JumpProvider().startSearchActivity("MainActivity")
        }
        binding.btGotoError.setOnClickListener {
            //传入一个错误的路径
            ARouter.getInstance()
                .build("/path/unknown")
                .navigation()
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        Log.d(TAG, "onActivityResult: ${data?.getStringExtra("key")}")
    }
}
  • LoginActivity

@Route(path = LoginActivity.PATH)
class LoginActivity : AppCompatActivity() {

    companion object {
        const val PATH = "/app/login"
    }

    @JvmField
    @Autowired
    var from = ""

    lateinit var binding: ActivityLoginBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_login)
        binding.lifecycleOwner = this
        //设置参数的自动注入
        ARouter.getInstance().inject(this)
        //打印
        Log.d(TAG, "onCreate: from=$from")
        initView()
    }

    private fun initView() {
        binding.btLogin.setOnClickListener {
            LoginRepositoryProvider.getInstance().login()
            finish()
        }
        binding.btLogout.setOnClickListener {
            LoginRepositoryProvider.getInstance().logout()
            finish()
        }
    }

}

private const val TAG = "LoginActivity"
  • EditActivity(低层模块,Editor模块中)
    • 传入extras = RouteFlag.FLAG_LOGIN用于登录拦截,进入该页面需要先登录
    • private val jumpProvider by lazy { ARouter.getInstance().navigation(IIProvider::class.java) }子模块下获取IIProvider的实例 jumpProvider
@Route(path = EditActivity.PATH, extras = RouteFlag.FLAG_LOGIN)
class EditActivity : AppCompatActivity() {
    companion object {
        const val PATH = "/editor/edit"
    }

    private lateinit var binding: ActivityEditorBinding
    private val jumpProvider by lazy { ARouter.getInstance().navigation(IIProvider::class.java) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_editor)
        binding.btGotoSearch.setOnClickListener {
            jumpProvider.startSearchActivity("EditorActivity")
        }
    }


}

定义一个拦截器(例如登录拦截)

//定义一个拦截器,需要name随便写一个即可
@Interceptor(name = "/service/interceptor", priority = 9)
class GlobalInterceptor : IInterceptor {

    private var context: Context? = null

    override fun init(context: Context?) {
        this.context = context
    }

    override fun process(postcard: Postcard?, callback: InterceptorCallback?) {
        //错误后失效的时间
        postcard?.timeout = 1
        val flag = postcard?.extra
        if (flag?.and(RouteFlag.FLAG_LOGIN) != 0) {
            //判断是否已经登录
            val isLogin = LoginRepositoryProvider.getInstance().isLogin()
            if (!isLogin) {
                ARouter.getInstance().build(LoginActivity.PATH)
                    .navigation()
                showToast("需要登录")
            } else {
                //已经登录不需要拦截
                callback?.onContinue(postcard)
            }
        } else {
            //不需要拦截
            callback?.onContinue(postcard)
        }
    }

    private fun showToast(msg: String) {
        //拦截器在子线程中,需要切换到主线程更新UI
        Handler(Looper.getMainLooper()).post {
            Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
        }
    }

}

定义一个全局的降级服务(重定向)

//全局降级服务,当路由的时候,目标不存在,重定向到统一错误页面
//定义一个任意的path注解即可生效
@Route(path = "/service/degrade")
class DegradeServiceImp : DegradeService {
    override fun init(context: Context?) {

    }

    //页面没有找到时触发
    override fun onLost(context: Context?, postcard: Postcard?) {
        ARouter.getInstance().build(ErrorActivity.PATH)
            .greenChannel()//greenChannel不会被拦截器拦截
            .navigation()
    }
}

生成的文件的目录

在这里插入图片描述

使用中的问题

  • const val PATH = “/app/main”
    const val PATH = “/editor/edit”
    不同模块下的页面要分配在不同的group中,例如app下的MainActivity定义group为app,editor模块下的EditActivity定义group为editor
  • ARouter拦截器多次点击后失效问题
    点击打开一个需要校验用户登录状态的 Activity 时,当用户没有登录重复的点击返回再点击,大概10次左右 ARouter 路由失效(所有路由进入不了拦截器)。这个问题整了半天才发现拦截器出现问题导致再次进入拦截器的时间延时了300秒,Postcard 类中有定义:private int timeout = 300; 导航超时 300 秒。
    解决办法,将超时设置小一些,例如:postcard?.timeout = 1

参考资料

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,我可以为你解答关于Android MVP模式和使用Kotlin模拟登录的问题。 首先,MVP模式是一种架构模式,它将应用程序分为三个部分:模型(Model),视图(View)和控制器(Presenter)。MVP模式的主要目的是将业务逻辑和用户界面分离,从而实现更好的代码可读性和可维护性。 在Android开发,MVP模式通常用于处理复杂的UI和业务逻辑。下面是一个使用Kotlin模拟登录Android MVP模式示例: 1. 创建一个包含登录逻辑的模型 ```kotlin interface LoginModel { fun login(username: String, password: String, callback: LoginCallback) } interface LoginCallback { fun onSuccess() fun onFailure(error: String) } class LoginModelImpl : LoginModel { override fun login(username: String, password: String, callback: LoginCallback) { // 模拟登录 if (username == "admin" && password == "123456") { callback.onSuccess() } else { callback.onFailure("用户名或密码错误") } } } ``` 2. 创建一个视图接口和一个实现视图接口的Activity ```kotlin interface LoginView { fun showLoading() fun hideLoading() fun showToast(msg: String) } class LoginActivity : AppCompatActivity(), LoginView { private lateinit var presenter: LoginPresenter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) presenter = LoginPresenterImpl(this, LoginModelImpl()) btn_login.setOnClickListener { presenter.login(et_username.text.toString(), et_password.text.toString()) } } override fun showLoading() { // 显示加载 } override fun hideLoading() { // 隐藏加载 } override fun showToast(msg: String) { // 显示Toast } } ``` 3. 创建一个控制器(Presenter)来连接模型和视图 ```kotlin interface LoginPresenter { fun login(username: String, password: String) } class LoginPresenterImpl(private val view: LoginView, private val model: LoginModel) : LoginPresenter { override fun login(username: String, password: String) { view.showLoading() model.login(username, password, object : LoginCallback { override fun onSuccess() { view.hideLoading() view.showToast("登录成功") } override fun onFailure(error: String) { view.hideLoading() view.showToast(error) } }) } } ``` 在此示例,LoginActivity是视图,LoginPresenterImpl是控制器(Presenter),LoginModelImpl是模型。当用户单击“登录”按钮时,LoginActivity将调用LoginPresenterImpl的login方法,该方法将调用LoginModelImpl的login方法进行模拟登录。如果登录成功,LoginPresenterImpl将通知LoginActivity以显示“登录成功”消息;否则,它将通知LoginActivity显示错误消息。 希望这个示例可以帮助你理解Android MVP模式和使用Kotlin模拟登录的实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值