반응형

안드로이드의 앱과  PC의  JAVA 서버 프로그램 간에 Serial Port Profile(SPP)를 이용한 블루투스 통신을 다룹니다.  SPP는 블루투스를 이용하여 시리얼 통신을 무선으로 대체할 수 있도록 합니다.




1. 자바 코드 사용 방법

 

2. 페어링 및 COM 포트 추가

 

3. 실행 결과

 

4. 코드 설명

 

5. 소스코드

    5.1. PC용 JAVA 코드

         5.1.1. Server.java

    5.2. 안드로이드 코드

        5.2.1. AndroidManifest.xml

        5.2.2. activity_main.xml

        5.2.3. MainActivity.java






2015. 12.  5 최초작성

2019. 11. 26  Android 코드를 androidx로 수정 

2021. 10. 17  PC 코드 실행시 에러 해결

                      Caused by: java.lang.NoClassDefFoundError: javax/bluetooth/BluetoothStateException

 

                     Android 코드 확인 및 연동 테스트는 아직 못했습니다. 



1. 자바 코드 사용 방법

 

1. https://eclipse.org/downloads/eclipse-packages/  에서 Eclipse IDE for Java Developers 항목에 있는 Windows x86_64 다운로드 받습니다.  

 




압축을 풀면 eclipse 폴더가 생성됩니다. 원하는 위치에 이동시켜 사용할 수도 있습니다.  

 




eclipse.exe 파일을 선택하고 마우스 오른쪽 버튼을 누릅니다.

메뉴에서 작업 표시줄에 고정을 선택하거나 보내기 > 바탕 화면에 바로가기 만들기를 선택합니다.

 


현재는 이클립스에 포함된 JRE를 사용하기 때문에 추가 설치없이 이클립스 실행이 가능합니다.


이클립스를 실행하려면 JDK가 필요합니다. 아래 링크에서  Java SE Development Kit 를 다운로드 받아 설치하세요.
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html


또는 Android Studio에 포함된 JRE를 사용할 수 있습니다. 
아래 경로를 시스템 변수 path에 추가하세요.

C:\Program Files\Android\Android Studio\jre\bin\




2. 이클립스를 실행시키면 워크스페이스 경로를 물어봅니다. Workspace 항목에 자바 프로젝트를 저장할 원하는 경로를 적어줍니다.

 

Use this as the default and do not ask again을 체크하면 이후 워크스페이스 경로를 다시 물어보지 않고 설정한 경로를 사용하게 됩니다.

뒤에서 이 경로를 확인해야 하니 기억해두세요.  Launch를 클릭합니다.

 




3. 메뉴에서 File > New > Java Project를 선택합니다.  Project name을 적어주고 Finish를 클릭합니다.

 



무시해도 됩니다. Don’t Create를 클릭하세요. 

 




4. Package Explorer에서 프로젝트의 src 폴더를 클릭하고 마우스 오른쪽 버튼을 누릅니다. 메뉴에서 New >  File을 선택합니다.

 




5. New File 창에서 File name을 적어주고 Finish를 클릭합니다. 

 



 Package Explorer에서 프로젝트의 src 폴더 아래에 추가한 자바파일이 보이게 됩니다. 

 




6. http://snapshot.bluecove.org/distribution/download/2.1.1-SNAPSHOT/2.1.1-SNAPSHOT.63에서 bluecove-2.1.1-SNAPSHOT.jar를 다운로드 받습니다. 

 

http://www.java2s.com/Code/Jar/b/Downloadbluecove211jar.htm 에서 다운로드 받으세요. )



프로젝트 폴더에 libs 폴더를 만들어서 그 안에 복사해줍니다.

프로젝트 폴더 위치는 앞에서 정한 워크스페이스 경로입니다.

 



이클립스에서 프로젝트 이름을 클릭하고 F5키를 눌러줘야  Package Explorer에서 libs 폴더와 추가한 bluecove-2.1.1.jar 파일이 보입니다.

 




7. Package Explorer에서 프로젝트 이름을 클릭하고 마우스 오른쪽 버튼을 누릅니다. 메뉴에서 Properties를 선택합니다.

 




8. Properties 창의 왼쪽에 있는 패널에서 Java Build Path를 클릭합니다.

 




8. Libraries 탭을 클릭합니다. 그리고나서 Classpath를 클릭한 후, 오른쪽에 보이는 버튼들 중에서 Add JARs..를 클릭합니다.

 




