第四章:导航与路由

本章深入 Android 现代导航体系:Navigation Component 原理、Compose Navigation 类型安全路由、Deep Link 与 App Link 配置、Fragment 返回栈管理,以及多模块导航架构。


📋 章节目录

主题
4.1 Navigation Component 原理与 NavGraph
4.2 Fragment 导航与返回栈管理
4.3 Compose Navigation(NavHost / NavController)
4.4 类型安全路由(Kotlin Serialization)
4.5 Deep Link 与 App Link
4.6 多模块导航与嵌套导航图

核心架构

复制代码
NavController(导航控制器)
    │
    ├── NavGraph(导航图:目的地 + 路径的集合)
    │     ├── NavDestination(目的地:Fragment / Activity / Dialog)
    │     └── NavAction(动作:目的地 + 参数 + 动画)
    │
    └── NavBackStack(返回栈:记录导航历史)

NavHost(容器:承载目的地 UI 的 Fragment/Composable)
kotlin 复制代码
// build.gradle.kts
dependencies {
    val navVersion = "2.8.0"
    implementation("androidx.navigation:navigation-fragment-ktx:$navVersion")
    implementation("androidx.navigation:navigation-ui-ktx:$navVersion")
    implementation("androidx.navigation:navigation-compose:$navVersion")
}
xml 复制代码
<!-- res/navigation/main_nav_graph.xml -->
<?xml version="1.0" encoding="utf-8"?>
<navigation
    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:id="@+id/main_nav_graph"
    app:startDestination="@id/homeFragment">

    <!-- 主页 -->
    <fragment
        android:id="@+id/homeFragment"
        android:name="com.example.ui.home.HomeFragment"
        android:label="首页"
        tools:layout="@layout/fragment_home">

        <!-- 跳转到产品详情 -->
        <action
            android:id="@+id/action_home_to_detail"
            app:destination="@id/productDetailFragment"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />

        <!-- Deep Link -->
        <deepLink
            android:id="@+id/deepLinkHome"
            app:uri="https://www.example.com/home" />
    </fragment>

    <!-- 产品详情 -->
    <fragment
        android:id="@+id/productDetailFragment"
        android:name="com.example.ui.detail.ProductDetailFragment"
        android:label="产品详情"
        tools:layout="@layout/fragment_product_detail">

        <!-- 参数定义 -->
        <argument
            android:name="productId"
            app:argType="integer"
            android:defaultValue="-1" />
        <argument
            android:name="productName"
            app:argType="string"
            android:defaultValue="" />

        <deepLink
            android:id="@+id/deepLinkDetail"
            app:uri="https://www.example.com/product/{productId}" />
    </fragment>

    <!-- 购物车(弹出对话框)-->
    <dialog
        android:id="@+id/cartBottomSheet"
        android:name="com.example.ui.cart.CartBottomSheetFragment" />

    <!-- 嵌套 NavGraph(登录子图)-->
    <navigation
        android:id="@+id/authGraph"
        android:label="认证流程"
        app:startDestination="@id/loginFragment">
        <fragment android:id="@+id/loginFragment"
            android:name="com.example.ui.auth.LoginFragment" />
        <fragment android:id="@+id/registerFragment"
            android:name="com.example.ui.auth.RegisterFragment" />
    </navigation>

</navigation>

4.2 Fragment 导航与返回栈管理

kotlin 复制代码
// MainActivity.kt - 设置 NavController
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var navController: NavController

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 获取 NavController
        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHostFragment.navController

        // 设置 Toolbar(自动处理标题和返回按钮)
        val appBarConfig = AppBarConfiguration(
            // 顶级目的地(不显示返回按钮)
            topLevelDestinations = setOf(
                R.id.homeFragment,
                R.id.searchFragment,
                R.id.profileFragment
            )
        )
        setupActionBarWithNavController(navController, appBarConfig)

        // 绑定底部导航
        binding.bottomNavigation.setupWithNavController(navController)

        // 监听导航变化
        navController.addOnDestinationChangedListener { _, destination, _ ->
            when (destination.id) {
                R.id.homeFragment, R.id.searchFragment -> {
                    binding.bottomNavigation.isVisible = true
                }
                else -> {
                    binding.bottomNavigation.isVisible = false
                }
            }
        }
    }

    override fun onSupportNavigateUp(): Boolean {
        return navController.navigateUp() || super.onSupportNavigateUp()
    }
}

// HomeFragment.kt - 触发导航
class HomeFragment : Fragment(R.layout.fragment_home) {

