记录 Android 面试题, 有时间过来翻翻。

博主博客

目录

  • 四十一、什么是硬件加速?注意事项?
  • 四十二、ContentProvider如何进行权限控制?
  • 四十三、Fragment状态如何保存?
  • 四十四、Activity中创建的线程与Service中创建的线程有何区别?
  • 四十五、如何计算Bitmap内存占用?如何避免OOM?
  • 四十六、如何实现应用更新(灰度、强制、增量)?
  • 四十七、Android为什么需要签名?
  • 四十八、bindService如何与Activity生命周期联动?
  • 四十九、如何使用Gradle配置多渠道包?
  • 五十、Activity与Fragment、Fragment间如何通信?

四十一、什么是硬件加速?注意事项?

(一)核心概念与原理

1. 硬件加速的定义

硬件加速(Hardware Acceleration)是指利用设备的图形处理单元(GPU)来执行视图的绘制和渲染工作,以替代传统的中央处理器(CPU)软件绘制模式。这是Android系统提升图形渲染性能的核心技术。

2. 工作原理对比

// 软件绘制(CPU渲染) vs 硬件加速(GPU渲染)

// ❌ 软件绘制流程(Android 4.0前主要方式)
1. View.invalidate() → 2. 标记脏区域 → 3. 遍历View树
4. 每个View调用onDraw(Canvas) → 5. CPU执行绘制指令
6. 结果写入位图 → 7. 位图上传到GPU → 8. 屏幕显示

// ✅ 硬件加速流程(现代Android默认)
1. View.invalidate() → 2. 记录绘制操作到显示列表(DisplayList)
3. 显示列表上传到GPU → 4. GPU并行执行绘制命令
5. 结果直接渲染到帧缓冲区 → 6. 屏幕显示

关键组件

  • DisplayList:存储绘制命令序列的中间表示
  • RenderNode:封装View的绘制属性和显示列表
  • RenderThread:专门处理渲染的独立线程(Android 5.0+)

3. 版本演进历史

Android版本 硬件加速状态 重要变化
3.0(API 11) 可选开启 首次引入硬件加速,需手动启用
4.0(API 14) 默认开启(Activity级别) 成为默认渲染方式
5.0(API 21) 全面优化 引入RenderThread,分离UI线程与渲染线程
8.0(API 26) 性能提升 优化了字体渲染和图形管线
10.0(API 29) Vulkan支持 可选Vulkan后端,进一步降低驱动开销

(二)硬件加速的优势与局限性

1. 主要优势

性能提升

  • 流畅度:60fps的流畅动画成为可能
  • 并行计算:GPU擅长并行处理大量图形操作
  • 离屏缓存:自动管理图层合成,减少重绘
  • 电源效率:特定操作在GPU上更节能

视觉质量

  • 抗锯齿:更好地支持几何图形的抗锯齿
  • 阴影效果:实时阴影和模糊效果
  • 3D变换:流畅的3D旋转、缩放动画

2. 已知限制与不支持的绘制操作

(1)完全不支持的Canvas操作
// ❌ 以下操作在硬件加速下会被忽略或抛出异常
class UnsupportedHardwareView : View() {
    override fun onDraw(canvas: Canvas) {
        // 1. 裁剪路径(最常遇到的问题)
        val path = Path().apply { addCircle(100f, 100f, 50f, Path.Direction.CW) }
        canvas.clipPath(path) // 部分设备上无效或异常
        
        // 2. 路径文字绘制
        canvas.drawTextOnPath("Hello", path, 0f, 0f, paint)
        
        // 3. 绘制Picture
        val picture = Picture()
        canvas.drawPicture(picture)
        
        // 4. 某些混合模式
        paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
        
        // 5. 图层过滤
        paint.maskFilter = BlurMaskFilter(10f, BlurMaskFilter.Blur.NORMAL)
    }
}
(2)部分支持但有性能影响的操作
class ExpensiveHardwareView : View() {
    override fun onDraw(canvas: Canvas) {
        // 1. 大量小文本绘制 - 每个drawText()都是单独调用
        repeat(1000) { i ->
            canvas.drawText("Item $i", 0f, i * 20f, paint)
        }
        
        // 2. 复杂Path绘制
        val complexPath = Path().apply {
            // 包含大量曲线和点的路径
            cubicTo(/* 控制点 */)
        }
        canvas.drawPath(complexPath, paint) // 需要CPU光栅化
        
        // 3. 频繁修改的Bitmap
        canvas.drawBitmap(dynamicBitmap, 0f, 0f, paint)
        // 每次修改都需重新上传纹理到GPU
    }
}

(三)控制硬件加速的方法

1. 多层级控制策略

(1)Application级别控制(AndroidManifest.xml)
<application 
    android:hardwareAccelerated="true" <!-- 默认值 -->
    android:allowBackup="true">
    
    <!-- 单个Activity禁用硬件加速 -->
    <activity 
        android:name=".SoftwareDrawingActivity"
        android:hardwareAccelerated="false">
    </activity>
</application>
(2)Window级别控制(Java/Kotlin)
// 在Activity中
class CustomActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // 方法1:禁用整个Window的硬件加速(罕见需求)
        window.setFlags(
            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
        )
        
        // 方法2:代码控制(API 11+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            window.setFlags(
                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
            )
        }
    }
}
(3)View级别控制(最常用)
class HybridView : View() {
    init {
        // 方法1:完全禁用此View的硬件加速
        setLayerType(LAYER_TYPE_SOFTWARE, null)
        
        // 方法2:使用硬件图层但软件渲染(特殊情况)
        // setLayerType(LAYER_TYPE_HARDWARE, null) // 缓存到纹理
        // setLayerType(LAYER_TYPE_SOFTWARE, null) // 禁用硬件加速
        
        // 方法3:XML中控制(API 11+)
        // android:layerType="software" 或 "hardware" 或 "none"
    }
    
    override fun onDraw(canvas: Canvas) {
        // 临时切换到软件绘制执行特定操作
        val saved = canvas.isHardwareAccelerated
        if (hasUnsupportedOperation) {
            setLayerType(LAYER_TYPE_SOFTWARE, null)
            drawUnsupportedContent(canvas)
            setLayerType(LAYER_TYPE_HARDWARE, null)
        } else {
            drawNormalContent(canvas)
        }
    }
}

2. 现代替代方案:使用Canvas的不同实现

fun drawWithCompatibility(canvas: Canvas) {
    // 检测当前Canvas是否支持硬件加速
    when {
        canvas.isHardwareAccelerated -> {
            // 硬件加速Canvas
            drawHardwareAcceleratedContent(canvas)
        }
        canvas is RecordingCanvas -> {
            // Android 5.0+的录制Canvas(用于DisplayList)
            drawModernContent(canvas)
        }
        else -> {
            // 软件Canvas
            drawSoftwareCompatibleContent(canvas)
        }
    }
}

(四)检测与调试硬件加速

1. 运行时检测

// 检测View是否启用硬件加速
fun checkHardwareAcceleration(view: View) {
    Log.d("HardwareAccel", "View加速状态: ${view.isHardwareAccelerated}")
    Log.d("HardwareAccel", "Canvas加速状态: ${view.canvas?.isHardwareAccelerated}")
    Log.d("HardwareAccel", "Layer类型: ${view.layerType}")
    
    // 检查整个窗口
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        Log.d("HardwareAccel", 
            "Window加速: ${view.window?.attributes?.flags?.let { 
                it and WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED != 0
            }}")
    }
}

2. 使用开发者选项

  1. 显示硬件层更新:硬件加速的View会用绿色高亮
  2. 调试GPU过度绘制:识别渲染性能问题
  3. Profile GPU Rendering:分析每帧的渲染时间
  4. 启用4x MSAA:强制开启多重采样抗锯齿(OpenGL ES 2.0+)

3. 代码级性能分析

class HardwareAccelProfiler {
    fun profileViewDrawing(view: View) {
        // 方法1:使用Trace
        Trace.beginSection("HardwareAccelProfile")
        view.invalidate()
        view.post {
            Trace.endSection()
        }
        
        // 方法2:计算绘制时间
        val start = System.nanoTime()
        view.draw(Canvas()) // 注意:这不会触发实际渲染
        val duration = System.nanoTime() - start
        Log.d("Profiler", "绘制耗时: ${duration / 1_000_000}ms")
    }
}

(五)最佳实践与优化建议

1. 自定义View的兼容性设计

class CompatibleCustomView(context: Context) : View(context) {
    private var useSoftwareRendering = false
    
    override fun onDraw(canvas: Canvas) {
        // 方案1:根据API级别选择实现
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            drawLegacyImplementation(canvas)
            return
        }
        
        // 方案2:检测并回退到软件渲染
        if (canvas.isHardwareAccelerated && hasUnsupportedOperations) {
            drawSoftwareFallback(canvas)
        } else {
            drawHardwareOptimized(canvas)
        }
    }
    
    private fun drawHardwareOptimized(canvas: Canvas) {
        // 使用GPU友好的绘制方式
        // 1. 优先使用矩形、圆形等基本图形
        // 2. 减少Path的使用,特别是复杂Path
        // 3. 合并多个绘制操作
        // 4. 使用缓存Bitmap
    }
    
    private fun drawSoftwareFallback(canvas: Canvas) {
        // 临时切换到软件渲染
        setLayerType(LAYER_TYPE_SOFTWARE, null)
        super.onDraw(canvas)
        setLayerType(LAYER_TYPE_HARDWARE, null)
    }
}

2. 性能优化技巧

(1)减少过度绘制
class OptimizedView : View() {
    override fun onDraw(canvas: Canvas) {
        // ❌ 避免:每次绘制完整背景
        // canvas.drawColor(Color.WHITE)
        
        // ✅ 优化:只绘制变化区域
        if (backgroundChanged) {
            canvas.drawRect(dirtyRect, backgroundPaint)
        }
        
        // ✅ 使用canvas.clipRect()限制绘制区域
        canvas.save()
        canvas.clipRect(visibleRect)
        drawContent(canvas)
        canvas.restore()
    }
}
(2)合理使用图层缓存
fun manageLayersIntelligently(view: View) {
    // 场景1:执行动画时启用硬件图层
    view.animate().rotation(360f).apply {
        withLayer() // 自动管理图层状态
        start()
    }
    
    // 场景2:复杂但静态的内容
    if (view.hasComplexStaticContent) {
        view.setLayerType(LAYER_TYPE_HARDWARE, null) // 缓存为纹理
    }
    
    // 场景3:频繁变化的内容 - 避免硬件图层
    if (view.contentChangesFrequently) {
        view.setLayerType(LAYER_TYPE_NONE, null) // 禁用缓存
    }
}

3. 现代Android开发的考虑

(1)Jetpack Compose中的硬件加速
@Composable
fun HardwareAcceleratedCompose() {
    Canvas(modifier = Modifier.fillMaxSize()) {
        // Compose底层自动使用硬件加速
        // 但开发者仍需注意绘制操作的性能
        
        // ❌ 避免在每帧创建新对象
        val path = remember { Path() }
        path.reset()
        path.addCircle(size.width / 2, size.height / 2, 50f)
        
        drawPath(
            path = path,
            color = Color.Blue,
            style = Stroke(2.dp.toPx())
        )
    }
}
(2)RenderEffect API(Android 12+)
// 使用硬件加速的渲染效果
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    val blurEffect = RenderEffect.createBlurEffect(
        10f, 10f, 
        Shader.TileMode.MIRROR
    )
    view.setRenderEffect(blurEffect) // GPU加速的模糊效果
}

(六)常见问题与解决方案

1. Q:为什么开启硬件加速后性能反而下降?

A:可能原因及解决方案:

  • 原因1:使用了不支持硬件加速的绘制操作
    • 解决:检测并替换为兼容API,或临时禁用硬件加速
  • 原因2:纹理上传开销过大(频繁修改Bitmap)
    • 解决:使用Bitmap.copy(Config.HARDWARE)或纹理缓存
  • 原因3:过度使用硬件图层
    • 解决:仅在必要时使用LAYER_TYPE_HARDWARE

2. Q:如何确保自定义View在所有设备上表现一致?

A:兼容性策略:

fun ensureConsistentRendering(view: View) {
    // 1. 使用View的绘制回调而不是直接操作Canvas
    ViewCompat.setLayerType(view, ViewCompat.LAYER_TYPE_HARDWARE, null)
    
    // 2. 使用支持库的兼容API
    if (ViewCompat.isHardwareAccelerated(view)) {
        // 硬件加速路径
    } else {
        // 软件回退路径
    }
    
    // 3. 提供配置选项
    view.tag = if (useHardwareAccel) "hardware" else "software"
}

3. Q:硬件加速与内存使用的关系?

A:内存管理要点:

  • 纹理内存:GPU需要存储纹理数据,大Bitmap会占用显存
  • 显示列表:每个View需要存储绘制命令,复杂层级增加内存
  • 图层缓存LAYER_TYPE_HARDWARE会创建纹理缓存,增加内存使用

监控方法

// 使用adb命令监控
// adb shell dumpsys gfxinfo <package_name>

// 代码中检测
Debug.getMemoryInfo(ActivityManager.MemoryInfo())

(七)面试回答要点总结

  1. 核心定义:利用GPU进行视图绘制,替代CPU软件渲染
  2. 默认状态:Android 4.0(API 14)起默认开启
  3. 主要优势
    • 提升渲染性能,支持60fps动画
    • GPU并行处理,效率更高
    • 更好的视觉效果(抗锯齿、阴影等)
  4. 关键限制
    • 部分Canvas操作不支持(clipPath、drawTextOnPath等)
    • 需要额外的GPU内存
    • 可能增加电池消耗(复杂的GPU操作)
  5. 控制方法
    • Application/Activity级别:AndroidManifest.xml配置
    • View级别:setLayerType(LAYER_TYPE_SOFTWARE, null)
    • 代码控制:检测Canvas类型,选择不同绘制策略
  6. 最佳实践
    • 优先使用硬件加速,必要时才禁用
    • 自定义View提供软件回退方案
    • 避免在每帧创建新对象
    • 合理使用图层缓存
  7. 调试工具
    • 开发者选项中的GPU调试工具
    • Profile GPU Rendering
    • Traceview和Systrace

现代开发建议
在Jetpack Compose时代,硬件加速由框架自动管理,但开发者仍需了解底层原理以优化性能。对于传统View系统,建议采用渐进式增强策略:默认使用硬件加速,为不支持的操作提供软件回退,并通过性能分析工具持续优化。

四十二、ContentProvider如何进行权限控制?

(一)权限控制体系概述

1. ContentProvider权限控制层级

ContentProvider采用四级权限控制体系,从粗粒度到细粒度依次为:

控制层级 作用范围 适用场景 声明方式
全局权限 整个 Provider 简单应用场景 android:permission
读写分离权限 整个 Provider 读写操作需要不同权限 android:readPermission
android:writePermission
路径级权限 特定 URI 路径 不同表/记录集不同权限 <path-permission>
临时权限 单次 URI 访问 分享给其他应用临时访问 grantUriPermissions

2. 现代Android权限控制架构

// 权限检查的核心流程
class SecureContentProvider : ContentProvider() {
    override fun query(uri: Uri, ...): Cursor? {
        // 1. 系统自动检查声明的权限
        // 2. 开发者可添加自定义权限检查
        checkCallingPermission("自定义权限")
        
        // 3. 根据URI路径进行细粒度控制
        when (matcher.match(uri)) {
            USERS_CODE -> checkUserAccess(uri)
            ADMIN_CODE -> requireAdminPermission()
        }
        
        // 4. 返回数据(可能过滤敏感字段)
        return filteredCursor(baseCursor)
    }
}

(二)权限声明与配置

1. 基础权限声明(AndroidManifest.xml)

(1)全局统一权限
<!-- 最简单但最不灵活:所有操作需要同一权限 -->
<provider
    android:name=".MyContentProvider"
    android:authorities="com.example.app.provider"
    android:exported="true"
    android:permission="com.example.app.PERMISSION_ACCESS_PROVIDER" />
(2)读写分离权限(推荐)
<provider
    android:name=".SecureContentProvider"
    android:authorities="com.example.app.provider"
    android:exported="true"
    android:readPermission="com.example.app.READ_DATA"
    android:writePermission="com.example.app.WRITE_DATA"
    
    <!-- 启用临时权限授予 -->
    android:grantUriPermissions="true">
    
    <!-- 路径级权限控制 -->
    <path-permission
        android:path="/users"
        android:permission="com.example.app.ACCESS_USERS"
        android:readPermission="com.example.app.READ_USERS"
        android:writePermission="com.example.app.WRITE_USERS" />
    
    <!-- 特定路径完全禁止外部访问 -->
    <path-permission
        android:path="/admin"
        android:permission="" /> <!-- 空字符串表示无权限可访问 -->
    
    <!-- 权限组定义(可选) -->
    <meta-data
        android:name="android.content.requiredPermissions"
        android:resource="@array/required_permissions" />
</provider>

2. 权限数组资源

<!-- res/values/arrays.xml -->
<resources>
    <string-array name="required_permissions">
        <item>com.example.app.READ_DATA</item>
        <item>com.example.app.WRITE_DATA</item>
        <item>android.permission.INTERNET</item>
    </string-array>
</resources>

(三)路径级细粒度控制

1. URI路径匹配模式

<provider
    android:name=".MultiTableProvider"
    android:authorities="com.example.app.provider"
    android:exported="true"
    android:grantUriPermissions="true">
    
    <!-- 模式1:前缀匹配 -->
    <path-permission
        android:pathPrefix="/public/"
        android:permission="com.example.app.ACCESS_PUBLIC" />
    
    <!-- 模式2:精确路径 -->
    <path-permission
        android:path="/users/admin"
        android:permission="com.example.app.ACCESS_ADMIN"
        android:readPermission="com.example.app.READ_ADMIN" />
    
    <!-- 模式3:通配符(注意:path不支持通配符,需通过代码实现) -->
    <!-- 实际通过UriMatcher在代码中控制 -->
</provider>

2. 代码中的路径权限验证

class MultiTableProvider : ContentProvider() {
    private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
        addURI(authority, "public/*", PUBLIC_CODE)
        addURI(authority, "users/#", USER_CODE)
        addURI(authority, "admin/*", ADMIN_CODE)
    }
    
    override fun query(uri: Uri, ...): Cursor? {
        when (uriMatcher.match(uri)) {
            PUBLIC_CODE -> {
                // 公共数据:只需基础读取权限
                context?.checkCallingPermission("com.example.app.READ_PUBLIC")
            }
            USER_CODE -> {
                // 用户数据:需要用户级别权限
                val userId = ContentUris.parseId(uri)
                if (!hasAccessToUser(callingUid, userId)) {
                    throw SecurityException("No access to user $userId")
                }
            }
            ADMIN_CODE -> {
                // 管理员数据:需要管理员权限
                context?.checkCallingPermission("com.example.app.ACCESS_ADMIN")
                // 额外检查:调用者必须是管理员用户
                checkAdminUser(uri)
            }
        }
        return super.query(uri, ...)
    }
    
    private fun hasAccessToUser(callerUid: Int, userId: Long): Boolean {
        // 实现业务逻辑:检查调用者是否有权限访问该用户数据
        // 例如:只能访问自己的数据,除非是管理员
        return if (isAdmin(callerUid)) {
            true
        } else {
            val callerUserId = getUserIdForUid(callerUid)
            callerUserId == userId
        }
    }
}

(四)临时权限授予机制

1. Intent FLAG机制

// 授予其他应用临时访问权限
fun shareContentWithTempPermission(context: Context, uri: Uri) {
    val intent = Intent(Intent.ACTION_VIEW).apply {
        data = uri
        // 关键:授予临时读取权限
        flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
        
        // 可选:同时授予写权限
        addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
        
        // 可选:授予永久权限(危险,不推荐)
        // addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
        
        // 可选:指定接收包名,限制授予范围
        `package` = "com.example.receiverapp"
    }
    
    // 启动Activity或发送Broadcast
    context.startActivity(intent)
}

// 在接收方检查权限
fun checkUriPermission(context: Context, uri: Uri): Boolean {
    return context.checkUriPermission(
        uri,
        Binder.getCallingPid(),
        Binder.getCallingUid(),
        Intent.FLAG_GRANT_READ_URI_PERMISSION
    ) == PackageManager.PERMISSION_GRANTED
}

2. grantUriPermissions属性详解

<provider
    android:name=".MyProvider"
    android:grantUriPermissions="true">
    <!-- 
    可选值:
    true: 允许通过Intent授予任何URI的临时权限
    false: 禁止临时权限授予
    不指定<grant-uri-permission>子元素:所有URI都可被授予权限
    -->
    
    <!-- 更精细的控制:指定哪些URI可以授予临时权限 -->
    <grant-uri-permission android:path="/shared/*" />
    <grant-uri-permission android:pathPrefix="/temp/" />
    <grant-uri-permission android:path="/public/data.jpg" />
</provider>

3. 现代临时权限管理(Android 10+)

// Android 10引入的权限管理API
@RequiresApi(Build.VERSION_CODES.Q)
fun manageUriPermissions() {
    val uri = // 要共享的URI
    
    // 创建临时权限授予
    val permissionFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
    
    // 授予权限给特定包
    context.grantUriPermission(
        "com.example.receiver",  // 目标包名
        uri,
        permissionFlags
    )
    
    // 撤销权限
    context.revokeUriPermission(uri, permissionFlags)
    
    // 查询已授予的权限
    val persistedUriPermissions = context.contentResolver.persistedUriPermissions
    persistedUriPermissions.forEach { permission ->
        Log.d("UriPermissions", 
            "URI: ${permission.uri}, isRead: ${permission.isReadPermission}")
    }
}

(五)运行时权限与ContentProvider集成

1. 动态权限检查(Android 6.0+)

class SecureProviderWithRuntimePermission : ContentProvider() {
    companion object {
        // 定义运行时权限
        private const val PERMISSION_SELF_DESTRUCT = 
            "com.example.app.SELF_DESTRUCT_DATA"
    }
    
    override fun delete(uri: Uri, ...): Int {
        // 检查自定义权限
        if (isDestructiveOperation(uri)) {
            checkCallingOrSelfPermission(PERMISSION_SELF_DESTRUCT)
        }
        
        // 检查Android运行时权限
        if (requiresLocation(uri)) {
            // 注意:Provider中通常不能请求运行时权限
            // 只能检查是否已授予
            if (context?.checkCallingPermission(
                Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED) {
                throw SecurityException("Location permission required")
            }
        }
        
        return super.delete(uri, ...)
    }
}

2. 客户端权限处理

class ContentProviderClient {
    fun accessProtectedContent(context: Context) {
        // 步骤1:检查并请求运行时权限
        if (ContextCompat.checkSelfPermission(
                context, 
                Manifest.permission.READ_EXTERNAL_STORAGE
            ) != PackageManager.PERMISSION_GRANTED) {
            
            ActivityCompat.requestPermissions(
                context as Activity,
                arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
                REQUEST_CODE
            )
            return
        }
        
        // 步骤2:访问ContentProvider
        val cursor = context.contentResolver.query(
            contentUri,
            projection,
            selection,
            selectionArgs,
            sortOrder
        )
        
        // 步骤3:处理可能的SecurityException
        try {
            // 使用cursor
        } catch (e: SecurityException) {
            // 权限不足,可能是:
            // 1. 未在Manifest声明权限
            // 2. 临时权限已过期
            // 3. 路径级权限限制
            Log.e("Security", "Access denied: ${e.message}")
            showPermissionError(context)
        }
    }
}

(六)高级权限控制模式

1. 基于角色的访问控制(RBAC)

class RBACContentProvider : ContentProvider() {
    // 定义角色
    enum class UserRole { GUEST, USER, EDITOR, ADMIN }
    
    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        val callerRole = getUserRole(callingUid)
        val requiredRole = getRequiredRoleForUri(uri, Operation.INSERT)
        
