안드로이드 어플을 코틀린이란 언어로 개발하면서 알게된 내용들을 잊지 않기위해.
혹은.. 나처럼 초보 개발자들의 삽질을 덜어주기 위해 기록 시작
개발하다보면 compile 에러가 많이 발생하는데.. 주로 버젼문제였음
그래서 개발한 버젼도 공유합니다
compileSdkVersion 28
targetSdkVersion 26
ext.kotlin_version = '1.3.0'
이번 포스팅과는 관련이 없지만 스케줄러를 작성하면서 coroutine이 코틀린 버젼에 따라서 차이가 많이 났다.
그냥 참고..
블로그를 집중있게 열심히 쓰는 성격이 아니라 예쁘게 정리는 못함.
1. main-java 디렉토리 아래 Bluetooth라는 폴더를 만들고 1개의 코틀린 클래스 파일과 1개의 오브젝트 파일을 만들어서 작성
<- 나 역시 누군가의 블로그에서 퍼온거라서.. 출처는 -http://www.hardcopyworld.com/ngine/aduino/index.php/archives/3145
AndroidManifest.xml에 아래 코드 추가
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
2. Bluetooth폴더 생성
app \ src \ main\java\com\()\프로젝트명\bluetooth 폴더 생성
3. 폴더 내에 BluetoothManager.kt 파일과ConnectionInfo.kt(class) 파일 생성
-BluetoothManager.kt 파일
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothServerSocket
import android.bluetooth.BluetoothSocket
import android.os.Bundle
import android.os.Handler
import android.util.Log
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.*
/**
* This class does all the work for setting up and managing Bluetooth
* connections with other devices. It has a thread that listens for
* incoming connections, a thread for connecting with a device, and a
* thread for performing data transmissions when connected.
*/
class BluetoothManager {
companion object {
// Debugging
private const val TAG = "BluetoothManager"
// Constants that indicate the current connection state
const val STATE_NONE = 0 // we're doing nothing
const val STATE_LISTEN = 1 // now listening for incoming connections
const val STATE_CONNECTING = 2 // now initiating an outgoing connection
const val STATE_CONNECTED = 3 // now connected to a remote device
// Message types sent from the BluetoothManager to Handler
const val MESSAGE_STATE_CHANGE = 1
const val MESSAGE_READ = 2
const val MESSAGE_WRITE = 3
const val MESSAGE_DEVICE_NAME = 4
const val MESSAGE_TOAST = 5
// Data field of scan result
const val MSG_DEVICE_NAME = "device_name"
const val MSG_DEVICE_ADDRESS = "device_address"
// Name for the SDP record when creating server socket
private const val NAME = "BluetoothManager"
// Unique UUID for this application
private val MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
private const val RECONNECT_DELAY_MAX = (60 * 60 * 1000).toLong()
private var mInstance: BluetoothManager = BluetoothManager()
fun getInstance(): BluetoothManager {
return mInstance
}
}
// Member fields
private val mAdapter = BluetoothAdapter.getDefaultAdapter()
private var mAcceptThread: AcceptThread? = null
private var mConnectThread: ConnectThread? = null
private var mConnectedThread: ConnectedThread? = null
private var mHandler: Handler? = null
private var mReconnectDelay: Long = (15 * 1000).toLong()
private var mConnectTimer: Timer? = null
private var mIsServiceStopped: Boolean = false
/**
* Return the current connection state. */
/**
* Set the current state of the chat connection
* @param state An integer defining the current connection state
*/
// Give the new state to the Handler so the UI Activity can update
var state: Int = STATE_NONE
set(new_state) {
Log.d(TAG, "setState() $field -> $new_state")
field = new_state
if (new_state == STATE_CONNECTED)
cancelRetryConnect()
mHandler?.obtainMessage(MESSAGE_STATE_CHANGE, new_state, -1)?.sendToTarget()
}
init {
state = STATE_NONE
}
fun getAdapter(): BluetoothAdapter {
return mAdapter
}
fun setHandler(handler: Handler) {
mHandler = handler
}
fun isBluetoothEnabled(): Boolean {
return mAdapter.isEnabled
}
fun getScanMode(): Int {
return mAdapter.scanMode
}
fun startDiscovery() {
mAdapter.startDiscovery()
}
fun cancelDiscovery() {
mAdapter.cancelDiscovery()
}
fun isDiscovering(): Boolean {
return mAdapter.isDiscovering
}
fun getBondedDevices(): Set<BluetoothDevice> {
return mAdapter.bondedDevices
}
/**
* Start the chat service. Specifically start AcceptThread to begin a
* session in listening (server) mode. Called by the Activity onResume() */
@Synchronized
fun start() {
Log.d(TAG, "Starting BluetoothManager...")
// Cancel any thread attempting to make a connection
if (mConnectThread != null) {
mConnectThread!!.cancel()
mConnectThread = null
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread!!.cancel()
mConnectedThread = null
}
// Start the thread to listen on a BluetoothServerSocket
if (mAcceptThread == null) {
mAcceptThread = AcceptThread()
mAcceptThread?.start()
}
state = STATE_LISTEN
mIsServiceStopped = false
}
/**
* Start the ConnectThread to initiate a connection to a remote device.
* @param address The BluetoothDevice address to connect
*/
fun connect(address: String?) {
address ?: return
connect(mAdapter.getRemoteDevice(address))
}
/**
* Start the ConnectThread to initiate a connection to a remote device.
* @param device The BluetoothDevice to connect
*/
@Synchronized
fun connect(device: BluetoothDevice?) {
Log.d(TAG, "Connecting to: $device")
device ?: return
if (state == STATE_CONNECTED)
return
// Cancel any thread attempting to make a connection
if (state == STATE_CONNECTING) {
if (mConnectThread != null) {
mConnectThread!!.cancel()
mConnectThread = null
}
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread!!.cancel()
mConnectedThread = null
}
// Start the thread to connect with the given device
mConnectThread = ConnectThread(device)
mConnectThread?.start()
state = STATE_CONNECTING
}
/**
* Start the ConnectedThread to begin managing a Bluetooth connection
* @param socket The BluetoothSocket on which the connection was made
* @param device The BluetoothDevice that has been connected
*/
@Synchronized
fun connected(socket: BluetoothSocket, device: BluetoothDevice) {
Log.d(TAG, "connected")
// Cancel the thread that completed the connection
if (mConnectThread != null) {
mConnectThread!!.cancel()
mConnectThread = null
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {
mConnectedThread!!.cancel()
mConnectedThread = null
}
// Cancel the accept thread because we only want to connect to one device
if (mAcceptThread != null) {
mAcceptThread!!.cancel()
mAcceptThread = null
}
// Start the thread to manage the connection and perform transmissions
mConnectedThread = ConnectedThread(socket)
mConnectedThread?.start()
// Change state
state = STATE_CONNECTED
// Send the name of the connected device back to the UI Activity
val msg = mHandler?.obtainMessage(MESSAGE_DEVICE_NAME)
if(msg != null && device.address != null && device.name != null) {
val bundle = Bundle()
bundle.putString(MSG_DEVICE_ADDRESS, device.address)
bundle.putString(MSG_DEVICE_NAME, device.name)
msg.data = bundle
mHandler?.sendMessage(msg)
}
}
/**
* Stop all threads
*/
@Synchronized
fun stop() {
Log.d(TAG, "stop")
if (mConnectThread != null) {
mConnectThread!!.cancel()
mConnectThread = null
}
if (mConnectedThread != null) {
mConnectedThread!!.cancel()
mConnectedThread = null
}
if (mAcceptThread != null) {
mAcceptThread!!.cancel()
mAcceptThread = null
}
state = STATE_NONE
mIsServiceStopped = true
cancelRetryConnect()
}
/**
* Write to the ConnectedThread in an unsynchronized manner
* @param out The bytes to write
* @see ConnectedThread.write
*/
fun write(out: ByteArray) {
// Create temporary object
var r: ConnectedThread? = null
// Synchronize a copy of the ConnectedThread
synchronized(this) {
if (state != STATE_CONNECTED) return
r = mConnectedThread
}
// Perform the write unsynchronized
r?.write(out)
}
/**
* Indicate that the connection attempt failed and notify the UI Activity.
*/
private fun connectionFailed() {
Log.d(TAG, "BluetoothManager :: connectionFailed()")
state = STATE_LISTEN
// Reserve re-connect timer
reserveRetryConnect()
}
/**
* Indicate that the connection was lost and notify the UI Activity.
*/
private fun connectionLost() {
Log.d(TAG, "BluetoothManager :: connectionLost()")
state = STATE_LISTEN
// Reserve re-connect timer
reserveRetryConnect()
}
/**
* Automatically retry bluetooth connection.
*/
private fun reserveRetryConnect() {
if (mIsServiceStopped)
return
mReconnectDelay *= 2
if (mReconnectDelay > RECONNECT_DELAY_MAX)
mReconnectDelay = RECONNECT_DELAY_MAX
if (mConnectTimer != null) {
try {
mConnectTimer!!.cancel()
} catch (e: IllegalStateException) {
e.printStackTrace()
}
}
mConnectTimer = Timer()
mConnectTimer!!.schedule(ConnectTimerTask(), mReconnectDelay)
}
private fun cancelRetryConnect() {
if (mConnectTimer != null) {
try {
mConnectTimer!!.cancel()
mConnectTimer!!.purge()
} catch (e: IllegalStateException) {
e.printStackTrace()
}
mConnectTimer = null
mReconnectDelay = (15 * 1000).toLong()
}
}
/**
* This thread runs while listening for incoming connections. It behaves
* like a server-side client. It runs until a connection is accepted
* (or until cancelled).
*/
private inner class AcceptThread : Thread() {
// The local server socket
private val mmServerSocket: BluetoothServerSocket?
init {
var tmp: BluetoothServerSocket? = null
// Create a new listening server socket
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID)
} catch (e: IOException) {
Log.e(TAG, "listen() failed" + e.toString())
}
mmServerSocket = tmp
}
override fun run() {
Log.d(TAG, "BEGIN mAcceptThread" + this)
var socket: BluetoothSocket? = null
// Listen to the server socket if we're not connected
while (this@BluetoothManager.state != STATE_CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
if (mmServerSocket != null) {
this@BluetoothManager.state = STATE_CONNECTING
socket = mmServerSocket.accept()
}
} catch (e: IOException) {
this@BluetoothManager.state = STATE_NONE
Log.e(TAG, "accept() failed", e)
break
}
// If a connection was accepted
if (socket != null) {
when (this@BluetoothManager.state) {
STATE_LISTEN, STATE_CONNECTING ->
// Situation normal. Start the connected thread.
connected(socket, socket!!.remoteDevice)
STATE_NONE, STATE_CONNECTED ->
// Either not ready or already connected. Terminate new socket.
try {
socket!!.close()
} catch (e: IOException) {
Log.e(TAG, "Could not close unwanted socket", e)
}
else -> {
}
}
}
}
Log.i(TAG, "END mAcceptThread")
}
fun cancel() {
Log.d(TAG, "cancel " + this)
try {
mmServerSocket?.close()
} catch (e: IOException) {
Log.e(TAG, "close() of server failed" + e.toString())
}
this@BluetoothManager.state = STATE_NONE
}
} // End of class AcceptThread
/**
* This thread runs while attempting to make an outgoing connection
* with a device. It runs straight through; the connection either
* succeeds or fails.
*/
private inner class ConnectThread(private val mmDevice: BluetoothDevice) : Thread() {
private val mmSocket: BluetoothSocket?
init {
var tmp: BluetoothSocket? = null
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
tmp = mmDevice.createRfcommSocketToServiceRecord(MY_UUID)
} catch (e: IOException) {
Log.e(TAG, "create() failed", e)
}
mmSocket = tmp
}
override fun run() {
Log.i(TAG, "BEGIN mConnectThread")
name = "ConnectThread"
// Always cancel discovery because it will slow down a connection
mAdapter.cancelDiscovery()
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket!!.connect()
} catch (e: IOException) {
connectionFailed()
// Close the socket
try {
mmSocket!!.close()
} catch (e2: IOException) {
Log.e(TAG, "unable to close() socket during connection failure", e2)
}
// Start the service over to restart listening mode
this@BluetoothManager.start()
return
}
// Reset the ConnectThread because we're done
synchronized(this@BluetoothManager) {
mConnectThread = null
}
// Start the connected thread
connected(mmSocket, mmDevice)
}
fun cancel() {
try {
mmSocket!!.close()
} catch (e: IOException) {
Log.e(TAG, "close() of connect socket failed", e)
}
}
} // End of class ConnectThread
/**
* This thread runs during a connection with a remote device.
* It handles all incoming and outgoing transmissions.
*/
private inner class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {
private val mmInStream: InputStream?
private val mmOutStream: OutputStream?
init {
Log.d(TAG, "create ConnectedThread")
var tmpIn: InputStream? = null
var tmpOut: OutputStream? = null
// Get the BluetoothSocket input and output streams
try {
tmpIn = mmSocket.inputStream
tmpOut = mmSocket.outputStream
} catch (e: IOException) {
Log.e(TAG, "temp sockets not created", e)
}
mmInStream = tmpIn
mmOutStream = tmpOut
}
override fun run() {
Log.i(TAG, "BEGIN mConnectedThread")
var bytes: Int
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
val buffer = ByteArray(2048)
Arrays.fill(buffer, 0x00.toByte())
bytes = mmInStream!!.read(buffer)
// Send the obtained bytes to the main thread
mHandler?.obtainMessage(MESSAGE_READ, bytes, -1, buffer)?.sendToTarget()
} catch (e: IOException) {
Log.e(TAG, "disconnected", e)
connectionLost()
break
}
}
}
/**
* Write to the connected OutStream.
* @param buffer The bytes to write
*/
fun write(buffer: ByteArray) {
try {
mmOutStream!!.write(buffer)
// Disabled: Share the sent message back to the main thread
// mHandler.obtainMessage(Constants.MESSAGE_WRITE, -1, -1, buffer)
// .sendToTarget();
} catch (e: IOException) {
Log.e(TAG, "Exception during write")
}
}
fun cancel() {
try {
mmSocket.close()
} catch (e: IOException) {
Log.e(TAG, "close() of connect socket failed")
}
}
} // End of class ConnectedThread
/**
* Auto connect timer
*/
private inner class ConnectTimerTask : TimerTask() {
override fun run() {
if (mIsServiceStopped)
return
mHandler?.post {
if (state == STATE_CONNECTED || state == STATE_CONNECTING)
return@post
Log.d(TAG, "ConnectTimerTask :: Retry connect()")
val addrs = ConnectionInfo.deviceAddress
val ba = BluetoothAdapter.getDefaultAdapter()
if (ba != null && addrs != null) {
val device = ba.getRemoteDevice(addrs)
if (device != null) {
connect(device)
}
}
}
}
}
}
</pre>
ConnectionInfo.kt 파일 전문
import android.content.Context
import com.movements.pipegps.ApplicationClass
import com.movements.pipegps.utils.Const
/**
* Remember connection informations for future use
*/
object ConnectionInfo {
//Login URL
// Device MAC address
var deviceAddress: String? = null
// Name of the connected device
var deviceName: String? = null
set(name) {
val context = ApplicationClass.getAppContext()
val prefs = context?.getSharedPreferences(Const.PREFERENCE_NAME, Context.MODE_PRIVATE) ?: return
val editor = prefs.edit()
editor.putString(Const.PREFERENCE_CONN_INFO_ADDRESS, deviceAddress)
editor.putString(Const.PREFERENCE_CONN_INFO_NAME, name)
editor.commit()
field = name
}
init {
val context = ApplicationClass.getAppContext()
val prefs = context?.getSharedPreferences(Const.PREFERENCE_NAME, Context.MODE_PRIVATE)
deviceAddress = prefs?.getString(Const.PREFERENCE_CONN_INFO_ADDRESS, null)
deviceName = prefs?.getString(Const.PREFERENCE_CONN_INFO_NAME, null)
}
/**
* Reset connection info
*/
fun resetConnectionInfo() {
deviceAddress = null
deviceName = null
}
}
'Kotlin' 카테고리의 다른 글
Json Object Request (Login 예제) (0) | 2020.01.21 |
---|---|
thread안에선 다른짓 하지말자 (0) | 2020.01.21 |
Login 만들기 -JsonObjectRequest 사용 (0) | 2020.01.21 |
블루투스통신하기_(3) (0) | 2020.01.21 |
블루투스통신하기_(2) (0) | 2020.01.21 |