    // Safe Args 生成的方向类(推荐,类型安全)
    // kapt("androidx.navigation:navigation-safe-args-gradle-plugin:$navVersion")
    private val navController by lazy { findNavController() }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.btnGoToDetail.setOnClickListener {
            // ✅ 使用 Safe Args(类型安全)
            val action = HomeFragmentDirections.actionHomeToDetail(
                productId = 1001,
                productName = "Android 深度指南"
            )
            navController.navigate(action)
        }

        // 带 NavOptions 的导航(控制返回栈)
        binding.btnGoToHome.setOnClickListener {
            navController.navigate(
                R.id.homeFragment,
                null,
                NavOptions.Builder()
                    .setPopUpTo(R.id.homeFragment, inclusive = false) // 清空至主页
                    .setLaunchSingleTop(true) // 防止重复创建
                    .build()
            )
        }
    }

    private lateinit var binding: FragmentHomeBinding
}

// ProductDetailFragment.kt - 接收参数
class ProductDetailFragment : Fragment(R.layout.fragment_product_detail) {

    // Safe Args 自动生成
    private val args: ProductDetailFragmentArgs by navArgs()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val productId = args.productId
        val productName = args.productName

        binding.tvTitle.text = productName
        loadProduct(productId)
    }

    // 返回结果给上一个页面(Fragment Result API)
    private fun onAddToCartSuccess() {
        setFragmentResult("add_to_cart", bundleOf("product_id" to args.productId))
        findNavController().popBackStack()
    }

    private fun loadProduct(id: Int) { /* ... */ }
    private lateinit var binding: FragmentProductDetailBinding
}

// HomeFragment 接收返回结果
class HomeFragmentReceiver : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // ✅ Fragment Result API(替代 startActivityForResult)
        setFragmentResultListener("add_to_cart") { _, bundle ->
            val productId = bundle.getInt("product_id")
            showToast("商品 $productId 已加入购物车")
        }
    }

    private fun showToast(message: String) { /* ... */ }
}

基础配置

kotlin 复制代码
// 路由常量定义
object Routes {
    const val HOME = "home"
    const val PRODUCT_LIST = "product_list"
    const val PRODUCT_DETAIL = "product_detail/{productId}"
    const val PROFILE = "profile"
    const val CART = "cart"
    const val SEARCH = "search?query={query}"

    fun productDetail(productId: Int) = "product_detail/$productId"
    fun search(query: String = "") = "search?query=$query"
}

// 主 NavHost 配置
@Composable
fun AppNavHost(
    navController: NavHostController = rememberNavController(),
    startDestination: String = Routes.HOME
) {
    NavHost(
        navController = navController,
        startDestination = startDestination,
        // 全局过渡动画
        enterTransition = {
            slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Left, tween(300))
        },
        exitTransition = {
            slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Left, tween(300))
        },
        popEnterTransition = {
            slideIntoContainer(AnimatedContentTransitionScope.SlideDirection.Right, tween(300))
        },
        popExitTransition = {
            slideOutOfContainer(AnimatedContentTransitionScope.SlideDirection.Right, tween(300))
        }
    ) {
        composable(Routes.HOME) {
            HomeScreen(
                onProductClick = { productId ->
                    navController.navigate(Routes.productDetail(productId))
                },
                onSearchClick = {
                    navController.navigate(Routes.search())
                }
            )
        }

        composable(
            route = Routes.PRODUCT_DETAIL,
            arguments = listOf(
                navArgument("productId") {
                    type = NavType.IntType
                    defaultValue = -1
                }
            ),
            deepLinks = listOf(
                navDeepLink {
                    uriPattern = "https://www.example.com/product/{productId}"
                }
            )
        ) { backStackEntry ->
            val productId = backStackEntry.arguments?.getInt("productId") ?: -1
            ProductDetailScreen(
                productId = productId,
                onNavigateUp = { navController.popBackStack() },
                onCartClick = { navController.navigate(Routes.CART) }
            )
        }

        composable(
            route = Routes.SEARCH,
            arguments = listOf(
                navArgument("query") {
                    type = NavType.StringType
                    defaultValue = ""
                    nullable = true
                }
            )
        ) { backStackEntry ->
            val query = backStackEntry.arguments?.getString("query") ?: ""
            SearchScreen(initialQuery = query)
        }

        // 嵌套导航图(认证流程)
        navigation(
            route = "auth",
            startDestination = "login"
        ) {
            composable("login") {
                LoginScreen(
                    onLoginSuccess = {
                        navController.navigate(Routes.HOME) {
                            popUpTo("auth") { inclusive = true }
                        }
                    },
                    onRegisterClick = {
                        navController.navigate("register")
                    }
                )
            }
            composable("register") {
                RegisterScreen(
                    onRegisterSuccess = {
                        navController.popBackStack("login", inclusive = false)
                    }
                )
            }
        }
    }
}

// 带底部导航的 Scaffold
@Composable
fun MainScaffold() {
    val navController = rememberNavController()
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = navBackStackEntry?.destination?.route

    val bottomNavItems = listOf(
        BottomNavItem(Routes.HOME, Icons.Default.Home, "首页"),
        BottomNavItem(Routes.PRODUCT_LIST, Icons.Default.ShoppingBag, "购物"),
        BottomNavItem(Routes.CART, Icons.Default.ShoppingCart, "购物车"),
        BottomNavItem(Routes.PROFILE, Icons.Default.Person, "我的")
    )

    // 只在顶级页面显示底部导航
    val showBottomNav = bottomNavItems.any { it.route == currentRoute }

    Scaffold(
        bottomBar = {
            AnimatedVisibility(visible = showBottomNav) {
                NavigationBar {
                    bottomNavItems.forEach { item ->
                        NavigationBarItem(
                            selected = currentRoute == item.route,
                            onClick = {
                                navController.navigate(item.route) {
                                    // 避免多次压栈
                                    popUpTo(navController.graph.findStartDestination().id) {
                                        saveState = true
                                    }
                                    launchSingleTop = true
                                    restoreState = true
                                }
                            },
                            icon = { Icon(item.icon, contentDescription = item.label) },
                            label = { Text(item.label) }
                        )
                    }
                }
            }
        }
    ) { padding ->
        AppNavHost(
            navController = navController,
            modifier = Modifier.padding(padding)
        )
    }
}

data class BottomNavItem(val route: String, val icon: ImageVector, val label: String)

传递返回结果(Compose)

kotlin 复制代码
// 子页面设置结果
@Composable
fun AddressPickerScreen(navController: NavController) {
    val addresses = listOf("北京市朝阳区", "上海市浦东新区")

    LazyColumn {
        items(addresses) { address ->
            ListItem(
                headlineContent = { Text(address) },
                modifier = Modifier.clickable {
                    // ✅ 通过 previousBackStackEntry 传递结果
                    navController.previousBackStackEntry
                        ?.savedStateHandle
                        ?.set("selected_address", address)
                    navController.popBackStack()
                }
            )
        }
    }
}

// 父页面接收结果
@Composable
fun CheckoutScreen(navController: NavController) {
    // ✅ 从 currentBackStackEntry.savedStateHandle 接收
    val selectedAddress by navController.currentBackStackEntry
        ?.savedStateHandle
        ?.getStateFlow("selected_address", "")
        ?.collectAsStateWithLifecycle()
        ?: remember { mutableStateOf("") }

    Column {
        Text("收货地址: $selectedAddress")
        Button(onClick = { navController.navigate("address_picker") }) {
            Text("选择地址")
        }
    }
}

4.4 类型安全路由(Kotlin Serialization)

Navigation 2.8.0+ 支持基于 Kotlin Serialization 的类型安全路由,彻底告别字符串路由。

kotlin 复制代码
// build.gradle.kts
plugins {
    kotlin("plugin.serialization") version "2.0.0"
}
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
    implementation("androidx.navigation:navigation-compose:2.8.0")
}

// 定义类型安全路由(无需字符串)
@Serializable
object HomeRoute

@Serializable
object CartRoute

@Serializable
data class ProductDetailRoute(val productId: Int, val productName: String)

@Serializable
data class SearchRoute(val query: String = "")

// NavHost 使用类型安全路由
@Composable
fun TypeSafeNavHost() {
    val navController = rememberNavController()

    NavHost(navController = navController, startDestination = HomeRoute) {
        composable<HomeRoute> {
            HomeScreen(
                onProductClick = { id, name ->
                    navController.navigate(ProductDetailRoute(id, name))
                }
            )
        }

        composable<ProductDetailRoute> { backStackEntry ->
            // ✅ 直接获取类型安全的路由参数(无需手动解析)
            val route = backStackEntry.toRoute<ProductDetailRoute>()
            ProductDetailScreen(
                productId = route.productId,
                productName = route.productName,
                onNavigateUp = { navController.popBackStack() }
            )
        }

        composable<SearchRoute> { backStackEntry ->
            val route = backStackEntry.toRoute<SearchRoute>()
            SearchScreen(initialQuery = route.query)
        }

        composable<CartRoute> {
            CartScreen(onNavigateUp = { navController.popBackStack() })
        }
    }
}

配置 App Links(HTTP Deep Link)