9. libs 폴더에 있는 bluecove-2.1.1.jar 파일을 선택하고 OK를 클릭합니다.

 




10. Libraries 탭의 Classpath 항목에 bluecove-2.1.1.jar 파일을 확인할 수 있습니다.

OK를 클릭합니다.

 

Apply and Close 버튼을 클릭합니다. 

 



 Project Explorer의  Referenced Libraries에서도 추가한 bluecove-2.1.1-SNAPSHOT.jar 파일을 확인할 수 있습니다.

 



11. 본 포스팅 마지막에 있는 PC용 자바 코드를 프로젝트의 자바 파일에 복사해줍니다.

 




12. 실행시켜보면 다음과 같은 로그가 보이게 됩니다.

 

[Sun Oct 17 07:54:39 KST 2021] Local Bluetooth device...

 

BlueCove version 2.1.1-SNAPSHOT on winsock

[Sun Oct 17 07:54:40 KST 2021] address: F8AC6555F207

[Sun Oct 17 07:54:40 KST 2021] name: WEBNAUTES-PC

[Sun Oct 17 07:54:40 KST 2021] Opened connection successful.

[Sun Oct 17 07:54:40 KST 2021] Server is now running.

[Sun Oct 17 07:54:40 KST 2021] wait for client requests...




2. 페어링 및 COM 포트 추가

블루투스 디바이스 간에 데이터를 송수신하려면 먼저 페어링을 해야 합니다.  

한쪽에서 자신을 검색할 수 있도록 해주고 상대방이 자신을 찾아서 페어링을 요청하여 양쪽의 인증번호가 동일한지 확인하고 양쪽에서 등록을 해줘야 페어링이 이루어집니다.

 

안드로이드 폰과  윈도우가 설치된  PC간의 페어링 과정을 예로 들어 보겠습니다.  

윈도우 버전마다 페어링 과정이 조금씩 차이가 있지만 거의 동일합니다.  

여기에서는 windows 10 RS2를 기준으로 합니다. 



1. 안드로이드 폰의 설정에서 블루투스를 활성화해줍니다.  이 화면을 유지하면 PC에서 안드로이드폰이 검색됩니다 .

 




2.  윈도우의 작업 표시줄 오른쪽 끝에 있는 블루투스 아이콘을 더블클릭합니다.

 

  




3. “Bluetooth 또는 기타 디바이스 추가”를 클릭합니다.




3.  추가할 디바이스 유형으로 Bluetooth를 선택합니다.

 




4.  추가할 안드로이드폰을 선택합니다.  이 시점에서 안드로이드폰의  블루투스 설정 화면이 켜져있어야 합니다. 화면이 꺼져있으면 찾지 못합니다. 

 




5.  PC와 안드로이드 폰에서 똑같은 숫자가 뜨는지 확인하고  각각 연결과 등록을 선택합니다.

 

 




6.  연결됨으로 표시되면 완료 버튼을 클릭합니다. 

 



화면 오른쪽 아래에 장치 설정 중 메시지가 보이는데

 



다시 장치 설정 완료 메시지가 보일 때까지  잠시 기다립니다.

 




7.  PC에서 안드로이드폰이 연결됨에서 페이링됨으로 변경됩니다.

 



안드로이드폰에서는 등록된 기기 항목에 PC가 추가됩니다.

 



여기까지 해도  포스팅에 포함된 코드를 사용하여 정상적으로 안드로이드와 PC 간에 블루투스 통신이 이루어집니다. 하지만 문제가 발생한다면 8번부터 진행하세요..




8. 윈도우의 블루투스 설정 창을 아래로 스크롤해서 혹은 오른쪽에 위치한  추가 Bluetooth 옵션을 클릭합니다.

 




9. Bluetooth 설정 창의 COM 포트 탭에서 추가를 클릭합니다.

 




10.  “수신(장치에서 연결 시작)”을 선택하고 확인 버튼을 클릭합니다.

 




11. 수신용으로 포트가 추가됩니다.  확인을 클릭하여 Bluetooth 설정 창을 닫습니다.

 



3. 실행 결과

 

1. 이클립스에서 Ctrl + F11을 눌러서 자바 서버프로그램을 실행시킵니다.  

아래와 같은 메시지가 보입니다..

 

[Mon Apr 24 15:26:11 KST 2017] Local Bluetooth device...

 

