Android callbackFlow 使用场景详解

Android callbackFlow 使用场景详解

什么是 callbackFlow?

callbackFlow 是 Kotlin Flow 的一个构建器,专门用于将基于回调的异步 API 转换为 Flow

它解决了传统回调地狱问题,使代码更加简洁和可读。


核心使用场景

1. 📍 位置监听(Location Updates)

LocationManagerFusedLocationProviderClient 的回调转换为 Flow:

kotlin 复制代码
/**
 * 将位置更新回调转换为 Flow
 * 使用 callbackFlow 持续监听位置变化
 */
fun LocationManager.locationFlow(provider: String): Flow<Location> = callbackFlow {
    // 创建位置监听器
    val mLocationListener = object : LocationListener {
        override fun onLocationChanged(location: Location) {
            // 发送位置数据到 Flow
            trySend(location)
        }

        override fun onProviderDisabled(provider: String) {
            // 关闭 Flow 并携带异常信息
            cancel(CancellationException("Provider disabled: $provider"))
        }
    }

    // 注册位置监听
    requestLocationUpdates(provider, 1000L, 0f, mLocationListener)

    // 当 Flow 被取消时,注销监听器,防止内存泄漏
    awaitClose {
        removeUpdates(mLocationListener)
    }
}
kotlin 复制代码
/**
 * 位置监听测试代码
 */
fun testLocationFlow() {
    val scope = CoroutineScope(Dispatchers.Main)
    scope.launch {
        locationManager.locationFlow(LocationManager.GPS_PROVIDER)
            .collect { location ->
                println("纬度: ${location.latitude}, 经度: ${location.longitude}")
            }
    }
}

2. 🌐 网络状态监听(Network State)

监听网络连接状态变化:

kotlin 复制代码
/**
 * 监听网络连接状态变化
 * 将 ConnectivityManager 的回调转换为 Flow
 */
fun Context.networkStateFlow(): Flow<Boolean> = callbackFlow {
    val mConnectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

    // 创建网络回调
    val mNetworkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            // 网络可用,发送 true
            trySend(true)
        }

        override fun onLost(network: Network) {
            // 网络断开,发送 false
            trySend(false)
        }
    }

    val mRequest = NetworkRequest.Builder()
        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        .build()

    // 注册网络回调
    mConnectivityManager.registerNetworkCallback(mRequest, mNetworkCallback)

    // Flow 取消时注销回调
    awaitClose {
        mConnectivityManager.unregisterNetworkCallback(mNetworkCallback)
    }
}
kotlin 复制代码
/**
 * 网络状态监听测试代码
 */
fun testNetworkStateFlow(context: Context) {
    val scope = CoroutineScope(Dispatchers.Main)
    scope.launch {
        context.networkStateFlow()
            .distinctUntilChanged() // 过滤重复状态
            .collect { isConnected ->
                println(if (isConnected) "网络已连接" else "网络已断开")
            }
    }
}


3. 🎤 传感器数据监听(Sensor Data)

将传感器回调转换为 Flow:

kotlin 复制代码
/**
 * 将传感器数据回调转换为 Flow
 * 持续监听传感器数值变化
 */
fun SensorManager.sensorFlow(sensorType: Int): Flow<SensorEvent> = callbackFlow {
    val mSensor = getDefaultSensor(sensorType)
        ?: throw IllegalArgumentException("Sensor type $sensorType not available")

    // 创建传感器监听器
    val mSensorListener = object : SensorEventListener {
        override fun onSensorChanged(event: SensorEvent) {
            // 发送传感器事件
            trySend(event)
        }

        override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
            // 精度变化,可根据需求处理
        }
    }

    // 注册传感器监听
    registerListener(mSensorListener, mSensor, SensorManager.SENSOR_DELAY_NORMAL)

    // Flow 取消时注销监听
    awaitClose {
        unregisterListener(mSensorListener)
    }
}
kotlin 复制代码
/**
 * 传感器监听测试代码
 */
fun testSensorFlow(sensorManager: SensorManager) {
    val scope = CoroutineScope(Dispatchers.Default)
    scope.launch {
        sensorManager.sensorFlow(Sensor.TYPE_ACCELEROMETER)
            .collect { event ->
                println("X: ${event.values[0]}, Y: ${event.values[1]}, Z: ${event.values[2]}")
            }
    }
}

4. 🔍 搜索框防抖(SearchView TextWatcher)

将文本变化监听转换为 Flow,实现防抖搜索:

