안드로이드 스튜디오에서 블루투스 기능을 구현하는 데 도움 요청드립니다

plusultra179의 이미지

안드로이드 스튜디오를 공부하면서 블루투스 구현을 시도해보고 있습니다.
블루투스 api를 사용하여 블루투스 켜기/끄기, 페어링된 리스트 보여주기, 페어링된 기기 연결하기까지
구현하기 위한 코드입니다.
다른 기능은 되는데 기기 연결 부분이 문제가 있는 것 같습니다.
스레드를 서버, 클라이언트 양방향으로 사용하려고 하고 블루투스가 켜지면 서버 스레드가 생성됩니다.
페어링된 기기 리스트에서 기기를 선택하면 클라이언트 스레드가 생성되고 연결을 시도하는 게 의도인데
실제 디바이스에서 실행해 보니 클라이언트 측의 기기에서 앱이 꺼지는 현상이 발생합니다.
제가 안드로이드의 스레드 원리를 부정확하게 알고 쓴 걸로 예상하는데 힌트 주시면 정말 감사하겠습니다.
(UUID는 제가 임의로 UUID String을 할당해서 했었는데 이게 문제인가 싶어 00001101-0000-1000-8000-00805F9B34FB로 바꿔보기도 하였으나 문제는 계속 발생했습니다.)
안드로이드 실력자분들 도움 부탁드립니다.

package com.practice.bluetoothmodule;
 
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
 
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
 
public class MainActivity extends AppCompatActivity {
 
    TextView bthStatus;
    Button bthOn;
    Button bthOff;
    Button bthCon;
 
    BluetoothAdapter bthAdapter;
    Set<BluetoothDevice> pairedDevices;
    List<String> listofPairedDevices;
    private String btAddress=null;
 
    BluetoothDevice bthDevice;
 
    AcceptThread acceptThread;
    ConnectThread connectThread;
    private Handler mHandler = null;
 
    final static int BT_REQUEST_ENABLE=1;
    final static UUID BT_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        bthStatus = (TextView)findViewById(R.id.text_check);
        bthOn = (Button)findViewById(R.id.bth_on);
        bthOff=(Button)findViewById(R.id.bth_off);
        bthCon=(Button)findViewById(R.id.dev_con);
 
        bthAdapter=BluetoothAdapter.getDefaultAdapter();
 
