Android_BLE 基于Jetpack Bluetooth实现文件传输指南。

一、技术选型说明

  1. 蓝牙协议选择

    • 大文件传输:使用 Classic Bluetooth RFCOMM Socket(适合 1MB 以上文件)
    • 小数据包传输:使用 BLE(适合实时性要求高的场景)
  2. Jetpack 组件整合

gradle 复制代码
dependencies {
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2"
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"
    implementation "androidx.work:work-runtime-ktx:2.8.1"
}

二、核心实现步骤

1. 权限配置

xml 复制代码
<!-- Android 12+ 需要新增权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />

<!-- 文件传输需要 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2. 设备发现与配对

示例代码:

kotlin 复制代码
class BluetoothViewModel : ViewModel() {
    private val _devices = MutableStateFlow<List<BluetoothDevice>>(emptyList())
    val devices: StateFlow<List<BluetoothDevice>> = _devices

    private val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            when(intent.action) {
                BluetoothDevice.ACTION_FOUND -> {
                    val device = intent.getParcelableExtra<BluetoothDevice>(BluetoothDevice.EXTRA_DEVICE)
                    device?.let { 
                        _devices.update { current -> current + it }
                    }
                }
            }
        }
    }

    fun startDiscovery(context: Context) {
        context.registerReceiver(receiver, IntentFilter(BluetoothDevice.ACTION_FOUND))
        BluetoothAdapter.getDefaultAdapter()?.startDiscovery()
    }

    fun stopDiscovery(context: Context) {
        BluetoothAdapter.getDefaultAdapter()?.cancelDiscovery()
        context.unregisterReceiver(receiver)
    }
}

3、文件传输核心实现

示例代码:

kotlin 复制代码
class FileTransferService : Service() {
    private val channelId = "BluetoothTransfer"
    private var socket: BluetoothSocket? = null

    override fun onBind(intent: Intent?): IBinder? = null

    override fun onCreate() {
        createNotificationChannel()
        startForeground(1, createNotification())
    }

    private fun createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                channelId,
                "File Transfer",
                NotificationManager.IMPORTANCE_LOW
            )
            getSystemService(NotificationManager::class.java)
                .createNotificationChannel(channel)
        }
    }

    private fun createNotification() = NotificationCompat.Builder(this, channelId)
        .setContentTitle("文件传输中")
        .setSmallIcon(R.drawable.ic_transfer)
        .build()

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        intent?.getParcelableExtra<BluetoothDevice>("device")?.let { device ->
            CoroutineScope(Dispatchers.IO).launch {
                try {
                    socket = device.createRfcommSocketToServiceRecord(
                        UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
                    )
                    socket?.connect()
                    transferFile(intent.getParcelableExtra("fileUri")!!)
                } catch (e: IOException) {
                    Log.e("BluetoothTransfer", "Transfer failed", e)
                } finally {
                    socket?.close()
                    stopSelf()
                }
            }
        }
        return START_NOT_STICKY
    }

    private fun transferFile(uri: Uri) {
        contentResolver.openInputStream(uri)?.use { input ->
            socket?.outputStream?.use { output ->
                val buffer = ByteArray(8192)
                var bytesRead: Int
                while (input.read(buffer).also { bytesRead = it } != -1) {
                    output.write(buffer, 0, bytesRead)
                    output.flush()
                }
            }
        }
    }
}

4、使用 WorkManager 管理传输任务

示例代码:

kotlin 复制代码
class BluetoothTransferWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        val device = inputData.getParcelable<BluetoothDevice>("device")!!
        val fileUri = inputData.getString("fileUri")!!.toUri()

        return try {
            withContext(Dispatchers.IO) {
                val socket = device.createRfcommSocketToServiceRecord(
                    UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
                )
                socket.connect()
                
                applicationContext.contentResolver.openInputStream(fileUri)?.use { input ->
                    socket.outputStream.use { output ->
                        input.copyTo(output)
                    }
                }
                Result.success()
            }
        } catch (e: Exception) {
            Result.retry()
        }
    }
}

// 在 ViewModel 中启动传输
fun startTransfer(device: BluetoothDevice, uri: Uri) {
    val request = OneTimeWorkRequestBuilder<BluetoothTransferWorker>()
        .setInputData(workDataOf(
            "device" to device,
            "fileUri" to uri.toString()
        ))
        .build()

    WorkManager.getInstance(context).enqueue(request)
}

三、优化实践

1. 传输进度监控

示例代码:

kotlin 复制代码
class TransferProgressMonitor(private val callback: (Float) -> Unit) : FileTransferService() {
    override fun transferFile(uri: Uri) {
        val totalBytes = contentResolver.openFileDescriptor(uri, "r")?.use {
            it.statSize
        } ?: 0L

        var transferredBytes = 0L
        contentResolver.openInputStream(uri)?.use { input ->
            socket?.outputStream?.use { output ->
                val buffer = ByteArray(8192)
                var bytesRead: Int
                while (input.read(buffer).also { bytesRead = it } != -1) {
                    output.write(buffer, 0, bytesRead)
                    transferredBytes += bytesRead
                    val progress = transferredBytes.toFloat() / totalBytes
                    callback(progress.coerceIn(0f, 1f))
                }
            }
        }
    }
}