xml 复制代码
<!-- AndroidManifest.xml -->
<activity
    android:name=".ui.main.MainActivity"
    android:exported="true">

    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="https"
            android:host="www.example.com"
            android:pathPrefix="/product/" />
    </intent-filter>

    <!-- 自定义 Scheme(不需要 autoVerify)-->
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="myapp"
            android:host="product" />
    </intent-filter>
</activity>
kotlin 复制代码
// 处理 Deep Link(Compose Navigation)
@Composable
fun AppNavHost() {
    val navController = rememberNavController()

    NavHost(navController, startDestination = HomeRoute) {
        composable<ProductDetailRoute>(
            deepLinks = listOf(
                navDeepLink { uriPattern = "https://www.example.com/product/{productId}" },
                navDeepLink { uriPattern = "myapp://product?id={productId}" }
            )
        ) { backStackEntry ->
            val route = backStackEntry.toRoute<ProductDetailRoute>()
            ProductDetailScreen(productId = route.productId, productName = route.productName, onNavigateUp = {})
        }
    }
}

// MainActivity 处理 Deep Link
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            val navController = rememberNavController()

            // ✅ handleDeepLink 自动将 intent 中的 URI 映射到对应页面
            LaunchedEffect(Unit) {
                intent?.let { navController.handleDeepLink(it) }
            }

            AppNavHost(navController = navController)
        }
    }

    // 处理已运行时的 Deep Link(如推送通知)
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        // navController.handleDeepLink(intent) // 在 Composable 中通过 LaunchedEffect 处理
    }
}

生成 assetlinks.json(验证 App Link)

json 复制代码
// 需要托管到:https://www.example.com/.well-known/assetlinks.json
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.myapp",
      "sha256_cert_fingerprints": [
        "AA:BB:CC:DD:..."
      ]
    }
  }
]
bash 复制代码
# 获取签名 SHA-256(用于 assetlinks.json)
keytool -list -v -keystore your-release-key.jks -alias your-alias

4.6 多模块导航与嵌套导航图

多模块导航架构

复制代码
:app
  └── AppNavHost(组合所有模块路由)

:feature:home
  └── homeNavGraph(extension function)

:feature:product  
  └── productNavGraph(extension function)

:feature:auth
  └── authNavGraph(extension function)

:core:navigation(共享路由定义)
  └── Routes / Destination(各模块共享)
kotlin 复制代码
// :core:navigation 模块
// Destinations.kt(所有模块共享)
@Serializable object HomeDestination
@Serializable data class ProductDetailDestination(val productId: Int)
@Serializable object CartDestination
@Serializable object LoginDestination

// NavigationActions 接口(各模块实现)
interface HomeNavigationActions {
    fun navigateToProductDetail(productId: Int)
    fun navigateToCart()
    fun navigateToSearch()
}

// :feature:home 模块
// homeNavGraph.kt
fun NavGraphBuilder.homeNavGraph(
    navController: NavController
) {
    composable<HomeDestination> {
        HomeScreen(
            onProductClick = { productId ->
                navController.navigate(ProductDetailDestination(productId))
            },
            onCartClick = {
                navController.navigate(CartDestination)
            }
        )
    }
}

// :feature:product 模块
fun NavGraphBuilder.productNavGraph(
    navController: NavController
) {
    composable<ProductDetailDestination> { backStackEntry ->
        val dest = backStackEntry.toRoute<ProductDetailDestination>()
        ProductDetailScreen(
            productId = dest.productId,
            onNavigateUp = { navController.popBackStack() }
        )
    }
}

// :app 模块
// AppNavHost.kt
@Composable
fun AppNavHost(navController: NavHostController) {
    NavHost(navController, startDestination = HomeDestination) {
        homeNavGraph(navController)
        productNavGraph(navController)
        authNavGraph(navController)
        cartNavGraph(navController)
    }
}

Demo 代码:chapter04

kotlin 复制代码
// chapter04/NavigationDemo.kt
package com.example.androiddemos.chapter04

import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.*
import kotlinx.serialization.Serializable

// 类型安全路由
@Serializable object DemoHome
@Serializable data class DemoDetail(val id: Int, val title: String)
@Serializable object DemoCart

