2010년 5월 5일 수요일

안드로이드 블루투스 프로그래밍 - BlueWatch Project #2





안드로이드는 블루투스 프로토콜 스택을 포함하고 있기 때문에 블루투스 디바이스들과 무선으로 데이터를 교환할 수 있다. 어플리케이션 프레임웍은 안드로이드 블루투스 API를 사용해 블루투스에 억세스 할 수 있다. 블루투스 API를 사용하면 다음과 같은 작업을 할 수 있다.

  • 다른 블루투스 디바이스 검색
  • 페어링 된 블루투스 디바이스를 위한 로컬 블루투스 아답터 퀘리
  • RFCOMM 채널 설정
  • SDP(Service Discovery Protocol)을 통한 다른 디바이스와의 커넥션
  • 양방향 데이터 전송
  • 복수 커넥션 관리

- 기초

이 문서는 블루투스를 사용해 통신하는데 필요한 4가지 주요 태스크(블루투스 셋업, 페어링 되어 있거나 주변에 있는 기기 검색, 디바이스와 연결, 디바이스간 데이터 전송)를 수행하기 위해 안드로이드 블루투스 API를 어떻게 사용하는가를 설명한다.
모든 블루투스 API는 android.bluetooth 패키지에 들어있다.  다음은 블루투스 연결을 만드는데 필요한 클래스들의 요약이다.

  • BluetoothAdapter - 로컬 블루투스 아답터 하드웨어를 나타낸다. BluetoothAdapter는 모든 블루투스를 통한 상호작용의 엔트리포인트이다. 이 객체를 사용해서 다른 블루투스 디바이스 찾기, 페어링 된 디바이스 퀘리, 알려진 MAC address를 사용해 BluetoothDevice 인스턴스 얻기, 다른 디바이스에서 부터의 통신 요구를 기다리기 위한 BluetoothServerSocket 만들기를 할 수 있다.
  • BluetoothDevice - 상대방의 블루투스 디바이스를 나타낸다. 이 객체를 사용하면 BluetoothSocket을 통해 상대방 디바이스와 커넥션을 요구하거나 이름, 주소, 클래스, 페어링 상태등의 정보를 퀘리할 수 있다.
  • BluetoothSocket - 블루투스 소켓을 위한 인터페이스를 나타낸다. 어플리케이션이 InputStream과 OutputStream을 사용해서 다른 블루투스 디바이스와 데이터 교환을 할 수 있는 연결 포인트이다.
  • BluetoothServerSocket – Incoming 리퀘스트를 위해 listen하고 있는 오픈된 서버소켓(TCP ServerSocket과 유사)을 나타낸다. 두대의 안드로이드 디바이스를 연결하기 위해 한쪽의 디바이스는 이 클래스를 사용해서 서버소켓을 오픈해야만 한다. 원격 블루투스 디바이스가 디바이스에 커넥션 리퀘스트를 할 때 BluetoothServerSocket은 커넥션이 연결되면 연결된 BluetoothSocket을 리턴해준다.
  • BluetoothClass - 블루투스 디바이스의 일반적 특성과 기능을 나타낸다. 이 클래스는 디바이스의 디바이스 클래스와 서비스를 정의하는 읽기 전용 속성의 집합이다.

- 블루투스 퍼미션

어플리케이션에서 블루투스 기능을 사용하려면 최소한 BLUETOOTH와 BLUETOOTH_ADMIN 둘중에 하나의 블루투스 퍼미션을 선언해줘야 한다. 커넥션 요구, 커넥션 accept, 데이터 전송등의 블루투스 통신을 하기 위해서는 BLUETOOTH 퍼미션이 필요하다.
디바이스 discovery를 시작하거나 블루투스 설정을 조작하려면 BLUETOOTH_ADMIN 퍼미션이 필요하다.
BLUETOOTH_ADMIN 퍼미션을 사용하려면 BLUETOOTH 퍼미션도 꼭 있어야만 한다. 매니페스트 파일에 블루투스 퍼미션을 선언해준다.

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




- 블루투스 셋업

어플리케이션이 블루투스로 통신을 하기 전에 디바이스가 블루투스를 지원하는지 확인할 필요가 있다. 그리고 블루투스를 지원한다면 활성화 되었는지도 확인해줘야 한다. 만일 블루투스를 지원하지 않으면 블루투스 기능을 비활성화 시켜야 한다. 블루투스를 지원하지만 활성화 되어 있지 않으면 사용자가 어플리케이션을 떠나지 않고 블루투스를 활성화하도록 요구할 수 있다. 이 작업은 BluetoothAdapter를 사용해서 두 단계로 수행할 수 있다.

1.BluetoothAdapter 를 얻는다.
모든 블루투스 액티비티를 위해 BluetoothAdapter가 요구된다. BluetoothAdapter를 얻기 위해서는 스태틱 메소드인 getDefaultAdapter()를 호출하면 된다. 그러면 디바이스의 블루투스 아답터를 나타내는 BluetoothAdapter 인스턴스를 리턴한다.

BluetoothAdapter mBTAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBTAdapter == null) {
    // device does not support Bluetooth
}


2.블루투스 활성화
블루투스가 활성화 되어있는지 확인해야 한다. isEnabled()를 호출해서 블루투스가 현재 활성화되어 있는지 확인한다. 메소드가 false를 리턴하면 블루투스가 비활성화되어 있는 것이다. 블루투스를 활성화 시키려면 ACTION_REQUEST_ENABLE 인텐트로 startActivityForResult()를 호출하면 된다.

If (!mBTAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}


그림 1과 같이 블루투스를 활성화하기 위한 퍼미션을 요구하는 대화창이 나타난다. 사용자가 “Yes”를 선택하면 시스템은 블루투스를 활성화시키고 그 과정이 끝나면 어플리케이션으로 포커스가 돌아오게 된다.
블루투스 활성화가 성공하면 액티비티는 onActivityResult() 콜백에서 RESULT_OK를 리턴받게 된다. 블루투스가 에러로 인해 (또는 사용자가 “No”를 선택해서) 활성화되지 못하면 RESULT_CANCELED가 리턴된다. 옵션으로 블루투스 상태가 변경될 때 마다 시스템이 브로드캐스하는 ACTION_STATE_CHANGED 인텐트를 listen하도록 할 수도 있다.

- 디바이스 검색