2. 断点续传实现

示例代码:

kotlin 复制代码
class ResumableTransferWorker : CoroutineWorker(...) {
    override suspend fun doWork(): Result {
        val sessionFile = File(cacheDir, "transfer_session")
        var session = sessionFile.readSession() ?: TransferSession()

        try {
            while (session.transferred < session.totalSize) {
                transferChunk(session)
                sessionFile.writeSession(session)
            }
            return Result.success()
        } catch (e: Exception) {
            return if (runAttemptCount < 3) Result.retry() else Result.failure()
        }
    }

    private suspend fun transferChunk(session: TransferSession) {
        // 实现分块传输逻辑
    }
}

data class TransferSession(
    val totalSize: Long = 0,
    val transferred: Long = 0
)

四、注意事项

1、版本兼容处理

kotlin 复制代码
fun checkBluetoothAvailability(): Boolean {
    return when {
        Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT -> {
            // 4.4 以下版本不支持 BLE
            false
        }
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            // Android 12+ 需要检查新权限
            checkSelfPermission(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        }
        else -> true
    }
}

2、传输稳定性优化

  • 设置合理的 MTU 大小(512 bytes 为推荐值)
  • 实现 ACK 确认机制
  • 添加数据校验(CRC32/MD5)

3、典型错误处理

示例代码:

kotlin 复制代码
fun handleTransferError(exception: Exception) {
    when (exception) {
        is SocketTimeoutException -> showError("连接超时")
        is IOException -> showError("IO 异常")
        is SecurityException -> requestPermissions()
        else -> showError("未知错误")
    }
}

五、扩展功能建议

1、多设备并行传输

示例代码:

kotlin 复制代码
class ParallelTransferManager {
    private val transfers = ConcurrentHashMap<String, Job>()

    fun addTransfer(device: BluetoothDevice, file: File) {
        transfers[device.address] = CoroutineScope(Dispatchers.IO).launch {
            // 实现并行传输逻辑
        }
    }
}

2、文件加密传输

示例代码:

kotlin 复制代码
fun encryptData(data: ByteArray, key: SecretKey): ByteArray {
    val cipher = Cipher.getInstance("AES/GCM/NoPadding")
    cipher.init(Cipher.ENCRYPT_MODE, key)
    return cipher.doFinal(data)
}

3、传输历史记录

示例代码:

kotlin 复制代码
@Entity
data class TransferRecord(
    @PrimaryKey val id: String,
    val fileName: String,
    val deviceName: String,
    val timestamp: Long,
    val success: Boolean
)

@Dao
interface TransferDao {
    @Query("SELECT * FROM TransferRecord ORDER BY timestamp DESC")
    fun getAll(): Flow<List<TransferRecord>>
}

本方案结合了传统蓝牙的文件传输能力与 Jetpack 组件的现代化架构优势,实现了:

  • 完整的生命周期管理
  • 后台传输可靠性
  • 进度可视化
  • 断点续传能力
  • 完善的错误处理机制

建议根据实际业务需求选择合适的技术组合,对于需要兼容旧设备的场景可优先使用经典蓝牙方案,对于低功耗需求场景可考虑 BLE 分片传输方案。

更多分享

  1. Android_BLE开发------扫描
  2. Android_BLE开发------连接
  3. Android_BLE开发------读写
  4. Android_BLE开发------绑定
  5. Android_BLE开发------优化
相关推荐
Yeah_0day7 分钟前
移动安全Android——客户端静态安全
android·app测试·安卓客户端测试·组件导出安全测试·安装包签名·反编译保护·应用完整性校验
奔跑吧 android6 小时前
【android bluetooth 协议分析 02】【bluetooth hal 层详解 6】【bt_vendor_opcode_t 介绍】
android·hal·bt·aosp13·hidl_1.0
zhifanxu10 小时前
Android开发常用Kotlin高级语法
android·开发语言·kotlin
qq_3364117510 小时前
【笔记】Trae+Andrioid Studio+Kotlin开发安卓WebView应用
android·笔记·kotlin
Tony__Ferguson11 小时前
数据结构——优先级队列(PriorityQueue)
android·java·数据结构
熙曦Sakura14 小时前
【MySQL】用户管理
android·mysql·adb
xiangxiongfly91516 小时前
Android 倒计时总结
android·倒计时·flow·timer·handler·countdowntimer
AI新视界16 小时前
MySQL高可用革命:Orchestrator实现零干预的故障转移与智能拓扑管理
android
一起搞IT吧17 小时前
Camera相机人脸识别系列专题分析之四:Camera相机领域人脸识别和人脸属性检测介绍
android·图像处理·人工智能·计算机视觉
我的青春不太冷18 小时前
Android高级开发第二篇 - JNI 参数传递与 Java → C → Java 双向调用
android·java·c语言