@Composable
fun Chapter04NavigationDemo() {
    val navController = rememberNavController()
    val currentEntry by navController.currentBackStackEntryAsState()

    val showBottomBar = currentEntry?.destination?.route in listOf(
        DemoHome::class.qualifiedName,
        DemoCart::class.qualifiedName
    )

    Scaffold(
        bottomBar = {
            if (showBottomBar) {
                NavigationBar {
                    NavigationBarItem(
                        selected = currentEntry?.destination?.route == DemoHome::class.qualifiedName,
                        onClick = {
                            navController.navigate(DemoHome) {
                                launchSingleTop = true
                                restoreState = true
                            }
                        },
                        icon = { Icon(Icons.Default.Home, null) },
                        label = { Text("首页") }
                    )
                    NavigationBarItem(
                        selected = currentEntry?.destination?.route == DemoCart::class.qualifiedName,
                        onClick = {
                            navController.navigate(DemoCart) {
                                launchSingleTop = true
                                restoreState = true
                            }
                        },
                        icon = { Icon(Icons.Default.ShoppingCart, null) },
                        label = { Text("购物车") }
                    )
                }
            }
        }
    ) { padding ->
        NavHost(
            navController = navController,
            startDestination = DemoHome,
            modifier = Modifier.padding(padding)
        ) {
            composable<DemoHome> {
                DemoHomeScreen(
                    onItemClick = { id ->
                        navController.navigate(DemoDetail(id, "产品 #$id"))
                    }
                )
            }
            composable<DemoDetail> { entry ->
                val route = entry.toRoute<DemoDetail>()
                DemoDetailScreen(
                    detail = route,
                    onBack = { navController.popBackStack() }
                )
            }
            composable<DemoCart> {
                DemoCartScreen()
            }
        }
    }
}

@Composable
private fun DemoHomeScreen(onItemClick: (Int) -> Unit) {
    Column(
        modifier = Modifier.fillMaxSize().padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        Text("导航 Demo - 首页", style = MaterialTheme.typography.headlineSmall)
        Text("点击产品跳转到详情页(类型安全路由)", style = MaterialTheme.typography.bodyMedium)
        Spacer(Modifier.height(8.dp))
        repeat(5) { index ->
            Card(
                modifier = Modifier.fillMaxWidth(),
                onClick = { onItemClick(index + 1) }
            ) {
                Row(
                    modifier = Modifier.padding(16.dp),
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text("产品 #${index + 1}")
                    Icon(Icons.Default.ChevronRight, contentDescription = null)
                }
            }
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun DemoDetailScreen(detail: DemoDetail, onBack: () -> Unit) {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text(detail.title) },
                navigationIcon = {
                    IconButton(onClick = onBack) {
                        Icon(Icons.Default.ArrowBack, contentDescription = "返回")
                    }
                }
            )
        }
    ) { padding ->
        Column(
            modifier = Modifier.padding(padding).padding(16.dp).fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text("产品 ID: ${detail.id}", style = MaterialTheme.typography.headlineMedium)
            Spacer(Modifier.height(8.dp))
            Text(detail.title, style = MaterialTheme.typography.bodyLarge)
            Spacer(Modifier.height(24.dp))
            Button(onClick = onBack) { Text("返回首页") }
        }
    }
}

@Composable
private fun DemoCartScreen() {
    Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        Text("购物车页面", style = MaterialTheme.typography.headlineSmall)
    }
}

章节总结

知识点 必掌握程度 面试频率
Navigation Component 核心概念 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Fragment 返回栈管理 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Safe Args 类型安全参数 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Compose Navigation 基础 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
类型安全路由(Serialization) ⭐⭐⭐⭐ ⭐⭐⭐
Deep Link / App Link ⭐⭐⭐⭐ ⭐⭐⭐⭐
Fragment Result API ⭐⭐⭐⭐ ⭐⭐⭐
多模块导航架构 ⭐⭐⭐⭐ ⭐⭐⭐

👉 下一章:第五章------网络与数据持久化

相关推荐
2501_916007473 小时前
iOS逆向工程:详细解析ptrace反调试机制的破解方法与实战步骤
android·macos·ios·小程序·uni-app·cocoa·iphone
空中海4 小时前
第三章:状态管理与 Jetpack 架构组件
android·架构
峥嵘life4 小时前
Android + Kiro AI软件开发实战教程
android·后端·学习
流星雨在线4 小时前
安卓路由技术选型调研
android·therouter·arouter
千里马学框架4 小时前
Ubuntu 24 搭建aosp源码环境详细笔记
android·linux·ubuntu·framework·安卓·aosp·源码环境
空中海5 小时前
安卓 第六章:主题、样式与国际化
android
Ehtan_Zheng5 小时前
7个Kotlin Delegate
android
用户69371750013845 小时前
2026 Android 开发,现在还能入行吗?
android·前端·ai编程
YBZha5 小时前
Android Camera2 + OpenGL 竖屏或横屏预览会有“轻微拉伸”
android