BlueCove version 2.1.1-SNAPSHOT on winsock

[Mon Apr 24 15:26:12 KST 2017] address: F8633F2710E0

[Mon Apr 24 15:26:12 KST 2017] name: NOTE

[Mon Apr 24 15:26:12 KST 2017] Opened connection successful.

[Mon Apr 24 15:26:12 KST 2017] Server is now running.

[Mon Apr 24 15:26:12 KST 2017] wait for client requests...




2. 안드로이드폰에서 앱을 실행시키면 페이링되어있는 디바이스 목록을 보여주는데 이중에  PC를 선택해줍니다.

 




3. 연결에 성공하면 상단에  connected to PC이름이라는 메시지가 출력됩니다.

서버에서 보내준 메시지가 출력됩니다. 

 



이클립스에서  안드로이드 폰의 주소를 출력하며 접속되었다는 것을 확인할 수 있습니다. 

현재 접속된 클라이언트 수는 1입니다. 

[Mon Apr 24 15:27:36 KST 2017] 현재 접속 중인 클라이언트 수: 1
[Mon Apr 24 15:27:36 KST 2017] Open streams...
[Mon Apr 24 15:27:36 KST 2017] Remote device
[Mon Apr 24 15:27:36 KST 2017] address: 5C70A3D86B57
[Mon Apr 24 15:27:36 KST 2017] Client is connected...
[Mon Apr 24 15:27:36 KST 2017] wait for client requests...
[Mon Apr 24 15:27:36 KST 2017] Me : 에코 서버에 접속하셨습니다.
[Mon Apr 24 15:27:36 KST 2017] Me : 보내신 문자를 에코해드립니다.
[Mon Apr 24 15:27:36 KST 2017] ready




4. 안드로이드 폰의 앱에서 문자열을 입력한 후,  SEND를 누르면

 

   



입력한 문자가 다시  에코되는 것을 볼 수 있습니다.

 



이클립스에서도 수신된 문자를 확인 할 수 있으며, 서버가 에코해주었음이 표시됩니다.

 

[Mon Apr 24 15:31:18 KST 2017] 5C70A3D86B57: 한글
[Mon Apr 24 15:31:18 KST 2017] Me : 한글
[Mon Apr 24 15:31:18 KST 2017] ready




5. 다른 안드로이드폰에서 서버에 접속하여 문자열을 보내면 똑같이 동작합니다. 

현재 접속된 클라이언트 수는 2입니다. 

 

[Mon Apr 24 15:36:08 KST 2017] 현재 접속 중인 클라이언트 수: 2
[Mon Apr 24 15:36:08 KST 2017] Open streams...
[Mon Apr 24 15:36:08 KST 2017] Remote device
[Mon Apr 24 15:36:08 KST 2017] address: BCF5AC7C3A47
[Mon Apr 24 15:36:08 KST 2017] Client is connected...
[Mon Apr 24 15:36:08 KST 2017] wait for client requests...
[Mon Apr 24 15:36:08 KST 2017] Me : 에코 서버에 접속하셨습니다.
[Mon Apr 24 15:36:08 KST 2017] Me : 보내신 문자를 에코해드립니다.
[Mon Apr 24 15:36:08 KST 2017] ready
[Mon Apr 24 15:36:13 KST 2017] BCF5AC7C3A47: 태스트
[Mon Apr 24 15:36:13 KST 2017] Me : 태스트
[Mon Apr 24 15:36:13 KST 2017] ready




6. 안드로이드 폰에서 백버튼을 눌러서 앱을 종료하면 클라언트가 접속을 끊었음을 알려줍니다. 

현재 접속중인 클라이언트 수는 1로 줄어듭니다.

[Mon Apr 24 15:44:24 KST 2017] Client has been disconnected
[Mon Apr 24 15:44:24 KST 2017] 현재 접속 중인 클라이언트 수: 1




7. 서버를 종료하기 위해서는 이클립스 콘솔창에 있는 빨간 사각형 아이콘을  클릭합니다. 

 



그리고 빨간색 사각형으로 표시한 X아이콘을 클릭합니다 

 



4. 코드 설명

1. 디바이스에서 블루투스를 지원하는지 체크합니다.

 

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
showErrorDialog("This device is not implement Bluetooth.");
return;
}




2. 디바이스의 블루투스 기능이 활성화 되어있는지 체크합니다.