BluetoothAdapter를 사용하면 디바이스 discovery 또는 페어링 된 디바이스 목록을 퀘리해서 원격 블루투스 디바이스를 찾을 수 있다.
디바이스 discovery는 주변의 활성화 된 블루투스 디바이스를 찾고 각각에 대한 정보를 요구하는 검색 단계이다. 하지만 통신가능 범위에 들어있는 블루투스 디바이스라 해도 현재 discoverable 하도록 활성화 되어 있어야만 discovery 요구에 응답한다. 디바이스가 discoverable 상태인 경우 discovery 요구에 디바이스 이름, 클래스, MAC 주소같은 정보를 공유함으로서 응답한다. 이 정보를 사용해서 discovery를 수행한 디바이스는 발견된 디바이스에 커넥션을 시작하도록 선택할 수 있다.
일단 원격 디바이스와 처음으로 연결이 이루어지면 자동으로 사용자에게 페어링을 할 것인가 물어보게 된다.  디바이스 페어링이 이루어지면 상대 디바이스에 대한 기본 정보(디아비스 이름, 클래스, MAC 주소 등)가 저장되고 그 내용은 블루투스 API를 통해 읽을 수 있게 된다. 이미 알고 있는 원격디바이스의 MAC 주소를 사용하면 아무때나 (물론 해당 디바이스가 통신 가능범위에 있다는 가정 하에) discovery를 수행할 필요 없이 바로 커넥션 과정을 시작할 수 있다.
페어링과 연결된것의 차이점은 잘 알고 있어야 한다. 페어링은 두 디바이스가 각자 상대방의 존재를 알고 있고 인증과정에 사용할 link-key를 공유하고 있어 서로간에 암호화 된 연결을 설정할 수 있다는걸 의미한다. 연결된것은 디바이스가 현재 RFCOMM 채널을 공유하고 있어 서로 데이터를 전송할 수 있는 상태를 의미한다.
현재 안드로이드 블루투스 API는 RFCOMM 커넥션을 설정하기 전에 디바이스가 페어링 되어야만 한다. (블루투스 API에서 암호화된 커넥션을 시작하려고 할 때 페어링이 자동을 이루어진다.)
다음의 섹션은 페어링 된 디바이스를 찾거나, 디바이스 discovery를 사용해 새 디바이스를 찾는 방법을 설명한다.
주: 안드로이드 디바이스는 기본적으로 not discoverable 상태이다. 시스템 설정을 통해 짧은 시간동안 디바이스를 discoverable 상태로 만들거나 어플리케이션에서 직접 discoverable 상태로 만들어 줄 수 있다.

- 페어링 된 디바이스 퀘리

디바이스 discovery를 수행하기 전에 원하는 디바이스가 이미 페어링 되어 있는가 확인해 볼 필요가 있다.  확인하기 위해서 getBondedDevices()를 호출하면 된다. 그러면 페어링 된 디바이스들의 집합인 BluetoothDevices 를 돌려준다. 예를 들어 페어링 된 모든 디바이스를 퀘리한 다음 ArrayAdapter를 사용해 페어링 된 각 디바이스의 이름을 보여줄 수 있다.

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() <> 0) {
    for (BluetoothDevice device : pairedDevices) {
        mArrayAdapter.add(device.getName() + “\n” + device.getAddress());
    }
}


BluetoothDevice 객체에서 연결을 시작하기 위해 필요한 정보는 MAC address만 있으면 된다. 위의 예제에서 이 정보는 사용자에게 보여지는 ArrayAdapter의 일부분에 저장되어 있다. MAC 주소는 나중에 연결을 시작하기 위해 추출할수도 있다.

- 디바이스 discovery

디바이스 discovery를 시작하려면 startDiscovery()를 호출하면 된다. 이 과정은 비동기식이라 메소드를 호출하면 discovery가 성공적으로 시작되었나 결과를 알려주는 boolean값을 곧바로 돌려준다. Discovery과정은 보통 12초간의 inquiry scan후 발견된 각 디바이스에 대해 이름을 가져오기 위한 page scan으로 이루어진다.
어플리케이션은 각 발견된 디바이스에 대한 정보를 받기 위해  ACTION_FOUND 인텐트를 위한 BroadcastReceiver를 등록해야만 한다. 각 디바이스마다 시스템이 ACTION_FOUND 인텐트를 브로드캐스트 한다. 이 인텐트는 각각 BluetoothDevice와  BluetoothClass가 들어있는 EXTRA_DEVICE와 EXTRA_CLASS 필드를 전달한다. 예제로 디바이스가 발견되었을 때 브로드캐스트를 처리하는 핸들러를 등록하는 방법이다.

Final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            mArrayAdapter.add(device.getName() + “\n” + device.getAddress());
        }
    }
};

BroadcastReceiverIntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND;
registerReceiver(mReceiver, filter);


커넥션을 시작하기 위해 BluetoothDevice 객체에서 필요한 정보는 MAC 주소뿐이다. 이 예에서는 사용자에게 보여지는 ArrayAdapter의 일부분에 저장되어 있다.

주의: 디바이스  discovery를 수행하는건 블루투스 아답터에게 매우 부담이 큰 작업으로 매우 많은 리소스를 요구한다. 커넥션 할 디바이스를 찾았다면 커넥션을 시작하려고 시도하기 전에  cancelDiscovery()를 호출해서 discovery를 멈춰야 한다. 또한 이미 다른 디바이스와 커넥션 되어 있으면   discovery과정동안  대역폭이 활 떨어질수도 있기 때문에 커넥션 된  상태에서는 discovery를 하지 않아야 한다.                                                          
- Discoverable 활성화

다른 디바이스가 자신의 디바이스를 검색할 수 있도록 해 주려면 startActivityForResult(Intent, int)에 ACTION_REQUEST_DISCOVERABLE 액션 인텐트를 넣어 호출해주면 된다. 이 메소드를 호출하면 어플리케이션을 멈추지 않고 시스템 설정을 통해 discoverable 모드를 활성화 하도록 요청한다. 기본적으로 디바이스는 120초동안 discoverable 모드로 있게 된다. EXTRA_DISCOVERABLE_DURATION 인텐트 extra를 추가해서 이 시간을 바꿔줄 수 있다. (최대 300초)

Intent discoverableIntent = new Intent(BluetoothAdpater.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);