        if (callerRole.ordinal < requiredRole.ordinal) {
            throw SecurityException(
                "Required role: $requiredRole, " +
                "caller role: $callerRole"
            )
        }
        
        // 数据级权限控制
        val filteredValues = filterValuesByRole(values, callerRole)
        return super.insert(uri, filteredValues)
    }
    
    private fun getUserRole(uid: Int): UserRole {
        // 从数据库或系统服务获取用户角色
        // 可根据包名、用户账户等判断
        return when {
            isSystemApp(uid) -> UserRole.ADMIN
            isSignedWithReleaseKey(uid) -> UserRole.EDITOR
            else -> UserRole.USER
        }
    }
}

2. 数据行级权限控制

class RowLevelSecurityProvider : ContentProvider() {
    override fun query(uri: Uri, ...): Cursor? {
        val baseCursor = super.query(uri, ...)
        
        // 创建包装Cursor,过滤无权限的行
        return SecurityFilterCursor(baseCursor).apply {
            setFilter { rowValues ->
                // 检查当前调用者是否有权限访问该行
                val rowId = rowValues.getAsLong("_id")
                val ownerId = rowValues.getAsLong("owner_id")
                
                // 权限逻辑:只能访问自己的数据,除非是管理员
                callingUid == ownerId || isAdmin(callingUid)
            }
        }
    }
}

// 自定义Cursor包装器
class SecurityFilterCursor(
    private val delegate: Cursor
) : CursorWrapper(delegate) {
    
    private val allowedPositions = mutableListOf<Int>()
    private var currentIndex = -1
    
    init {
        // 遍历所有行,收集有权限的行
        delegate.moveToFirst()
        while (!delegate.isAfterLast) {
            if (filter(delegate)) {
                allowedPositions.add(delegate.position)
            }
            delegate.moveToNext()
        }
    }
    
    override fun getCount(): Int = allowedPositions.size
    
    override fun moveToPosition(position: Int): Boolean {
        if (position < 0 || position >= allowedPositions.size) {
            return false
        }
        currentIndex = position
        return delegate.moveToPosition(allowedPositions[position])
    }
    
    // 其他方法需要相应重写...
}

(七)安全最佳实践

1. 权限设计原则

<!-- 最小权限原则示例 -->
<provider
    android:name=".BestPracticeProvider"
    android:authorities="com.example.app.provider"
    
    <!-- 默认不导出,除非明确需要 -->
    android:exported="false"
    
    <!-- 需要时才启用临时权限 -->
    android:grantUriPermissions="false"
    
    <!-- 使用签名级权限保护敏感数据 -->
    android:permission="com.example.app.SIGNATURE_PERMISSION"
    
    <!-- 或者:仅允许相同签名的应用访问 -->
    android:readPermission="android.permission.???">
    
    <!-- 签名验证定义 -->
    <meta-data
        android:name="android.content.requiredSignatures"
        android:value="<hex-signature>" />
</provider>

2. 输入验证与SQL注入防护

class SafeContentProvider : ContentProvider() {
    override fun query(uri: Uri, ...): Cursor? {
        // 1. 验证URI格式
        validateUri(uri)
        
        // 2. 清理和验证参数
        val safeSelection = sanitizeSelection(selection)
        val safeSelectionArgs = selectionArgs?.map { arg ->
            SQLiteDatabaseUtils.escapeSqlString(arg.toString())
        }?.toTypedArray()
        
        // 3. 使用参数化查询
        val db = databaseHelper.readableDatabase
        return db.query(
            table = getTableName(uri),
            columns = projection,
            selection = safeSelection,
            selectionArgs = safeSelectionArgs,
            groupBy = null,
            having = null,
            orderBy = sortOrder
        )
    }
    
    private fun sanitizeSelection(selection: String?): String? {
        // 移除潜在的SQL注入字符
        return selection?.replace(";", "")?.replace("--", "")
    }
}

3. 敏感数据过滤

class SensitiveDataProvider : ContentProvider() {
    override fun query(uri: Uri, ...): Cursor? {
        val cursor = super.query(uri, ...)
        
        // 根据调用者权限过滤敏感字段
        return when {
            hasFullAccess(callingUid) -> cursor
            hasPartialAccess(callingUid) -> {
                // 移除敏感列
                val filteredProjection = projection?.filterNot { column ->
                    column in SENSITIVE_COLUMNS
                }?.toTypedArray()
                
                // 返回仅包含允许列的Cursor
                return MatrixCursor(filteredProjection ?: DEFAULT_COLUMNS).apply {
                    cursor?.use {
                        while (it.moveToNext()) {
                            addRow(buildRow(it, filteredProjection))
                        }
                    }
                }
            }
            else -> throw SecurityException("Access denied")
        }
    }
}

(八)调试与测试

1. 权限检查工具

// 调试工具类
object PermissionDebugger {
    fun dumpProviderPermissions(context: Context, authority: String) {
        val providerInfo = context.packageManager
            .resolveContentProvider(authority, PackageManager.GET_META_DATA)
        
        providerInfo?.let { info ->
            Log.d("PermissionDebug", "Provider: ${info.name}")
            Log.d("PermissionDebug", "Read permission: ${info.readPermission}")
            Log.d("PermissionDebug", "Write permission: ${info.writePermission}")
            Log.d("PermissionDebug", "Exported: ${info.exported}")
            
            // 检查路径权限
            info.pathPermissions?.forEach { pathPerm ->
                Log.d("PermissionDebug", 
                    "Path: ${pathPerm.path}, Type: ${pathPerm.type}")
            }
        }
    }
    
    fun testUriAccess(context: Context, uri: Uri): Boolean {
        return try {
            context.contentResolver.query(uri, null, null, null, null)
            true
        } catch (e: SecurityException) {
            Log.w("PermissionTest", "Access denied to $uri")
            false
        }
    }
}

2. ADB测试命令

# 测试ContentProvider访问
adb shell content query --uri content://com.example.app.provider/users

# 检查权限
adb shell dumpsys package com.example.app | grep -A 20 "Provider:"

# 授予临时权限测试
adb shell am start -a android.intent.action.VIEW \
  -d "content://com.example.app.provider/public/data" \
  --grant-read-uri-permission

(九)面试回答要点总结

  1. 四级权限体系
    • 全局权限、读写分离权限、路径级权限、临时权限
  2. 核心配置元素
    • android:permission:全局权限
    • android:readPermission/android:writePermission:读写分离
    • <path-permission>:路径级控制
    • android:grantUriPermissions:启用临时权限
  3. 临时权限授予
    • 通过Intent的FLAG_GRANT_*_URI_PERMISSION标志
    • 可限制授予特定包或应用
    • 临时权限在接收应用销毁后失效(除非使用FLAG_GRANT_PERSISTABLE_URI_PERMISSION
  4. 安全最佳实践
    • 最小权限原则:只授予必要权限
    • 默认不导出(android:exported="false"
    • 对输入进行严格验证,防止SQL注入
    • 根据调用者身份过滤敏感数据
  5. 现代特性
    • Android 10+增强的URI权限管理API
    • 签名级权限保护(相同签名应用共享)
    • 运行时权限与ContentProvider结合使用
  6. 调试技巧
    • 使用checkUriPermission()验证权限
    • ADB命令测试ContentProvider访问
    • 日志记录权限拒绝事件

一句话总结
ContentProvider的权限控制是一个多层次防御体系,从Provider级别的全局控制到URI路径级别的细粒度控制,结合Android的权限系统和临时授权机制,可以有效保护数据安全。现代开发应遵循最小权限原则,默认不导出Provider,并对所有输入进行验证。

四十三、Fragment状态如何保存?

(一)Fragment状态保存机制概述

1. 自动保存机制

Fragment的状态保存由FragmentManager自动管理,触发场景包括:

  • 配置更改(如屏幕旋转、语言切换)
  • 内存不足时Activity被回收重建
  • Fragment被移除但保留在返回栈addToBackStack
  • ViewPager/ViewPager2中的Fragment切换
// Fragment状态保存的生命周期顺序
1. Fragment.onSaveInstanceState()  // 保存实例状态
2. Fragment.onPause()
3. Fragment.onStop()
4. Fragment.onDestroyView()       // 视图销毁,但Fragment实例可能保留
5. Fragment.onDestroy()           // 可能被调用(非回栈情况)
6. Fragment.onDetach()

// 恢复时的反向顺序
1. Fragment.onAttach()
2. Fragment.onCreate()           // 接收savedInstanceState
3. Fragment.onCreateView()       // 重建视图
4. Fragment.onViewCreated()      // 视图恢复完成
5. Fragment.onViewStateRestored() // 视图状态已恢复
6. Fragment.onStart()
7. Fragment.onResume()

(二)状态保存的三种级别

1. View状态自动保存

Fragment中的View状态(如EditText文本、CheckBox状态)会自动保存和恢复,前提是:

  • View有唯一的ID(android:id
  • 使用AndroidX Fragment库(1.2.0+)
<!-- 有ID的View会自动保存状态 -->
<EditText
    android:id="@+id/et_username"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

<!-- 无ID的View不会自动保存 -->
<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

2. Fragment实例状态保存

class MyFragment : Fragment() {
    private var customData: String? = null
    
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // 保存自定义数据到Bundle
        outState.putString("CUSTOM_DATA_KEY", customData)
        outState.putInt("ANOTHER_KEY", 123)
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 恢复保存的状态
        savedInstanceState?.let { bundle ->
            customData = bundle.getString("CUSTOM_DATA_KEY")
            val number = bundle.getInt("ANOTHER_KEY")
            // 恢复UI状态
            restoreUIState()
        }
    }
    
    // 也可以在onCreate中恢复
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        savedInstanceState?.let {
            customData = it.getString("CUSTOM_DATA_KEY")
        }
    }
}

3. 视图模型状态保存(现代推荐方式)

(1)使用ViewModel + SavedStateHandle
// 需要依赖:implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.5.1"
class SavedStateViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    
    // 使用LiveData自动保存和恢复
    val userName: MutableLiveData<String> = 
        savedStateHandle.getLiveData("user_name", "")
    
    // 直接操作SavedStateHandle
    fun saveUserData(name: String, age: Int) {
        savedStateHandle["user_name"] = name
        savedStateHandle["user_age"] = age
    }
    
    fun getUserName(): String? = savedStateHandle["user_name"]
    
    // 保存复杂对象(需要序列化)
    val user: LiveData<User> = savedStateHandle.getLiveData("user")
    
    fun saveUser(user: User) {
        savedStateHandle["user"] = user // User需要实现Parcelable
    }
}
(2)在Fragment中使用SavedStateViewModel
class MyFragment : Fragment() {
    // 使用by viewModels委托获取ViewModel
    private val viewModel: SavedStateViewModel by viewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 观察LiveData,状态会自动保存和恢复
        viewModel.userName.observe(viewLifecycleOwner) { name ->
            etUserName.setText(name)
        }
        
        // 保存数据
        btnSave.setOnClickListener {
            viewModel.saveUserData(
                etUserName.text.toString(),
                etAge.text.toString().toIntOrNull() ?: 0
            )
        }
    }
}

(三)不同场景下的状态处理

1. 配置更改(屏幕旋转)

class ConfigChangeFragment : Fragment() {
    // 使用retainInstance保持Fragment实例(已弃用,不推荐)
    // init { retainInstance = true }
    
    // 现代方式:使用ViewModel
    private val viewModel: ConfigViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 即使配置更改,ViewModel也会保留
        Log.d("Fragment", "ViewModel data: ${viewModel.data.value}")
    }
}

2. 返回栈中的Fragment状态

class BackStackFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // 当Fragment从返回栈返回时,onCreateView会再次调用
        // 但savedInstanceState可能为null,因为视图状态已自动恢复
        return inflater.inflate(R.layout.fragment_backstack, container, false)
    }
    
    override fun onViewStateRestored(savedInstanceState: Bundle?) {
        super.onViewStateRestored(savedInstanceState)
        // 这是检查视图状态是否已恢复的最佳位置
        if (savedInstanceState != null) {
            // 手动恢复的状态
        }
        // View的自动状态恢复已完成
    }
}

3. ViewPager2中的Fragment状态

// ViewPager2默认使用FragmentStateAdapter,会保存Fragment状态
class MyPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
    override fun getItemCount(): Int = 3
    
    override fun createFragment(position: Int): Fragment {
        return when (position) {
            0 -> TabFragment.newInstance("Tab1")
            1 -> TabFragment.newInstance("Tab2")
            else -> TabFragment.newInstance("Tab3")
        }
    }
}

class TabFragment : Fragment() {
    companion object {
        private const val ARG_TAB_NAME = "tab_name"
        
        fun newInstance(tabName: String): TabFragment {
            return TabFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_TAB_NAME, tabName)
                }
            }
        }
    }
    
    // ViewPager2会保存离屏Fragment的状态
    // 可以使用setItemSavePolicy()控制保存行为
    // adapter.setItemSavePolicy(ItemSavePolicy.VIEWHOLDER_ONLY)
}

(四)状态保存的最佳实践

1. 选择合适的恢复位置

class BestPracticeFragment : Fragment() {
    private var uiState: UiState? = null
    
    // 选择1:onCreate - 适合轻量数据
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        savedInstanceState?.let { bundle ->
            // 恢复非UI相关的数据
            uiState = bundle.getParcelable("UI_STATE")
        }
    }
    
    // 选择2:onViewCreated - 适合需要视图的数据
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 恢复视图相关的数据
        savedInstanceState?.let { bundle ->
            val scrollPosition = bundle.getInt("SCROLL_POSITION", 0)
            recyclerView.scrollToPosition(scrollPosition)
        }
    }
    
    // 选择3:onViewStateRestored - 确保视图已完全恢复
    override fun onViewStateRestored(savedInstanceState: Bundle?) {
        super.onViewStateRestored(savedInstanceState)
        // 此时所有View的自动状态恢复已完成
        // 可以安全地执行依赖视图状态的操作
    }
    
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // 保存数据
        outState.putParcelable("UI_STATE", uiState)
        outState.putInt("SCROLL_POSITION", 
            (view?.findViewById<RecyclerView>(R.id.recyclerView)
                ?.layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition() ?: 0)
    }
}

2. 处理异步操作的状态

class AsyncFragment : Fragment() {
    private val viewModel: AsyncViewModel by viewModels()
    private var job: Job? = null
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 使用ViewModel处理异步操作
        viewModel.data.observe(viewLifecycleOwner) { data ->
            updateUI(data)
        }
        
        // 或者使用ViewLifecycleOwner的协程
        job = viewLifecycleOwner.lifecycleScope.launch {
            try {
                val data = fetchData()
                updateUI(data)
            } catch (e: Exception) {
                // 处理异常
            }
        }
    }
    
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        // 注意:不能保存协程或回调引用
        // 保存必要的标识信息,用于恢复时重新加载
        outState.putString("LAST_LOADED_ID", lastLoadedId)
    }
    
    override fun onDestroyView() {
        super.onDestroyView()
        // 取消协程,避免内存泄漏
        job?.cancel()
    }
}

3. 使用SavedStateRegistry(高级)

class AdvancedFragment : Fragment() {
    private lateinit var savedStateRegistry: SavedStateRegistry
    private var savedStateProvider = SavedStateRegistry.SavedStateProvider {
        // 提供要保存的状态
        Bundle().apply {
            putString("advanced_state", "custom_value")
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        savedStateRegistry = requireActivity()
            .supportFragmentManager
            .findFragmentByTag(tag)
            ?.savedStateRegistry
            ?: error("SavedStateRegistry not available")
        
        // 注册状态提供者
        savedStateRegistry.registerSavedStateProvider(
            "custom_provider",
            savedStateProvider
        )
        
        // 恢复状态
        val savedState = savedStateRegistry
            .consumeRestoredStateForKey("custom_provider")
        savedState?.let { bundle ->
            val value = bundle.getString("advanced_state")
            // 使用恢复的值
        }
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 取消注册
        savedStateRegistry.unregisterSavedStateProvider("custom_provider")
    }
}

(五)常见问题与解决方案

1. Fragment视图状态不恢复

问题:旋转屏幕后EditText内容消失
解决方案

// 检查1:确保View有ID
<EditText
    android:id="@+id/et_input"  <!-- 必须有ID -->
    ... />

// 检查2:使用AndroidX Fragment 1.2.0+
// build.gradle
dependencies {
    implementation "androidx.fragment:fragment-ktx:1.5.5"
}

// 检查3:避免在onCreateView中重新创建视图时丢失数据
override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    // ❌ 错误:每次都创建新视图
    // return inflater.inflate(R.layout.fragment_layout, container, false)
    
    // ✅ 正确:复用已有视图
    if (view != null) {
        return view!!
    }
    return inflater.inflate(R.layout.fragment_layout, container, false)
}

2. 大型对象的状态保存

class LargeDataFragment : Fragment() {
    // ❌ 错误:将大型对象保存到Bundle
    // override fun onSaveInstanceState(outState: Bundle) {
    //     outState.putSerializable("large_bitmap", bitmap) // 可能TransactionTooLargeException
    // }
    
    // ✅ 正确:保存引用,重建时重新加载
    private var imageId: String? = null
    
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putString("image_id", imageId) // 只保存ID
    }
    
    private fun loadImage(imageId: String) {
        // 从磁盘或网络加载图片
        viewModel.loadImage(imageId)
    }
    
    // ✅ 或者使用ViewModel + Repository模式
    private val viewModel: ImageViewModel by viewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        imageId = savedInstanceState?.getString("image_id")
        imageId?.let { viewModel.loadImage(it) }
    }
}

3. Fragment状态与Activity状态的协调

class CoordinatedFragment : Fragment() {
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        
        // 与Activity协调状态保存
        (activity as? MainActivity)?.let { activity ->
            outState.putAll(activity.getSharedState())
        }
    }
    
    override fun onViewStateRestored(savedInstanceState: Bundle?) {
        super.onViewStateRestored(savedInstanceState)
        
        // 从Activity恢复协调的状态
        savedInstanceState?.let { bundle ->
            (activity as? MainActivity)?.restoreSharedState(bundle)
        }
    }
}

(六)现代Android架构的状态管理

1. 使用Navigation组件管理状态

class NavigationFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Navigation组件自动保存和恢复Fragment状态
        // 需要确保使用NavHostFragment
        
        // 通过SavedStateHandle获取参数
        val args: NavigationFragmentArgs by navArgs()
        val userId = args.userId
        
        // 或者从SavedStateHandle获取
        val savedStateHandle = findNavController()
            .currentBackStackEntry
            ?.savedStateHandle
        
        savedStateHandle?.getLiveData<String>("result_key")
            ?.observe(viewLifecycleOwner) { result ->
                // 处理返回结果
            }
    }
    
    fun returnResult() {
        findNavController().previousBackStackEntry
            ?.savedStateHandle
            ?.set("result_key", "success")
        findNavController().popBackStack()
    }
}

2. 使用DataStore替代SharedPreferences

// 持久化状态的最佳实践
class DataStoreFragment : Fragment() {
    private val dataStore: DataStore<Preferences> by lazy {
        requireContext().createDataStore(name = "settings")
    }
    
    private val EXAMPLE_COUNTER = intPreferencesKey("example_counter")
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 读取持久化状态
        lifecycleScope.launch {
            dataStore.data.map { preferences ->
                preferences[EXAMPLE_COUNTER] ?: 0
            }.collect { count ->
                updateCounterUI(count)
            }
        }
        
        // 保存状态
        btnIncrement.setOnClickListener {
            lifecycleScope.launch {
                dataStore.edit { settings ->
                    val current = settings[EXAMPLE_COUNTER] ?: 0
                    settings[EXAMPLE_COUNTER] = current + 1
                }
            }
        }
    }
}

(七)调试状态保存

1. 启用调试日志

class DebugFragment : Fragment() {
    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        Log.d("FragmentState", "Saved bundle keys: ${outState.keySet()}")
        Log.d("FragmentState", "Bundle size: ${measureBundleSize(outState)} bytes")
    }
    
    private fun measureBundleSize(bundle: Bundle): Int {
        val parcel = Parcel.obtain()
        bundle.writeToParcel(parcel, 0)
        val size = parcel.dataSize()
        parcel.recycle()
        return size
    }
    
    override fun onViewStateRestored(savedInstanceState: Bundle?) {
        super.onViewStateRestored(savedInstanceState)
        Log.d("FragmentState", "Restored: ${savedInstanceState != null}")
    }
}

2. 使用Fragment测试工具

// 测试状态保存
@RunWith(AndroidJUnit4::class)
class FragmentStateTest {
    @Test
    fun testFragmentStateSaveAndRestore() {
        val scenario = launchFragmentInContainer<MyFragment>()
        
        // 模拟配置更改
        scenario.recreate()
        
        // 验证状态恢复
        scenario.onFragment { fragment ->
            assertThat(fragment.savedData).isEqualTo("expected")
        }
    }
}

(八)面试回答要点总结

  1. 自动保存机制
    • FragmentManager自动保存Fragment状态和View状态
    • View需要ID才能自动保存状态
    • 使用AndroidX Fragment库确保兼容性
  2. 手动状态保存
    • 重写onSaveInstanceState()保存自定义数据到Bundle
    • 避免保存大型对象(可能导致TransactionTooLargeException)
    • 只保存最小必要数据(如ID、关键状态)
  3. 状态恢复位置
    • onCreate():恢复非UI相关的轻量数据
    • onCreateView()/onViewCreated():恢复视图相关的数据
    • onViewStateRestored():确认所有视图状态已恢复后执行操作
  4. 现代最佳实践
    • 使用ViewModel:处理配置更改时的数据保持
    • 使用SavedStateHandle:自动保存和恢复ViewModel中的数据
    • 避免retainInstance:已弃用,使用ViewModel替代
    • 结合Navigation组件:管理Fragment导航和状态
  5. 常见问题处理
    • View状态不恢复 → 检查View ID和使用AndroidX Fragment
    • TransactionTooLargeException → 只保存引用,不保存大型对象
    • 异步操作中断 → 使用ViewModel或保存标识以便重新加载
  6. 架构推荐
    数据层(Repository)
    	↑
    ViewModel(使用SavedStateHandle)
    	↑
    Fragment(观察LiveData/StateFlow)
    

一句话总结
Fragment状态保存是一个多层次的过程,包括View的自动保存、Fragment实例状态的手动保存和ViewModel的数据保持。现代Android开发推荐使用ViewModel + SavedStateHandle的组合来处理状态管理,既能处理配置更改,又能保证数据在进程死亡后恢复。

四十四、Activity中创建的线程与Service中创建的线程有何区别?

(一)生命周期管理区别

1. Activity线程的生命周期特性

class ActivityThreadExample : AppCompatActivity() {
    private lateinit var backgroundThread: Thread
    private var isThreadRunning = false
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // Activity中创建线程
        backgroundThread = Thread {
            isThreadRunning = true
            try {
                while (isThreadRunning) {
                    // 执行后台任务
                    Thread.sleep(1000)
                    runOnUiThread {
                        // 更新UI
                    }
                }
            } catch (e: InterruptedException) {
                // 线程被中断
            }
        }
        backgroundThread.start()
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // ❌ 必须手动停止线程,否则会导致内存泄漏
        isThreadRunning = false
        backgroundThread.interrupt()
        // 注意:即使调用interrupt(),线程也可能不会立即停止
    }
    
    // 问题:当Activity因配置更改(如旋转)重建时,
    // 旧Activity销毁,但线程可能仍在运行并持有Activity引用
}

