2014년 6월 4일 수요일

Bluetooth Programming in Android (1/2)

안드로이드 앱에서 블루투스를 사용하려면 각각 기능에 해당하는 permission이 필요하다. Permission은 안드로이드에게 앱이 어떤 기능을 필요로 하는지 알려주는 역할을 한다. 앱을 설치할 때 어떤 permission을 필요로 하는지 알려주고 허가할 것인지 물어보는 창이 열리는걸 본 적이 있을 것이다.

앱이 어떤  permission을 사용하는지 알려주기 위해 필요한 작업은 다음과 같다.

먼저 프로젝트의 AndroidManifest.xml 파일을 연다.


이 파일을 열면 아래와 같은 화면이 나오는데 'Permissions' 탭을 선택한 다음 오른쪽의 Add... 버튼을 누른다.



버튼을 누르면 나오는 화면에서 Uses Permission을 선택한다.



그 다음 android.permissions.BLUETOOTH를 선택하고 파일을 저장한다.



BluetoothAdapter


BluetoothAdapter는 폰의 블루투스 모듈을 사용하기 위한 오브젝트이다.

스태틱 메소드인 getBluetoothAdapter()를 호출해서 오브젝트의 디폴트 인스턴스를 가져온다.

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

만일 폰에 블루투스 모듈이 없으면 null을 리턴한다.  이 경우 Toast를 사용해 에러메시지를 표시하고 앱을 종료한다.

Toast.makeText(this, "This app requires a bluetooth capable phone", Toast.LENGTH_SHORT).show();
finish();

블루투스 모듈에 대한 인스턴스를 가져온 다음 모듈이 활성화 되어 있는지 확인해야만 한다. 모듈 활성화 여부를 확인하는데 isEnabled() 메소드를 사용한다.

mBluetoothAdapter.isEnabled();

모듈이 활성화 되어 있지 않으면 Intent를 사용해 사용자에게 활성화 시키도록 요청할 수 있다. 먼저 request를 식별하기 위한 상수를 정의한 다음 새 Intent를 만들어 OS에 보낸다.

private final int REQUEST_ENABLE_BT = 1;
...
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

앱은 백그라운드로 들어가고 아래와 같이 사용자에게 앱이 블루투스를 활성화 시키도록 허가할 것인지 묻는 메시지 팝업이 열린다.



사용자가 request를 허가(또는 거부)하면 안드로이드는 앱의 onActivityResult 메소드를 호출해서 request의 허가/거부를 확인할 수 있다.

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == REQUEST_ENABLE_BT)
    if (resultCode == RESULT_OK) listPairedDevices();

페어링 된 디바이스


첫번째 예제에서 앱은 페어링 된 디바이스를 화면에 표시한다. 블루투스 디바이스는 연결해서 사용하기 전에 먼저 페어링 되어야만 한다.

getBondedDevices() 메소드는 BluetoothDevice 오브젝트 집합을 리턴한다.

Set pairedDevices = mBluetoothAdapter.getBondedDevices();

각 디바이스는 이름과 (서로 다른) 주소를 가진다. 루프를 사용해 TextArea에 페어링 된 디바이스들을 표시한다.

for (BluetoothDevice pairedDevice : pairedDevices) {
  textView2.append(Html.fromHtml("<strong>" + pairedDevice.getName() + "</strong>"));
  textView2.append(" (" + pairedDevice.getAddress() + ")\n");
}

아래가 앱의 스크린 캡춰이다.



여기서 사용한 소스코드는 전부 저자의 github에서 다운받을 수 있다.

 Discovery 와 Permissions


새 블루투스 디바이스를 찾기 위한 discovery 기능을 사용하려면 새로운 permission(BLUETOOTH_ADMIN)이 필요하다.



새 검색을 시작하려면 BluetoothAdapter 오브젝트의 startDiscovery 메소드를 호출한다.

mBluetoothAdapter.startDiscovery();

이 메소드는 비동기 메소드이다. 즉 이 메소드를 호출하면 안드로이드에게 새 검색을 시작하라는 요청을 넘겨주고 바로 리턴한다. 새 디바이스가 발견되거나 discovery가 끝난걸 앱에게 알려주기 위해서는 BroadcastReceiver를 사용해야만 한다.

Receiver 와 IntentFilter


앱은 안드로이드에게 어떤 이벤트가 발생한 걸 통보해 달라고 요청할 수도 있다. 예를 들어 문자메시지가 오거나 배터리가 거의 없을 때 같은 경우가 될 수 있다. 이런 이벤트들은 broadcast 메시지를 사용해 통보된다. 그러므로 앱은 BroadcastReceiver 오브젝트를 통해 메시지를 받을 수 있다.

BroadcastReceiver 오브젝트가 인스턴스화 되고 나면 OS에게 어떤 notification을 받을건지 알려줘야만 한다. 받기를 원하는 각 이벤트마다 필터(IntentFilter)를 만든다. 그리고 registerReceiver() 메소드를 사용해 안드로이드가 IntentFilter에 지정되어 있는 이벤트를 주어진 BroadcastReceiver에게 통보하도록 요구한다.



이 예제에서 우리는 2개의 이벤트를 모니터링 할 필요가 있다.
  • Bluetooth.ACTION_FOUND - 새 디바이스가 발견될 때
  • Bluetooth.ACTION_DISCOVERY_FINISHED - discovery가 끝날 때
해당하는 IntentFilter를 만들어 receiver를 등록한다.

IntentFilter deviceFoundFilter = new IntentFilter(Bluetooth.ACTION_FOUND);
IntentFilter discoveryFinishedFilter = new IntentFilter(Bluetooth.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver, deviceFoundFilter);
registerReceiver(mReceiver, discoveryFinishedFilter);

BraodcastReceiver는 onReceive() 메소드를 꼭 만들어줘야만 한다. 이 메소드는 새 이벤트가 발생한 걸 알려주기 위해 안드로이드에 의해 호출된다.

mReceiver = new BroadcastReceiver() {
  public void onReceive(Context context, Intent intent) {
    String action = intent.getAction();

action 스트링을 보면 어떤 이벤트가 발생했는지 알 수 있다.

if (BluetoothDevice.ACTION_FOUND.equals(action)) {
  // show the new device
}
if (BluetoothDevice.ACTION_DISCOVERY_FINISHED).equals(action)) {
  // enable the SCAN button
}


이 예제를 위한 앱은 매우 간단하다. 버튼을 누르면 검색을 시작하고 장치가 발견되면 리스트에 장치의 이름과 주소를 추가한다. 역시 소스코드는 저자의 github에서 다운받으면 된다.


Detect a state change


안드로이드는 멀티태스킹 OS이다. 당신의 앱이 실행되는 동안 다른 앱이나 사용자가 블루투스 모듈의 상태를 바꿔 놓을수도 있다. 예를 들어 모듈을 비활성화 시켜 버릴 수 있다. 그렇기 때문에 안드로이드에게 모듈의 상태에 어떤 변화가 생기면 그걸 통보하도록 요청할 수 있다.

먼저 OS에서의 notification을 받을 BroadcastReceiver를 만든다.

mReceiver = new BroadcastReceiver() {

이 receiver의 onReceive() 메소드에서 들어 온 메시지가 블루투스 모듈의 상태변화에 관한 것인지 확인한다.

public void onReceive(Context context, Intent intent) {
  String action = intent.getAction();
  if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {

상태변화에 관한 것이 맞다면, Intent의 extra parameter에서 변화된 현 상태를 가져올 수 있다. state는 int값이므로 getIntExtra 메소드를 사용해야만 하고 메소드를 호출할 때 파라메타의 이름과 디폴트값을 지정해주면 된다. 디폴트값은 지정한 이름의 파라메타가 없을 때 리턴할 값이다.

int actualState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);

그리고 receiver를 등록하는걸 잊으면 안된다.

IntentFilter stateChangedFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(mReceiver, stateChangedFilter);

블루투스 모듈을 위한 4개의 상태가 정의되어 있다.

  • BluetoothAdapter.STATE_ON
  • BluetoothAdapter.STATE_OFF
  • BluetoothAdapter.STATE_TURNING_ON
  • BluetoothAdapter.STATE_TURNING_OFF
위에 설명한 걸 정리한 앱은 다음과 같다. 앱은 화면에 텍스트와 아이콘으로 현재 상태를 나타내고 상태가 변경될 때 마다 내용을 자동으로 업데이트 한다. 아래 데모 앱의 소스는 저자의 github에서 다운받으면 된다.







* 이 글은 Luca Dentella의 튜토리얼 시리즈 Android e Bluetooth 를 저자의 승락을 받고 번역한 글입니다. 흔쾌히 허락해 준 저자 Luca에게 감사드리며...



Un enorme grazie a Luca~





댓글 4개:

  1. 정말 많은 도움 받고 있습니다. 감사합니다!!
    저는 pc와 안드로이드 앱을 블루투스로 연결하여 pc를 제어할 수 있는 앱을 개발 중인데요, 코드를 실행시키면 에러는 없지만 앱이 실행되지 않고 바로 중지됩니다. 왜 그러는 것인지 조언해주시면 감사드리겠습니다.

    답글삭제
    답글
    1. 어떻게 종료가 되는지 자세한 증상을 모르면 제가 어떻게 도움을 드릴 수가 없을거 같네요.

      삭제
  2. 좋은 글 감사합니다! 예제 코드들만 보면서 공부하니 이해 속도가 느렸는데 감사드립니다!

    답글삭제
  3. 예제 코드들만 보면서 공부하기에는 이해 속도가 느렸는데 좋은 글 써주셔서 훨씬 이해가 수월합니다. 감사합니다!

    답글삭제