그림 2와 같은 다이얼로그가 떠서 사용자에게 디바이스를 discoverable 상태로 만들도록 허가할 것인지 묻는다. “Yes”를 선택하면 디바이스는 정해진 시간동안 discoverable상태가 된다. 액티비티는 result code에 디바이스가 discoverable되는 시간값이 들어가서 onActivityResult() 콜백을 호출받게 된다. 사용자가 “No”를 선택하거나 에러가 발생하면 result code는 Activity.RESULT_CANCELLED가 된다.
디바이스는 discoverable 시간동안 아무 반응이 없이 조용히 있는다. 만일 discoverable모드가 변경될 때 통보를 받고 싶으면 ACTION_SCAN_MODE_CHANGED 인텐트에 대한 BroadcastReceiver를 등록할 수 있다. 이 인텐트에는 각각 이전 스캔모드와 변경된 새 스캔모드가 들어있는 EXTRA_PREVIOUS_SCAN_MODE와 EXTRA_SCAN_MODE라는 extra 필드를 가지고 있다. 각 필드에 들어갈 수 있는 값은 SCAN_MODE_CONNECTABLE_DISCOVERABLE,  SCAN_MODE_CONNECTABLE,  SCAN_MODE_NONE으로 각각 discoverable 모드, discoverable은 아니지만 커넥션을 받아들일 수는 있는 모드, discoverable도 아니고 커넥션도 받아들일 수 없는 모드를 나타낸다.
원격 디바이스와 커넥션을 시작하고 싶은 경우는 자신의 디바이스를 discoverable모드로 만들 필요는 없다. 원격 디바이스가 커넥션을 시작하기 전에 디바이스를 발견해야만 하기 때문에 내 디바이스의 discoverable 모드를 활성화 시키는건 어플리케이션이 서버소켓을 사용해서 incoming 연결을 accept할 때만 필요하다.

- 디바이스 커넥션

두 디바이스에서 실행되는 어플리케이션간에 커넥션을 만들기 위해서는 서버쪽과 클라이언트쪽 메카니즘을 모두 구현해 줘야만 한다. 한 디바이스는 서버소켓을 열어줘야 하고 다른 디바이스가 서버 디바이스의 MAC 주소를 사용해서 커넥션을 시작해야만 하기 때문이다. 서버와 클라이언트는 같은 RFCOMM 채널에 각각 커넥션 된 BluetoothSocket을 가지고 있을 때 서로 커넥트 된 것으로 간주된다. 이 지점에서 각 디바이스는 입, 출력 스트림을 얻어 데이터 전송을 시작할 수 있다. 이 섹션에서는 두 디바이스간에 커넥션을 시작하는 방법에 대해서 설명한다.
서버 디바이스와 클라이언트 디바이스는 서로 다른 방법으로 필요한 BluetoothSocket을 얻는다. 서버는 incoming 연결이 accept될 때 소켓을 받게 된다. 클라이언트는 서버로의 RFCOMM 채널을 열 때 소켓을 받게 된다.



한가지 구현 테크닉은 두 디바이스를 모두 서버로 동작하도록 하기 위해 서버소켓을 열고 커넥션을 기다리는 것이다. 그러면 어느 디바이스건 클라이언트로서 상대 디바이스로 커넥션을 시작할 수 있다. 다른 방법으로는 한 디바이스는 명시적으로 서버로 지정해 서버소켓을 열고 커넥션을 기다리고 다른 디바이스는 단순히 클라이언트로 커넥션을 시작할 수 있다.
주) 두 디바이스가 미리 페어링 되어 있지 않으면 안드로이드 프레임웍은 그림 3과 같이 자동으로 페어링을 요구하는 다이얼로그를 띄워준다. 그러므로 디바이스를 커넥트 하려고 할 때 어플리케이션은 디바이스가 미리 페어링 되어 있는지 여부를 걱정할 필요가 없다. RFCOMM 커넥션 시도는 사용자가 성공적으로 페어링을 마치거나 페어링을 거부하거나 또는 어떤 이유로건 페어링이 실패할 때 까지 블럭된다.

서버로 동작
두 디바이스를 커넥트하려고 할 때 하나의 디바이스는 BluetoothServerSocket을 열어 서버로 동작해야만 한다. 서버소켓의 목적은 incoming 커넥션 요구를 기다리다 accept되면 커넥션 된 BluetoothSocket을 제공해 주는 것이다. BluetoothServerSocket에서 BluetoothSocket이 얻어지고 더 이상의 커넥션을 accept할 필요가 없으면  BluetoothServerSocket은 제거해도 된다.

UUID란...
Universally Unique IDentifier(UUID)는 유일하게 정보를 식별하는데 사용하기 위한 128비트 포맷의 표준화 된 문자열 ID이다. UUID의 포인트는 이 숫자가 충분히 크기 때문에 랜덤하게 아무 숫자나 골라도 다른 UUID들과 겹치지 않는다는 것이다. 여기서는 어플리케이션의 블루투스 서비스를 식별하는데 사용된다. 어플리케이션에 사용할 UUID를 얻기 위해서 인터넷상의 여러가지 랜덤 UUID 생성기중에 하나를 사용할 수 있고 fromString(String)으로 UUID를 초기화 하면 된다.
서버소켓을 셋업하고 연결을 accept하는 기본적인 절차이다.

1.listenUsingRfcommWithServiceRecord(String, UUID)를 호출해서 BluetoothServerSocket을 얻어온다.
스트링은 서비스에 대한 식별할 수 있는 이름으로 시스템이 디바이스의 새 SDP 데이터베이스 엔트리에 자동으로 그 이름을 기록한다. UUID 또한 SDP엔트리에 포함되어 클라이언트와 커넥션 agreement를 위한 기초가 된다. 즉 클라이언트가 디바이스와 커넥션하려고 시도할 때 커넥션하길 원하는 서비스를 유일하게 식별하는 UUID를 제공한다. 커넥션이 이뤄지기 위해서는 이 UUID가 일치해야만 한다.

2.accept()를 호출해서 커넥션 요구를 listen하기 시작한다.
이 메소드는 블럭킹 호출이다. 커넥션이 accept되거나 익셉션이 발생해야만 리턴된다. 리모트 디바이스가 listen하고 있는 서버소켓에 등록한 UUID와 일치하는 커넥션 요구에만 연결이 만들어진다. 성공하면 accept()는 커넥션 된 BluetoothSocket을 리턴한다.


3.더 이상의 추가 커넥션이 필요하지 않으면 close()를 호출한다.
이 메소드를 호출하면 서버소켓과 관련된 리소스를 release한다. 하지만 accept()가 리턴한 커넥션 된 BluetoothSocket은 닫지 않는다. TCP/IP와 달리 RFCOMM은 클라이언트에서 한번에 하나의 커넥션만 허용하기 때문에 대부분의 경우에 커넥션이 만들어지면 곧바로 BluetoothServerSocket을 close()하는게 합리적이다.