Activity线程的生命周期问题

  • 配置更改:Activity重建时,线程继续运行但可能引用已销毁的Activity
  • 内存泄漏风险:线程持有Activity的强引用,导致无法被垃圾回收
  • 资源浪费:后台线程可能在Activity不再需要时继续消耗资源

2. Service线程的生命周期特性

class ServiceThreadExample : Service() {
    private lateinit var serviceThread: Thread
    private var isServiceRunning = false
    
    override fun onCreate() {
        super.onCreate()
        serviceThread = Thread {
            isServiceRunning = true
            while (isServiceRunning) {
                // 执行后台任务
                performBackgroundWork()
            }
        }
        serviceThread.start()
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 同样需要手动停止线程
        isServiceRunning = false
        serviceThread.interrupt()
    }
    
    override fun onBind(intent: Intent?): IBinder? = null
}

Service线程的优势

  • 独立生命周期:Service可以在Activity销毁后继续运行
  • 适合长时间任务:如下载、同步数据等
  • 进程优先级:前台Service可以提高进程优先级,减少被系统杀死的风险

(二)现代Android后台任务管理演进

1. 传统方式的局限

// ❌ 已弃用或不推荐的传统方式
class LegacyApproaches {
    // 1. AsyncTask(已废弃)
    // 问题:内存泄漏、配置更改处理复杂、已从API 30移除
    
    // 2. 简单Service + Thread
    // 问题:Android 8.0+对后台服务有限制
    
    // 3. IntentService(已废弃)
    // 替代:使用WorkManager或JobIntentService
}

2. 现代后台任务解决方案对比

方案 适用场景 生命周期管理 系统限制兼容性 推荐度
Activity线程 短时UI相关任务 需手动管理 无特殊限制 ⭐⭐
Service线程 长时间后台任务 Service生命周期 Android 8.0+需前台服务 ⭐⭐⭐
WorkManager 延迟、可重复、可靠任务 自动管理 全版本兼容 ⭐⭐⭐⭐⭐
协程 + ViewModel UI相关异步任务 自动取消 无限制 ⭐⭐⭐⭐
前台服务 用户感知的长时间任务 Service生命周期 需显示通知 ⭐⭐⭐⭐

(三)WorkManager - 官方推荐的后台任务管理器

1. 基本使用

// 1. 定义Worker
class UploadWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {
    
    override suspend fun doWork(): Result {
        // 执行后台工作
        return try {
            uploadData()
            Result.success()
        } catch (e: Exception) {
            if (runAttemptCount < 3) {
                Result.retry() // 重试
            } else {
                Result.failure() // 失败
            }
        }
    }
}

// 2. 调度任务
fun scheduleUploadWork() {
    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .setRequiresCharging(false)
        .build()
    
    val uploadWork = OneTimeWorkRequestBuilder<UploadWorker>()
        .setConstraints(constraints)
        .setBackoffCriteria(
            BackoffPolicy.EXPONENTIAL,
            10, TimeUnit.SECONDS
        )
        .build()
    
    WorkManager.getInstance(context).enqueue(uploadWork)
}

// 3. 观察任务状态
WorkManager.getInstance(context)
    .getWorkInfoByIdLiveData(uploadWork.id)
    .observe(this) { workInfo ->
        when (workInfo?.state) {
            WorkInfo.State.SUCCEEDED -> { /* 成功 */ }
            WorkInfo.State.FAILED -> { /* 失败 */ }
            WorkInfo.State.RUNNING -> { /* 运行中 */ }
            else -> { /* 其他状态 */ }
        }
    }

2. WorkManager的优势

  • 向后兼容:自动选择最佳实现(JobScheduler, AlarmManager, GcmNetworkManager)
  • 保证执行:即使应用退出或设备重启,任务也会被执行
  • 灵活调度:支持一次性、周期性、链式任务
  • 约束条件:网络状态、充电状态、存储空间等

(四)前台服务(Android 8.0+)

1. 前台服务实现

class MyForegroundService : Service() {
    private val channelId = "ForegroundServiceChannel"
    private val notificationId = 1
    
    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
    }
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 启动前台服务
        val notification = buildNotification()
        startForeground(notificationId, notification)
        
        // 在后台线程执行任务
        CoroutineScope(Dispatchers.IO).launch {
            performLongRunningTask()
        }
        
        return START_STICKY
    }
    
    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "Foreground Service Channel",
                NotificationManager.IMPORTANCE_LOW
            ).apply {
                description = "用于前台服务的通知通道"
            }
            
            val manager = getSystemService(NotificationManager::class.java)
            manager.createNotificationChannel(channel)
        }
    }
    
    private fun buildNotification(): Notification {
        return NotificationCompat.Builder(this, channelId)
            .setContentTitle("服务运行中")
            .setContentText("正在执行后台任务...")
            .setSmallIcon(R.drawable.ic_notification)
            .setPriority(NotificationCompat.PRIORITY_LOW)
            .build()
    }
    
    override fun onBind(intent: Intent?): IBinder? = null
}

// 启动前台服务(Android 9.0+需要权限)
fun startForegroundService(context: Context) {
    val intent = Intent(context, MyForegroundService::class.java)
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        // Android 8.0+ 必须使用startForegroundService
        context.startForegroundService(intent)
    } else {
        context.startService(intent)
    }
}

2. 前台服务权限配置

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<service
    android:name=".MyForegroundService"
    android:enabled="true"
    android:exported="false" />

(五)协程在现代Android中的最佳实践

1. Activity/Fragment中的协程使用

class ModernActivity : AppCompatActivity() {
    // 使用lifecycleScope,自动取消协程
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        lifecycleScope.launch {
            // 主线程执行
            val data = withContext(Dispatchers.IO) {
                // IO线程执行耗时操作
                fetchDataFromNetwork()
            }
            // 返回主线程更新UI
            updateUI(data)
        }
        
        // 监听生命周期状态
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // 只在STARTED状态收集Flow
                viewModel.dataFlow.collect { data ->
                    updateUI(data)
                }
            }
        }
    }
    
    // 不需要手动取消,lifecycleScope会自动管理
}

2. Service中的协程使用

class CoroutineService : Service() {
    private val serviceScope = CoroutineScope(
        SupervisorJob() + Dispatchers.IO
    )
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        serviceScope.launch {
            performBackgroundTask()
        }
        return START_STICKY
    }
    
    override fun onDestroy() {
        super.onDestroy()
        serviceScope.cancel() // 手动取消协程作用域
    }
    
    override fun onBind(intent: Intent?): IBinder? = null
}

(六)进程优先级与线程管理

1. Android进程优先级

// 不同组件创建的线程对进程优先级的影响
class ProcessPriorityExample {
    // 前台Activity:最高优先级
    // 可见Activity:较高优先级
    // 后台Service:中等优先级(前台服务更高)
    // 缓存进程:低优先级(可能被系统回收)
    
    fun manageThreadPriority() {
        Thread {
            // 设置线程优先级(Android中的nice值)
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
            
            // 线程优先级常量:
            // THREAD_PRIORITY_DEFAULT (0)
            // THREAD_PRIORITY_LOWEST (19)
            // THREAD_PRIORITY_BACKGROUND (10)
            // THREAD_PRIORITY_FOREGROUND (-2)
            // THREAD_PRIORITY_DISPLAY (-4)
            // THREAD_PRIORITY_URGENT_DISPLAY (-8)
            // THREAD_PRIORITY_AUDIO (-16)
            // THREAD_PRIORITY_URGENT_AUDIO (-19)
            
            performTask()
        }.start()
    }
}

2. 线程池管理

object ThreadPoolManager {
    // 统一管理线程池,避免创建过多线程
    private val ioExecutor: ExecutorService by lazy {
        Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors(),
            ThreadFactory { runnable ->
                Thread(runnable, "IO-Thread").apply {
                    priority = Process.THREAD_PRIORITY_BACKGROUND
                }
            }
        )
    }
    
    private val computationExecutor: ExecutorService by lazy {
        Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() * 2,
            ThreadFactory { runnable ->
                Thread(runnable, "Compute-Thread").apply {
                    priority = Process.THREAD_PRIORITY_DEFAULT
                }
            }
        )
    }
    
    fun executeIoTask(task: () -> Unit) {
        ioExecutor.execute(task)
    }
    
    fun executeComputationTask(task: () -> Unit) {
        computationExecutor.execute(task)
    }
}

(七)不同场景下的选择策略

1. 根据任务类型选择方案

class TaskStrategy {
    fun executeTaskBasedOnType(taskType: TaskType) {
        when (taskType) {
            // 场景1:短时UI更新任务
            TaskType.UI_UPDATE -> {
                // 使用Activity中的协程
                lifecycleScope.launch {
                    updateUI()
                }
            }
            
            // 场景2:网络请求
            TaskType.NETWORK_REQUEST -> {
                // 使用ViewModel + 协程
                viewModelScope.launch {
                    fetchData()
                }
            }
            
            // 场景3:长时间后台任务(用户不感知)
            TaskType.BACKGROUND_SYNC -> {
                // 使用WorkManager
                WorkManager.getInstance(context)
                    .enqueue(OneTimeWorkRequestBuilder<SyncWorker>().build())
            }
            
            // 场景4:长时间后台任务(用户感知)
            TaskType.DOWNLOAD_FILE -> {
                // 使用前台服务
                startForegroundService(DownloadService::class.java)
            }
            
            // 场景5:延迟执行任务
            TaskType.DELAYED_TASK -> {
                // 使用AlarmManager或WorkManager
                scheduleDelayedWork()
            }
        }
    }
}

2. 内存泄漏预防策略

class LeakPrevention {
    // 策略1:使用弱引用或软引用
    class SafeRunnable(private val activityRef: WeakReference<Activity>) : Runnable {
        override fun run() {
            val activity = activityRef.get()
            if (activity != null && !activity.isDestroyed) {
                // 安全使用Activity
            }
        }
    }
    
    // 策略2:使用LifecycleObserver
    class LifecycleAwareTask(private val lifecycle: Lifecycle) : LifecycleObserver {
        
        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        fun cleanup() {
            // 清理资源
        }
        
        fun startTask() {
            if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
                // 执行任务
            }
        }
    }
}

(八)面试回答要点总结

  1. 生命周期核心区别
    • Activity线程:生命周期与Activity绑定,必须手动管理,存在内存泄漏风险
    • Service线程:可独立于Activity运行,适合长时间任务,但需考虑Service生命周期
  2. 现代演进趋势
    • AsyncTask已废弃:从Android 11(API 30)移除
    • IntentService已废弃:推荐使用WorkManager或带协程的Service
    • 后台限制:Android 8.0+对后台服务有严格限制
  3. 推荐方案
    • WorkManager:官方推荐,向后兼容,保证任务执行
    • 前台服务:用户感知的长时间任务(需显示通知)
    • 协程 + ViewModel:UI相关异步任务,自动生命周期管理
  4. 选择策略
    • 短时UI任务 → Activity/ViewModel协程
    • 可靠后台任务 → WorkManager
    • 用户感知长任务 → 前台服务
    • 进程优先级任务 → 结合前台服务和线程优先级
  5. 内存管理
    • 避免在Activity中创建长时间运行的线程
    • 使用弱引用或Lifecycle-aware组件
    • 及时取消协程和线程
  6. 最佳实践
    • 遵循单一职责原则:分离UI逻辑和业务逻辑
    • 使用架构组件:ViewModel + Repository + 协程
    • 测试后台任务:在不同API级别和设备上测试

综合建议
在Android开发中,应优先使用WorkManager处理后台任务,使用协程处理UI相关的异步操作。对于需要持续运行且用户可感知的任务,使用前台服务并遵循最新的系统限制。避免在Activity中直接创建和管理长时间运行的线程。

四十五、如何计算Bitmap内存占用?如何避免OOM?

(一)Bitmap内存占用计算详解

1. 基础计算公式

// Bitmap内存占用公式
fun calculateBitmapMemory(width: Int, height: Int, config: Bitmap.Config): Long {
    val bytesPerPixel = when (config) {
        Bitmap.Config.ALPHA_8 -> 1     // 每个像素1字节(8位透明度)
        Bitmap.Config.RGB_565 -> 2     // 每个像素2字节(R5+G6+B5)
        Bitmap.Config.ARGB_4444 -> 2   // 每个像素2字节(已弃用)
        Bitmap.Config.ARGB_8888 -> 4   // 每个像素4字节(8位ARGB)
        Bitmap.Config.RGBA_F16 -> 8    // 每个像素8字节(Android 8.0+,广色域)
        else -> 4                      // 默认
    }
    return (width * height * bytesPerPixel).toLong()
}

// 示例:计算1920×1080 ARGB_8888图片内存
val memory = calculateBitmapMemory(1920, 1080, Bitmap.Config.ARGB_8888)
// 结果:1920 * 1080 * 4 = 8,294,400字节 ≈ 7.91MB

2. 实际内存计算考虑因素

class ActualBitmapMemory {
    // 1. 不同Android版本的内存位置
    fun getBitmapActualMemory(bitmap: Bitmap): Long {
        return when {
            // Android 2.3-7.1:像素数据在Java堆
            Build.VERSION.SDK_INT < Build.VERSION_CODES.O -> {
                bitmap.byteCount
            }
            // Android 8.0+:像素数据在Native堆,但有开销
            else -> {
                bitmap.allocationByteCount
            }
        }
    }
    
    // 2. 内存开销计算
    fun getTotalMemoryUsage(bitmap: Bitmap): Long {
        val pixelMemory = bitmap.allocationByteCount
        
        // 额外的管理开销(约10%-20%)
        val overhead = when (Build.VERSION.SDK_INT) {
            in Build.VERSION_CODES.O..Build.VERSION_CODES.Q -> {
                // Native分配器开销
                (pixelMemory * 0.15).toLong()
            }
            Build.VERSION_CODES.R -> {
                // Android 11+优化了内存管理
                (pixelMemory * 0.1).toLong()
            }
            else -> {
                // 其他版本
                (pixelMemory * 0.2).toLong()
            }
        }
        
        return pixelMemory + overhead
    }
}

3. 屏幕密度与内存关系

// 不同dpi设备的内存差异
class DensityMemoryCalculator {
    fun calculateWithDensity(
        imageWidth: Int, 
        imageHeight: Int,
        config: Bitmap.Config,
        targetDensity: Int,
        sourceDensity: Int
    ): Long {
        // 考虑缩放因子
        val scaleFactor = targetDensity.toFloat() / sourceDensity
        val scaledWidth = (imageWidth * scaleFactor).toInt()
        val scaledHeight = (imageHeight * scaleFactor).toInt()
        
        return calculateBitmapMemory(scaledWidth, scaledHeight, config)
    }
    
    // 示例:xxhdpi设备加载hdpi图片
    // xxhdpi: 480dpi, hdpi: 240dpi
    val memory = calculateWithDensity(1000, 1000, Bitmap.Config.ARGB_8888, 480, 240)
    // 实际内存:2000×2000×4 = 16MB(放大2倍)
}

(二)OOM产生原因与监控

1. Bitmap OOM常见场景

class BitmapOOMScenarios {
    // 场景1:加载超大图片
    fun loadLargeImage(): Bitmap? {
        return try {
            // 直接加载大图可能导致OOM
            BitmapFactory.decodeResource(resources, R.drawable.large_image)
        } catch (e: OutOfMemoryError) {
            Log.e("OOM", "加载大图时内存不足")
            null
        }
    }
    
    // 场景2:内存泄漏 - Bitmap未释放
    class BitmapLeak {
        private val bitmapCache = mutableListOf<Bitmap>()
        
        fun addToCache(bitmap: Bitmap) {
            bitmapCache.add(bitmap) // 持有引用,无法被回收
        }
    }
    
    // 场景3:多张大图同时存在
    fun loadMultipleImages(): List<Bitmap> {
        val bitmaps = mutableListOf<Bitmap>()
        for (i in 1..10) {
            // 每次加载都创建新Bitmap
            val bitmap = BitmapFactory.decodeResource(resources, R.drawable.image)
            bitmaps.add(bitmap) // 内存累加
        }
        return bitmaps
    }
}

2. 内存监控工具

class BitmapMemoryMonitor {
    // 1. 获取当前Bitmap总内存
    fun getTotalBitmapMemory(): Long {
        val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
        val memoryInfo = ActivityManager.MemoryInfo()
        activityManager.getMemoryInfo(memoryInfo)
        
        // 总可用内存
        val totalMemory = Runtime.getRuntime().totalMemory()
        val freeMemory = Runtime.getRuntime().freeMemory()
        val usedMemory = totalMemory - freeMemory
        
        Log.d("Memory", "总内存: ${totalMemory / 1024 / 1024}MB")
        Log.d("Memory", "已用内存: ${usedMemory / 1024 / 1024}MB")
        Log.d("Memory", "系统剩余内存: ${memoryInfo.availMem / 1024 / 1024}MB")
        
        return usedMemory
    }
    
    // 2. 检测内存泄漏(开发阶段)
    fun setupStrictMode() {
        if (BuildConfig.DEBUG) {
            StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
                .detectLeakedClosableObjects()
                .detectLeakedRegistrationObjects()
                .penaltyLog()
                .build())
        }
    }
    
    // 3. 使用Profile GPU Rendering
    fun trackBitmapAllocations() {
        // 在开发者选项中启用:
        // 1. Profile GPU Rendering
        // 2. 显示硬件层更新
        // 3. 调试GPU过度绘制
    }
}

(三)避免OOM的现代解决方案

1. 图片加载库的最佳实践

(1)Glide(Google推荐)
// Glide自动处理内存管理和缓存
Glide.with(context)
    .load(imageUrl)
    .apply(RequestOptions()
        .override(1000, 1000)          // 指定加载尺寸
        .format(DecodeFormat.PREFER_RGB_565) // 使用RGB_565减少内存
        .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC) // 自动缓存策略
        .skipMemoryCache(false)        // 启用内存缓存
    )
    .into(imageView)

// 内存优化配置
val memoryCache = LruResourceCache((MemorySizeCalculator(context)
    .memoryCacheSize.toFloat() * 0.8).toLong())
    
Glide.init(context, GlideBuilder()
    .setMemoryCache(memoryCache)
    .setBitmapPool(LruBitmapPool(
        MemorySizeCalculator(context)
            .bitmapPoolSize.toLong()
    ))
)
(2)Coil(Kotlin协程优先)
// Coil自动处理内存和生命周期
imageView.load(imageUrl) {
    size(1000, 1000)                    // 限制尺寸
    allowHardware(false)                // 禁用硬件加速位图(需要时)
    bitmapConfig(Bitmap.Config.RGB_565) // 使用RGB_565
    memoryCachePolicy(CachePolicy.ENABLED) // 启用内存缓存
    diskCachePolicy(CachePolicy.ENABLED)   // 启用磁盘缓存
    placeholder(R.drawable.placeholder)  // 占位图
    error(R.drawable.error)             // 错误图
}

2. 原生优化策略

(1)inSampleSize采样压缩
fun decodeSampledBitmapFromResource(
    res: Resources, 
    resId: Int, 
    reqWidth: Int, 
    reqHeight: Int
): Bitmap {
    val options = BitmapFactory.Options().apply {
        inJustDecodeBounds = true // 只获取尺寸,不加载像素
    }
    BitmapFactory.decodeResource(res, resId, options)
    
    // 计算采样率
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
    
    // 设置配置
    options.inJustDecodeBounds = false
    options.inPreferredConfig = Bitmap.Config.RGB_565 // 使用更省内存的配置
    options.inMutable = true // 如果需要修改
    
    return BitmapFactory.decodeResource(res, resId, options)
}

fun calculateInSampleSize(
    options: BitmapFactory.Options,
    reqWidth: Int,
    reqHeight: Int
): Int {
    val (height, width) = options.run { outHeight to outWidth }
    var inSampleSize = 1
    
    if (height > reqHeight || width > reqWidth) {
        val halfHeight = height / 2
        val halfWidth = width / 2
        
        // 计算采样率,保持宽高大于需求
        while (halfHeight / inSampleSize >= reqHeight 
            && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}
(2)BitmapRegionDecoder加载局部大图
fun loadPartialBitmap(assetName: String, region: Rect): Bitmap? {
    return try {
        val inputStream = assets.open(assetName)
        val decoder = BitmapRegionDecoder.newInstance(inputStream, false)
        
        val options = BitmapFactory.Options().apply {
            inPreferredConfig = Bitmap.Config.RGB_565
        }
        
        // 只加载指定区域
        decoder.decodeRegion(region, options).also {
            decoder.recycle()
            inputStream.close()
        }
    } catch (e: IOException) {
        null
    }
}

// 使用示例:加载长图的部分区域
val visibleRegion = Rect(0, scrollY, width, scrollY + height)
val partialBitmap = loadPartialBitmap("long_image.jpg", visibleRegion)
(3)inBitmap内存复用(API 11+)
object BitmapPool {
    private val reusableBitmaps = Stack<Bitmap>()
    
    fun getReusableBitmap(options: BitmapFactory.Options): Bitmap? {
        // 从池中获取可重用的Bitmap
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // Android 4.4+ 可以重用大小相同或更大的Bitmap
            reusableBitmaps.find { it.isMutable && it.allocationByteCount >= calculateSize(options) }
        } else {
            // Android 4.0-4.3 需要大小完全相同的Bitmap
            reusableBitmaps.find { it.isMutable && it.width == options.outWidth && it.height == options.outHeight && it.config == options.inPreferredConfig }
        }
    }
    
    fun decodeReusableBitmap(resId: Int, width: Int, height: Int): Bitmap? {
        val options = BitmapFactory.Options().apply {
            inJustDecodeBounds = true
        }
        BitmapFactory.decodeResource(resources, resId, options)
        
        // 从池中获取可重用的Bitmap
        val reusableBitmap = getReusableBitmap(options)
        if (reusableBitmap != null) {
            options.inMutable = true
            options.inBitmap = reusableBitmap
        }
        
        options.inJustDecodeBounds = false
        options.inSampleSize = calculateInSampleSize(options, width, height)
        options.inPreferredConfig = Bitmap.Config.RGB_565
        
        return BitmapFactory.decodeResource(resources, resId, options)
    }
    
    fun recycleBitmap(bitmap: Bitmap) {
        if (bitmap.isMutable && !bitmap.isRecycled) {
            reusableBitmaps.push(bitmap)
        }
    }
}

(四)高级优化技术

1. 使用Android Profiler分析内存

class MemoryProfilerHelper {
    // 1. 记录内存分配
    fun startMemoryTracking() {
        Debug.startMethodTracing("bitmap_memory")
        // 执行Bitmap操作
        Debug.stopMethodTracing()
    }
    
    // 2. 使用Android Studio的Profiler
    // - Memory Profiler查看实时内存
    // - Allocation Tracker追踪分配
    // - Heap Dump分析对象引用
    
    // 3. 代码中记录内存快照
    fun dumpHeapSnapshot(context: Context) {
        val file = File(context.externalCacheDir, "heapdump.hprof")
        Debug.dumpHprofData(file.absolutePath)
    }
}

2. 分页加载大图列表

class LargeImagePagingAdapter : PagingDataAdapter<ImageItem, ImageViewHolder>(COMPARATOR) {
    
    override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
        val item = getItem(position)
        item?.let {
            // 仅加载可见项的图片
            Glide.with(holder.itemView)
                .load(it.url)
                .apply(RequestOptions()
                    .override(holder.itemView.width, holder.itemView.height)
                    .onlyRetrieveFromCache(true) // 优先从缓存加载
                )
                .into(holder.imageView)
        }
    }
    
    override fun onViewRecycled(holder: ImageViewHolder) {
        super.onViewRecycled(holder)
        // 清理回收的ViewHolder中的图片
        Glide.with(holder.itemView).clear(holder.imageView)
    }
}