        bthOn.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View view){
                bluetoothOn();
            }
        });
 
        bthOff.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View view){
                bluetoothOff();
            }
        });
 
        bthCon.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View view){
                listPairedDevices();
            }
        });
 
    }
 
    @Override
    public void onStart(){
        super.onStart();
    }
 
    @Override
    public void onDestroy(){
        super.onDestroy();
 
        if(acceptThread != null){
            acceptThread.cancel();
            acceptThread=null;
        }
 
        if(connectThread != null){
            connectThread.cancel();
            connectThread=null;
        }
 
 
    }
 
    void bluetoothOn(){
        if(bthAdapter == null){
            Toast.makeText(getApplicationContext(),"블루투스를 지원하지 않는 기기입니다",Toast.LENGTH_SHORT).show();
        }
        else{
            if(bthAdapter.isEnabled()){
                Toast.makeText(getApplicationContext(),"블루투스가 이미 활성화된 상태입니다",Toast.LENGTH_SHORT).show();
            }
            else{
                Toast.makeText(getApplicationContext(),"블루투스가 활성화 되어 있지 않습니다",Toast.LENGTH_SHORT).show();
                Intent intentBthEnable = new Intent(bthAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(intentBthEnable,BT_REQUEST_ENABLE);
            }
        }
    }
 
    void bluetoothOff(){
        if(bthAdapter.isEnabled()){
            bthAdapter.disable();
            Toast.makeText(getApplicationContext(),"블루투스가 비활성화 되었습니다.",Toast.LENGTH_SHORT).show();
            bthStatus.setText("비활성화");
        }
        else{
            Toast.makeText(getApplicationContext(),"블루투스가 이미 비활성화 상태입니다",Toast.LENGTH_SHORT).show();
        }
    }
 
    @Override
    protected void onActivityResult(int requestCode,int resultCode,Intent data){
        switch(requestCode){
            case BT_REQUEST_ENABLE:
                if(resultCode == RESULT_OK){
                    Toast.makeText(getApplicationContext(),"블루투스 활성화",Toast.LENGTH_LONG).show();
                    bthStatus.setText("활성화");
                    start();
                } else if(resultCode == RESULT_CANCELED){
                    Toast.makeText(getApplicationContext(),"취소",Toast.LENGTH_LONG).show();
                    bthStatus.setText("비활성화");
                }
                break;
        }
        super.onActivityResult(requestCode,resultCode,data);
    }
 
    void listPairedDevices(){
        if(bthAdapter.isEnabled()){
            pairedDevices = bthAdapter.getBondedDevices();
 
            if(pairedDevices.size()>0){
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setTitle("장치 선택");
 
                listofPairedDevices = new ArrayList();
                for(BluetoothDevice device: pairedDevices){
                    listofPairedDevices.add(device.getName());
                }
                final CharSequence[] items= listofPairedDevices.toArray(new CharSequence[ofPairedDevices.size()]);
                listofPairedDevices.toArray(new CharSequence[ofPairedDevices.size()]);
 
                builder.setItems(items, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int item) {
                        connectSelectedDevice(items[item].toString());
                    }
                });
                AlertDialog alert=builder.create();
                alert.show();
            }
            else{
                Toast.makeText(getApplicationContext(),"페어링된 장치가 없습니다",Toast.LENGTH_LONG).show();
            }
        }
        else{
            Toast.makeText(getApplicationContext(),"블루투스가 비활성화 되어 있습니다.",Toast.LENGTH_LONG).show();
        }
    }
 
    void connectSelectedDevice(String selectedDeviceName){
        for(BluetoothDevice tempDevice: pairedDevices){
            if(selectedDeviceName.equals(tempDevice.getName())){
                bthDevice=tempDevice;
                btAddress=bthDevice.getAddress();
                break;
            }
        }
        connect(bthDevice);
        //Toast.makeText(getApplicationContext(),"연결 시도"+bthDevice.getName(),Toast.LENGTH_LONG).show();
    }
 
    public synchronized void start(){
        acceptThread = new AcceptThread();
        acceptThread.start();
    }
 
    public synchronized void connect(BluetoothDevice device){
        connectThread=new ConnectThread(device);
        connectThread.start();
    }
 
    private class AcceptThread extends Thread{
        private final BluetoothServerSocket mmServerSocket;
 
        public AcceptThread(){
            BluetoothServerSocket tmp=null;
            try{
                tmp=bthAdapter.listenUsingRfcommWithServiceRecord("Listen",BT_UUID);
                Toast.makeText(getApplicationContext(),"서버 열림",Toast.LENGTH_LONG).show();
            }catch(IOException e){
                Toast.makeText(getApplicationContext(),"서버 안열림",Toast.LENGTH_LONG).show();
            }
            mmServerSocket=tmp;
        }
 
        public void run(){
            BluetoothSocket socket=null;
            while(true){
                try{
                    socket=mmServerSocket.accept();
                } catch(IOException e){
                    break;
                }
                if(socket != null){
                    Toast.makeText(getApplicationContext(),"연결됨",Toast.LENGTH_SHORT).show();
                    try{
                        sleep(3000);
                    } catch (Exception e){}
                }
            }
        }
 
        public void cancel(){
            try{
                mmServerSocket.close();
            } catch(IOException e){
                e.printStackTrace();
            }
        }
    }
 
 
    private class ConnectThread extends  Thread{
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;
 
        public ConnectThread(BluetoothDevice device){
            BluetoothSocket tmp=null;
            mmDevice=device;
            try{
                tmp=device.createRfcommSocketToServiceRecord(BT_UUID);
                //Toast.makeText(getApplicationContext(),"클라 초기화 됨.",Toast.LENGTH_LONG).show();
            }catch (IOException e){
                //Toast.makeText(getApplicationContext(),"클라 초기화 실패.",Toast.LENGTH_LONG).show();
            }
            mmSocket=tmp;
            //Toast.makeText(getApplicationContext(),"클라 초기화",Toast.LENGTH_LONG).show();
        }
 
        public void run() {
            try {
                bthAdapter.cancelDiscovery();
                mmSocket.connect();
                Toast.makeText(getApplicationContext(), "클라이언트 연결", Toast.LENGTH_LONG).show();
            } catch (IOException e) {
                Toast.makeText(getApplicationContext(), "연결실패", Toast.LENGTH_LONG).show();
            }
        }
 
        public void cancel(){
            try{
                mmSocket.close();
            } catch (IOException e){
                e.printStackTrace();
            }
        }
    }
 
}
[/][/]

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.