众所周知,维护公司的项目经常会有线上问题,并且由于商务等原因我们经常要对线上的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就好了