3. 使用VectorDrawable和XML图形

// 替代方案:使用矢量图减少内存
class VectorDrawableExample {
    // XML矢量图(几乎不占内存)
    // <vector>资源
    
    // 使用AnimatedVectorDrawable实现简单动画
    // <animated-vector>资源
    
    // 注意:复杂矢量图可能渲染性能较差
    // 权衡:简单图形用Vector,复杂图片用WebP/PNG
}

(五)不同场景下的优化策略

1. 列表/网格视图优化

class RecyclerViewOptimization {
    // 优化1:使用合适的图片尺寸
    fun setupRecyclerView(recyclerView: RecyclerView) {
        recyclerView.apply {
            // 固定大小提高性能
            setHasFixedSize(true)
            
            // 添加预加载
            layoutManager = LinearLayoutManager(context).apply {
                initialPrefetchItemCount = 4
            }
            
            // 添加回收监听
            addOnScrollListener(object : RecyclerView.OnScrollListener() {
                override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                    when (newState) {
                        RecyclerView.SCROLL_STATE_IDLE -> {
                            // 停止滚动时加载完整质量图片
                            loadFullQualityImages()
                        }
                        RecyclerView.SCROLL_STATE_DRAGGING -> {
                            // 滚动时加载缩略图
                            loadThumbnails()
                        }
                    }
                }
            })
        }
    }
}

2. 大图查看器优化

class LargeImageViewer : AppCompatActivity() {
    private lateinit var photoView: PhotoView
    private lateinit var decoder: BitmapRegionDecoder
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 使用SubsamplingScaleImageView等专门的大图查看库
        val imageView = SubsamplingScaleImageView(this).apply {
            setMinimumTileDpi(160)
            setDoubleTapZoomScale(2f)
            setMaxScale(10f)
            setBitmapDecoderFactory { 
                // 自定义解码器,控制内存
                SkiaImageRegionDecoder()
            }
        }
        
        // 加载大图
        val inputStream = assets.open("huge_image.jpg")
        decoder = BitmapRegionDecoder.newInstance(inputStream, false)
        
        // 分块加载
        imageView.setImage(ImageSource.asset("huge_image.jpg"))
    }
    
    override fun onDestroy() {
        super.onDestroy()
        decoder.recycle() // 及时释放资源
    }
}

(六)错误处理与降级策略

1. OOM异常捕获与处理

class SafeBitmapLoader {
    fun safeLoadBitmap(context: Context, resId: Int, maxMemoryMB: Int = 50): Bitmap? {
        return try {
            // 计算可用内存
            val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val memoryInfo = ActivityManager.MemoryInfo()
            activityManager.getMemoryInfo(memoryInfo)
            
            // 如果内存不足,使用更激进压缩
            val options = if (memoryInfo.lowMemory) {
                BitmapFactory.Options().apply {
                    inSampleSize = 4
                    inPreferredConfig = Bitmap.Config.RGB_565
                }
            } else {
                BitmapFactory.Options().apply {
                    inSampleSize = 2
                    inPreferredConfig = Bitmap.Config.ARGB_8888
                }
            }
            
            BitmapFactory.decodeResource(context.resources, resId, options)
        } catch (e: OutOfMemoryError) {
            // 降级策略
            Log.e("BitmapLoader", "OOM caught, using fallback")
            
            // 尝试更小的配置
            val fallbackOptions = BitmapFactory.Options().apply {
                inSampleSize = 8
                inPreferredConfig = Bitmap.Config.RGB_565
            }
            
            try {
                BitmapFactory.decodeResource(context.resources, resId, fallbackOptions)
            } catch (e2: OutOfMemoryError) {
                // 返回占位图或null
                createPlaceholderBitmap()
            }
        }
    }
    
    private fun createPlaceholderBitmap(): Bitmap {
        return Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888).apply {
            eraseColor(Color.LTGRAY)
        }
    }
}

2. 内存警告处理

class MemoryWarningReceiver : ComponentCallbacks2 {
    override fun onTrimMemory(level: Int) {
        when (level) {
            ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE -> {
                // 清理最早的缓存
                clearOldestCache(0.3f)
            }
            ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW -> {
                // 清理更多缓存
                clearOldestCache(0.5f)
            }
            ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> {
                // 清理大部分缓存
                clearOldestCache(0.8f)
            }
            ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {
                // UI隐藏,可以清理UI相关资源
                clearUICache()
            }
        }
    }
    
    override fun onConfigurationChanged(newConfig: Configuration) {}
    override fun onLowMemory() {
        // 内存极低,清理所有缓存
        clearAllCache()
    }
}

(七)现代Android开发最佳实践

1. 使用ImageDecoder(API 28+)

@RequiresApi(Build.VERSION_CODES.P)
fun loadWithImageDecoder(context: Context, resId: Int): Bitmap? {
    return try {
        val source = ImageDecoder.createSource(context.resources, resId)
        ImageDecoder.decodeBitmap(source) { decoder, info, source ->
            // 设置缩放
            decoder.setTargetSize(1000, 1000)
            
            // 设置裁剪
            decoder.crop = Rect(100, 100, 900, 900)
            
            // 设置颜色空间
            decoder.isMutable = false
            
            // 设置内存配置
            decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
        }
    } catch (e: Exception) {
        null
    }
}

2. 使用AndroidX的检查工具

// 使用AndroidX的检查库检测问题
class BitmapDebugTools {
    fun setupDebugTools(context: Context) {
        // 1. 使用App Inspection(Android Studio)
        // 2. 使用AndroidX Benchmark测试性能
        // 3. 使用AndroidX Tracing记录操作
        
        if (BuildConfig.DEBUG) {
            // 启用严格模式
            StrictMode.enableDefaults()
            
            // 启用内存泄漏检测
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
                activityManager.isWatchHeap = true
            }
        }
    }
}

(八)面试回答要点总结

  1. 内存计算公式
    • 基础公式:宽度 × 高度 × 每像素字节数
    • 常见配置:ARGB_8888(4字节)、RGB_565(2字节)、ALPHA_8(1字节)
    • 实际内存:考虑缩放因子、内存对齐、管理开销
  2. OOM避免策略
    • 压缩图片:使用inSampleSizeinScaledinDensity控制尺寸
    • 配置优化:根据需求选择合适Bitmap.Config
    • 内存复用:使用inBitmap(API 11+)复用内存
    • 分块加载:使用BitmapRegionDecoder加载局部大图
  3. 现代最佳实践
    • 使用图片加载库:Glide、Coil、Picasso自动管理内存和缓存
    • 使用矢量图形:简单图形使用VectorDrawable
    • 监控内存:使用Profiler、StrictMode、内存警告回调
    • 分页加载:列表中使用Paging 3库
  4. 错误处理
    • 捕获OutOfMemoryError并提供降级方案
    • 响应onTrimMemory()onLowMemory()回调
    • 实现优雅的失败恢复机制
  5. 架构设计
    • 分离图片加载逻辑到Repository层
    • 使用ViewModel管理图片加载状态
    • 实现内存感知的缓存策略

一句话总结
Bitmap内存管理需要综合考虑计算公式、Android版本差异、设备特性和使用场景。现代开发应优先使用成熟的图片加载库,结合内存监控和优化策略,在保证用户体验的前提下最小化内存占用。

四十六、如何实现应用更新(灰度、强制、增量)?

(一)应用更新架构设计

1. 现代应用更新架构

// 三层更新架构设计
object UpdateArchitecture {
    // 1. 数据层 - 版本信息管理
    interface UpdateRepository {
        suspend fun checkUpdate(): UpdateInfo
        suspend fun downloadApk(url: String, isIncremental: Boolean): File
        suspend fun applyIncrementalUpdate(oldApk: File, patch: File): File
    }
    
    // 2. 业务层 - 更新策略管理
    class UpdateManager(
        private val repository: UpdateRepository,
        private val strategy: UpdateStrategy
    ) {
        fun checkAndUpdate() {
            when (strategy) {
                is ForceUpdateStrategy -> showForceUpdate()
                is GrayUpdateStrategy -> checkGrayUpdate()
                is IncrementalUpdateStrategy -> checkIncrementalUpdate()
            }
        }
    }
    
    // 3. 表示层 - UI交互
    class UpdateActivity : AppCompatActivity() {
        private val viewModel: UpdateViewModel by viewModels()
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            viewModel.updateState.observe(this) { state ->
                when (state) {
                    is UpdateState.ForceUpdate -> showForceDialog()
                    is UpdateState.GrayUpdate -> showGrayUpdate(state)
                    is UpdateState.IncrementalUpdate -> downloadPatch()
                }
            }
        }
    }
}

(二)强制更新实现方案

1. 强制更新流程设计

class ForceUpdateManager(private val context: Context) {
    
    // 1. 检查强制更新条件
    suspend fun checkForceUpdate(): Boolean {
        val currentVersion = getCurrentVersion()
        val serverVersion = fetchLatestVersion()
        
        return serverVersion.isForceUpdate && 
               serverVersion.code > currentVersion.code
    }
    
    // 2. 显示无法关闭的对话框
    fun showForceUpdateDialog(updateInfo: UpdateInfo) {
        val dialog = AlertDialog.Builder(context)
            .setTitle("强制更新")
            .setMessage(updateInfo.description)
            .setCancelable(false) // 不可取消
            .setPositiveButton("立即更新") { _, _ ->
                startDownload(updateInfo.downloadUrl)
            }
            .create()
        
        // 禁用返回键
        dialog.setOnKeyListener { _, keyCode, _ ->
            keyCode == KeyEvent.KEYCODE_BACK
        }
        
        dialog.show()
    }
    
    // 3. 后台强制更新机制
    fun handleBackgroundForceUpdate() {
        if (shouldForceUpdateInBackground()) {
            // 在后台静默下载
            downloadInBackground()
            
            // 应用重启时安装
            scheduleInstallOnRestart()
            
            // 如果用户长时间不重启,可以提示
            showRestartNotification()
        }
    }
    
    // 4. 紧急强制更新 - 应用无法使用
    fun enforceCriticalUpdate() {
        // 显示全屏更新界面
        val intent = Intent(context, ForceUpdateActivity::class.java).apply {
            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        }
        context.startActivity(intent)
    }
}

2. 强制更新Activity示例

class ForceUpdateActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_force_update)
        
        // 禁用返回键
        onBackPressedDispatcher.addCallback(this, false) {
            // 不执行任何操作
        }
        
        // 设置全屏
        window.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )
        
        // 开始下载
        startDownload()
        
        // 监听下载进度
        observeDownloadProgress()
    }
    
    private fun startDownload() {
        val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
        val request = DownloadManager.Request(Uri.parse(updateUrl)).apply {
            setTitle("应用更新")
            setDescription("正在下载新版本")
            setDestinationInExternalPublicDir(
                Environment.DIRECTORY_DOWNLOADS,
                "app_update.apk"
            )
            setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
            setAllowedOverMetered(true)
            setAllowedOverRoaming(false)
        }
        
        val downloadId = downloadManager.enqueue(request)
        
        // 监听下载完成
        registerReceiver(downloadCompleteReceiver,
            IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
        )
    }
    
    private val downloadCompleteReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
            if (id == downloadId) {
                installApk()
            }
        }
    }
}

(三)灰度更新实现方案

1. 灰度发布策略引擎

class GrayUpdateStrategy(
    private val userProvider: UserProvider,
    private val experimentConfig: ExperimentConfig
) {
    
    data class GrayRule(
        val percentage: Int,           // 放量百分比 1-100
        val whitelist: Set<String>,    // 白名单用户ID
        val blacklist: Set<String>,    // 黑名单用户ID
        val conditions: List<Condition>, // 附加条件
        val channels: Set<String>,     // 渠道限制
        val versions: VersionRange,    // 版本范围限制
        val regions: Set<String>       // 地域限制
    )
    
    sealed class Condition {
        data class DeviceCondition(
            val minApi: Int,
            val maxApi: Int,
            val manufacturers: Set<String>
        ) : Condition()
        
        data class UserCondition(
            val userLevel: Int,
            val installDays: IntRange
        ) : Condition()
    }
    
    // 判断用户是否在灰度范围内
    fun shouldReceiveGrayUpdate(userId: String): Boolean {
        val rule = experimentConfig.getCurrentRule()
        
        // 1. 检查白名单
        if (userId in rule.whitelist) return true
        
        // 2. 检查黑名单
        if (userId in rule.blacklist) return false
        
        // 3. 检查渠道
        if (!rule.channels.contains(getChannel())) return false
        
        // 4. 检查版本
        if (!rule.versions.contains(getCurrentVersion())) return false
        
        // 5. 检查附加条件
        if (!checkConditions(rule.conditions)) return false
        
        // 6. 百分比放量
        return isInPercentage(userId, rule.percentage)
    }
    
    // 一致性哈希算法确保用户始终在相同分组
    private fun isInPercentage(userId: String, percentage: Int): Boolean {
        val hash = userId.hashCode() and 0x7FFFFFFF // 转为正数
        val bucket = hash % 100
        return bucket < percentage
    }
    
    // 获取用户分组(用于A/B测试)
    fun getUserGroup(userId: String, experimentId: String): String {
        val hash = "$userId$experimentId".hashCode() and 0x7FFFFFFF
        return when (hash % 100) {
            in 0..49 -> "control"      // 对照组
            in 50..99 -> "treatment"   // 实验组
            else -> "control"
        }
    }
}

2. 灰度数据收集与分析

class GrayUpdateAnalytics {
    
    // 记录灰度指标
    fun trackGrayUpdateMetrics(updateInfo: UpdateInfo, userAction: UserAction) {
        val metrics = mapOf(
            "experiment_id" to updateInfo.experimentId,
            "user_id" to getUserId(),
            "version" to updateInfo.version,
            "channel" to getChannel(),
            "action" to userAction.name,
            "timestamp" to System.currentTimeMillis(),
            "device_info" to getDeviceInfo()
        )
        
        // 上报到分析平台
        Analytics.logEvent("gray_update_metrics", metrics)
        
        // 本地记录,供后续分析
        saveLocalMetrics(metrics)
    }
    
    enum class UserAction {
        UPDATE_SHOWN,      // 更新提示显示
        UPDATE_ACCEPTED,   // 用户接受更新
        UPDATE_DECLINED,   // 用户拒绝更新
        UPDATE_SUCCESS,    // 更新成功
        UPDATE_FAILED,     // 更新失败
        UPDATE_CANCELLED   // 更新取消
    }
    
    // 分析灰度效果
    suspend fun analyzeGrayEffect(experimentId: String): GrayEffect {
        val metrics = fetchExperimentMetrics(experimentId)
        
        return GrayEffect(
            acceptanceRate = calculateAcceptanceRate(metrics),
            successRate = calculateSuccessRate(metrics),
            crashRate = calculateCrashRate(metrics),
            performanceImpact = calculatePerformanceImpact(metrics),
            businessImpact = calculateBusinessImpact(metrics)
        )
    }
}

3. 动态配置与热更新

class DynamicUpdateConfig(private val remoteConfig: FirebaseRemoteConfig) {
    
    // 从远程配置获取灰度规则
    suspend fun fetchGrayRules(): GrayRule {
        // 先尝试从远程获取
        remoteConfig.fetchAndActivate().addOnCompleteListener { task ->
            if (task.isSuccessful) {
                val ruleJson = remoteConfig.getString("gray_update_rule")
                parseGrayRule(ruleJson)
            }
        }
        
        // 使用本地缓存作为后备
        return loadCachedGrayRule()
    }
    
    // 动态调整灰度比例
    fun adjustGrayPercentage(experimentId: String, newPercentage: Int) {
        val configMap = mapOf(
            "gray_update_rule" to updateGrayRulePercentage(experimentId, newPercentage)
        )
        
        remoteConfig.setConfigSettingsAsync(
            remoteConfigSettings {
                minimumFetchIntervalInSeconds = 60 // 最小更新间隔
            }
        )
        
        remoteConfig.setDefaultsAsync(configMap)
    }
}

(四)增量更新实现方案

1. 增量更新架构

class IncrementalUpdateSystem {
    
    // 服务端生成差分包
    object PatchGenerator {
        fun generatePatch(oldApk: File, newApk: File): PatchFile {
            // 使用bsdiff算法
            val patch = BsDiff.diff(oldApk, newApk)
            
            // 计算并添加校验和
            val checksum = calculateChecksum(newApk)
            
            return PatchFile(
                patchData = patch,
                newVersion = getVersion(newApk),
                oldVersion = getVersion(oldApk),
                newChecksum = checksum,
                patchSize = patch.size,
                fullSize = newApk.length()
            )
        }
        
        // 使用HDiffPatch(更好的压缩率)
        fun generateHDiffPatch(oldApk: File, newApk: File): ByteArray {
            return HDiffPatch.createPatch(oldApk, newApk)
        }
    }
    
    // 客户端合并差分包
    object PatchApplier {
        suspend fun applyPatch(
            oldApk: File,
            patch: File,
            outputApk: File
        ): Result<File> = withContext(Dispatchers.IO) {
            return@withContext try {
                // 1. 验证旧APK完整性
                if (!verifyApkIntegrity(oldApk)) {
                    return@withContext Result.failure(Exception("旧APK损坏"))
                }
                
                // 2. 验证差分包签名
                if (!verifyPatchSignature(patch)) {
                    return@withContext Result.failure(Exception("差分包签名无效"))
                }
                
                // 3. 应用补丁
                val mergedApk = BsDiff.patch(oldApk, patch)
                
                // 4. 验证新APK完整性
                if (!verifyApkIntegrity(mergedApk)) {
                    return@withResult Result.failure(Exception("合并后APK损坏"))
                }
                
                // 5. 验证签名(Android 7.0+需要V2/V3签名验证)
                if (!verifyApkSignature(mergedApk)) {
                    return@withResult Result.failure(Exception("APK签名无效"))
                }
                
                Result.success(mergedApk)
            } catch (e: Exception) {
                Result.failure(e)
            }
        }
        
        // 处理Android不同的签名方案
        private fun verifyApkSignature(apk: File): Boolean {
            return when {
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> {
                    // Android 9.0+ 支持V3签名
                    verifyV3Signature(apk)
                }
                Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> {
                    // Android 7.0+ 支持V2签名
                    verifyV2Signature(apk)
                }
                else -> {
                    // 使用V1签名验证
                    verifyV1Signature(apk)
                }
            }
        }
    }
}

2. 增量更新客户端实现

class IncrementalUpdateClient(
    private val context: Context,
    private val repository: UpdateRepository
) {
    
    // 检查增量更新可用性
    suspend fun checkIncrementalUpdate(): IncrementalUpdateInfo? {
        val currentVersion = getCurrentVersionCode()
        val latestVersion = repository.getLatestVersion()
        
        // 获取可用的增量包列表
        val availablePatches = repository.getAvailablePatches(currentVersion)
        
        return availablePatches.maxByOrNull { it.targetVersion }?.let { patch ->
            IncrementalUpdateInfo(
                fromVersion = currentVersion,
                toVersion = patch.targetVersion,
                patchSize = patch.size,
                fullSize = patch.fullSize,
                savings = calculateSavings(patch),
                checksum = patch.newChecksum
            )
        }
    }
    
    // 执行增量更新
    suspend fun performIncrementalUpdate(updateInfo: IncrementalUpdateInfo): UpdateResult {
        return try {
            // 1. 下载差分包
            val patchFile = repository.downloadPatch(
                updateInfo.fromVersion,
                updateInfo.toVersion
            )
            
            // 2. 获取当前APK文件
            val currentApk = getCurrentApkFile() ?: return UpdateResult.FAILED
            
            // 3. 应用补丁
            val newApk = PatchApplier.applyPatch(currentApk, patchFile)
            
            // 4. 安装新APK
            installApk(newApk)
            
            UpdateResult.SUCCESS
        } catch (e: IOException) {
            // 增量更新失败,回退到全量更新
            fallbackToFullUpdate()
            UpdateResult.FALLBACK
        } catch (e: Exception) {
            UpdateResult.FAILED
        }
    }
    
    // 获取当前APK文件
    private fun getCurrentApkFile(): File? {
        return try {
            val packageInfo = context.packageManager
                .getPackageArchiveInfo(context.packageCodePath, 0)
            File(context.packageCodePath)
        } catch (e: Exception) {
            null
        }
    }
}

3. 差分算法优化

object AdvancedPatchAlgorithm {
    
    // 多种差分算法比较
    enum class PatchAlgorithm(val displayName: String) {
        BSDIFF("bsdiff"),        // 通用二进制差分
        HDIF("hdiffpatch"),      // 更快的差分速度
        XDELTA("xdelta"),        // 适合网络传输
        ZDELTA("zdelta"),        // 更好的压缩率
        COURGETTE("courgette")   // Chrome使用,更小的差分
    }
    
    // 根据APK特性选择最佳算法
    fun chooseBestAlgorithm(oldApk: File, newApk: File): PatchAlgorithm {
        val apkInfo = analyzeApkStructure(oldApk)
        
        return when {
            // 资源文件变化大,使用高压缩率算法
            apkInfo.resourceChangedRatio > 0.7 -> PatchAlgorithm.ZDELTA
            
            // 主要是Native库变化,使用bsdiff
            apkInfo.nativeLibChangedRatio > 0.5 -> PatchAlgorithm.BSDIFF
            
            // 小改动,使用快速算法
            apkInfo.totalChangedRatio < 0.1 -> PatchAlgorithm.HDIF
            
            // 默认选择
            else -> PatchAlgorithm.COURGETTE
        }
    }
    
    // 生成智能差分包(包含多种算法的补丁)
    fun generateSmartPatch(oldApk: File, newApk: File): SmartPatch {
        val algorithm = chooseBestAlgorithm(oldApk, newApk)
        val patch = generatePatchWithAlgorithm(oldApk, newApk, algorithm)
        
        // 添加备用补丁(使用不同算法)
        val fallbackPatch = generatePatchWithAlgorithm(
            oldApk, newApk, getFallbackAlgorithm(algorithm)
        )
        
        return SmartPatch(
            primaryPatch = patch,
            fallbackPatch = fallbackPatch,
            algorithm = algorithm,
            metadata = generatePatchMetadata(oldApk, newApk)
        )
    }
}

(五)现代更新方案集成

1. Google Play应用内更新(In-app Updates)

class PlayInAppUpdateManager(private val context: Context) {
    
    private val appUpdateManager: AppUpdateManager by lazy {
        AppUpdateManagerFactory.create(context)
    }
    