활성화되어 있지 않다면 사용자에게 블루투스를 켜도록 요청합니다.

 

if (!mBluetoothAdapter.isEnabled()) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_BLUETOOTH_ENABLE);
}






3. 블루투스가 활성화 되어 있다면 showPairedDevicesListDialog() 메소드를 호출합니다.

 

if (!mBluetoothAdapter.isEnabled()) {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, REQUEST_BLUETOOTH_ENABLE);
}
else {
Log.d(TAG, "Initialisation successful.");

showPairedDevicesListDialog();
}





4. 페어링 되어 있는 블루투스 장치들의 목록을 보여줍니다.

목록에서 블루투스 장치를 선택하면 선택한 디바이스를 인자로 하여 ConnectTask AsyncTask를 실행합니다.

 

public void showPairedDevicesListDialog()
{
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
final BluetoothDevice[] pairedDevices = devices.toArray(new BluetoothDevice[0]);

if ( pairedDevices.length == 0 ){
showQuitDialog( "No devices have been paired.\n"
+"You must pair it with another device.");
return;
}

String[] items;
items = new String[pairedDevices.length];
for (int i=0;i<pairedDevices.length;i++) {
items[i] = pairedDevices[i].getName();
}

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Select device");
builder.setCancelable(false);
builder.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();

// Attempt to connect to the device
ConnectTask task = new ConnectTask(pairedDevices[which]);
task.execute();
}
});
builder.create().show();
}



 

 

 

5. 시리얼 통신(SPP)을 하기 위한 RFCOMM 블루투스 소켓을 생성합니다. (ConnectTask)

 

mBluetoothDevice = bluetoothDevice;
mConnectedDeviceName = bluetoothDevice.getName();

//SPP
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");