accept()는 블럭킹 메소드라 어플리케이션의 다른 동작을 막기 때문에 메인 액티비티 UI 스레드에서 호출하면 안된다. 일반적으로 새로운 스레드에서 BluetoothSocket이나 BluetoothServerSocket에 관련된 모든 작업을 처리하는게 합리적이다. 다른 스레드에서 BluetoothServerSocket의 accept() 같이 블럭킹 된 것을 취소하고 바로 리턴하도록 하려면 close()를 호출하면 된다. 그리고 BluetoothServerSocket 또는 BluetoothSocket의 모든 메소드는 스레드-세이프하다.

예제) incoming 연결을 accept하는 서버 컴포넌트를 위한 간단한 스레드
private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;
  
    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket,
        // because mmServerSocket is final
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app's UUID string, also used by the client code
            tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { }
        mmServerSocket = tmp;
    }
    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                break;
            }
            // If a connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in a separate thread)
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }
    /** Will cancel the listening socket, and cause the thread to finish */
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}
이 예제에서 한개의 incoming 커넥션만 필요하기 때문에 커넥션이 accept되고 BluetoothSocket이 얻어지자 마자 어플리케이션은 얻은 BluetoothSocket을 별도의 스레드로 보낸 다음 BluetoothServerSocket을 닫고 루프를 빠져나온다.
accept()가 BluetoothSocket을 리턴할 때 소켓은 이미 커넥션 되어 있기 때문에 따로 connect()를 호출할 필요는 없다. manageConnectedSocket()은 어플리케이션에서 데이터 전송을 위한 스레드를 시작하는 fictional 메소드이다.
일반적으로 incoming 커넥션을 listen하는게 끝나면 곧바로 BluetoothServerSocket을 닫아준다. 이 예제에서도 BluetoothSocket이 얻어지자 마자 close()를 호출했다. 또한 listen하고 있는 서버소켓을 멈출 필요가 있을 때 private BluetoothSocket을 닫을 수 있는 public 메소드를 스레드에서 제공하기도 한다.

클라이언트로 동작
원격 디바이스와 커넥션을 시작하려면 우선 원격 디바이스를 나타내는 BluetoothDevice 객체를 얻어야만 한다. 그리고 나면 BluetoothDevice를 사용해서 BluetoothSocket을 얻어 커넥션을 시작한다.

기본적인 절차이다.

1.BluetoothDevice를 사용해서 createRfcommSocketToServiceRecord(UUID)를 호출해서 BluetoothSocket을 얻는다.
이 호출은 BluetoothDevice에 연결하는 BluetoothSocket을 초기화한다. 여기서 건네지는 UUID는 서버 디바이스가 자신의 BluetoothServerSocket(listenUsingRfcommWithServiceRecord(String, UUID)를 사용해서)을 열었을 때 사용한 UUID와 일치해야만 한다. 동일한 UUID를 사용하는건 UUID스트링을 어플리케이션 코드에 하드코딩하고 서버와 클라이언트 양쪽 코드에서 그걸 참조하면 되는 간단한 문제이다.

2.connect()를 호출해서 연결을 시작한다.
시스템은 UUID를 매치하기 위해 원격 디바이스 SDP lookup을 수행한다. Lookup이 성공하고 원격 디바이스가 커넥션을 accept하면 연결동안 사용할 RFCOMM채널을 공유하고 connect()가 리턴한다. 이 메소드는 블럭킹 호출이다. 어떤 이유로건 커넥션이 실패하거나 connect() 메소드가 time out (약 12초)이 되면 exception을 발생한다.
connect()는 블럭킹 호출이기 때문에 이 커넥션 절차는 언제나 메인 액티비티 스레드와 독립된 별개의 스레드에서 수행되어야만 한다.
주: connect()를 호출할 때 디바이스는 언제나 디바이스 discovery를 수행하고 있지 않는지 확인해야만 한다. Discovery가 진행중이면 커넥션 시도는 확연히 느려져서 실패할 가능성이 커진다.

예제) Bluetooth 커넥션을 시작하는 스레드

private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;
    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket,
        // because mmSocket is final
        BluetoothSocket tmp = null;
        mmDevice = device;
        // Get a BluetoothSocket to connect with the given BluetoothDevice
        try {
            // MY_UUID is the app's UUID string, also used by the server code
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) { }
        mmSocket = tmp;
    }

    public void run() {
        // Cancel discovery because it will slow down the connection
        mAdapter.cancelDiscovery();
        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out
            try {
                mmSocket.close();
            } catch (IOException closeException) { }
            return;
        }
        // Do work to manage the connection (in a separate thread)
        manageConnectedSocket(mmSocket);
    }
    /** Will cancel an in-progress connection, and close the socket */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}

cancelDiscovery()는 커넥션이 만들어지기 전에 호출되는걸 볼 수 있다. 커넥션이 되기 전에라도 언제나 호출할 수 있고 실제적으로 실행 여부를 확인하지 않고 호출해도 안전하다. (하지만 그래도 상태를 확인하고 싶으면 isDiscovering()을 사용하면 된다.) manageConnectedSocket()은 데이터 전송을 위한 스레드를 시작하는 어플리케이션에 있는 fictional 메소드이다.
BluetoothSocket이 끝나면 clean up을 위해 언제나 close()를 호출해줘야 한다. 이 메소드를 호출해 줌으로서 곧바로 커넥션 된 소켓을 닫고 내부 리소스를 clean up 하게 된다.

- 연결 관리

두 디바이스를 성공적으로 커넥션하게 되면 각 디바이스는 커넥션 된 BluetoothSocket을 가지게 된다. 이 소켓을 통해 디바이스간에 데이터를 교환할 수 있게 된다. BluetoothSocket을 사용해서 임의의 데이터를 전송하기 위한 일반적 절차는 매우 간단하다.

1.각각 getInputStream()과 getOutputStream()을 사용해 소켓을 통한 전송을 처리할 InputStream과 OutputStream을 얻는다.
2.read(byte[])와 write(byte[])를 사용해서 데이터를 읽고 쓴다.