    // 灵活更新(Flexible Update)
    suspend fun checkFlexibleUpdate(): AppUpdateResult {
        val appUpdateInfo = appUpdateManager.appUpdateInfo
        
        return if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
            && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)) {
            
            // 启动灵活更新
            val result = appUpdateManager.startUpdateFlowForResult(
                appUpdateInfo,
                AppUpdateType.FLEXIBLE,
                context as Activity,
                REQUEST_CODE_UPDATE
            )
            
            AppUpdateResult.FlexibleUpdateStarted(result)
        } else {
            AppUpdateResult.NoUpdateAvailable
        }
    }
    
    // 即时更新(Immediate Update)
    fun checkImmediateUpdate(): AppUpdateResult {
        val appUpdateInfo = appUpdateManager.appUpdateInfo
        
        return if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
            && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
            && appUpdateInfo.updatePriority() >= 4) { // 高优先级更新
            
            val result = appUpdateManager.startUpdateFlowForResult(
                appUpdateInfo,
                AppUpdateType.IMMEDIATE,
                context as Activity,
                REQUEST_CODE_UPDATE
            )
            
            AppUpdateResult.ImmediateUpdateStarted(result)
        } else {
            AppUpdateResult.NoUpdateAvailable
        }
    }
    
    // 监听更新状态
    fun monitorUpdateState() {
        appUpdateManager.registerListener { state ->
            when (state) {
                is InstallStatus.DOWNLOADING -> {
                    val progress = state.bytesDownloaded() / state.totalBytesToDownload()
                    updateDownloadProgress(progress)
                }
                is InstallStatus.DOWNLOADED -> {
                    showUpdateReadyDialog()
                }
                is InstallStatus.INSTALLED -> {
                    onUpdateInstalled()
                }
                is InstallStatus.FAILED -> {
                    onUpdateFailed(state.errorCode())
                }
            }
        }
    }
}

2. 自建更新服务架构

class CustomUpdateService {
    
    // 微服务架构
    object UpdateMicroservices {
        // 1. 版本管理服务
        interface VersionService {
            fun getLatestVersion(channel: String): VersionInfo
            fun getVersionHistory(appId: String): List<VersionInfo>
            fun getIncrementalPatches(fromVersion: String): List<PatchInfo>
        }
        
        // 2. 发布管理服务
        interface ReleaseService {
            fun createRelease(release: Release): ReleaseResult
            fun setGrayReleaseRule(releaseId: String, rule: GrayRule)
            fun promoteRelease(releaseId: String, percentage: Int)
        }
        
        // 3. 统计分析服务
        interface AnalyticsService {
            fun trackUpdateEvent(event: UpdateEvent)
            fun getUpdateMetrics(releaseId: String): UpdateMetrics
            fun getCrashReport(version: String): CrashReport
        }
        
        // 4. CDN分发服务
        interface CDNService {
            fun uploadApk(apk: File, version: String): CDNUrl
            fun uploadPatch(patch: File, fromVersion: String, toVersion: String): CDNUrl
            fun purgeCache(urls: List<String>)
        }
    }
    
    // 客户端SDK
    class UpdateSDK(
        private val baseUrl: String,
        private val appId: String,
        private val channel: String
    ) {
        
        suspend fun checkUpdate(): UpdateResponse {
            return withContext(Dispatchers.IO) {
                val response = apiClient.post<UpdateResponse>(
                    "$baseUrl/check-update",
                    CheckUpdateRequest(
                        appId = appId,
                        version = getCurrentVersion(),
                        channel = channel,
                        deviceId = getDeviceId(),
                        userId = getUserId(),
                        locale = Locale.getDefault().toString()
                    )
                )
                
                // 解析响应,根据策略决定更新行为
                parseUpdateResponse(response)
            }
        }
        
        fun downloadUpdate(
            url: String,
            isIncremental: Boolean,
            callback: DownloadCallback
        ) {
            val downloader = if (isIncremental) {
                IncrementalDownloader(callback)
            } else {
                FullDownloader(callback)
            }
            
            downloader.download(url)
        }
    }
}

3. 混合更新策略

class HybridUpdateStrategy(
    private val playUpdateManager: PlayInAppUpdateManager,
    private val customUpdateManager: CustomUpdateManager
) {
    
    // 智能选择更新源
    suspend fun smartCheckUpdate(): UpdateStrategy {
        return if (isGooglePlayAvailable()) {
            // 优先使用Google Play更新
            tryGooglePlayUpdate()
        } else {
            // 回退到自定义更新
            tryCustomUpdate()
        }
    }
    
    private suspend fun tryGooglePlayUpdate(): UpdateStrategy {
        return try {
            val playResult = playUpdateManager.checkFlexibleUpdate()
            when (playResult) {
                is AppUpdateResult.UpdateAvailable -> {
                    UpdateStrategy.UsePlayStore(playResult)
                }
                else -> tryCustomUpdate()
            }
        } catch (e: Exception) {
            // Google Play服务不可用
            tryCustomUpdate()
        }
    }
    
    private suspend fun tryCustomUpdate(): UpdateStrategy {
        val customResult = customUpdateManager.checkUpdate()
        return when {
            customResult.isForceUpdate -> UpdateStrategy.ForceUpdate(customResult)
            customResult.isGrayUpdate -> UpdateStrategy.GrayUpdate(customResult)
            customResult.hasIncremental -> UpdateStrategy.IncrementalUpdate(customResult)
            else -> UpdateStrategy.FullUpdate(customResult)
        }
    }
    
    // 多CDN源下载
    fun downloadWithFallback(
        primaryUrl: String,
        fallbackUrls: List<String>,
        callback: DownloadCallback
    ) {
        val downloader = ResilientDownloader(
            urls = listOf(primaryUrl) + fallbackUrls,
            callback = callback,
            retryPolicy = ExponentialBackoffRetryPolicy(maxRetries = 3)
        )
        
        downloader.start()
    }
}

(六)安全与监控

1. 更新安全机制

class UpdateSecurityManager {
    
    // 1. APK签名验证
    fun verifyApkSignature(apkFile: File): Boolean {
        return when {
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> {
                // V3签名验证
                verifyV3Signature(apkFile)
            }
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> {
                // V2签名验证
                verifyV2Signature(apkFile)
            }
            else -> {
                // V1签名验证
                verifyV1Signature(apkFile)
            }
        }
    }
    
    // 2. 差分包签名验证
    fun verifyPatchSignature(patchFile: File, publicKey: String): Boolean {
        val signature = readSignatureFromPatch(patchFile)
        val data = readDataFromPatch(patchFile)
        
        return verifyRSASignature(data, signature, publicKey)
    }
    
    // 3. 下载完整性检查
    fun verifyDownloadIntegrity(
        file: File,
        expectedChecksum: String,
        algorithm: String = "SHA-256"
    ): Boolean {
        val actualChecksum = calculateChecksum(file, algorithm)
        return actualChecksum == expectedChecksum
    }
    
    // 4. 防篡改机制
    fun verifyUpdateRequest(request: UpdateRequest): Boolean {
        // 验证时间戳(防止重放攻击)
        if (System.currentTimeMillis() - request.timestamp > 5 * 60 * 1000) {
            return false
        }
        
        // 验证设备指纹
        if (!verifyDeviceFingerprint(request.deviceId)) {
            return false
        }
        
        // 验证签名
        return verifyRequestSignature(request)
    }
}

2. 监控与告警

class UpdateMonitoringSystem {
    
    // 关键指标监控
    object UpdateMetrics {
        // 1. 更新成功率监控
        fun trackUpdateSuccess(version: String, duration: Long) {
            Metrics.record("update.success", duration, 
                "version" to version,
                "channel" to getChannel()
            )
        }
        
        // 2. 失败原因分析
        fun trackUpdateFailure(version: String, error: UpdateError) {
            Metrics.record("update.failure", 
                "version" to version,
                "error_code" to error.code,
                "error_message" to error.message
            )
            
            // 触发告警
            if (error.isCritical) {
                AlertManager.sendCriticalAlert("update_failed", error)
            }
        }
        
        // 3. 灰度发布监控
        fun trackGrayUpdateMetrics(experimentId: String, metrics: GrayMetrics) {
            Metrics.record("gray_update.metrics", 
                "experiment_id" to experimentId,
                "acceptance_rate" to metrics.acceptanceRate,
                "crash_rate" to metrics.crashRate,
                "rollback_rate" to metrics.rollbackRate
            )
        }
    }
    
    // 实时监控面板
    class UpdateDashboard {
        fun showRealTimeMetrics() {
            // 显示:
            // - 当前更新版本分布
            // - 更新成功率
            // - 下载速度
            // - 用户反馈
            // - 崩溃率对比
        }
    }
    
    // A/B测试分析
    suspend fun analyzeABTest(experimentId: String): ABTestResult {
        val controlGroup = fetchGroupMetrics(experimentId, "control")
        val treatmentGroup = fetchGroupMetrics(experimentId, "treatment")
        
        return ABTestAnalyzer.compare(
            controlGroup = controlGroup,
            treatmentGroup = treatmentGroup,
            metrics = listOf(
                Metric.CRASH_RATE,
                Metric.USER_ENGAGEMENT,
                Metric.BUSINESS_CONVERSION
            )
        )
    }
}

(七)最佳实践总结

1. 更新策略选择矩阵

object UpdateStrategyMatrix {
    
    fun chooseStrategy(context: UpdateContext): UpdateStrategy {
        return when {
            // 紧急安全修复 → 强制更新
            context.isSecurityFix && context.severity == Severity.CRITICAL ->
                Strategy.FORCE_UPDATE
                
            // 新功能发布 → 灰度更新
            context.isNewFeature && !context.isBreakingChange ->
                Strategy.GRAY_UPDATE
                
            // 大版本升级 → 增量+全量混合
            context.versionDifference >= 2 ->
                Strategy.HYBRID_INCREMENTAL
                
            // 小版本更新 → 增量更新
            context.versionDifference == 1 &&
            context.estimatedPatchSize < 10 * 1024 * 1024 -> // 小于10MB
                Strategy.INCREMENTAL
                
            // 默认 → 全量更新
            else -> Strategy.FULL_UPDATE
        }
    }
    
    data class UpdateContext(
        val isSecurityFix: Boolean,
        val severity: Severity,
        val isNewFeature: Boolean,
        val isBreakingChange: Boolean,
        val versionDifference: Int,
        val estimatedPatchSize: Long,
        val networkCondition: NetworkCondition,
        val userSegment: UserSegment
    )
}

2. 降级与回滚策略

class RollbackManager {
    
    // 1. 自动回滚机制
    fun setupAutoRollback() {
        // 监控新版本崩溃率
        Crashlytics.addCrashListener { crashReport ->
            if (crashReport.version == currentVersion &&
                calculateCrashRate(crashReport) > ROLLBACK_THRESHOLD) {
                
                // 触发自动回滚
                triggerRollback()
                
                // 通知用户
                notifyUserOfRollback()
                
                // 上报事件
                Analytics.logEvent("auto_rollback_triggered", mapOf(
                    "version" to currentVersion,
                    "crash_rate" to calculateCrashRate(crashReport)
                ))
            }
        }
    }
    
    // 2. 版本降级支持
    fun allowVersionDowngrade(): Boolean {
        // 检查是否允许降级
        return SharedPreferences.getBoolean("allow_downgrade", false)
    }
    
    // 3. 数据兼容性处理
    fun handleDataBackwardCompatibility(oldVersion: String, newVersion: String) {
        // 版本降级时,处理数据迁移
        when {
            newVersion < oldVersion -> {
                // 降级,可能需要清理新版本数据
                cleanUpNewVersionData()
            }
            newVersion > oldVersion -> {
                // 升级,执行数据迁移
                migrateData(oldVersion, newVersion)
            }
        }
    }
}

(八)面试回答要点总结

  1. 强制更新
    • 不可取消的对话框或全屏界面
    • 后台静默下载重启安装
    • 紧急情况下可禁用应用功能直至更新
  2. 灰度更新
    • 多维度用户分组:百分比、白名单、设备、地域等
    • 一致性哈希确保用户分组稳定
    • A/B测试与数据收集分析
    • 动态调整放量比例
  3. 增量更新
    • 使用bsdiff/HDiffPatch等差分算法
    • 考虑Android签名方案(V1/V2/V3)兼容性
    • 完整性验证和回退机制
    • 智能算法选择(根据APK变化特征)
  4. 现代更新方案
    • Google Play应用内更新:优先使用,原生体验好
    • 自建更新服务:完全可控,支持复杂策略
    • 混合策略:智能选择最优更新源
  5. 安全与监控
    • APK签名验证防止篡改
    • 差分包签名和完整性检查
    • 实时监控更新成功率、崩溃率等指标
    • 自动回滚机制保障稳定性
  6. 最佳实践
    • 根据更新类型选择合适的策略
    • 提供降级和回滚能力
    • 考虑网络状况和用户体验
    • 完善的测试和监控体系

技术选型建议

  • 海外市场:优先集成Google Play In-app Updates
  • 国内市场:自建更新服务 + 应用市场分发
  • 大型应用:增量更新 + 灰度发布 + 智能CDN
  • 关键应用:强制更新 + 安全验证 + 实时监控

四十七、Android为什么需要签名?

(一)签名的核心作用

1. 身份验证(Authentication)

// 系统通过签名验证应用来源
class SignatureVerification {
    fun verifyAppPublisher(packageName: String): String? {
        val packageInfo = context.packageManager.getPackageInfo(
            packageName, 
            PackageManager.GET_SIGNATURES
        )
        
        // 获取签名证书信息
        val signatures = packageInfo.signatures
        val cert = signatures[0].toByteArray()
        val md = MessageDigest.getInstance("SHA-256")
        val digest = md.digest(cert)
        
        // 生成签名指纹(发布者唯一标识)
        val fingerprint = digest.joinToString(":") { "%02X".format(it) }
        
        // 与预置的受信任证书对比
        return if (isTrustedCertificate(fingerprint)) {
            getPublisherName(fingerprint) // 返回发布者名称
        } else {
            null // 未知发布者
        }
    }
}

身份验证的实际意义

  • 防止恶意仿冒:确保"微信"应用确实来自腾讯,而不是第三方仿冒
  • 企业应用分发:企业内部分发应用时验证内部开发者身份
  • 系统应用验证:预装系统应用使用平台签名,确保系统完整性

2. 完整性保护(Integrity)

// APK完整性校验流程
object ApkIntegrityChecker {
    fun verifyApkIntegrity(apkPath: String): Boolean {
        // 1. 验证MANIFEST.MF文件中的文件哈希
        if (!verifyManifestHashes(apkPath)) {
            return false // 文件被篡改
        }
        
        // 2. 验证CERT.SF签名文件
        if (!verifySignatureFile(apkPath)) {
            return false // 签名无效
        }
        
        // 3. 验证CERT.RSA/DSA证书链
        if (!verifyCertificateChain(apkPath)) {
            return false // 证书链无效
        }
        
        // 4. 对于V2/V3签名,验证APK签名块
        if (hasV2PlusSignature(apkPath)) {
            if (!verifyApkSigningBlock(apkPath)) {
                return false
            }
        }
        
        return true
    }
    
    private fun verifyManifestHashes(apkPath: String): Boolean {
        // 遍历APK中所有文件,计算SHA-256并比对
        ZipFile(apkPath).use { zip ->
            for (entry in zip.entries()) {
                if (!entry.name.startsWith("META-INF/")) {
                    val input = zip.getInputStream(entry)
                    val actualHash = calculateSHA256(input)
                    val expectedHash = getHashFromManifest(entry.name)
                    
                    if (actualHash != expectedHash) {
                        Log.e("Integrity", "文件 ${entry.name} 已被篡改")
                        return false
                    }
                }
            }
        }
        return true
    }
}

完整性保护机制

  • 安装时验证:系统在安装APK时自动验证签名
  • 运行时保护:Android 7.0+引入APK签名方案v2/v3,保护整个APK不被篡改
  • 防逆向保护:修改APK中的代码或资源会导致签名失效

3. 更新控制(Update Control)

// 更新时签名验证
class UpdateSignatureChecker {
    fun canUpdate(currentPackageName: String, newApkPath: String): Boolean {
        // 获取已安装应用的签名
        val currentSignatures = getInstalledAppSignatures(currentPackageName)
        
        // 获取新APK的签名
        val newSignatures = getApkSignatures(newApkPath)
        
        // 比较签名是否相同
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            // Android 9.0+ 支持比较签名行
            currentSignatures.contentEquals(newSignatures)
        } else {
            // 比较签名证书
            compareSignatures(currentSignatures, newSignatures)
        }
    }
    
    fun getApkSignatures(apkPath: String): Array<Signature> {
        val packageParserClass = Class.forName("android.content.pm.PackageParser")
        val packageParser = packageParserClass.newInstance()
        
        val apkFile = File(apkPath)
        val parsePackageMethod = packageParserClass.getMethod(
            "parsePackage", 
            File::class.java, 
            Int::class.javaPrimitiveType
        )
        
        val packageObj = parsePackageMethod.invoke(packageParser, apkFile, 0)
        val field = packageObj.javaClass.getDeclaredField("mSignatures")
        field.isAccessible = true
        
        return field.get(packageObj) as Array<Signature>
    }
}

更新控制的业务场景

  • 应用商店更新:确保只有原始开发者能发布更新
  • 多版本共存:不同签名的相同包名应用可以共存(如开发版和正式版)
  • 企业应用管理:企业可以控制只有自己签名的应用才能更新员工设备

(二)Android签名方案演进

1. 签名方案对比

签名方案 引入版本 保护范围 优点 缺点
v1 (JAR签名) Android 1.0 APK内的单个文件 兼容性好,工具成熟 不保护APK整体结构,容易被篡改
v2 (APK签名) Android 7.0 整个APK(ZIP结构) 更强的完整性保护,验证更快 需要Android 7.0+
v3 (APK签名) Android 9.0 支持密钥轮换和版本控制 支持密钥更新,向前兼容 需要Android 9.0+
v4 (增量签名) Android 11 支持按文件签名,适合大APK 增量更新友好,验证部分文件 需要Android 11+

2. v2/v3签名结构

// APK签名块结构示例
data class ApkSigningBlock(
    // 签名块大小
    val size: Long,
    // 多个签名者块
    val signerBlocks: List<SignerBlock>,
    // 填充数据
    val padding: ByteArray
)

data class SignerBlock(
    // 签名算法ID
    val algorithmId: Int,
    // 签名数据
    val signature: ByteArray,
    // 证书链
    val certificates: List<X509Certificate>,
    // 带长度前缀的属性
    val signedAttributes: ByteArray,
    // v3特有:支持密钥轮换
    val proofOfRotation: ProofOfRotation? = null
)

data class ProofOfRotation(
    // 历史密钥列表
    val rotatedCertificates: List<RotatedCertificate>,
    // 轮换策略
    val rotationStrategy: RotationStrategy
)

3. 签名验证流程

# 使用apksigner工具验证签名
apksigner verify --verbose app.apk

# 验证v2/v3签名
apksigner verify --min-sdk-version 24 app.apk

# 检查特定签名方案
apksigner verify --print-certs --v2-signing-enabled app.apk

# 输出示例:
# Verifies
# Verified using v1 scheme (JAR signing): true
# Verified using v2 scheme (APK Signature Scheme v2): true
# Verified using v3 scheme (APK Signature Scheme v3): true
# Number of signers: 1
# Signer #1 certificate DN: CN=Your Company, OU=Android, O=Your Organization
# Signer #1 certificate SHA-256 digest: ab:cd:ef:...

(三)密钥管理最佳实践

1. 密钥生成与存储

// 安全的密钥生成和存储方案
object SecureKeyManager {
    
    // 1. 使用Android Keystore系统(推荐)
    fun generateKeyInAndroidKeystore(
        alias: String,
        userAuthenticationRequired: Boolean = false
    ): KeyStore {
        val keyStore = KeyStore.getInstance("AndroidKeyStore")
        keyStore.load(null)
        
        val keyGenerator = KeyGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_AES,
            "AndroidKeyStore"
        )
        
        val keyGenSpec = KeyGenParameterSpec.Builder(
            alias,
            KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
        ).apply {
            setBlockModes(KeyProperties.BLOCK_MODE_GCM)
            setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
            setKeySize(256)
            if (userAuthenticationRequired) {
                setUserAuthenticationRequired(true)
                setUserAuthenticationValidityDurationSeconds(30)
            }
        }.build()
        
        keyGenerator.init(keyGenSpec)
        keyGenerator.generateKey()
        
        return keyStore
    }
    
    // 2. 密钥轮换策略
    data class KeyRotationPolicy(
        val rotationPeriodDays: Int = 365, // 一年轮换一次
        val keepPreviousKeys: Int = 2,     // 保留2个旧密钥
        val automaticRotation: Boolean = true,
        val rotationTrigger: RotationTrigger = RotationTrigger.TIME_BASED
    )
    
    // 3. 密钥备份方案
    fun backupSigningKey(
        keyAlias: String,
        encryptionPassword: String
    ): BackupResult {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // 使用加密的密钥备份
            val keyInfo = keyStore.getEntry(keyAlias, null) as KeyStore.SecretKeyEntry
            val encryptedKey = encryptKey(keyInfo.secretKey.encoded, encryptionPassword)
            
            // 存储到安全位置(如加密的云存储)
            saveToSecureStorage(encryptedKey)
            
            BackupResult.SUCCESS
        } else {
            BackupResult.UNSUPPORTED
        }
    }
}

2. Google Play应用签名(推荐方案)

// 使用Google Play应用签名服务
class PlayAppSigningManager {
    
    // 优势:
    // 1. Google管理签名密钥,避免丢失
    // 2. 支持自动密钥轮换
    // 3. 优化APK大小(应用包大小优化)
    
    fun setupPlayAppSigning() {
        // 步骤1:生成上传密钥(本地保管)
        generateUploadKey()
        
        // 步骤2:在Play Console注册上传证书
        registerUploadCertificate()
        
        // 步骤3:Google生成应用签名密钥(Google保管)
        // 步骤4:后续更新使用上传密钥签名,Google自动重新签名
    }
    
    fun migrateToPlayAppSigning(): MigrationResult {
        return try {
            // 1. 备份原始签名密钥
            backupOriginalKey()
            
            // 2. 在Play Console选择"使用Google管理密钥"
            enableGoogleManagedSigning()
            
            // 3. 上传使用新密钥签名的APK
            uploadApkWithNewKey()
            
            // 4. 验证迁移成功
            verifyMigration()
            
            MigrationResult.SUCCESS
        } catch (e: KeyLostException) {
            MigrationResult.ERROR_KEY_LOST
        }
    }
}

(四)签名相关的高级特性

1. 同签名应用的特权共享

<!-- AndroidManifest.xml配置 -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.app1">
    
    <!-- 共享用户ID,允许同签名应用共享数据 -->
    <sharedUserId 
        android:name="com.example.sharedid"
        android:sharedUserMaxSdkVersion="28" />
    
    <!-- 签名权限定义 -->
    <permission
        android:name="com.example.PRIVATE_PERMISSION"
        android:protectionLevel="signature" />
    
    <!-- ContentProvider共享 -->
    <provider
        android:name=".SharedProvider"
        android:authorities="com.example.sharedprovider"
        android:exported="true"
        android:permission="com.example.PRIVATE_PERMISSION" />
</manifest>
// 同签名应用数据共享
class SharedDataManager {
    fun accessSharedDataFromOtherApp() {
        // 检查签名是否相同
        if (checkSameSignature("com.example.app1", "com.example.app2")) {
            // 可以访问共享的ContentProvider
            val cursor = contentResolver.query(
                Uri.parse("content://com.example.sharedprovider/data"),
                null, null, null, null
            )
            
            // 可以调用共享的Service
            val intent = Intent().apply {
                component = ComponentName(
                    "com.example.app2",
                    "com.example.app2.SharedService"
                )
            }
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
    }
    
    private fun checkSameSignature(package1: String, package2: String): Boolean {
        val sig1 = getPackageSignatures(package1)
        val sig2 = getPackageSignatures(package2)
        
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            PackageManager.CERT_INPUT_SHA256.let { type ->
                val hash1 = packageManager.getPackageInfo(package1, type).signatures
                val hash2 = packageManager.getPackageInfo(package2, type).signatures
                hash1.contentEquals(hash2)
            }
        } else {
            sig1.contentEquals(sig2)
        }
    }
}

2. 签名权限保护