kotlin 复制代码
/**
 * 将 EditText 文本变化监听转换为 Flow
 * 配合 debounce 实现防抖搜索
 */
fun EditText.textChangeFlow(): Flow<String> = callbackFlow {
    // 创建文本监听器
    val mTextWatcher = object : TextWatcher {
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            // 发送当前文本内容
            trySend(s?.toString() ?: "")
        }

        override fun afterTextChanged(s: Editable?) {}
    }

    // 注册文本监听
    addTextChangedListener(mTextWatcher)

    // Flow 取消时移除监听
    awaitClose {
        removeTextChangedListener(mTextWatcher)
    }
}
kotlin 复制代码
/**
 * 搜索防抖测试代码
 */
fun testTextChangeFlow(editText: EditText) {
    val scope = CoroutineScope(Dispatchers.Main)
    scope.launch {
        editText.textChangeFlow()
            .debounce(300)              // 300ms 防抖
            .filter { it.isNotEmpty() } // 过滤空字符串
            .distinctUntilChanged()     // 过滤重复内容
            .collect { keyword ->
                println("搜索关键词: $keyword")
            }
    }
}

5. 📷 相机预览帧(Camera2 API)

将 Camera2 的图像回调转换为 Flow:

kotlin 复制代码
/**
 * 将 ImageReader 的图像回调转换为 Flow
 * 持续获取相机预览帧数据
 */
fun ImageReader.imageFlow(): Flow<Image> = callbackFlow {
    // 设置图像可用监听
    val mImageListener = ImageReader.OnImageAvailableListener { reader ->
        // 获取最新图像帧并发送
        val image = reader.acquireLatestImage()
        if (image != null) {
            trySend(image)
        }
    }

    // 注册监听器
    setOnImageAvailableListener(mImageListener, null)

    // Flow 取消时清理资源
    awaitClose {
        setOnImageAvailableListener(null, null)
    }
}

6. 🔵 蓝牙扫描(BLE Scan)

将蓝牙扫描回调转换为 Flow:

kotlin 复制代码
/**
 * 将蓝牙 BLE 扫描回调转换为 Flow
 * 持续接收扫描到的蓝牙设备
 */
fun BluetoothLeScanner.scanFlow(): Flow<ScanResult> = callbackFlow {
    // 创建扫描回调
    val mScanCallback = object : ScanCallback() {
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            // 发送扫描结果
            trySend(result)
        }

        override fun onScanFailed(errorCode: Int) {
            // 扫描失败,关闭 Flow
            cancel(CancellationException("BLE scan failed, errorCode: $errorCode"))
        }
    }

    // 开始扫描
    startScan(mScanCallback)

    // Flow 取消时停止扫描
    awaitClose {
        stopScan(mScanCallback)
    }
}

总结对比

使用场景 传统方式 callbackFlow 优势
位置监听 回调嵌套 链式操作,易于组合
网络状态 广播接收器 生命周期安全
传感器数据 手动管理注册/注销 awaitClose 自动清理
搜索防抖 手动实现防抖逻辑 配合 debounce 操作符
蓝牙扫描 状态管理复杂 结构清晰,易于维护

⚠️ 注意事项

  1. 必须调用 awaitClose:用于清理资源,防止内存泄漏
  2. 使用 trySend 而非 sendtrySend 不会抛出异常,更安全
  3. 背压处理 :默认缓冲区大小为 Channel.RENDEZVOUS,可通过 buffer() 操作符调整
  4. 生命周期绑定 :建议配合 lifecycleScopeviewModelScope 使用,避免泄漏
相关推荐
常利兵2 小时前
安卓启动页Logo适配秘籍:告别“奇形怪状”的展示
android·java·开发语言
帅次2 小时前
Android 性能优化专题面试稿
android·面试·性能优化
pengyu2 小时前
【Kotlin 协程修仙录 · 筑基境 · 初阶】 | 根本大法:结构化并发的父子约束与取消传播
android·kotlin
ifuleyou16682 小时前
《Inter问题》
android·开发语言·kotlin
夏沫琅琊3 小时前
android 通话记录相关
android·kotlin
MonkeyKing3 小时前
蓝蓝牙核心基础概念详解:2.4GHz频段、跳频、信道、广播、连接、配对
android·ios
我命由我123453 小时前
Android 广播 - 显式广播与隐式广播
android·java·开发语言·java-ee·kotlin·android studio·android-studio
YaBingSec3 小时前
玄机网络安全靶场:JBoss 5.x_6.x 反序列化漏洞(CVE-2017-12149)
android·网络·笔记·安全·web安全·ssh