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开发------优化
相关推荐
今阳10 分钟前
鸿蒙开发笔记-11-LazyForEach 数据懒加载
android·华为·harmonyos
pengyu11 分钟前
系统化掌握Flutter组件之Transform:空间魔法师
android·flutter·dart
岸芷漫步16 分钟前
retrofit框架分析
android
&有梦想的咸鱼&16 分钟前
Android OKHttp缓存模块原理分析
android·okhttp·缓存
顾林海19 分钟前
解锁Android Activity:从原理到实战的深度剖析
android
双鱼大猫1 小时前
Android Dalvik虚拟机内存参数优化指南
android
双鱼大猫1 小时前
深入解析adb install安装全流程
android
顾林海1 小时前
Flutter Dart 运算符全面解析
android·前端
小白马丶1 小时前
Jetpack源码解读(一)——Lifecycle
android·android jetpack
&有梦想的咸鱼&2 小时前
Android Glide 请求构建与管理模块原理深入剖析
android·glide