try {
mBluetoothSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(uuid);

 

 

6. 주변 블루투스 디바이스 찾는 것을 중지합니다.

 

mBluetoothAdapter.cancelDiscovery();



7. 블루투스 소켓을 성공적으로 생성했다면 ConnectedTask AsyncTask를 실행합니다. 

 

@Override
protected void onPostExecute(Boolean isSucess) {

if ( isSucess ) {
connected(mBluetoothSocket);
}

  ...........................................

public void connected( BluetoothSocket socket ) {
mConnectedTask = new ConnectedTask(socket);
mConnectedTask.execute();
}





8. 실제 데이터를 주고 받는 처리를 ConnectedTask에서 합니다. 

doInBackground 메소드에서 대기하며 수신되는 문자열이 있으면 받아서  버퍼에 저장합니다.

write 메소드는 문자열을 전송할 때 호출되어 집니다. 

 

private class ConnectedTask extends AsyncTask<Void, String, Boolean> {

    .....................................................

@Override
protected Boolean doInBackground(Void... params) {

byte [] readBuffer = new byte[1024];
int readBufferPosition = 0;


while (true) {

if ( isCancelled() ) return false;

try {

int bytesAvailable = mInputStream.available();

if(bytesAvailable > 0) {

byte[] packetBytes = new byte[bytesAvailable];

mInputStream.read(packetBytes);

for(int i=0;i<bytesAvailable;i++) {

byte b = packetBytes[i];
if(b == '\n')
{
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0, encodedBytes, 0,
encodedBytes.length);
String recvMessage = new String(encodedBytes, "UTF-8");

readBufferPosition = 0;

Log.d(TAG, "recv message: " + recvMessage);
publishProgress(recvMessage);
}
else
{
readBuffer[readBufferPosition++] = b;
}
}
}
} catch (IOException e) {

Log.e(TAG, "disconnected", e);
return false;
}
}

}

    ..............................................................................

void write(String msg){

msg += "\n";

try {
mOutputStream.write(msg.getBytes());
mOutputStream.flush();
} catch (IOException e) {
Log.e(TAG, "Exception during send", e );
}

mInputEditText.setText(" ");
}
}




5. 소스코드

5.1. PC용 JAVA 코드

5.1.1. Server.java

 

/*
*
* webnautes@naver.com
*
* 참고
* http://www.kotemaru.org/2013/10/30/android-bluetooth-sample.html
*/

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector; 
import javax.microedition.io.StreamConnection; 
import javax.microedition.io.StreamConnectionNotifier; 
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Date;


public class Server{
 
    public static void main(String[] args){
   
   
log("Local Bluetooth device...\n");
       
    LocalDevice local = null;
try {

local = LocalDevice.getLocalDevice();
} catch (BluetoothStateException e2) {

}  

    log( "address: " + local.getBluetoothAddress() );
    log( "name: " + local.getFriendlyName() );
   
   
    Runnable r = new ServerRunable();
    Thread thread = new Thread(r);
    thread.start();
   
    }
   
     
    private static void log(String msg) { 
   
        System.out.println("["+(new Date()) + "] " + msg); 
    }

}


class ServerRunable implements Runnable{
 
//UUID for SPP
final UUID uuid = new UUID("0000110100001000800000805F9B34FB", false);
    final String CONNECTION_URL_FOR_SPP = "btspp://localhost:"
    + uuid +";name=SPP Server";
 
    private StreamConnectionNotifier mStreamConnectionNotifier = null
    private StreamConnection mStreamConnection = null;
    private int count = 0;
 
   
@Override
public void run() {

    try {
   
mStreamConnectionNotifier = (StreamConnectionNotifier) Connector
.open(CONNECTION_URL_FOR_SPP);

log("Opened connection successful.");
} catch (IOException e) {

log("Could not open connection: " + e.getMessage());
return;
}
 

    log("Server is now running.");

   
   
        while(true){
       
        log("wait for client requests...");

try {

mStreamConnection = mStreamConnectionNotifier.acceptAndOpen();
} catch (IOException e1) {

log("Could not open connection: " + e1.getMessage() );
}

       
count++;
log("현재 접속 중인 클라이언트 수: " + count);


        new Receiver(mStreamConnection).start();
        }

}

       
   
    class Receiver extends Thread {
   
    private InputStream mInputStream = null;
        private OutputStream mOutputStream = null;
        private String mRemoteDeviceString = null;
        private StreamConnection mStreamConnection = null;
       
       
        Receiver(StreamConnection streamConnection){
       
        mStreamConnection = streamConnection;

try {
   
mInputStream = mStreamConnection.openInputStream();
mOutputStream = mStreamConnection.openOutputStream();

log("Open streams...");
} catch (IOException e) {

log("Couldn't open Stream: " + e.getMessage());

Thread.currentThread().interrupt();
return;
}


try {
       
RemoteDevice remoteDevice
= RemoteDevice.getRemoteDevice(mStreamConnection);

        mRemoteDeviceString = remoteDevice.getBluetoothAddress();
       
log("Remote device");
log("address: "+ mRemoteDeviceString);
       
} catch (IOException e1) {

log("Found device, but couldn't connect to it: " + e1.getMessage());
return;
}

log("Client is connected...");
        }
       
       
    @Override
public void run() {
   
try {

    Reader mReader = new BufferedReader(new InputStreamReader
        ( mInputStream, Charset.forName(StandardCharsets.UTF_8.name())));

    boolean isDisconnected = false;
   
Sender("에코 서버에 접속하셨습니다.");
Sender( "보내신 문자를 에코해드립니다.");
   
while(true){

log("ready");

       
            StringBuilder stringBuilder = new StringBuilder();
            int c = 0;
           
           
while ( '\n' != (char)( c = mReader.read()) ) {

if ( c == -1){

log("Client has been disconnected");

count--;
log("현재 접속 중인 클라이언트 수: " + count);

isDisconnected = true;
Thread.currentThread().interrupt();

break;
}

stringBuilder.append((char) c);
}

            if ( isDisconnected ) break;
           
            String recvMessage = stringBuilder.toString();
        log( mRemoteDeviceString + ": " + recvMessage );

            Sender(recvMessage);
}

} catch (IOException e) {

log("Receiver closed" + e.getMessage());
}
}
   

    void Sender(String msg){
       
            PrintWriter printWriter = new PrintWriter(new BufferedWriter
            (new OutputStreamWriter(mOutputStream,
            Charset.forName(StandardCharsets.UTF_8.name()))));
       
    printWriter.write(msg+"\n");
    printWriter.flush();
   
    log( "Me : " + msg );
    }
}
   
   
    private static void log(String msg) { 
   
        System.out.println("["+(new Date()) + "] " + msg); 
    }
       
}  





5.2. 안드로이드 코드

5.2.1. AndroidManifest.xml

앱에서 블루투스를 사용하기 위해 필요한 퍼미션을 추가해줍니다.

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.tistory.webnautes.android_bluetooth_client">

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />



5.2.2. activity_main.xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:paddingBottom="10dp"
    android:paddingTop="10dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="connection state : "/>

        <TextView
            android:id="@+id/connection_status_textview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text=""/>

    </LinearLayout>

    <LinearLayout
        android:weightSum="1"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/input_string_edittext"
            android:hint="input text here"
            android:layout_weight="0.8"
            android:layout_width="0dp"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@+id/send_button"
            android:layout_weight="0.2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="Send" />
    </LinearLayout>

    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/message_listview"/>

</LinearLayout>




5.2.3. MainActivity.java

 

/*
*
* webnautes@naver.com
*
* 참고
* https://github.com/googlesamples/android-BluetoothChat
* http://www.kotemaru.org/2013/10/30/android-bluetooth-sample.html
*/


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity
{
    private final int REQUEST_BLUETOOTH_ENABLE = 100;

    private TextView mConnectionStatus;
    private EditText mInputEditText;

    ConnectedTask mConnectedTask = null;
    static BluetoothAdapter mBluetoothAdapter;
    private String mConnectedDeviceName = null;
    private ArrayAdapter<String> mConversationArrayAdapter;
    static boolean isConnectionError = false;
    private static final String TAG = "BluetoothClient";

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button sendButton = (Button)findViewById(R.id.send_button);
        sendButton.setOnClickListener(new View.OnClickListener(){
            public void onClick(View v){
                String sendMessage = mInputEditText.getText().toString();
                if ( sendMessage.length() > 0 ) {
                    sendMessage(sendMessage);
                }
            }
        });
        mConnectionStatus = (TextView)findViewById(R.id.connection_status_textview);
        mInputEditText = (EditText)findViewById(R.id.input_string_edittext);
        ListView mMessageListview = (ListView) findViewById(R.id.message_listview);

        mConversationArrayAdapter = new ArrayAdapter<>( this,
                android.R.layout.simple_list_item_1 );
        mMessageListview.setAdapter(mConversationArrayAdapter);


        Log.d( TAG, "Initalizing Bluetooth adapter...");

        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter == null) {
            showErrorDialog("This device is not implement Bluetooth.");
            return;
        }

        if (!mBluetoothAdapter.isEnabled()) {
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, REQUEST_BLUETOOTH_ENABLE);
        }
        else {
            Log.d(TAG, "Initialisation successful.");

            showPairedDevicesListDialog();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if ( mConnectedTask != null ) {

            mConnectedTask.cancel(true);
        }
    }


    private class ConnectTask extends AsyncTask<Void, Void, Boolean> {

        private BluetoothSocket mBluetoothSocket = null;
        private BluetoothDevice mBluetoothDevice = null;

        ConnectTask(BluetoothDevice bluetoothDevice) {
            mBluetoothDevice = bluetoothDevice;
            mConnectedDeviceName = bluetoothDevice.getName();

            //SPP
            UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");

            try {
                mBluetoothSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(uuid);
                Log.d( TAG, "create socket for "+mConnectedDeviceName);

            } catch (IOException e) {
                Log.e( TAG, "socket create failed " + e.getMessage());
            }

            mConnectionStatus.setText("connecting...");
        }


        @Override
        protected Boolean doInBackground(Void... params) {

            // Always cancel discovery because it will slow down a connection
            mBluetoothAdapter.cancelDiscovery();

            // Make a connection to the BluetoothSocket
            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
                mBluetoothSocket.connect();
            } catch (IOException e) {
                // Close the socket
                try {
                    mBluetoothSocket.close();
                } catch (IOException e2) {
                    Log.e(TAG, "unable to close() " +
                            " socket during connection failure", e2);
                }

                return false;
            }

            return true;
        }


        @Override
        protected void onPostExecute(Boolean isSucess) {

            if ( isSucess ) {
                connected(mBluetoothSocket);
            }
            else{

                isConnectionError = true;
                Log.d( TAG,  "Unable to connect device");
                showErrorDialog("Unable to connect device");
            }
        }
    }


    public void connected( BluetoothSocket socket ) {
        mConnectedTask = new ConnectedTask(socket);
        mConnectedTask.execute();
    }



    private class ConnectedTask extends AsyncTask<Void, String, Boolean> {

        private InputStream mInputStream = null;
        private OutputStream mOutputStream = null;
        private BluetoothSocket mBluetoothSocket = null;

        ConnectedTask(BluetoothSocket socket){

            mBluetoothSocket = socket;
            try {
                mInputStream = mBluetoothSocket.getInputStream();
                mOutputStream = mBluetoothSocket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "socket not created", e );
            }

            Log.d( TAG, "connected to "+mConnectedDeviceName);
            mConnectionStatus.setText( "connected to "+mConnectedDeviceName);
        }


        @Override
        protected Boolean doInBackground(Void... params) {

            byte [] readBuffer = new byte[1024];
            int readBufferPosition = 0;


            while (true) {

                if ( isCancelled() ) return false;

                try {

                    int bytesAvailable = mInputStream.available();

                    if(bytesAvailable > 0) {

                        byte[] packetBytes = new byte[bytesAvailable];

                        mInputStream.read(packetBytes);

                        for(int i=0;i<bytesAvailable;i++) {

                            byte b = packetBytes[i];
                            if(b == '\n')
                            {
                                byte[] encodedBytes = new byte[readBufferPosition];
                                System.arraycopy(readBuffer, 0, encodedBytes, 0,
                                        encodedBytes.length);
                                String recvMessage = new String(encodedBytes, "UTF-8");

                                readBufferPosition = 0;

                                Log.d(TAG, "recv message: " + recvMessage);
                                publishProgress(recvMessage);
                            }
                            else
                            {
                                readBuffer[readBufferPosition++] = b;
                            }
                        }
                    }
                } catch (IOException e) {

                    Log.e(TAG, "disconnected", e);
                    return false;
                }
            }

        }

        @Override
        protected void onProgressUpdate(String... recvMessage) {

            mConversationArrayAdapter.insert(mConnectedDeviceName + ": " + recvMessage[0], 0);
        }

        @Override
        protected void onPostExecute(Boolean isSucess) {
            super.onPostExecute(isSucess);

            if ( !isSucess ) {


                closeSocket();
                Log.d(TAG, "Device connection was lost");
                isConnectionError = true;
                showErrorDialog("Device connection was lost");
            }
        }

        @Override
        protected void onCancelled(Boolean aBoolean) {
            super.onCancelled(aBoolean);

            closeSocket();
        }

        void closeSocket(){

            try {

                mBluetoothSocket.close();
                Log.d(TAG, "close socket()");

            } catch (IOException e2) {

                Log.e(TAG, "unable to close() " +
                        " socket during connection failure", e2);
            }
        }

        void write(String msg){

            msg += "\n";

            try {
                mOutputStream.write(msg.getBytes());
                mOutputStream.flush();
            } catch (IOException e) {
                Log.e(TAG, "Exception during send", e );
            }

            mInputEditText.setText(" ");
        }
    }


    public void showPairedDevicesListDialog()
    {
        Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();
        final BluetoothDevice[] pairedDevices = devices.toArray(new BluetoothDevice[0]);

        if ( pairedDevices.length == 0 ){
            showQuitDialog( "No devices have been paired.\n"
                    +"You must pair it with another device.");
            return;
        }

        String[] items;
        items = new String[pairedDevices.length];
        for (int i=0;i<pairedDevices.length;i++) {
            items[i] = pairedDevices[i].getName();
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Select device");
        builder.setCancelable(false);
        builder.setItems(items, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();

                ConnectTask task = new ConnectTask(pairedDevices[which]);
                task.execute();
            }
        });
        builder.create().show();
    }



    public void showErrorDialog(String message)
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Quit");
        builder.setCancelable(false);
        builder.setMessage(message);
        builder.setPositiveButton("OK"new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                if ( isConnectionError  ) {
                    isConnectionError = false;
                    finish();
                }
            }
        });
        builder.create().show();
    }


    public void showQuitDialog(String message)
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Quit");
        builder.setCancelable(false);
        builder.setMessage(message);
        builder.setPositiveButton("OK"new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                finish();
            }
        });
        builder.create().show();
    }

    void sendMessage(String msg){

        if ( mConnectedTask != null ) {
            mConnectedTask.write(msg);
            Log.d(TAG, "send message: " + msg);
            mConversationArrayAdapter.insert("Me:  " + msg, 0);
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_BLUETOOTH_ENABLE) {
            if (resultCode == RESULT_OK) {
                //BlueTooth is now Enabled
                showPairedDevicesListDialog();
            }
            if (resultCode == RESULT_CANCELED) {
                showQuitDialog("You need to enable bluetooth");
            }
        }
    }


}




반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts