Android APP路由中心设计

72 阅读4分钟

众所周知,维护公司的项目经常会有线上问题,并且由于商务等原因我们经常要对线上的app做出交互上的修改,那么问题就来了,有没有不用发版就能简单修改线上的交互,或者说能够及时止损的处理呢。

大伙首先都想到的是:

1、大量使用H5页面 (需要对资源做缓存处理、更激进的方案是做离线包、不然网络不好的时候体验也很差)

2、热更新方案(原生热更新/react native热更新,维护成本高,实现起来较复杂,小公司整不起(我们就是~))

我分享下我们团队做的处理(其实都是我在整~)

我们的处理是做了一个app全局的路由中心, 主要原理是:首页和一些app固定的广告位置里面的点击 服务器返回的对应链接,然后链接会走到这个路由中心去处理跳转,如果需要修改线上的交互的话修改特定返回的链接就好了,前提是路由中心要处理这个跳转。

实现的好处是:

1、运营只需要在管理后台简单修改链接就能改变app特定页面的交互。

2、维护成本低,只需要前期设计好路由中心,不支持的链接可以拉起一个全屏webview让h5部门处理或弹窗说不支持让用户升级版本就好。

3、可以顺带处理scheme协议拉起app特定页面

4、如果是多模块项目,路由中心还可以减少代码量,我们的路由中心是写在一个透明activity上的,如果要跨模块拉起弹窗的话,我们是要在组件里面注入然后拿到那个模块暴露的接口方法的,我们就可以统一写在这个路由中心的activity里面。

路由中心的代码实现:

/**
 * 路由中心设计演示代码
 * 
 * 主要功能:
 * 1. 统一处理深度链接 (Deep Link)
 * 2. 管理模块间跳转
 * 3. 权限控制和登录验证
 * 4. 降级处理和错误兜底
 */

class RouteCenter : AppCompatActivity() {
    
    // ==================== 核心路由处理 ====================
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 设置为透明Activity,不影响用户体验
        setupTransparentActivity()
        
        // 解析传入的URL
        val url = intent.getStringExtra("url") ?: ""
        val uri = Uri.parse(url)
        
        // 核心路由分发逻辑
        routeDispatch(uri)
    }
    
    /**
     * 核心路由分发方法
     */
    private fun routeDispatch(uri: Uri) {
        val path = uri.path ?: ""
        val queryParams = parseQueryParams(uri)
        
        when {
            // 1. 新闻详情页
            path.contains("/news-detail/") -> {
                val newsId = path.substringAfter("/news-detail/")
                routeToNewsDetail(newsId)
            }
            
            // 2. 交易页面 (需要登录)
            path.contains("/trade") -> {
                checkLoginAndRoute {
                    val symbol = queryParams["symbol"] ?: ""
                    routeToTrade(symbol, queryParams)
                }
            }
            
            // 3. VIP页面
            path.contains("/vip") -> {
                val vipType = queryParams["type"] ?: "basic"
                routeToVip(vipType)
            }
            
            // 4. 聊天室
            path.contains("/chat") -> {
                val userId = queryParams["uid"] ?: ""
                val action = queryParams["action"] ?: ""
                routeToChat(userId, action)
            }
            
            // 5. 市场行情
            path.contains("/market") -> {
                val category = queryParams["category"] ?: "spot"
                val tab = queryParams["tab"] ?: "0"
                routeToMarket(category, tab)
            }
            
            // 6. H5页面 (兜底方案)
            path.contains("/webview") -> {
                val webUrl = queryParams["url"] ?: ""
                routeToWebView(webUrl)
            }
            
            // 7. 不支持的链接 - 降级处理
            else -> {
                handleUnsupportedRoute(uri.toString())
            }
        }
    }
    
    // ==================== 具体路由实现 ====================
    
    /**
     * 跳转到新闻详情
     */
    private fun routeToNewsDetail(newsId: String) {
        val intent = Intent("action.news.detail").apply {
            putExtra("news_id", newsId)
            setPackage(packageName)
        }
        startActivity(intent)
        finish()
    }
    
    /**
     * 跳转到交易页面
     */
    private fun routeToTrade(symbol: String, params: Map<String, String>) {
        // 这里可以调用交易模块的路由接口
        TradeRouter.launchTrade(
            context = this,
            symbol = symbol,
            orderType = params["orderType"],
            onComplete = { finish() }
        )
    }
    
    /**
     * 跳转到VIP页面
     */
    private fun routeToVip(vipType: String) {
        val intent = Intent("action.vip.page").apply {
            putExtra("vip_type", vipType)
            setPackage(packageName)
        }
        startActivity(intent)
        finish()
    }
    
    /**
     * 跳转到聊天室
     */
    private fun routeToChat(userId: String, action: String) {
        when (action) {
            "customer_service" -> {
                ChatRouter.routeToCustomerService(this)
            }
            "ai_analysis" -> {
                ChatRouter.routeToAIAnalysis(this, userId)
            }
            else -> {
                ChatRouter.routeToChatRoom(this, userId)
            }
        }
        finish()
    }
    
    /**
     * 跳转到市场页面
     */
    private fun routeToMarket(category: String, tab: String) {
        val intent = Intent("action.market.list").apply {
            putExtra("category", category)
            putExtra("tab_index", tab.toIntOrNull() ?: 0)
            setPackage(packageName)
        }
        startActivity(intent)
        finish()
    }
    
    /**
     * 跳转到WebView (兜底方案)
     */
    private fun routeToWebView(url: String) {
        val intent = Intent("action.webview").apply {
            putExtra("url", url)
            setPackage(packageName)
        }
        startActivity(intent)
        finish()
    }
    
    // ==================== 辅助方法 ====================
    
    /**
     * 登录检查
     */
    private fun checkLoginAndRoute(action: () -> Unit) {
        if (LoginManager.isLoggedIn()) {
            action.invoke()
        } else {
            // 跳转到登录页面
            LoginManager.startLogin(this) { success ->
                if (success) {
                    action.invoke()
                } else {
                    finish()
                }
            }
        }
    }
    
    /**
     * 解析URL参数
     */
    private fun parseQueryParams(uri: Uri): Map<String, String> {
        val params = mutableMapOf<String, String>()
        uri.queryParameterNames.forEach { key ->
            uri.getQueryParameter(key)?.let { value ->
                params[key] = value
            }
        }
        return params
    }
    
    /**
     * 处理不支持的路由
     */
    private fun handleUnsupportedRoute(url: String) {
        // 方案1: 弹窗提示升级
        showUpgradeDialog()
        
        // 方案2: 降级到H5页面
        // routeToWebView(url)
    }
    
    /**
     * 显示升级提示
     */
    private fun showUpgradeDialog() {
        AlertDialog.Builder(this)
            .setTitle("功能升级")
            .setMessage("此功能需要升级到最新版本才能使用")
            .setPositiveButton("立即升级") { _, _ ->
                // 跳转到应用商店
                AppUpdateManager.goToAppStore(this)
                finish()
            }
            .setNegativeButton("取消") { _, _ ->
                finish()
            }
            .setOnDismissListener { finish() }
            .show()
    }
    
    /**
     * 设置透明Activity
     */
    private fun setupTransparentActivity() {
        // 设置无标题栏
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        
        // 设置透明背景
        window.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )
        
        // 设置沉浸式状态栏
        WindowCompat.setDecorFitsSystemWindows(window, false)
    }
    
    /**
     * 处理触摸事件 - 点击空白区域关闭
     */
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        if (event?.action == MotionEvent.ACTION_DOWN) {
            finish()
            return true
        }
        return super.onTouchEvent(event)
    }
}

// ==================== 配套的路由管理器 ====================

/**
 * 路由配置管理器
 * 可以从服务端动态配置路由规则
 */
object RouteConfig {
    
    // 路由规则配置 (可从服务端获取)
    private val routeRules = mapOf(
        "/news-detail/{id}" to "action.news.detail",
        "/trade" to "action.trade.main",
        "/vip/{type}" to "action.vip.page",
        "/chat" to "action.chat.room",
        "/market" to "action.market.list"
    )
    
    /**
     * 检查路由是否支持
     */
    fun isRouteSupported(path: String): Boolean {
        return routeRules.keys.any { pattern ->
            matchPattern(pattern, path)
        }
    }
    
    /**
     * 获取路由对应的Action
     */
    fun getRouteAction(path: String): String? {
        return routeRules.entries.find { (pattern, _) ->
            matchPattern(pattern, path)
        }?.value
    }
    
    private fun matchPattern(pattern: String, path: String): Boolean {
        // 简单的模式匹配实现
        val regex = pattern.replace("{id}", "\\d+")
            .replace("{type}", "\\w+")
        return path.matches(regex.toRegex())
    }
}

最后在自己想要的地方直接携带链接打开这个路由中心activity就好了