// 使用签名级权限保护敏感操作
class SignatureProtectedFeature {
    
    // 在Manifest中声明的签名权限
    private val SIGNATURE_PERMISSION = "com.example.SIGNATURE_PROTECTED"
    
    fun performProtectedOperation() {
        // 检查调用者是否有签名权限
        if (context.checkCallingPermission(SIGNATURE_PERMISSION) 
            == PackageManager.PERMISSION_GRANTED) {
            
            // 只有相同签名的应用可以执行此操作
            executeSensitiveOperation()
        } else {
            throw SecurityException("调用者缺少签名权限")
        }
    }
    
    // 验证调用者签名
    fun verifyCallerSignature(expectedPackage: String): Boolean {
        val callingUid = Binder.getCallingUid()
        val callingPackage = packageManager.getPackagesForUid(callingUid)?.firstOrNull()
        
        return callingPackage == expectedPackage && 
               checkSameSignature(context.packageName, callingPackage)
    }
}

(五)密钥丢失的严重后果与应对措施

1. 密钥丢失的影响

class KeyLossImpact {
    // 1. 无法发布应用更新
    data class UpdateFailureScenario(
        val scenario: String,
        val impact: String,
        val severity: Severity
    ) {
        companion object {
            val SCENARIOS = listOf(
                UpdateFailureScenario(
                    "安全漏洞修复",
                    "无法及时推送安全更新,用户面临风险",
                    Severity.CRITICAL
                ),
                UpdateFailureScenario(
                    "功能更新",
                    "无法添加新功能,用户可能转向竞品",
                    Severity.HIGH
                ),
                UpdateFailureScenario(
                    "兼容性修复",
                    "无法适配新Android版本,应用逐渐无法使用",
                    Severity.HIGH
                )
            )
        }
    }
    
    // 2. 品牌和用户信任损失
    fun calculateBrandDamage(): BrandDamageReport {
        return BrandDamageReport(
            lostUsers = estimateUserLoss(),
            reputationScore = calculateReputationImpact(),
            recoveryCost = estimateRecoveryCost()
        )
    }
    
    // 3. 收入损失
    fun calculateRevenueLoss(monthlyRevenue: Double): RevenueLossReport {
        val downtimeMonths = estimateRecoveryTime().toDouble() / 30.0
        val immediateLoss = monthlyRevenue * downtimeMonths
        
        // 长期损失(用户流失)
        val churnRate = 0.15 // 假设15%用户流失
        val longTermLoss = monthlyRevenue * 12 * churnRate
        
        return RevenueLossReport(
            immediateLoss = immediateLoss,
            longTermLoss = longTermLoss,
            totalLoss = immediateLoss + longTermLoss
        )
    }
}

2. 密钥丢失的应对策略

class KeyLossRecoveryStrategy {
    
    // 1. 预防措施
    object Prevention {
        // 多重备份策略
        fun createKeyBackupStrategy(): BackupStrategy {
            return BackupStrategy(
                localEncryptedBackup = true,
                cloudBackup = true, // 使用加密的云存储
                hardwareSecurityModule = false, // 企业级HSM
                physicalMediaBackup = true, // 加密的USB等
                backupLocations = setOf("safe1", "safe2", "offsite"),
                recoveryTestFrequency = Frequency.QUARTERLY
            )
        }
        
        // 使用Google Play应用签名
        fun migrateToManagedSigning(): MigrationPlan {
            return MigrationPlan(
                currentState = SigningState.SELF_MANAGED,
                targetState = SigningState.GOOGLE_MANAGED,
                steps = listOf(
                    "生成上传密钥",
                    "在Play Console配置",
                    "测试迁移流程",
                    "正式切换"
                ),
                rollbackPlan = RollbackPlan.ENABLED
            )
        }
    }
    
    // 2. 应急恢复流程
    class EmergencyRecovery {
        fun executeRecoveryPlan(lostKeyAlias: String): RecoveryResult {
            return try {
                // 步骤1:尝试从备份恢复
                val restoredKey = restoreFromBackup(lostKeyAlias)
                if (restoredKey != null) {
                    return RecoveryResult.BACKUP_RESTORED
                }
                
                // 步骤2:如果使用Play App Signing,联系Google支持
                if (isUsingPlayAppSigning()) {
                    val googleSupportResult = contactGooglePlaySupport()
                    if (googleSupportResult.canRecover) {
                        return RecoveryResult.GOOGLE_ASSISTED
                    }
                }
                
                // 步骤3:最后手段 - 发布新应用
                val newPackageName = generateNewPackageName()
                val migrationStrategy = createUserMigrationStrategy(newPackageName)
                
                RecoveryResult.NEW_APP_REQUIRED(migrationStrategy)
            } catch (e: Exception) {
                RecoveryResult.FAILED
            }
        }
        
        // 用户数据迁移方案
        private fun createUserMigrationStrategy(
            newPackageName: String
        ): UserMigrationStrategy {
            return UserMigrationStrategy(
                newPackageName = newPackageName,
                dataExportEnabled = true,
                automaticUpdateBlock = true,
                inAppNotification = true,
                promotionIncentive = PromotionIncentive.FREE_MONTH,
                timeline = MigrationTimeline(
                    announcementDate = "立即",
                    cutoffDate = "90天后",
                    oldAppRemovalDate = "180天后"
                )
            )
        }
    }
}

(六)现代签名实践与工具

1. 自动化签名流程(CI/CD集成)

// 使用Gradle自动化签名配置
android {
    signingConfigs {
        create("release") {
            // 从环境变量或密钥库文件读取
            storeFile = file(System.getenv("KEYSTORE_PATH") ?: "keystore.jks")
            storePassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
            keyAlias = System.getenv("KEY_ALIAS") ?: ""
            keyPassword = System.getenv("KEY_PASSWORD") ?: ""
            
            // 启用V3签名
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                v3SigningEnabled = true
            }
            
            // 启用V4签名(Android 11+)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                v4SigningEnabled = true
            }
        }
        
        create("debug") {
            storeFile = file("debug.keystore")
            storePassword = "android"
            keyAlias = "androiddebugkey"
            keyPassword = "android"
        }
    }
    
    buildTypes {
        getByName("release") {
            signingConfig = signingConfigs.getByName("release")
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

// 签名验证任务
tasks.register("verifyReleaseSignature") {
    dependsOn("assembleRelease")
    
    doLast {
        val apkFile = file("build/outputs/apk/release/app-release.apk")
        exec {
            commandLine = listOf(
                "apksigner", "verify",
                "--verbose",
                "--print-certs",
                apkFile.absolutePath
            )
        }
    }
}

2. 签名验证工具和API

// 编程方式验证签名
object ProgrammaticSignatureVerifier {
    
    // 1. 验证APK文件签名
    fun verifyApkFile(apkFile: File): VerificationResult {
        return try {
            val apkVerifier = ApkVerifier.Builder(apkFile).build()
            val result = apkVerifier.verify()
            
            VerificationResult(
                isVerified = result.isVerified,
                signingSchemeIds = result.signingSchemeIds,
                warnings = result.warnings,
                errors = result.errors
            )
        } catch (e: Exception) {
            VerificationResult(
                isVerified = false,
                errors = listOf(e.message ?: "验证失败")
            )
        }
    }
    
    // 2. 运行时验证自身签名
    fun verifySelfSignature(context: Context): Boolean {
        val packageName = context.packageName
        val packageInfo = context.packageManager.getPackageInfo(
            packageName,
            PackageManager.GET_SIGNATURES
        )
        
        // 计算签名指纹
        val signatures = packageInfo.signatures
        val md = MessageDigest.getInstance("SHA-256")
        val currentFingerprint = md.digest(signatures[0].toByteArray())
            .joinToString("") { "%02x".format(it) }
        
        // 与预期指纹比较(可硬编码或从服务器获取)
        val expectedFingerprint = getExpectedSignatureFingerprint()
        
        return currentFingerprint == expectedFingerprint
    }
    
    // 3. 防止重新打包检测
    fun checkForRepackaging(context: Context): Boolean {
        // 方法1:检查签名是否匹配
        if (!verifySelfSignature(context)) {
            return true // 可能被重新打包
        }
        
        // 方法2:检查调试状态
        if ((context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
            return true // 调试版本,可能被修改
        }
        
        // 方法3:检查安装来源
        val installerPackage = context.packageManager.getInstallerPackageName(context.packageName)
        if (installerPackage !in listOf("com.android.vending", "com.google.android.feedback")) {
            // 不是从Play Store安装
            return true
        }
        
        return false
    }
}

(七)面试回答要点总结

  1. 签名的三大核心作用
    • 身份验证:证明应用来自可信的发布者
    • 完整性保护:确保APK内容未被篡改
    • 更新控制:只有相同签名的应用才能更新
  2. 签名方案演进
    • v1 (JAR签名):基础方案,兼容性好但安全性较弱
    • v2/v3 (APK签名):保护整个APK结构,安全性强
    • v4 (增量签名):适合大应用,支持分块验证
  3. 密钥管理最佳实践
    • 使用Google Play应用签名:避免密钥丢失,Google托管密钥
    • 多备份策略:加密存储,多地备份
    • 自动化管理:CI/CD集成,减少人为错误
  4. 密钥丢失的严重后果
    • 无法发布安全更新和功能更新
    • 用户信任和品牌声誉受损
    • 可能需要重新发布应用,导致用户流失
  5. 高级特性应用
    • 同签名应用共享:共享数据、权限和组件
    • 签名权限保护:限制只有可信应用能访问敏感功能
    • 防逆向保护:检测应用是否被重新打包
  6. 现代开发实践
    • 优先使用v2/v3签名方案
    • 推荐使用Google Play App Signing
    • 在CI/CD中自动化签名流程
    • 定期测试密钥恢复流程

核心建议
对于新项目,强烈推荐使用Google Play应用签名服务。对于已有项目,尽快制定密钥备份策略并考虑迁移到托管签名。永远不要在代码库中硬编码密钥或密码,使用安全的密钥管理方案。

四十八、bindService如何与Activity生命周期联动?

(一)bindService基础生命周期关系

1. 标准绑定流程与生命周期

class BindServiceActivity : AppCompatActivity() {
    
    private var serviceBound = false
    private lateinit var serviceConnection: ServiceConnection
    private lateinit var myService: MyService.LocalBinder
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 1. 创建ServiceConnection
        serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
                // 绑定成功回调
                myService = binder as MyService.LocalBinder
                serviceBound = true
                Log.d("BindService", "Service connected")
                
                // 开始与服务交互
                myService.doSomething()
            }
            
            override fun onServiceDisconnected(name: ComponentName?) {
                // 服务异常断开(非正常解绑)
                serviceBound = false
                Log.w("BindService", "Service disconnected unexpectedly")
            }
        }
        
        // 2. 绑定服务
        val intent = Intent(this, MyService::class.java)
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        
        // 3. 解绑服务
        if (serviceBound) {
            unbindService(serviceConnection)
            serviceBound = false
            Log.d("BindService", "Service unbound in onDestroy")
        }
    }
}

绑定生命周期关键点

  1. onCreate()中绑定:Activity创建时建立连接
  2. onDestroy()中解绑:Activity销毁时断开连接
  3. 自动解绑机制:系统会在Activity销毁时自动解绑,但显式解绑是良好实践

(二)现代Android中的Service绑定模式

1. 使用Lifecycle感知的Service绑定

// Lifecycle感知的ServiceConnection
class LifecycleAwareServiceConnection(
    private val lifecycle: Lifecycle,
    private val onConnected: (IBinder) -> Unit,
    private val onDisconnected: () -> Unit = {}
) : ServiceConnection, LifecycleObserver {
    
    private var isConnected = false
    
    init {
        // 注册生命周期观察者
        lifecycle.addObserver(this)
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun cleanup() {
        // 生命周期结束时自动清理
        if (isConnected) {
            onDisconnected()
        }
    }
    
    override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
        isConnected = true
        binder?.let { onConnected(it) }
    }
    
    override fun onServiceDisconnected(name: ComponentName?) {
        isConnected = false
        onDisconnected()
    }
}

// 在Activity中使用
class ModernActivity : AppCompatActivity() {
    
    private lateinit var serviceConnection: LifecycleAwareServiceConnection
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        serviceConnection = LifecycleAwareServiceConnection(
            lifecycle = lifecycle,
            onConnected = { binder ->
                // 处理连接成功
                val service = (binder as MyService.LocalBinder).getService()
                service.doSomething()
            },
            onDisconnected = {
                // 处理断开连接
                Log.d("ModernActivity", "Service disconnected")
            }
        )
        
        // 绑定服务
        bindService(
            Intent(this, MyService::class.java),
            serviceConnection,
            Context.BIND_AUTO_CREATE
        )
    }
    
    // 不需要手动解绑,Lifecycle感知的连接会自动处理
}

2. ViewModel + Service绑定模式

// 使用ViewModel管理Service连接
class ServiceViewModel(application: Application) : AndroidViewModel(application) {
    
    private val _serviceState = MutableLiveData<ServiceState>()
    val serviceState: LiveData<ServiceState> = _serviceState
    
    private var serviceConnection: ServiceConnection? = null
    private var isBound = false
    
    fun bindService(context: Context) {
        if (isBound) return
        
        serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
                isBound = true
                _serviceState.value = ServiceState.Connected(binder)
            }
            
            override fun onServiceDisconnected(name: ComponentName?) {
                isBound = false
                _serviceState.value = ServiceState.Disconnected
            }
        }
        
        val intent = Intent(context, MyService::class.java)
        context.bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE)
    }
    
    fun unbindService(context: Context) {
        serviceConnection?.let { connection ->
            if (isBound) {
                context.unbindService(connection)
                isBound = false
                _serviceState.value = ServiceState.Disconnected
            }
        }
    }
    
    override fun onCleared() {
        super.onCleared()
        // ViewModel销毁时清理连接
        serviceConnection = null
    }
    
    sealed class ServiceState {
        object Disconnected : ServiceState()
        data class Connected(val binder: IBinder?) : ServiceState()
    }
}

// Activity中使用ViewModel
class ViewModelActivity : AppCompatActivity() {
    
    private val viewModel: ServiceViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 观察服务状态
        viewModel.serviceState.observe(this) { state ->
            when (state) {
                is ServiceViewModel.ServiceState.Connected -> {
                    // 更新UI
                    val service = (state.binder as MyService.LocalBinder).getService()
                    updateUI(service)
                }
                ServiceViewModel.ServiceState.Disconnected -> {
                    // 显示断开状态
                    showDisconnectedState()
                }
            }
        }
        
        // 绑定服务
        viewModel.bindService(this)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 注意:在Activity中解绑,而不是在ViewModel中
        // 因为ViewModel可能比Activity生命周期长
        viewModel.unbindService(this)
    }
}

(三)不同绑定标志的生命周期影响

1. 绑定标志详解

class BindingFlagsActivity : AppCompatActivity() {
    
    fun demonstrateBindingFlags() {
        // 1. BIND_AUTO_CREATE - 自动创建服务
        bindService(
            Intent(this, MyService::class.java),
            serviceConnection,
            Context.BIND_AUTO_CREATE  // 如果服务未运行则创建
        )
        
        // 2. BIND_ABOVE_CLIENT - 高优先级绑定
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
            bindService(
                Intent(this, CriticalService::class.java),
                serviceConnection,
                Context.BIND_AUTO_CREATE or Context.BIND_ABOVE_CLIENT
            )
        }
        
        // 3. BIND_IMPORTANT - 重要绑定(影响进程优先级)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            bindService(
                Intent(this, ImportantService::class.java),
                serviceConnection,
                Context.BIND_AUTO_CREATE or Context.BIND_IMPORTANT
            )
        }
        
        // 4. BIND_ADJUST_WITH_ACTIVITY - 与Activity优先级调整
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            bindService(
                Intent(this, BackgroundService::class.java),
                serviceConnection,
                Context.BIND_AUTO_CREATE or Context.BIND_ADJUST_WITH_ACTIVITY
            )
        }
        
        // 5. BIND_FOREGROUND_SERVICE - 绑定到前台服务
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            bindService(
                Intent(this, ForegroundBoundService::class.java),
                serviceConnection,
                Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE
            )
        }
    }
}

2. 绑定标志对生命周期的影响

绑定标志 生命周期影响 使用场景
BIND_AUTO_CREATE Activity绑定后服务自动创建,解绑后可能销毁 常规服务绑定
BIND_WAIVE_PRIORITY 不影响服务进程优先级 后台辅助服务
BIND_IMPORTANT 提升服务进程优先级 关键服务(如输入法)
BIND_ABOVE_CLIENT 服务优先级高于绑定客户端 系统关键服务
BIND_ADJUST_WITH_ACTIVITY 服务优先级随Activity调整 与UI紧密相关的服务
BIND_FOREGROUND_SERVICE 绑定到前台服务 Android 12+的音乐播放等

(四)配置更改时的Service绑定处理

1. 处理屏幕旋转等配置更改

class ConfigChangeActivity : AppCompatActivity() {
    
    // 方法1:保留Fragment实例
    private val serviceFragment = ServiceFragment()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 添加无UI的Fragment来持有Service连接
        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .add(serviceFragment, "ServiceFragment")
                .commit()
        }
        
        // 通过Fragment绑定服务
        serviceFragment.bindService(this)
    }
    
    // 无UI的Fragment,用于在配置更改时保持Service连接
    class ServiceFragment : Fragment() {
        
        private var serviceConnection: ServiceConnection? = null
        private var isBound = false
        
        fun bindService(context: Context) {
            if (isBound) return
            
            serviceConnection = object : ServiceConnection {
                override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
                    isBound = true
                    (activity as? ConfigChangeActivity)?.onServiceConnected(binder)
                }
                
                override fun onServiceDisconnected(name: ComponentName?) {
                    isBound = false
                    (activity as? ConfigChangeActivity)?.onServiceDisconnected()
                }
            }
            
            val intent = Intent(context, MyService::class.java)
            context.bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE)
        }
        
        override fun onDestroy() {
            super.onDestroy()
            serviceConnection?.let {
                if (isBound) {
                    requireContext().unbindService(it)
                }
            }
        }
        
        // 保持Fragment实例不被销毁
        init { retainInstance = true }
    }
    
    // 方法2:使用ViewModel + LiveData
    private val viewModel: ConfigViewModel by viewModels()
    
    private fun setupViewModel() {
        viewModel.serviceBinder.observe(this) { binder ->
            binder?.let {
                // 更新UI
                val service = (it as MyService.LocalBinder).getService()
                updateUI(service)
            }
        }
        
        // 只在第一次创建时绑定服务
        if (!viewModel.isServiceBound()) {
            viewModel.bindService(this)
        }
    }
}

2. 使用SavedStateHandle保存连接状态

class SavedStateViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    
    companion object {
        private const val SERVICE_BOUND_KEY = "service_bound"
        private const val SERVICE_BINDER_KEY = "service_binder"
    }
    
    private var serviceConnection: ServiceConnection? = null
    
    val isServiceBound: Boolean
        get() = savedStateHandle.get<Boolean>(SERVICE_BOUND_KEY) ?: false
    
    fun bindService(context: Context) {
        if (isServiceBound) return
        
        serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
                savedStateHandle[SERVICE_BOUND_KEY] = true
                // 注意:不能直接保存IBinder到SavedStateHandle
                // 可以通过其他方式传递
                onServiceBoundCallback?.invoke(binder)
            }
            
            override fun onServiceDisconnected(name: ComponentName?) {
                savedStateHandle[SERVICE_BOUND_KEY] = false
            }
        }
        
        val intent = Intent(context, MyService::class.java)
        context.bindService(intent, serviceConnection!!, Context.BIND_AUTO_CREATE)
    }
    
    fun unbindService(context: Context) {
        serviceConnection?.let {
            if (isServiceBound) {
                context.unbindService(it)
                savedStateHandle[SERVICE_BOUND_KEY] = false
            }
        }
    }
    
    private var onServiceBoundCallback: ((IBinder?) -> Unit)? = null
    
    fun setOnServiceBoundCallback(callback: (IBinder?) -> Unit) {
        this.onServiceBoundCallback = callback
    }
}

(五)Service端的生命周期响应

1. Service中的生命周期方法

class LifecycleAwareService : Service() {
    
    inner class LocalBinder : Binder() {
        fun getService(): LifecycleAwareService = this@LifecycleAwareService
    }
    
    private val binder = LocalBinder()
    
    // 客户端绑定到Service时调用
    override fun onBind(intent: Intent?): IBinder {
        Log.d("LifecycleAwareService", "onBind called")
        return binder
    }
    
    // 所有客户端解绑后调用
    override fun onUnbind(intent: Intent?): Boolean {
        Log.d("LifecycleAwareService", "onUnbind called")
        
        // 返回true表示希望下次绑定时收到onRebind()
        return true
    }
    
    // 当有客户端重新绑定,且onUnbind()返回true时调用
    override fun onRebind(intent: Intent?) {
        super.onRebind(intent)
        Log.d("LifecycleAwareService", "onRebind called")
    }
    
    // Service创建时调用
    override fun onCreate() {
        super.onCreate()
        Log.d("LifecycleAwareService", "onCreate called")
    }
    
    // Service销毁时调用
    override fun onDestroy() {
        super.onDestroy()
        Log.d("LifecycleAwareService", "onDestroy called")
    }
    
    // 跟踪绑定客户端数量
    private var boundClients = 0
    
    fun clientBound() {
        boundClients++
        Log.d("LifecycleAwareService", "Client bound, total: $boundClients")
    }
    
    fun clientUnbound() {
        boundClients--
        Log.d("LifecycleAwareService", "Client unbound, total: $boundClients")
        
        if (boundClients == 0) {
            // 没有客户端绑定,可以停止服务
            stopSelf()
        }
    }
}

2. 使用LifecycleService

// 使用AndroidX的LifecycleService
class MyLifecycleService : LifecycleService() {
    
    private val serviceLifecycleOwner = ServiceLifecycleOwner(this)
    
    override fun onCreate() {
        super.onCreate()
        
        // 添加生命周期观察者
        lifecycle.addObserver(object : LifecycleObserver {
            @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
            fun onCreateEvent() {
                Log.d("MyLifecycleService", "Lifecycle ON_CREATE")
            }
            
            @OnLifecycleEvent(Lifecycle.Event.ON_START)
            fun onStartEvent() {
                Log.d("MyLifecycleService", "Lifecycle ON_START")
            }
            
            @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
            fun onStopEvent() {
                Log.d("MyLifecycleService", "Lifecycle ON_STOP")
            }
            
            @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
            fun onDestroyEvent() {
                Log.d("MyLifecycleService", "Lifecycle ON_DESTROY")
            }
        })
    }
    
    override fun onBind(intent: Intent): IBinder? {
        // 通知生命周期状态变化
        serviceLifecycleOwner.onBind()
        return super.onBind(intent)
    }
    
    override fun onUnbind(intent: Intent?): Boolean {
        serviceLifecycleOwner.onUnbind()
        return super.onUnbind(intent)
    }
}

// 自定义LifecycleOwner用于Service
class ServiceLifecycleOwner(private val service: Service) : LifecycleOwner {
    
    private val lifecycleRegistry = LifecycleRegistry(this)
    
    init {
        lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED
    }
    
    fun onCreate() {
        lifecycleRegistry.currentState = Lifecycle.State.CREATED
    }
    
    fun onStart() {
        lifecycleRegistry.currentState = Lifecycle.State.STARTED
    }
    
    fun onBind() {
        // 绑定不会改变生命周期状态
    }
    
    fun onUnbind() {
        // 解绑不会改变生命周期状态
    }
    
    fun onDestroy() {
        lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
    }
    
    override fun getLifecycle(): Lifecycle = lifecycleRegistry
}

(六)常见问题与解决方案

1. 内存泄漏问题

class MemoryLeakPrevention {
    
    // ❌ 错误示例:可能导致内存泄漏
    class LeakyActivity : AppCompatActivity() {
        private var serviceConnection: ServiceConnection? = null
        private var service: MyService? = null
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            
            serviceConnection = object : ServiceConnection {
                override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
                    service = (binder as MyService.LocalBinder).getService()
                    // 问题:Service持有Activity引用
                    service!!.setCallback(object : MyService.Callback {
                        override fun onDataChanged(data: String) {
                            // 更新UI
                            updateUI(data)
                        }
                    })
                }
                
                override fun onServiceDisconnected(name: ComponentName?) {
                    service = null
                }
            }
        }
        
        override fun onDestroy() {
            super.onDestroy()
            // 忘记解绑或解绑失败
            // serviceConnection?.let { unbindService(it) }
        }
    }
    
    // ✅ 正确示例:避免内存泄漏
    class SafeActivity : AppCompatActivity() {
        
        private val serviceConnection = SafeServiceConnection()
        
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            
            bindService(
                Intent(this, MyService::class.java),
                serviceConnection,
                Context.BIND_AUTO_CREATE
            )
        }
        
        override fun onDestroy() {
            super.onDestroy()
            // 确保解绑
            try {
                unbindService(serviceConnection)
            } catch (e: IllegalArgumentException) {
                // 可能已经解绑,忽略异常
                Log.w("SafeActivity", "Service already unbound")
            }
        }
        
        // 使用弱引用避免循环引用
        inner class SafeServiceConnection : ServiceConnection {
            private val activityRef = WeakReference<SafeActivity>(this@SafeActivity)
            
            override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
                val activity = activityRef.get()
                if (activity != null && !activity.isDestroyed) {
                    // 安全更新UI
                    activity.updateUI("Connected")
                }
            }
            
            override fun onServiceDisconnected(name: ComponentName?) {
                val activity = activityRef.get()
                activity?.updateUI("Disconnected")
            }
        }
    }
}

2. 多Activity绑定同一Service

class MultiBindingService : Service() {
    
    private val clients = mutableMapOf<IBinder, ClientInfo>()
    private val binder = LocalBinder()
    
    inner class LocalBinder : Binder() {
        fun getService(): MultiBindingService = this@MultiBindingService
        
        fun registerClient(client: IBinder, clientInfo: ClientInfo) {
            clients[client] = clientInfo
            Log.d("MultiBindingService", "Client registered: ${clientInfo.name}")
        }
        
        fun unregisterClient(client: IBinder) {
            clients.remove(client)?.let { clientInfo ->
                Log.d("MultiBindingService", "Client unregistered: ${clientInfo.name}")
            }
        }
    }
    
    data class ClientInfo(val name: String, val priority: Int)
    
    override fun onBind(intent: Intent?): IBinder = binder
    
    override fun onUnbind(intent: Intent?): Boolean {
        // 返回true允许重新绑定
        return true
    }
    
    // 通知所有客户端
    fun notifyClients(message: String) {
        clients.keys.forEach { client ->
            try {
                // 通过Binder发送消息
                // 实际实现中可能需要自定义接口
            } catch (e: RemoteException) {
                // 客户端已断开
                clients.remove(client)
            }
        }
    }
}

(七)现代替代方案:使用WorkManager或协程

1. 替代bindService的现代方案

// 方案1:使用WorkManager处理后台任务
class ServiceReplacementActivity : AppCompatActivity() {
    
    fun startBackgroundWork() {
        val constraints = Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
        
        val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
            .setConstraints(constraints)
            .build()
        
        WorkManager.getInstance(this).enqueue(workRequest)
        
        // 观察工作状态
        WorkManager.getInstance(this)
            .getWorkInfoByIdLiveData(workRequest.id)
            .observe(this) { workInfo ->
                when (workInfo?.state) {
                    WorkInfo.State.SUCCEEDED -> {
                        val result = workInfo.outputData.getString("result")
                        updateUI(result)
                    }
                    WorkInfo.State.FAILED -> {
                        showError(workInfo.outputData.getString("error"))
                    }
                    else -> {
                        // 处理其他状态
                    }
                }
            }
    }
}

// 方案2:使用协程和Repository模式
class CoroutineBasedService {
    
    suspend fun performTask(): Result<String> = withContext(Dispatchers.IO) {
        try {
            // 执行耗时任务
            val result = doLongRunningTask()
            Result.success(result)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }
}

class ViewModelWithCoroutines : ViewModel() {
    
    private val repository = DataRepository()
    private val _uiState = MutableStateFlow<UiState>(UiState.Idle)
    val uiState: StateFlow<UiState> = _uiState
    
    fun loadData() {
        viewModelScope.launch {
            _uiState.value = UiState.Loading
            try {
                val data = repository.fetchData()
                _uiState.value = UiState.Success(data)
            } catch (e: Exception) {
                _uiState.value = UiState.Error(e.message)
            }
        }
    }
}

2. 使用Foreground Service + 协程(Android 8.0+)

class ModernForegroundService : Service() {
    
    private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
    
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 启动前台服务
        startForeground(NOTIFICATION_ID, createNotification())
        
        // 启动协程任务
        serviceScope.launch {
            performBackgroundWork()
        }
        
        return START_STICKY
    }
    
    private suspend fun performBackgroundWork() {
        // 使用协程执行耗时任务
        withContext(Dispatchers.IO) {
            repeat(10) { i ->
                delay(1000)
                updateNotification("Processing item $i")
            }
        }
        
        // 任务完成,停止服务
        stopSelf()
    }
    
    override fun onDestroy() {
        super.onDestroy()
        // 取消所有协程
        serviceScope.cancel()
    }
    
    override fun onBind(intent: Intent?): IBinder? = null
}

(八)面试回答要点总结

  1. 基本生命周期联动
    • 绑定时机:通常在onCreate()中调用bindService()
    • 解绑时机:在onDestroy()中调用unbindService()
    • 系统自动处理:Activity销毁时系统会自动解绑,但显式解绑是良好实践
  2. ServiceConnection的作用
    • onServiceConnected():绑定成功回调,获取IBinder进行通信
    • onServiceDisconnected():服务异常断开回调(非正常解绑)
  3. 内存泄漏预防
    • 确保在onDestroy()中解绑服务
    • 使用弱引用避免Service持有Activity引用
    • 处理IllegalArgumentException(服务已解绑的情况)
  4. 配置更改处理
    • 使用无UI的Fragment(setRetainInstance(true))保持Service连接
    • 使用ViewModel保存Service状态
    • 避免在每次配置更改时重复绑定
  5. 现代最佳实践
    • 使用Lifecycle感知的组件(如LifecycleServiceConnection)
    • 考虑使用ViewModel管理Service绑定状态
    • 对于后台任务,优先使用WorkManager或协程
  6. 绑定标志的影响
    • BIND_AUTO_CREATE:自动创建服务
    • BIND_IMPORTANT:提升服务优先级
    • BIND_ADJUST_WITH_ACTIVITY:服务优先级随Activity变化
  7. 替代方案
    • 短期任务:使用协程或线程池
    • 可延迟任务:使用WorkManager
    • 用户感知的长时间任务:使用前台服务
    • 跨进程通信:使用AIDL或Messenger

一句话总结
bindService与Activity的联动核心在于正确管理绑定和解绑的时机,避免内存泄漏,并处理配置更改等特殊情况。现代Android开发中,应考虑使用更高级的架构组件和异步处理方案替代传统的Service绑定模式。

四十九、如何使用Gradle配置多渠道包?

(一)多渠道打包基础配置

1. 基础Product Flavors配置

// build.gradle (Module级)
android {
    // 1. 定义维度(必须至少一个)
    flavorDimensions "channel", "environment"
    
    productFlavors {
        // 渠道维度配置
        huawei {
            dimension "channel"
            // 应用ID后缀(可选)
            applicationIdSuffix ".huawei"
            // 版本名后缀(可选)
            versionNameSuffix "-huawei"
            // 设置BuildConfig字段
            buildConfigField "String", "CHANNEL", "\"huawei\""
            // 替换清单文件中的占位符
            manifestPlaceholders = [
                CHANNEL_VALUE: "huawei",
                APP_NAME: "华为版应用"
            ]
        }
        
        xiaomi {
            dimension "channel"
            applicationIdSuffix ".xiaomi"
            versionNameSuffix "-xiaomi"
            buildConfigField "String", "CHANNEL", "\"xiaomi\""
            manifestPlaceholders = [
                CHANNEL_VALUE: "xiaomi",
                APP_NAME: "小米版应用"
            ]
        }
        
        // 环境维度配置
        dev {
            dimension "environment"
            applicationIdSuffix ".dev"
            versionNameSuffix "-dev"
            buildConfigField "boolean", "IS_DEBUG", "true"
        }
        
        prod {
            dimension "environment"
            buildConfigField "boolean", "IS_DEBUG", "false"
        }
    }
}

2. 动态生成多渠道配置(大量渠道时)

// 通过脚本动态生成渠道配置
android {
    flavorDimensions "channel"
    
    // 定义渠道列表
    def channels = [
        "huawei", "xiaomi", "oppo", "vivo", "tencent", 
        "baidu", "360", "meizu", "samsung", "googleplay"
    ]
    
    // 动态生成渠道配置
    channels.each { channelName ->
        create(channelName) {
            dimension "channel"
            applicationIdSuffix ".${channelName}"
            buildConfigField "String", "CHANNEL", "\"${channelName}\""
            manifestPlaceholders = [CHANNEL_VALUE: channelName]
            
            // 渠道特定配置
            if (channelName == "huawei") {
                // 华为渠道特殊配置
                resValue "string", "app_name", "华为版应用"
            } else if (channelName == "googleplay") {
                // Google Play渠道特殊配置
                versionNameSuffix "-gp"
                resValue "string", "app_name", "国际版应用"
            }
        }
    }
}

(二)多渠道打包的构建与使用

1. 构建命令

# 构建特定渠道的Release包
./gradlew assembleHuaweiRelease      # 华为渠道
./gradlew assembleXiaomiRelease      # 小米渠道
./gradlew assembleGoogleplayRelease  # Google Play渠道

# 构建所有渠道的Release包
./gradlew assembleRelease

# 构建特定构建变体(包含所有维度)
./gradlew assembleHuaweiProdRelease  # 华为生产环境Release
./gradlew assembleXiaomiDevDebug     # 小米开发环境Debug

# 查看所有可用变体
./gradlew tasks | grep assemble

2. Android Studio中的使用

// 1. 在Build Variants面板中选择变体
// 位置:View → Tool Windows → Build Variants
// 可以快速切换不同的渠道+构建类型组合

// 2. 代码中获取渠道信息
object ChannelUtils {
    fun getChannelName(): String {
        return BuildConfig.CHANNEL
    }
    
    fun isHuaweiChannel(): Boolean {
        return BuildConfig.CHANNEL == "huawei"
    }
    
    fun isDebugMode(): Boolean {
        return BuildConfig.IS_DEBUG
    }
}

// 3. 根据渠道执行不同逻辑
fun initThirdPartySDKs() {
    when (BuildConfig.CHANNEL) {
        "huawei" -> {
            // 初始化华为推送
            HmsPush.init(this)
        }
        "xiaomi" -> {
            // 初始化小米推送
            MiPushClient.registerPush(this, APP_ID, APP_KEY)
        }
        "googleplay" -> {
            // 初始化Firebase
            Firebase.initializeApp(this)
        }
    }
}

(三)多渠道资源定制

1. 渠道特定资源目录结构

src/
├── main/                    # 公共资源
│   ├── res/
│   │   ├── values/strings.xml
│   │   └── drawable/icon.png
│   └── AndroidManifest.xml
├── huawei/                  # 华为渠道特有资源
│   ├── res/
│   │   ├── values/strings.xml  # 覆盖主strings.xml
│   │   └── drawable-xxhdpi/icon.png  # 渠道特定图标
│   └── java/com/example/channel/HuaweiInitializer.kt
├── xiaomi/                  # 小米渠道特有资源
│   └── res/values/strings.xml
└── googleplay/              # Google Play渠道特有资源
    └── res/values/strings.xml

2. 渠道特定资源示例

<!-- src/huawei/res/values/strings.xml -->
<resources>
    <string name="app_name">华为应用市场</string>
    <string name="channel_name">华为渠道</string>
    <!-- 覆盖主模块中的字符串 -->
</resources>

<!-- src/googleplay/res/values/strings.xml -->
<resources>
    <string name="app_name">App Name</string>
    <string name="privacy_policy">Privacy Policy</string>
    <!-- 国际版特定翻译 -->
</resources>

3. 渠道特定代码

// src/huawei/java/com/example/channel/HuaweiInitializer.kt
class HuaweiInitializer : ChannelInitializer {
    override fun init(context: Context) {
        // 华为渠道初始化代码
        HmsAnalytics.init(context)
        // 华为渠道特有功能
    }
}

// src/googleplay/java/com/example/channel/GooglePlayInitializer.kt  
class GooglePlayInitializer : ChannelInitializer {
    override fun init(context: Context) {
        // Google Play渠道初始化
        FirebaseAnalytics.getInstance(context)
        // 遵守Google Play政策的功能调整
    }
}

// 在主代码中使用
class AppInitializer {
    fun initialize(context: Context) {
        // 根据渠道选择不同的初始化器
        val initializer = when (BuildConfig.CHANNEL) {
            "huawei" -> HuaweiInitializer()
            "googleplay" -> GooglePlayInitializer()
            else -> DefaultInitializer()
        }
        initializer.init(context)
    }
}

(四)渠道特定依赖管理

1. 按渠道配置依赖

dependencies {
    // 所有渠道通用依赖
    implementation 'androidx.core:core-ktx:1.8.0'
    implementation 'com.google.android.material:material:1.6.1'
    
    // 渠道特定依赖
    huaweiImplementation 'com.huawei.hms:push:6.7.0.300'
    huaweiImplementation 'com.huawei.hms:analytics:6.8.0.300'
    
    xiaomiImplementation 'com.xiaomi.mipush:sdk:3.0.9'
    
    googleplayImplementation platform('com.google.firebase:firebase-bom:31.0.0')
    googleplayImplementation 'com.google.firebase:firebase-analytics'
    googleplayImplementation 'com.google.firebase:firebase-crashlytics'
    
    // 仅开发环境使用的依赖
    devImplementation 'com.facebook.stetho:stetho:1.6.0'
    devImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
    
    // 使用渠道特定版本的依赖
    if (getCurrentFlavor() == "huawei") {
        implementation 'com.example:sdk:1.0-huawei'
    } else {
        implementation 'com.example:sdk:1.0'
    }
}

2. 渠道签名配置

android {
    signingConfigs {
        debug {
            storeFile file('debug.keystore')
            storePassword 'android'
            keyAlias 'androiddebugkey'
            keyPassword 'android'
        }
        
        // 华为渠道签名
        huaweiRelease {
            storeFile file('huawei.keystore')
            storePassword System.getenv('HUAWEI_STORE_PASSWORD')
            keyAlias System.getenv('HUAWEI_KEY_ALIAS')
            keyPassword System.getenv('HUAWEI_KEY_PASSWORD')
        }
        
        // 小米渠道签名
        xiaomiRelease {
            storeFile file('xiaomi.keystore')
            storePassword System.getenv('XIAOMI_STORE_PASSWORD')
            keyAlias System.getenv('XIAOMI_KEY_ALIAS')
            keyPassword System.getenv('XIAOMI_KEY_PASSWORD')
        }
    }
    
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    
    // 为不同渠道配置不同的签名
    productFlavors {
        huawei {
            signingConfig signingConfigs.huaweiRelease
        }
        xiaomi {
            signingConfig signingConfigs.xiaomiRelease
        }
        googleplay {
            // Google Play使用应用签名,这里配置上传密钥
            signingConfig signingConfigs.debug
        }
    }
}

(五)高级多渠道配置

1. 多渠道包批量生成脚本

// 在项目根目录创建多渠道打包脚本
// channel.gradle
ext {
    // 渠道配置
    channels = [
        "huawei": [
            "applicationIdSuffix": ".huawei",
            "versionNameSuffix": "-huawei",
            "manifestPlaceholders": [
                "CHANNEL": "huawei",
                "APP_NAME": "华为版"
            ]
        ],
        "xiaomi": [
            "applicationIdSuffix": ".xiaomi",
            "versionNameSuffix": "-xiaomi",
            "manifestPlaceholders": [
                "CHANNEL": "xiaomi",
                "APP_NAME": "小米版"
            ]
        ],
        "googleplay": [
            "applicationIdSuffix": ".gp",
            "versionNameSuffix": "-gp",
            "manifestPlaceholders": [
                "CHANNEL": "googleplay",
                "APP_NAME": "国际版"
            ]
        ]
    ]
}

// 在模块build.gradle中应用
apply from: '../channel.gradle'

android {
    productFlavors {
        channels.each { channelName, config ->
            create(channelName) {
                dimension "channel"
                applicationIdSuffix config.applicationIdSuffix
                versionNameSuffix config.versionNameSuffix
                manifestPlaceholders config.manifestPlaceholders
                buildConfigField "String", "CHANNEL", "\"${channelName}\""
            }
        }
    }
}

// 创建Gradle任务批量打包
task assembleAllChannels() {
    dependsOn channels.keySet().collect { channel ->
        "assemble${channel.capitalize()}Release"
    }
    
    doLast {
        println "所有渠道包已生成"
        // 可选:复制APK到指定目录
        copy {
            from "build/outputs/apk"
            into "dist/apks"
            include "**/*release.apk"
        }
    }
}

2. 使用Walle多渠道打包

// 对于大量渠道,使用Walle提高打包效率
// 1. 添加依赖
dependencies {
    implementation 'com.meituan.android.walle:library:1.1.7'
}

// 2. 应用插件
apply plugin: 'walle'

walle {
    // 指定渠道包的输出路径
    apkOutputFolder = new File("${project.buildDir}/outputs/channels")
    // 定制渠道包的APK的文件名称
    apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk'
    // 渠道配置文件
    channelFile = new File("${project.getProjectDir()}/channel")
}

// 3. 渠道文件格式(channel文件)
huawei #华为
xiaomi #小米
oppo #OPPO
vivo #VIVO

// 4. 打包命令
// ./gradlew clean assembleReleaseChannels

// 5. 代码中读取渠道信息
ChannelInfo channelInfo = WalleChannelReader.getChannelInfo(context)
String channel = channelInfo.getChannel()
Map<String, String> extraInfo = channelInfo.getExtraInfo()

(六)多渠道统计分析

1. 集成统计分析SDK

// 根据不同渠道初始化不同分析SDK
class AnalyticsManager(private val context: Context) {
    
    fun initialize() {
        when (BuildConfig.CHANNEL) {
            "huawei" -> {
                // 华为分析
                HiAnalyticsTools.enableLog()
                HiAnalytics.getInstance(context).apply {
                    setAnalyticsEnabled(true)
                    setAutoCollectionEnabled(true)
                }
            }
            "googleplay" -> {
                // Firebase分析
                FirebaseAnalytics.getInstance(context).apply {
                    setAnalyticsCollectionEnabled(true)
                    setUserProperty("channel", "googleplay")
                }
            }
            else -> {
                // 通用分析(如友盟)
                MobclickAgent.UMAnalyticsConfig(
                    context,
                    getUmengAppKey(),
                    BuildConfig.CHANNEL
                )
            }
        }
    }
    
    private fun getUmengAppKey(): String {
        // 不同渠道使用不同的友盟AppKey
        return when (BuildConfig.CHANNEL) {
            "huawei" -> "华为渠道的友盟Key"
            "xiaomi" -> "小米渠道的友盟Key"
            else -> "默认友盟Key"
        }
    }
}

2. 渠道数据上报

// 统一渠道数据上报接口
object ChannelAnalytics {
    
    // 上报渠道安装事件
    fun reportInstall(context: Context) {
        val channel = BuildConfig.CHANNEL
        val deviceId = getDeviceId()
        val appVersion = BuildConfig.VERSION_NAME
        
        // 上报到自有服务器
        reportToServer(
            event = "app_install",
            data = mapOf(
                "channel" to channel,
                "device_id" to deviceId,
                "version" to appVersion,
                "install_time" to System.currentTimeMillis()
            )
        )
        
        // 同时使用三方统计
        when {
            BuildConfig.CHANNEL == "huawei" -> {
                // 华为分析上报
                HiAnalyticsUtils.onReport(
                    HAEventType.SUBMITSCORE,
                    HAParamType.SCORE to 100
                )
            }
            BuildConfig.CHANNEL == "googleplay" -> {
                // Firebase上报
                FirebaseAnalytics.getInstance(context).logEvent(
                    "app_install",
                    Bundle().apply {
                        putString("channel", channel)
                    }
                )
            }
        }
    }
    
    // 获取渠道特定的配置
    fun getChannelConfig(): ChannelConfig {
        return when (BuildConfig.CHANNEL) {
            "huawei" -> ChannelConfig(
                feedbackUrl = "https://feedback.huawei.com",
                customerService = "950800",
                shareTitle = "华为应用市场推荐"
            )
            "googleplay" -> ChannelConfig(
                feedbackUrl = "https://play.google.com/store/apps/details?id=${BuildConfig.APPLICATION_ID}",
                customerService = "[email protected]",
                shareTitle = "Check out this app on Google Play"
            )
            else -> ChannelConfig()
        }
    }
}

(七)现代多渠道打包最佳实践

1. 使用Gradle变体过滤器

android {
    variantFilter { variant ->
        def names = variant.flavors*.name
        
        // 过滤掉不需要的变体组合
        if (names.contains("dev") && names.contains("huawei")) {
            // 华为渠道不需要开发版本
            setIgnore(true)
        }
        
        if (names.contains("prod") && variant.buildType.name == "debug") {
            // 生产环境不需要debug包
            setIgnore(true)
        }
    }
    
    // 配置构建变体
    buildTypes {
        release {
            // 发布包配置
        }
        debug {
            // 调试包配置
            applicationIdSuffix ".debug"
            versionNameSuffix "-debug"
        }
    }
}

2. 环境与渠道分离

// 推荐:渠道和环境分离,便于管理
android {
    flavorDimensions "market", "environment", "abi"
    
    productFlavors {
        // 应用市场维度
        china {
            dimension "market"
            buildConfigField "String", "MARKET", "\"china\""
            manifestPlaceholders = [
                "SERVER_URL": "https://api.china.example.com"
            ]
        }
        
        international {
            dimension "market"
            buildConfigField "String", "MARKET", "\"international\""
            manifestPlaceholders = [
                "SERVER_URL": "https://api.international.example.com"
            ]
        }
        
        // 环境维度
        staging {
            dimension "environment"
            applicationIdSuffix ".staging"
            versionNameSuffix "-staging"
            manifestPlaceholders = [
                "SERVER_URL": "https://staging-api.example.com"
            ]
        }
        
        production {
            dimension "environment"
            // 生产环境无后缀
        }
        
        // ABI维度(可选)
        arm32 {
            dimension "abi"
            ndk {
                abiFilters "armeabi-v7a"
            }
        }
        
        arm64 {
            dimension "abi"
            ndk {
                abiFilters "arm64-v8a"
            }
        }
    }
}

(八)多渠道包的测试与分发

1. 自动化测试

// 多渠道UI测试
@RunWith(AndroidJUnit4::class)
class MultiChannelUITest {
    