물론 implementation을 위해 고려해야 할 세부사항들이 있다. 먼저 무엇보다 모든 읽고 쓰기를 위한 별도의 스레드를 사용해야 한다. 이건 read(byte[])와 write(byte[])는 모두 블럭킹 호출이기 때문에 매우 중요하다. read(byte[])는 스트림에서 무언가 읽을게 있을때까지 블럭되어 있는다. write(byte[])는 일반적으로는 블럭되지 않지만 원격 디바이스가 충분히 빠르게 read(byte[])를 호출하지 않아 버퍼가 꽉 차는 경우 플로우 컨트롤을 위해 블럭될수도 있다. 그러므로 스레드의 메인 루프는 InputStream으로부터 읽기 전용으로 사용되어야 한다. 스레드의 분리된 public 메소드가 OutputStream으로 쓰기를 시작하도록 사용될 수 있다.

예제)
private class ConnectedThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket) {
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut = null;
        // Get the input and output streams, using temp objects because
        // member streams are final
        try {
            tmpIn = socket.getInputStream();
            tmpOut = socket.getOutputStream();
        } catch (IOException e) { }
        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run() {
        byte[] buffer = new byte[1024];    // buffer store for the stream
        int bytes; // bytes returned from read()
        // Keep listening to the InputStream until an exception occurs
        while (true) {
            try {
                // Read from the InputStream
                bytes = mmInStream.read(buffer);
                // Send the obtained bytes to the UI Activity
                mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                break;
            }
        }
    }
    /* Call this from the main Activity to send data to the remote device */
    public void write(byte[] bytes) {
        try {
            mmOutStream.write(bytes);
        } catch (IOException e) { }
    }

    /* Call this from the main Activity to shutdown the connection */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}
컨스트럭터가 필요한 스트림을 얻고 한번 실행되면 스레드는 InputStream을 통해 들어오는 데이터를 기다린다. read(byte[])가 스트림에서의 데이터를 리턴하면 그 데이터는 부모 클래스의 Handler 멤버를 사용해 메인 액티비티로 보내진다. 그리고 다시 스트림에서 데이터를 읽기 위해 기다리기 위해 돌아간다. Outgoing 데이터를 보내는건 단순히 메인 액티비티에서 스레드의 write() 메소드를 호출해 전송할 데이터를 전달해주면 된다.

스레드의 cancel() 메소드는 아무때나 BluetoothSocket을 닫아 connection을 멈출 수 있기 때문에 중요하다. 이 메소드는 블루투스 connection 사용이 끝나면 언제나 호출되어야 한다.

-----

Bluetooth programming in Android 1/2

Bluetooth programming in Android 2/2

두개의 관련 포스트가 추가되었습니다.

댓글 63개:

  1. 웹서핑하다가 이런 문서를 발견하다니



    형님 같은 분들이야 말로 이 삭막한 아이티 업계의 단비이십니다.



    잘 보고 갑니다!!!!



    만수무강하세요

    답글삭제
  2. if (!mBluetoothAdapter.isEnabled())에서 계속

    응용프로그램이 예상치 않게 중지되었다고 뜨는데 왜 그런지 혹시 아시나요?ㅜㅜ

    답글삭제
    답글
    1. BluetoothAdapter mBlueToothAdapter = BluetoothAdapter.getDefaultAdapter();
      이렇게 할당을 해주셔야 하는데, 이 부분이 잘 못되었을것 같은데요.

      삭제
  3. @tatchi - 2010/06/03 15:27
    일단 LogCat에서 에러메시지를 확인해 보셔야 할거 같습니다.

    답글삭제
  4. Nice brief and this mail helped me alot in my college assignement. Thanks you seeking your information.

    답글삭제
  5. 블루투스로 프로그램 만들고 있는데 혹시 특정 기계로



    안드로이드 이용해서 데이터를 받고싶은데 페어링은 되는데



    접속이 안되네요.. 안드로이드를 서버로 만들고 특정기계를 클라이언트



    로 만들어야 되나여?

    답글삭제
  6. @rock - 2010/06/24 22:03
    정확한 상황을 몰라 뭐라 말씀드리기 힘든데 일단 logcat 을 캡춰해 올려주시면 한번 확인해 보겠습니다.

    그리고 기본적으로 어느쪽에서 접속을 시도하는가 기준으로 접속을 시도하는쪽이 클라이언트, 접속을 기다리고 있는 쪽이 서버입니다. 아마도 특정 장비는 계속 켜져있고 안드로이드 폰으로 접속해서 데이터를 받아오는 형태면 특별한 이유가 없는 한 폰이 클라이언트가 되는게 맞을거 같습니다.

    답글삭제
  7. @tatchi - 2010/06/03 15:27
    혹시 다음에 볼사람을 위해 글남겨요..

    if (!mBluetoothAdapter.isEnabled())

    이부분에서 "응용프로그램이 예상치 않게 중지되었다 "라고 뜨는건 AndroidManifest.xml파일에 BLUETOOTH_ADMIN 선언을 안했거나 했어도 위치가 잘못지정되어있기때문입니다.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="di.test"

    android:versionCode="1"

    android:versionName="1.0">

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

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

    <application ......

    보시는것처럼 application선언위에다가 넣어주시면 해결될듯....

    답글삭제
  8. 조금늦었지만 질문올려봅니다.

    저기있는 코드들을 어디어디다가 올려놔야하나요?ㅠ

    이클립스를 다뤄본적이없어서 다루려니까 너무힘드네요..ㅠㅠ

    답글삭제
  9. @초보 - 2010/07/26 11:11
    어디다 올려야 된다...는건 답해드리기 힘든 질문이군요.

    저 부분은 프로그램 전체 코드가 아니고 그 중에 블투 통신을 담당하는 코드 부분이라 만드시는 프로그램의 일부로 들어가야 하는거지 저런식의 막연한 질문에는 제가 뭐라 답변을 드릴수가 없습니다.

    답글삭제
  10. 비아토르님 글 읽고 연구해서 우여곡절 끝에 블루투스 접속해서



    데이터 가져다 쓰고는 다 했습니다ㅎㅎ



    마지막으로 종료를 해야되는데 해당 엑티브티에서만



    블루투스를 종료할려면 어떻게 해야되나여?



    프로그램 자체를 죽으면 블루투스가 접속을 종료 하는데 프로세스 돌구



    있는 상태에서 블루투스만 종료 socket.close() 이런거



    쓰면 종료 되지않나여??

    답글삭제
  11. @rock - 2010/07/27 16:59
    블투 종료라는게 정확하게 어떤걸 말씀하시나 잘 모르겠습니다만 말씀하신대로 소켓을 close하면 현재 커넥션이 종료되게 되죠. 위의 예제 코드에서 이 부분입니다.



    try {

    mmSocket.close();

    } catch (IOException e) { }

    답글삭제
  12. 안녕하세요.. 정말 도움 되는 글이네요..^^감사합니다.



    소스를 보던 중 계속 의문이 남는게 있네요.



    BluetoothAdapter에 접근하다보면 IBluetooth 객체가 나옵니다.



    커널과 Android 최신버전 소스를 받아바도 IBluetooth는 인스턴스 선언만 되있고 정의 되 있는 부분을 못찼겠네요.ㅜㅜ



    어떤거지 잘 이해도 안되고요..ㅜ 번거로우시겠지만 조언 부탁드려여..~;

    답글삭제
  13. @레이 - 2010/08/17 11:52
    IBluetooth는 시스템 프라이빗 API라 안드로이드가 블루투스 서비스와 통신할때 사용하는거지 어플 개발자가 직접 억세스 할 일은 없습니다.

    답글삭제
  14. 정말 잘봤습니다~!! 감사해요ㅡ이해가 잘되네요

    근대 상대방이 bluetoochchat을 키고 있어야만 연결이 되고 프로그램을 끄고 있으면 연결이 안되던대 상대방이 bluetoochchat을 끄고 있으도 상대방에게 요청이 들어온걸 알릴려면 어떤식으로 바까야 할까요??

    답글삭제
  15. @엔귤 - 2010/08/18 11:16
    아마도 Samples 코드에 들어있는 BluetoothChat을 말씀하시는거 같은데 어느 경우에도 서버역활을 하는 쪽에서는 블투소켓을 열고 있어야만 합니다.

    블투 관련 부분을 Activity로 하지 말고 Service로 만들어 백그라운드에서 소켓을 열고 기다리게 해 주시면 됩니다. 그리고 전화기가 켜져 있을때는 언제나 요청을 받을 수 있게 해 주시려면 서비스를 제 이전 포스트를 참조해서 안드로이드가 부팅이 되자 마자 실행되게 해 주시면 되구요.

    답글삭제
  16. 답변정말 감사해요 ^^ 근대 백그라운드에서 소켓열고 기다리고 있으면 핸드폰 기기를 사용하는데 지장은 없는건가요?? 그리고 자기장치가 검색가능하게 300초까지 되던대 이게 서버소켓열어놓는 개념인가요??아직블루투스초보라..;ㅋ

    답글삭제
  17. @엔귤 - 2010/08/19 13:10
    백그라운드에서 소켓 열어놓고 기다리는게 휴대폰 사용에 별다른 지장은 없습니다.

    그리고 discoverable 모드는 서로 pairing되지 않은 디바이스가 나를 찾을 수 있도록 해 주는 모드입니다. 일단 두 기기간 pairing이 되어 있으면 discoverable 모드가 아니어도 상관 없습니다. 그리고 이 모드는 서버 소켓 열고 기다리는것과는 또 다른 이야기입니다.

    답글삭제
  18. 아정말유용한글이네요~ 아 근대 제가 만든프로그램은 블루투스상대를 스캔하는거라든지 검색가능하게 하는 기능을 버튼으로 만들어놨는데 한번 실행했다가 종료하면 버튼이벤트가 아예먹히질 않네요..먼가 해제를 안해줘서 그러는걸까요?? 제가 스태틱으로 블루수트아댑터같은 객체나 다른것들을 많이 선언했는데 그것과 관련이 있을까요?? 어렵네요 ㅠ

    답글삭제
  19. 안녕하세요. 저도 이번에 안드로이드 에서 블루투스 관련시켜 프로젝트를 하나 진행하려고 하는데 좋은 글 이네요.



    저.. 그리고 저도 하나 궁금한 사항이 있는데.. 일반 블루투스가 달린 기계(컴퓨터나, 핸드셋이 아님)와 안드로이드 블루투스랑 연결이 가능한가요? 그 일반기계에서는 블루투스를 통해서 자기의 데이터값을 송신함.



    만약에 연결된다면 핸드폰쪽에서 값을 수신하다가 송신도 할수 있는지

    궁금합니다.

    답글삭제
  20. @유노 - 2010/08/30 15:10
    물론 가능합니다. 현재 SPP profile만 지원하니까 컴퓨터나 다른 블투 디바이스가 SPP profile을 지원하면 블투를 통해 가상 시리얼포트를 만들어 시리얼 통신을 할 수 있습니다.



    http://www.amarino-toolkit.net/



    여기를 보시면 블투 모듈을 장착한 arduino 보드와 통신을 할 수 있는 프레임웍도 있습니다.

    답글삭제
  21. @비아토르 - 2010/08/30 15:40
    정말 정말 감사합니다. ㅠㅠ 되는지 안되는지 정말 궁금해 했는데 이렇게 된다는 답변을 보게되니 도전의지가 살아 나네요. 답변과 참조 링크 감사합니다. ^^

    답글삭제
  22. 혹시 디바이스끼리 블루투스 전송에서 이미지,동영상,mp3 파일이 있을때 한번에 일괄처리로 전송이 가능할까요?

    이미지 몇개 동영상 몇개 mp3몇개 이렇게 총 여러개를 전송하는방식이요.. 총 선택된 파일이 10개라고 하면 1/10 전송되고 2/10 전송되고 이렇게 차례로요. 제가 지금 그쪽을 만들고 있는데 블루투스를 잘 모르니까 될지 안될지 감이 안잡혀서요. 그리고 동영상같은건 700M가 넘는데 그런 대용량파일도 전송이 될까요?

    답글삭제
  23. @나이스가이 - 2010/08/31 16:40
    커넥션만 연결되면 그곳으로 무엇을 보내는가는 프로그래밍 코드를 어떻게 짜면 되냐 문제지 안될 이유는 전혀 없습니다.

    다만 전송속도가 그렇게 빠르지 않기 때문에 700메가를 넘는 파일을 전송하기에는 그렇게까지 적합해 보이지는 않습니다.

    답글삭제
  24. 비아토르님..죄송한데요.

    혹시 블루투스 파일전송 간단한 예제 파일 가지고 계신거 있으신가요?..

    혹시나 해서요.ㅜㅜ; 좀 막막해서요.

    답글삭제
  25. @나이스가이 - 2010/08/31 17:49
    실제 예제코드는 안드로이드 SDK의 samples/android-8/BluetoothChat 을 참조하시면 될겁니다. 거기에서 텍스트 메시지 대신에 파일올 열어 보내면 됩니다.

    답글삭제
  26. BluetoothChat 봤는데..이해가 잘 안되서요..

    실질적으로 /sdcard/안의 경로에 파일이 있는데 경로로 파일을 보내야 하는지 아니면 뭘로 보내야 하는지 잘모르겠어서요..

    죄송하지만 간단한 블루투스파일전송 예제 파일좀 공유해주실수 없으신지요?;; 부탁드리겠습니다.. 너무 초보라서 어디다 뭘 써야 할지도 잘모르겠네요...

    답글삭제
  27. 안녕하세요

    너무 좋은 글이예요ㅋ

    저도 안드로이드 블루투스로 프로그램하려고 하는데

    일단 연결소켓을 형성한후에 소켓을 닫기 전에는 프로그램도중에 언제나 데이터를 받을수 있는 것이지요?



    근데 안드로이드에서 블루투스 외에 그냥 pc코넥트선으로 시리얼통신해서 프로그램도중에 데이터를 받는 방법은 없을까요?

    답글삭제
  28. 안녕하세요. 좋은 글 읽었습니다.



    한가지 궁금한 사항이 있어서 글을 남겨요.



    이거 에뮬레이터에서는 안돌아 가나요 ?



    제가 자료를 찾아 보니까 그런거 같아서요.

    답글삭제
  29. @hi - 2010/09/08 22:41
    예. 현재 에뮬레이터에서는 블루투스를 지원하지 않습니다.

    답글삭제
  30. @굿걸 - 2010/09/08 13:48
    당연히 소켓이 열려있는 동안은 데이터 교환이 가능합니다.



    그리고 HTC의 초기 모델들은 자체 커넥터를 사용할 때 거기에는 시리얼 신호가 있었는데 현재는 USB케이블을 통해 가상 시리얼포트를 제공해주는 안드로이드 폰은 아직 못봤습니다.

    답글삭제
  31. Bluetooth Server Socket 과 Bluetooth Socket 클래스에 대한 설명이 뒤바뀌어 있네요.;; 이상하다 싶어서 원문 찾아봤거든요...

    그리고 원문 거의 그대로 번역해 두신 건데 원문 링크가 없네요 ^^;; 혹시 다른 분들을 위해 링크 드립니다. http://developer.android.com/guide/topics/wireless/bluetooth.html

    감사합니다.

    답글삭제
  32. @lobotomi - 2010/09/09 13:26
    말씀하신대로 두개가 순서가 바뀌어 있었네요. 찾아주셔서 감사합니다. 바로 수정했습니다.

    답글삭제
  33. sdk 2.1 디바이스와 sdk 2.2 device 로 bluetoothchat 프로그램 실행 중에



    java.io.iOException:service discovry failed 오류가 났는데 왜 나는지 모르겠



    네요;;; 아시는 분 좀 도와주세요 ㅋㅋ

    답글삭제
  34. 블루투스 검색 할때요.



    휴대기기만 보이게 할수 있나요?



    블루투스이어폰같은건 안보이게 할려구요.

    답글삭제
  35. @나이스가이 - 2010/09/30 10:50
    Device Discovery할 때 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);로 발견된 디바이스를 가져온 다음 device.getBluetoothClass() 로 Bluetooth Class를 읽어 타입이 헤드셋이면 mAdapter에 추가하지 않으면 되겠죠.



    Bluetooth Class에 관한건 http://developer.android.com/reference/android/bluetooth/BluetoothClass.html 를 참조하세요.

    답글삭제
  36. 블루투스에서 커넥션을 하게 되면 자동을 페어링이 되어있는지 안되어있는지 확인해서 페어링이 안되어 있으면 페어링 하는 다이얼 로그가 뜨게 되어있습니다.



    그런데 .. 페어링만 하는 API 함수가 BOND_BONDING 인거 같은데..



    어떻게 사용을 해야 커넥션 하기 전에 프로그래머가 페어링 창을 뛰울수 있는지 궁금합니다.

    답글삭제
  37. 좋은정보 감사합니다....

    앞으로도 님 좋은일 가득하시길....

    그럼 수고하시고요

    답글삭제
  38. 우연히 봤는데 정말 머리에 쏙쏙 들어오는 글이네요!!

    감사합니다. 잘봤습니다.

    답글삭제
  39. 안녕하세요.
    먼저 좋은 글 감사드립니다.

    저도 이번을 계기로 안드로이드 블루투스 API를 이용하여 프로젝트를 하고자 합니다. 위에 언급된 SDP profile만 지원가능하다고 하셨는데 그러면 HSP나 HFP는 아직 안되는 건가요? HFP 지원되는 디바이스와 안드로이드를 연결하고 싶어서 그렇습니다.

    답글삭제
  40. 혹시요...
    PC에서 유선 인터넷 연결되어 있고 블투로 핸폰에 인터넷 뿌려주는 프로그램 있을까요?

    답글삭제
  41. 안녕하세요. 항상 Bluetooth 가 discoverable하게 할 수는 없을가요..최대 300초 제한이 있는걸로 알고 있습니다만..

    이 제한을 풀 방법은 없는건가요...
    구글링을 찾아봐도 루팅해서 새로운 Rom으로 까는 방법이 있다곤 하는데....OTL

    답글삭제
  42. 표준화 된 방법은 없는거같고 모델에 따라 편법은 있는거 같습니다.

    http://blag.tsukasa.net.au/2009/08/25/always-discoverable-on-android/

    이 링크를 보시면 /system/etc/bluez 디렉토리에 있는 config 파일을 수정하는 방법을 사용하고 있습니다.

    답글삭제
  43. 안녕하십니까. 글너무 잘읽었습니다.

    대부분 구현을 다해였는데 혹시 블루투스에 기계가 다중으로 접속이 가능한것입니까?
    찾아보았는데 가능한지 잘모르겠더라고;;

    휴대폰 여러대를 블루투스로 연결하여 뭔가를 하려합니다.
    혹시 가능하면 어떻게 구현해야될지 한수 부탁드립니다.

    답글삭제
  44. 잘보고 따라하고 있습니다.
    그런데 그 연결하는 부분이 잘 안되서...

    accept()는 블럭킹 메소드라 어플리케이션의 다른 동작을 막기 때문에 메인 액티비티 UI 스레드에서 호출하면 안된다. 일반적으로 새로운 스레드에서 BluetoothSocket이나 BluetoothServerSocket에 관련된 모든 작업을 처리하는게 합리적이다

    이말에서 그럼 main에서 작성하지말고 또 다른 클래스파일을 생성해서 그곳에 새로 작성하면되는것인가요?
    자바를 발만담가논상태라,,이런말도 잘 이해가 안가네요..
    어쨌든 클래스를 새로 생성해서 만들어서 작성하였는데요..
    에러가.............ㅠㅠ
    저 연결하는부분쪽!을 다 하나씩 클래스로 만들어서 사용하면되는건가요??

    답글삭제
  45. 안녕하세요 BT로 App을 만들려다 검색했는데 큰 도움이 되었네요 ^^ 한가지 의문점이 BT Device를 검색하기 위해 BroadCast를 등록해서 onReceive에서 처리하는데 매번 검색할 때마다 Device를 잘 못찾아냅니다. 폰이 바로 옆에 있는데도 잘 찾지 못하는데 어떻게 이 문제를 접근해야할지 모르겠네요 ... ㅠ

    답글삭제
  46. 블루투스 통신해서 파일보내기 하고 잇는데요
    파일이 가긴 가다가 수신측에서 갑자기 shutdown socket 이라고 나오네요 ㅜㅜ 그래서 가다가 항상 해당 버퍼 크기 만큼 남겨놓고 종료가 되버리니 미칠지경입니다.

    답글삭제
    답글
    1. 보내는 곳과 받는곳의 데이터 싱크가 안맞아서 그런것같아요 저도 그랬었는데 수신측 소스를 고치니 되네요..ㅎ;;

      삭제
  47. Nxt랑 안드로이드랑 블루투스통신하려면 어떻게 하죠?

    답글삭제
  48. 이 프로그램을 통해서 랜덤 숫자를 전송하는 프로그램을 만들려고하는데
    어느쪽에 손을 대야할 지 몰르겠습니다 일단 4자리 고정숫자를 보내고싶은데 어디를 만져야할지 답변 부탁드립니다.

    답글삭제
  49. 블루투스를 통해서 랜덤 비밀번호를 전송하는 어플을 만드려고하는데요
    지금 오픈소스를 연구하구있는데, 안드로이드 초보라서요
    어디쪽을 건드려야 번호가 전송되고
    일단 고정 4자리를 보내고싶은데 어떻게 만져야할지 도움좀 부탁드립니다.

    답글삭제
  50. 질문있습니다.~
    안드로이드 단말기 + 블르투스모듈과 MCU를 이용한 RC카
    가 있는 상황일때
    구지 server와 client로 나누고자 하면 어떤게 서버고 클라일까요??
    안드로이드 단말기에서 블루투스서버소켓 클래스를 통해 리쓴하는 상태며
    블루투스모듈러가 장착된 디바이스와 커넥크가 됬을시 통신을 하며
    그 요구또한 안드로이드 단말기에서 터치 버튼을 통해 rc카 명령을 내린다면

    안드로이드 단말기가 서버와 클라이언트의 역할을 둘다 하는거 아닌가요???
    아니면 .. 서버와 클라이언트로 둘을 나누기에는 애매한건가요??

    답글삭제
  51. @달콤스 으음 글이 오래되긴했는데 제 생각은 접속시도하는 디바이스가 클라이언트이고 수락하는 디바이스가 서버이지싶네요

    답글삭제
  52. 안드로이드초보2012년 11월 10일 오전 3:33

    안녕하세요 올려주신 글 매우 잘 봤습니다. 아직 제가 초보이다 보니 배우고는 있지만 쉽지가 않은 부분이 있어서 질문을 드립니다. 일단 저는 안드로이드 단말기에서 블루투스 모듈로 일방적으로 데이터를 주는 용도로 사용을 할 건데.. 그러면 안드로이드 단말기가 클라이언트가 되고 블루투스 모듈이 서버가 되는 것이 맞죠..?
    그리고 제가 개발하려고 하는 앱의 경우 블루투스 연결 후 액티비티 간 이동이 잦은데 그럴 경우엔 블루투스를 서비스로 실행시키는 것이 바람직한가요??
    답변 꼭 부탁드려요 ㅠㅠ

    답글삭제
  53. 안녕하세요
    제가 프로젝트로 블루투스를 이용해서 모바일로 PC화면을 원격제어를 할려고하는데,제가 아직 초보라서 어느 부분부터 손을 대야할지
    어떤 부분을 공부해야할지 고민입니다.
    PC화면을 실시간으로 모바일로 가져올려면 어떻게 해야하죠? ㅜ^ㅠㅠ

    답글삭제
  54. 퍼갑니다. 좋네요. 자료..

    답글삭제
  55. 안녕하세요. 좋은 글 잘 읽었습니다 ! 다름이 아니라 제가 외부의 센서 값들을 받아서 안드로이드 창에 띄우고 싶은데요.. 제가 초보라 겨우겨우 따라서 블루투스 통신까지는 성공을 했습니다. 그리고 버튼을 여러개 만들었는데 그 버튼 하나하나 마다 숫자를 외부 센서가 있는 보드에 보내서 그 숫자에 맞는 센서들 값을 받아서 창에 띄우고 싶은데요... 너무 막막해서 질문드립니다... ㅜㅜ lhp3877@naver.com 연락주시면 감사하겠습니다......

    답글삭제
  56. device 연결할떄 manageConnectedSocket 함수가 정의되어있지않아서
    빨간밑줄이 그이는데 어떻게 해결해야되는건가요>??

    답글삭제
    답글
    1. 그건 위에 설명해 놓은것처럼 가상의 메소드입니다. 자신이 원하는 동작을 하도록 그 메소드를 만들어줘야 하죠.

      삭제
  57. 특정 기기(ex..블루투스 프린터 A)랑만 블루투스 연결을 하고 싶을 때는 어떻게 해야할까요..??

    답글삭제
  58. 자동으로 블루투스 커넥트를 하는 어플을 만들고 싶은대요, 스마트폰에 페어링하려는 블루투스 모듈을 비밀번호랑 같이 등록을하면, 이게 페어링이 된건가요?? 여기서 커넥트를 하기위해서 따로 어플리케이션이 필요한거구요? 블루투스모듈 이름과 맥주소를 알면 커넥트가 가능하고 페어링 가능 목록을 받아오는건 너무 배터리 소모가 큰거같고 그냥 몇초단위로 커넥트만 시도하면 되는데 어떻게 해야할지 조금만 도와주실수 있으실까요

    답글삭제
  59. buffer로 읽고 핸들러로 보내서 화면에 출력해 보면 글자가 앞쪽으로 잘려서 나옵니다. 원격 디바이스에서 보낸 글자를 통으로 캐치하지 못하는것 같은데 어디를 체크해 봐야 할까요?

    답글삭제