안드로이드 블루투스 통신
스마트 폰으로 아두이노 제어하기
요약
- 권한
- android.permission.BLUETOOTH
- android.permission.BLUETOOTH_ADMIN
- 스레드 구현
- 네트워크 작업은 Ui 스레드에서 할 수 없음
- 연결 및 데이터 수신은 작업 스레드에서 수행
프로젝트 만들기
프로젝트 생성
- 프로젝트명 : bt_ex
- 액티비티 유형 : Empty Activity
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.bt_ex"> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> :
위험 권한은 아님
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:id="@+id/stateBluetooth" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:text="" android:textAppearance="@style/TextAppearance.AppCompat.Medium" app:layout_constraintBaseline_toBaselineOf="@+id/btnScan" app:layout_constraintEnd_toStartOf="@+id/btnScan" app:layout_constraintStart_toStartOf="parent" /> <Button android:id="@+id/btnScan" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:text="Scan" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.recyclerview.widget.RecyclerView android:id="@+id/listDevice" android:layout_width="match_parent" android:layout_marginTop="16dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/btnScan" android:layout_height="0dp" /> </androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
package com.example.bt_ex import android.app.Activity import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.content.Intent import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import kotlinx.android.synthetic.main.activity_main.* import org.jetbrains.anko.AnkoLogger import org.jetbrains.anko.startActivity import org.jetbrains.anko.toast class MainActivity : AppCompatActivity(), AnkoLogger{ val bluetoothAdapter: BluetoothAdapter? by lazy { BluetoothAdapter.getDefaultAdapter() } private val REQUEST_ENABLE_BT = 100 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if( bluetoothAdapter == null) { toast("단말기는 블루투스를 지원하지 않습니다.") finish() return } checkBluetoothDevices() btnScan.setOnClickListener { scanDevice() } } fun checkBluetoothDevices() { if(bluetoothAdapter!!.isEnabled) { stateBluetooth.text ="Bluetooth is Enabled" // 블루투스 장치 검색 버튼 활성화 btnScan.isEnabled = true scanDevice() } else { stateBluetooth.text = "Bluetooth is Not Enabled!" var enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if(requestCode == REQUEST_ENABLE_BT) { if(resultCode == Activity.RESULT_OK) { // 활성화 요청에 대해 정상적으로 승인을 받음 stateBluetooth.text ="Bluetooth is Enabled" toast("블루투스가 활성화 되었습니다.") } else { stateBluetooth.text ="Bluetooth is Not Enabled" toast("블루투스 활성화 요청이 취소되었거나 예외가 발생하였습니다.") } } super.onActivityResult(requestCode, resultCode, data) } fun scanDevice() { // 페어링되어 있는 디바이스 집합 추출 val devices = bluetoothAdapter!!.bondedDevices if (devices.size == 0) { stateBluetooth.text = "연결된 블루투스가 장비가 없습니다." } else { stateBluetooth.text = "페어링 블루투스 장비(${devices.size}개)" val adapter = DeviceAdapter(devices.toList(), ::onIemClick) listDevice.adapter = adapter } } fun onIemClick(device: BluetoothDevice) { startActivity<BluetoothActivity>("device" to device) // toast("${device.name} 선택") } }
item_bluetoothdevice.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="5dp"> <TextView android:id="@+id/txtName" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Name" android:textAppearance="@style/TextAppearance.AppCompat.Medium" /> <TextView android:id="@+id/txtAddress" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Address" android:textAppearance="@style/TextAppearance.AppCompat.Small" /> </LinearLayout>
DeviceAdapter.kt
package com.example.bt_ex import android.bluetooth.BluetoothDevice import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.item_bluetoothdevice.view.* class DeviceAdapter(val deviceList: List<BluetoothDevice>, val onItemClick: (device: BluetoothDevice)->Unit ): RecyclerView.Adapter<DeviceAdapter.ViewHolder>() { class ViewHolder(val layoutView: View) : RecyclerView.ViewHolder(layoutView) { val txtName = layoutView.txtName val txtAddress = layoutView.txtAddress fun bind(device: BluetoothDevice) { txtName.text = device.name txtAddress.text = device.address.toString() } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val layout = LayoutInflater.from(parent.context) .inflate(R.layout.item_bluetoothdevice, parent, false) return ViewHolder(layout) } override fun getItemCount(): Int = deviceList.size override fun onBindViewHolder(holder: ViewHolder, position: Int) { val device = deviceList[position] holder.bind(device) holder.layoutView.setOnClickListener { onItemClick(device) } } }
BluetoothActivity
- 블루투스 연결 및 메시지 송수신 처리
- 작업스레드를 내부 클래스로 정의
- 연결 및 데이터 수신은 작업 스레드가 수행
activity_bluetooth.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/editMessage" android:layout_width="0px" android:layout_weight="1" android:layout_height="wrap_content"/> <Button android:id="@+id/btnSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="onSend" android:text="Send" /> </LinearLayout> <TextView android:id="@+id/receiveMsgView" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
BluetoothActivity.kt
package com.example.bt_ex import android.bluetooth.BluetoothDevice import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_bluetooth.* import org.jetbrains.anko.toast import java.io.BufferedReader import java.io.InputStreamReader import java.io.OutputStreamWriter import java.io.PrintWriter import java.util.* class BluetoothActivity : AppCompatActivity() { var out: PrintWriter? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_bluetooth) btnSend.setOnClickListener { // 메시지 전송 out?.print("${editMessage.text}\r\n") out?.flush() editMessage.setText("") } val device = intent.getParcelableExtra<BluetoothDevice>("device")!! BtWorkThread(device).start() } inner class BtWorkThread(val device: BluetoothDevice) : Thread() { override fun run() { try { var uuid = UUID.fromString( "00001101-0000-1000-8000-00805F9B34FB" ); val socket = device.createRfcommSocketToServiceRecord(uuid); socket.connect(); runOnUiThread { toast("블루투스 연결 성공") } val br = BufferedReader( InputStreamReader(socket.getInputStream()) ) out = PrintWriter(OutputStreamWriter(socket.getOutputStream())) while (!Thread.currentThread().isInterrupted()) { // 메시지 수신 val message = br.readLine(); runOnUiThread { receiveMsgView.text = "$message\n${receiveMsgView.text}"; } } socket.close() } catch (e: Exception) { runOnUiThread { toast("블루투스 연결 실패") } finish(); } } } override fun onDestroy() { super.onDestroy(); // btThread?.interrupt(); } }
실행 결과

'IoT 디바이스 활용 > AndroidStudio' 카테고리의 다른 글
AndroidStudio - 전자액자 (0) | 2020.11.18 |
---|---|
AndroidStudio - RecyclerView (0) | 2020.10.23 |
AndroidStudio - WebBrowser (0) | 2020.10.22 |
AndroidStudio - Dialog (0) | 2020.10.22 |
AndroidStudio - StopWatch (0) | 2020.10.22 |
댓글