    @Test
    fun testHuaweiChannel() {
        // 华为渠道特定测试
        val scenario = launchFragmentInContainer<HuaweiFragment>()
        
        scenario.onFragment { fragment ->
            // 验证华为渠道特定功能
            assertThat(fragment.getChannelName()).isEqualTo("huawei")
        }
    }
    
    @Test
    fun testGooglePlayChannel() {
        // Google Play渠道测试
        val scenario = launchFragmentInContainer<GooglePlayFragment>()
        
        scenario.onFragment { fragment ->
            // 验证Google Play特定功能
            assertThat(fragment.hasGooglePlayServices()).isTrue()
        }
    }
}

// 使用BuildConfig过滤测试
@RunWith(Parameterized::class)
class ChannelSpecificTest {
    
    companion object {
        @JvmStatic
        @Parameterized.Parameters
        fun data(): List<Array<Any>> {
            return listOf(
                arrayOf("huawei", true),   // 华为渠道支持支付
                arrayOf("xiaomi", true),   // 小米渠道支持支付
                arrayOf("googleplay", false) // Google Play不支持某些支付方式
            )
        }
    }
    
    @Parameterized.Parameter(0)
    lateinit var channel: String
    
    @Parameterized.Parameter(1)
    var supportsPayment: Boolean = false
    
    @Test
    fun testPaymentSupport() {
        // 模拟不同渠道
        BuildConfig.CHANNEL = channel
        
        val paymentManager = PaymentManager()
        assertThat(paymentManager.isPaymentSupported()).isEqualTo(supportsPayment)
    }
}

2. 多渠道分发

// 使用Gradle分发任务
task distributeToMarkets(type: Exec) {
    dependsOn 'assembleAllChannels'
    
    doFirst {
        // 上传到不同应用市场
        def apkDir = file("dist/apks")
        
        apkDir.listFiles().each { apkFile ->
            if (apkFile.name.contains("huawei")) {
                // 上传到华为应用市场
                exec {
                    commandLine 'curl', '-X', 'POST', 
                        'https://api.huawei.com/upload',
                        '-F', "file=@${apkFile.absolutePath}"
                }
            }
            
            if (apkFile.name.contains("googleplay")) {
                // 上传到Google Play Console
                exec {
                    commandLine 'java', '-jar', 'google-play-api.jar',
                        'upload', apkFile.absolutePath
                }
            }
        }
    }
    
    doLast {
        println "所有渠道包已上传"
        
        // 发送通知
        exec {
            commandLine './scripts/send_notification.sh', 
                '多渠道打包完成'
        }
    }
}

(九)面试回答要点总结

  1. 基础配置
    • 使用flavorDimensions定义维度
    • productFlavors中配置各个渠道
    • 可以使用applicationIdSuffixversionNameSuffix等区分不同渠道
  2. 构建命令
    • ./gradlew assembleHuaweiRelease 构建特定渠道
    • ./gradlew assembleRelease 构建所有渠道
    • 可以通过Build Variants面板在Android Studio中切换
  3. 渠道特定配置
    • 资源定制:在对应渠道的src目录下放置特定资源
    • 代码定制:创建渠道特定的Java/Kotlin类
    • 依赖管理:使用huaweiImplementation等配置渠道特定依赖
  4. 高级技巧
    • 动态生成渠道:使用Groovy脚本批量配置
    • Walle多渠道打包:提高大量渠道的打包效率
    • 变体过滤:使用variantFilter过滤不需要的变体组合
  5. 主要用途
    • 统计分析:追踪不同渠道的用户行为和下载量
    • 功能定制:为不同渠道定制功能、UI或服务
    • A/B测试:通过渠道分组进行功能测试
    • 合规适配:满足不同应用市场的政策要求
  6. 最佳实践
    • 渠道和环境维度分离
    • 使用BuildConfig字段在代码中识别渠道
    • 为重要渠道创建自动化测试
    • 做好渠道包的签名和安全管理

一句话总结
Gradle多渠道打包是通过配置不同的productFlavors来生成针对不同渠道的应用版本,每个渠道可以有独立的配置、资源和代码,便于渠道统计、功能定制和市场适配。

五十、Activity与Fragment、Fragment间如何通信?

(一)通信架构演进

1. 通信方式对比

通信方式 适用场景 优点 缺点 推荐度
接口回调 Fragment→Activity简单通信 类型安全,明确契约 需要手动管理,生命周期敏感 ⭐⭐⭐
ViewModel + LiveData 任何组件间通信 生命周期感知,配置更改存活 需要理解架构组件 ⭐⭐⭐⭐⭐
Activity Result API Fragment需要结果返回 类型安全,易于使用 仅适用于请求-响应模式 ⭐⭐⭐⭐
Shared ViewModel Fragment间通信 共享状态,响应式更新 需要共享的ViewModelScope ⭐⭐⭐⭐⭐
EventBus/RxBus 全局事件通信 完全解耦,灵活 类型安全差,难以调试 ⭐⭐
直接方法调用 简单场景 简单直接 紧耦合,生命周期问题

(二)Activity与Fragment通信

1. Activity → Fragment通信

(1)传统方式:findFragmentById/Tag
// ❌ 不推荐的传统方式
class MainActivity : AppCompatActivity() {
    
    private fun sendDataToFragment(data: String) {
        // 方式1:通过findFragmentById(需要Fragment有容器ID)
        val fragment = supportFragmentManager
            .findFragmentById(R.id.fragment_container) as? MyFragment
        fragment?.updateData(data)
        
        // 方式2:通过Tag查找
        val fragmentByTag = supportFragmentManager
            .findFragmentByTag("MyFragment") as? MyFragment
        fragmentByTag?.updateData(data)
        
        // 问题:类型转换不安全,Fragment可能不存在
    }
}
(2)现代方式:使用ViewModel
// ✅ 推荐:使用SharedViewModel
class SharedViewModel : ViewModel() {
    private val _dataToFragment = MutableLiveData<String>()
    val dataToFragment: LiveData<String> = _dataToFragment
    
    fun sendDataToFragment(data: String) {
        _dataToFragment.value = data
    }
}

// Activity中发送数据
class MainActivity : AppCompatActivity() {
    private val sharedViewModel: SharedViewModel by viewModels()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 发送数据到Fragment
        sharedViewModel.sendDataToFragment("Hello from Activity")
    }
}

// Fragment中接收数据
class MyFragment : Fragment() {
    private val sharedViewModel: SharedViewModel by activityViewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        sharedViewModel.dataToFragment.observe(viewLifecycleOwner) { data ->
            // 更新UI
            textView.text = data
        }
    }
}

2. Fragment → Activity通信

(1)传统方式:接口回调
// 定义通信接口
interface FragmentToActivityListener {
    fun onDataReceived(data: String)
    fun onButtonClicked()
}

class MyFragment : Fragment() {
    
    private var listener: FragmentToActivityListener? = null
    
    override fun onAttach(context: Context) {
        super.onAttach(context)
        // 确保Activity实现了接口
        listener = context as? FragmentToActivityListener
            ?: throw ClassCastException("$context must implement FragmentToActivityListener")
    }
    
    override fun onDetach() {
        super.onDetach()
        listener = null // 避免内存泄漏
    }
    
    private fun sendDataToActivity() {
        listener?.onDataReceived("Data from Fragment")
    }
}

// Activity实现接口
class MainActivity : AppCompatActivity(), FragmentToActivityListener {
    
    override fun onDataReceived(data: String) {
        // 处理来自Fragment的数据
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
    }
    
    override fun onButtonClicked() {
        // 处理按钮点击
    }
}
(2)现代方式:使用Activity Result API
// Fragment中设置结果
class MyFragment : Fragment() {
    
    private val resultLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val data = result.data?.getStringExtra("result_data")
            // 处理返回结果
        }
    }
    
    private fun startActivityForResult() {
        val intent = Intent(requireContext(), TargetActivity::class.java)
        resultLauncher.launch(intent)
    }
    
    // 或者设置Fragment结果
    private fun setFragmentResult() {
        setFragmentResult(
            "request_key",
            bundleOf("data" to "result from fragment")
        )
    }
}

// Activity中接收结果
class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 监听Fragment结果
        supportFragmentManager.setFragmentResultListener(
            "request_key",
            this
        ) { requestKey, bundle ->
            val data = bundle.getString("data")
            // 处理结果
        }
    }
}

(三)Fragment间通信

1. 通过Activity中转(传统方式)

// Fragment A发送数据
class FragmentA : Fragment() {
    
    private fun sendDataToFragmentB(data: String) {
        (activity as? MainActivity)?.sendDataToFragmentB(data)
    }
}

// Activity中转
class MainActivity : AppCompatActivity() {
    
    fun sendDataToFragmentB(data: String) {
        val fragmentB = supportFragmentManager
            .findFragmentById(R.id.fragment_b_container) as? FragmentB
        fragmentB?.receiveData(data)
    }
}

// Fragment B接收数据
class FragmentB : Fragment() {
    
    fun receiveData(data: String) {
        // 处理数据
        updateUI(data)
    }
}

2. 使用SharedViewModel(推荐)

// 共享ViewModel
class SharedViewModel : ViewModel() {
    
    // 使用StateFlow(替代LiveData)
    private val _communicationData = MutableStateFlow<CommunicationData?>(null)
    val communicationData: StateFlow<CommunicationData?> = _communicationData
    
    private val _events = MutableSharedFlow<Event>()
    val events: SharedFlow<Event> = _events
    
    fun sendData(data: CommunicationData) {
        _communicationData.value = data
    }
    
    suspend fun sendEvent(event: Event) {
        _events.emit(event)
    }
}

// Fragment A发送数据
class FragmentA : Fragment() {
    private val sharedViewModel: SharedViewModel by activityViewModels()
    
    private fun sendDataToFragmentB() {
        // 发送数据
        sharedViewModel.sendData(CommunicationData("Hello from FragmentA"))
        
        // 发送事件(协程作用域)
        viewLifecycleOwner.lifecycleScope.launch {
            sharedViewModel.sendEvent(Event.ButtonClicked)
        }
    }
}

// Fragment B接收数据
class FragmentB : Fragment() {
    private val sharedViewModel: SharedViewModel by activityViewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 观察数据变化
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                sharedViewModel.communicationData.collect { data ->
                    data?.let {
                        updateUI(it)
                    }
                }
            }
        }
        
        // 观察事件
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                sharedViewModel.events.collect { event ->
                    handleEvent(event)
                }
            }
        }
    }
}

3. 使用Fragment Result API(AndroidX Fragment 1.3.0+)

// Fragment A设置结果
class FragmentA : Fragment() {
    
    private fun sendResultToFragmentB() {
        // 设置Fragment结果
        setFragmentResult(
            "request_key",
            bundleOf("data" to "Hello from FragmentA")
        )
    }
}

// Fragment B请求并监听结果
class FragmentB : Fragment() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 设置结果监听器
        setFragmentResultListener("request_key") { requestKey, bundle ->
            val data = bundle.getString("data")
            // 处理数据
            updateUI(data)
        }
        
        // 或者使用Lifecycle-aware的监听器
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                fragmentResultListener("request_key") { requestKey, result ->
                    val data = result.getString("data")
                    updateUI(data)
                }.launch()
            }
        }
    }
}

(四)现代通信架构模式

1. 单一ViewModel + StateFlow模式

// 统一的UI状态管理
data class AppUiState(
    val userData: UserData? = null,
    val messages: List<Message> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null
)

class AppViewModel : ViewModel() {
    
    private val _uiState = MutableStateFlow(AppUiState())
    val uiState: StateFlow<AppUiState> = _uiState.asStateFlow()
    
    // 统一的Action处理
    fun handleAction(action: UiAction) {
        when (action) {
            is UiAction.LoadUser -> loadUser(action.userId)
            is UiAction.SendMessage -> sendMessage(action.message)
            is UiAction.Navigate -> navigateTo(action.destination)
        }
    }
    
    private fun loadUser(userId: String) {
        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true) }
            
            try {
                val user = repository.getUser(userId)
                _uiState.update { it.copy(userData = user, isLoading = false) }
            } catch (e: Exception) {
                _uiState.update { it.copy(error = e.message, isLoading = false) }
            }
        }
    }
}

// 所有Fragment共享同一个ViewModel
class UserFragment : Fragment() {
    private val viewModel: AppViewModel by activityViewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        viewLifecycleOwner.lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { uiState ->
                    // 只关心需要的部分
                    uiState.userData?.let { updateUserUI(it) }
                }
            }
        }
    }
}

2. 基于导航组件的通信

// 使用Navigation组件进行安全通信
class NavigationFragment : Fragment() {
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 获取导航参数
        val args: NavigationFragmentArgs by navArgs()
        val userId = args.userId
        
        // 使用SavedStateHandle传递数据
        val savedStateHandle = findNavController()
            .currentBackStackEntry
            ?.savedStateHandle
        
        // 设置返回结果
        savedStateHandle?.set("result_key", "data from fragment")
        
        // 监听返回结果
        savedStateHandle?.getLiveData<String>("result_key")
            ?.observe(viewLifecycleOwner) { result ->
                // 处理结果
            }
        
        // 导航到其他Fragment并传递参数
        val action = NavigationFragmentDirections
            .actionToDetailFragment(userId = "123", userName = "John")
        findNavController().navigate(action)
    }
}

3. 事件总线模式的现代化实现

// 使用SharedFlow实现类型安全的事件总线
object EventBus {
    
    // 私有的事件流
    private val _events = MutableSharedFlow<AppEvent>(
        extraBufferCapacity = 64,
        onBufferOverflow = BufferOverflow.DROP_OLDEST
    )
    
    // 公开的只读流
    val events: SharedFlow<AppEvent> = _events
    
    suspend fun sendEvent(event: AppEvent) {
        _events.emit(event)
    }
    
    // 便捷扩展函数
    fun CoroutineScope.listenEvents(
        lifecycle: Lifecycle,
        minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
        action: suspend (AppEvent) -> Unit
    ) {
        launch {
            lifecycle.repeatOnLifecycle(minActiveState) {
                events.collect { event ->
                    action(event)
                }
            }
        }
    }
}

// 密封类定义所有事件类型
sealed class AppEvent {
    data class UserLoggedIn(val user: User) : AppEvent()
    data class MessageReceived(val message: Message) : AppEvent()
    object NetworkConnected : AppEvent()
    object NetworkDisconnected : AppEvent()
}

// 在Fragment中使用
class MyFragment : Fragment() {
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 监听事件
        viewLifecycleOwner.lifecycleScope.listenEvents(lifecycle) { event ->
            when (event) {
                is AppEvent.UserLoggedIn -> updateUserUI(event.user)
                is AppEvent.MessageReceived -> showMessage(event.message)
                AppEvent.NetworkConnected -> showNetworkConnected()
                AppEvent.NetworkDisconnected -> showNetworkDisconnected()
            }
        }
        
        // 发送事件
        viewLifecycleOwner.lifecycleScope.launch {
            EventBus.sendEvent(AppEvent.UserLoggedIn(currentUser))
        }
    }
}

(五)通信中的生命周期管理

1. 安全的生命周期感知通信

// 使用Lifecycle-aware的通信工具
class LifecycleAwareCommunicator(
    private val lifecycleOwner: LifecycleOwner
) : LifecycleObserver {
    
    private val _messages = MutableLiveData<Event<String>>()
    val messages: LiveData<Event<String>> = _messages
    
    init {
        lifecycleOwner.lifecycle.addObserver(this)
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onStart() {
        // 可以开始通信
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun onStop() {
        // 停止通信,避免内存泄漏
    }
    
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun onDestroy() {
        // 清理资源
        lifecycleOwner.lifecycle.removeObserver(this)
    }
    
    fun sendMessage(message: String) {
        if (lifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            _messages.value = Event(message)
        }
    }
}

// 包装LiveData的Event类,避免粘性事件问题
open class Event<out T>(private val content: T) {
    var hasBeenHandled = false
        private set
    
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }
    
    fun peekContent(): T = content
}

2. ViewModelScope的自动清理

class SafeCommunicationViewModel : ViewModel() {
    
    // 使用Channel进行一次性通信
    private val _oneTimeEvents = Channel<OneTimeEvent>()
    val oneTimeEvents = _oneTimeEvents.receiveAsFlow()
    
    // 使用StateFlow进行状态通信
    private val _uiState = MutableStateFlow(UiState())
    val uiState = _uiState.asStateFlow()
    
    fun sendOneTimeEvent(event: OneTimeEvent) {
        viewModelScope.launch {
            _oneTimeEvents.send(event)
        }
    }
    
    override fun onCleared() {
        super.onCleared()
        // 自动清理所有协程
        _oneTimeEvents.close()
    }
}

// Fragment中安全接收
class MyFragment : Fragment() {
    private val viewModel: SafeCommunicationViewModel by viewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 收集一次性事件(自动取消)
        viewLifecycleOwner.lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.oneTimeEvents.collect { event ->
                    handleEvent(event)
                }
            }
        }
    }
}

(六)复杂场景下的通信模式

1. 多层嵌套Fragment通信

// 父Fragment协调子Fragment通信
class ParentFragment : Fragment() {
    
    private val childViewModel: ChildViewModel by viewModels()
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 父Fragment管理子Fragment的通信
        childViewModel.childEvents.observe(viewLifecycleOwner) { event ->
            when (event) {
                is ChildEvent.NeedParentAction -> {
                    // 处理子Fragment的请求
                    handleChildRequest(event.data)
                    
                    // 将结果返回给子Fragment
                    childViewModel.sendResultToChild(event.childId, event.result)
                }
            }
        }
    }
    
    private fun setupChildFragments() {
        // 动态添加子Fragment
        childFragmentManager.beginTransaction()
            .add(R.id.child_container, ChildFragment.newInstance("child1"))
            .commit()
    }
}

// 子Fragment通过ViewModel与父Fragment通信
class ChildFragment : Fragment() {
    
    private val childViewModel: ChildViewModel by viewModels(
        ownerProducer = { requireParentFragment() }
    )
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 发送事件给父Fragment
        childViewModel.sendEventToParent(
            ChildEvent.NeedParentAction(childId = "child1", data = "need help")
        )
        
        // 接收父Fragment的结果
        childViewModel.parentResponses.observe(viewLifecycleOwner) { response ->
            if (response.childId == arguments?.getString("childId")) {
                handleParentResponse(response)
            }
        }
    }
}

2. 跨模块通信

// 使用Hilt依赖注入进行跨模块通信
@Module
@InstallIn(SingletonComponent::class)
object CommunicationModule {
    
    @Singleton
    @Provides
    fun provideEventBus(): EventBus = EventBus()
}

// 在需要通信的模块中注入
class FeatureAFragment : Fragment() {
    
    @Inject
    lateinit var eventBus: EventBus
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // 发送跨模块事件
        eventBus.send(CrossModuleEvent.FeatureAEvent("data"))
        
        // 监听其他模块的事件
        eventBus.listen<CrossModuleEvent.FeatureBEvent> { event ->
            updateFromFeatureB(event.data)
        }
    }
}

// 统一的跨模块事件定义
sealed class CrossModuleEvent {
    data class FeatureAEvent(val data: String) : CrossModuleEvent()
    data class FeatureBEvent(val data: String) : CrossModuleEvent()
    data class FeatureCEvent(val data: Int) : CrossModuleEvent()
}

(七)调试与测试通信

1. 通信调试工具

// 通信调试代理
class CommunicationDebugProxy(
    private val delegate: CommunicationInterface
) : CommunicationInterface {
    
    override fun sendMessage(message: String) {
        Log.d("Communication", "Sending message: $message")
        delegate.sendMessage(message)
    }
    
    override fun receiveMessage(message: String) {
        Log.d("Communication", "Received message: $message")
        delegate.receiveMessage(message)
    }
}

// 使用StrictMode检测通信问题
fun setupCommunicationStrictMode() {
    if (BuildConfig.DEBUG) {
        StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
            .detectCustomSlowCalls()
            .penaltyLog()
            .build())
    }
}

// 通信性能监控
object CommunicationMonitor {
    
    private val communicationTimes = mutableMapOf<String, Long>()
    
    fun startCommunication(key: String) {
        communicationTimes[key] = System.currentTimeMillis()
    }
    
    fun endCommunication(key: String) {
        val startTime = communicationTimes[key]
        if (startTime != null) {
            val duration = System.currentTimeMillis() - startTime
            Log.d("CommunicationMonitor", "$key took ${duration}ms")
            
            // 上报到监控系统
            if (duration > 100) {
                reportSlowCommunication(key, duration)
            }
        }
    }
}

2. 通信测试

@RunWith(AndroidJUnit4::class)
class CommunicationTest {
    
    @Test
    fun testActivityFragmentCommunication() = runTest {
        // 测试接口回调
        val mockListener = mockk<FragmentToActivityListener>()
        val fragment = MyFragment()
        
        // 模拟Fragment附加到Activity
        val scenario = launchFragmentInContainer<MyFragment>()
        scenario.onFragment { 
            it.listener = mockListener
            it.sendDataToActivity()
        }
        
        // 验证接口被调用
        verify { mockListener.onDataReceived(any()) }
    }
    
    @Test
    fun testViewModelCommunication() = runTest {
        val viewModel = SharedViewModel()
        val testObserver = viewModel.dataToFragment.testObserver()
        
        // 发送数据
        viewModel.sendDataToFragment("test data")
        
        // 验证数据接收
        testObserver.assertValue("test data")
    }
    
    @Test
    fun testFragmentResultApi() = runTest {
        val scenario = launchFragmentInContainer<FragmentA>()
        
        scenario.onFragment { fragment ->
            // 设置结果
            fragment.setFragmentResult("key", bundleOf("data" to "test"))
            
            // 验证结果可以通过Fragment Result API获取
            val result = fragment.parentFragmentManager
                .getFragmentResult("key")
            
            assertThat(result?.getString("data")).isEqualTo("test")
        }
    }
}

(八)面试回答要点总结

  1. 通信方式演进
    • 传统方式:接口回调、findFragmentById、Bundle参数
    • 现代方式:ViewModel + LiveData/StateFlow、Fragment Result API
  2. Activity ↔ Fragment通信
    • Activity → Fragment:通过SharedViewModel发送数据,Fragment观察LiveData/StateFlow
    • Fragment → Activity:使用接口回调(传统)或通过ViewModel发送事件(现代)
  3. Fragment间通信
    • 推荐:共享ViewModel(by activityViewModels()),所有Fragment观察相同数据源
    • 替代:Fragment Result API(AndroidX Fragment 1.3.0+),适合请求-响应模式
    • 避免:直接Fragment引用或通过Activity硬编码中转
  4. 生命周期安全
    • 使用viewLifecycleOwner观察LiveData(Fragment中)
    • 使用repeatOnLifecycle(Lifecycle.State.STARTED)收集Flow
    • 避免在onDestroyView()后更新UI
  5. 架构模式选择
    • 简单场景:接口回调或Fragment Result API
    • 数据共享:SharedViewModel + StateFlow
    • 事件传递:SharedFlow或Channel
    • 跨模块:依赖注入 + 事件总线
  6. 最佳实践
    • 避免Fragment/Activity直接引用
    • 使用单向数据流(UDF)模式
    • 处理配置更改(ViewModel自动处理)
    • 测试通信逻辑
  7. 调试技巧
    • 使用StrictMode检测主线程通信
    • 添加通信日志和监控
    • 编写单元测试验证通信逻辑

现代Android通信黄金法则
优先使用ViewModel + LiveData/StateFlow进行组件间通信,特别是Fragment间通信。对于简单的请求-响应模式,使用Fragment Result API。始终确保通信是生命周期安全的,并在适当的时机清理资源。

参考文献