안드로이드 블루투스 예제 - andeuloideu beullutuseu yeje

https://developer.android.com

위의 사이트에서 제공하는 BluetoothLEGatt라는 BLE 튜토리얼 예제 소스를 분석하려고 한다.

실행 순서를 기준으로 차례로 따라가면서 하려고 한다.

파일 구조

예제 앱의 파일 구조는 위와 같다.

먼저 manifest를 봐보자.

Manifest 일부

activity가 2개 등록되어있고, service가 하나 등록되어있다.

LAUNCHER가 DeviceScanActivity에 있으니 처음 앱을 시작하면 DeviceScanActivity가 가장 먼저 뜨게 된다.

그럼 DeviceScanActivity로 가보면,

DeviceScanActivity 1

DeviceScanActivity의 윗부분인데, 저렇게 주석대로의 이유로 전역 객체들을 만들어 놓았다.

처음 DeviceScanActivity가 실행되면 LifeCycle에 의해 onCreate()가 호출될 것이다.

DeviceScanActivity 2

onCreate에서는 실제 스캔을 진행하지 않고, BLE를 지원하는지, Bluetooth를 지원하는지를 검사하고 Bluetooth Adapter를 생성하고 초기화만 하였다.

실질적인 스캔은 onResume에 했는데, 앱을 나갔다 들어와도 다시 스캔을 하도록 하려고 그렇게 한 것 같다.

DeviceScanActivity 3

이제 onResume을 보면,

Adapter가, 즉 기기가 블루투스가 켜져 있는지 확인한 다음, 꺼져있다면 블루투스를 활성화하도록 해주는 안드로이드 기본 다이얼로그 창에 접근 요청을 한다.

그 창에서 예를 누르게 되면 그 기기의 블루투스를 키고 창을 종료한다.

DeviceScanActivity에서도 그 창에서 사용자가 무엇을 눌렀는지 알 필요가 있다.

아니요를 눌렀을 때는 블루투스가 활성화되지 않은 상태이므로 앱을 꺼줘야 할테고, 예를 눌렀을 때는 계속 진행해 스캔을 시작해야 할 것이다.

그러기 위해

startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);

를 사용해 액티비티를 시작할 뿐 아니라, 결과값까지 받아 온다.

첫 번째 인자는 실행할 데이터가 담긴 인텐트 객체고, 두 번째 인자는 어떤 요청인지 구별하기 위한 상수이다.

그 결과 값은 창이 종료될 때 onActivityResult가 호출되면서 그 안에 담겨진다.

잠시 onActivityResult 함수를 보자.

DeviceScanActivity 4

requestCode, resultCode, data가 넘어오게 되는데 각각 이런 정보를 담고 있다.

requestCode: startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)를 호출할 때 넘겨준 요청 상수가 담긴다. 이 값을 통해서 어떤 액티비티에서 온 값인지 판별한다.

resultCode: 창에서 어떤 버튼을 눌렀는지에 대한 결과값이 담긴다.

data: 새로운 창에서 인텐트를 보냈다면 그 인텐트가 이곳이 담긴다.

위에 소스에서는 requestCode로 블루투스 다이얼로그 창으로부터 호출된건지 확인후, 그 창에서 아니오를 눌렀을 때만 앱을 종료하는 것으로 처리해줬다.

예를 눌렀을 경우 계속 OnResume을 진행할 것이다.

다시 OnResume으로 돌아가보면

DeviceScanActivity 3–2

이제 블루투스 활성화가 잘 끝났다면

listview adapter를 생성해 셋팅하는데

일반적으로 많이 하듯 LeDeviceListAdapter라는 BaseAdapter를 상속받아 정의한 클래스를 이용해 어댑터를 만들고 그걸 셋팅해줬다. DeviceScanActivity가 ListActivity를 상속받았기 때문에 이 클래스 자체가 ListView로 쓸 수 있다.

그리고 따로 만들어 놓은 scanLeDevice 함수를 통해 스캔을 시작한다.

먼저 LeDeviceListAdapter 클래스를 보자.

DeviceScanActivity 5–1DeviceScanActivity 5–2

이렇게 블루투스 장치들을 추가하고 getView에 뿌리는 등등을 작성했는데

진짜 일반적으로 어댑터를 만들었기 때문에 생략…

addDevice가 ArrayList에 장치를 추가하는 메소드다…

다시 돌아가서 DeviceScanActivity 3–2를 보면 그렇게 ListView를 만들고 이제 scanLeDevice를 실행한다.

DeviceScanActivity 6

스캔을 얼마 동안, 즉 스캔 시간을 설정하기 위해 핸들러를 이용해 SCAN_PERIOD 시간 후에

mBluetoothAdapter.stopLeScan(mLeScanCallback) 메소드로 스캔을 멈추도록 설정한다.

그리고 스캔을 시작한다.

mBluetoothAdapter.startLeScan(mLeScanCallback);

이 메소드로 BLE 장치를 스캔해 그 결과를 mLeScanCallback 콜백 함수에 넘겨주며 호출한다.

DeviceScanActivity 7

다음은 콜백 함수인 mLeScanCallback인데,

이 함수는 새로운 장치가 발견될때마다 onLeScan을 호출하는 듯 하다.

인자 중에 final BluetoothDevice device에 검색된 장치의 정보가 들어오게 된다.

그래서 리스트에 addDevice해주고 notifyDataSetChaged로 ListView의 getView를 호출해 리스트를 갱신한다.

여기까지 BLE 장치들을 검색하고 리스트에 뿌려주는 일을 하는 소스이다.

이제 리스트에 나열된 BLE 장치들을 클릭하면 그 장치와 연결되면서 새로운 액티비티(DeviceControlActivity.class)가 뜨도록 해야한다.

리스트에서 각 아이템을 클릭했을 때는

ListActivity를 상속한 DeviceScanActivity에서 오버라이딩 메소드인 onListItemClick가 호출되게 된다.

DeviceScanActivity 8

(ListView l, View v, int position, long id)가 인자로 넘어오는데

l은 listview 객체이고, v는 부모 객체, position은 클릭된 아이템의 위치, id는 그 아이템의 고유 번호가 들어있다.

그래서 DeviceControlActivity로 인텐트를 만들고 추가로 클릭된 디바이스의 정보를 담은 후

스캔을 중지하고

새로운 액티비티를 띄운다.

이제 DeviceControlActivity에서 그 장치와의 연결, GATT 서비스들을 화면에 뿌려주고, BLE장치에서 온 데이터를 읽는 것까지 하게 된다.

안드로이드의 블루투스 통신 예제입니다.

안드로이드의 블루투스 레퍼런스를 참고하고 싶으시다면 다음 링크로 들어가시면 됩니다.

레퍼런스 참고 -> 블루투스 API

1. 블루투스 통신 예제

이 예제를 활용하여 수정한다면 블루투스 통신을 자유자재로 사용할 수 있다.

이제부터 이 예제를 활용하여 블루투스 통신을 하는 예제를 만들어 보겠다.

2. 새 프로젝트 생성

액티비티에 버튼을 생성하여, 버튼을 눌렀을 경우 블루투스 연결을 할 수 있도록 만들어볼 예정이다.

메인화면 레이아웃은 다음과 같이 버튼과 현재 상황을 알려주는 TextView로 만들었다.

main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/btn_connect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="연결" />


    <TextView
        android:id="@+id/txt_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_connect"
        android:text="" />

</RelativeLayout>

기본 세팅이 끝났으니 이제부터 블루투스 통신에 관련된 코드를 작성해보겠다.

3. 권한 등록

제대로된 코딩을 시작하기 전에 권한등록(permission 등록)을 먼저 하도록 하자

블루투스 기능을 사용하기 위해서는 'BLUETOOTH'와 'BLUETOOTH_ADMIN'의 권한을 등록해줘야 한다.

BLUETOOTH : 커넥션 요구, 커넥션 수락, 데이터 전송 등의 블루투스 통신을 하기 위해 필요한 권한

BLUETOOTH_ADMIN : 디바이스 검색 시작, 블루투스 설정을 조작하기 위해 필요

(BLUETOOTH_ADMIN 권한을 사용하려면 BLUETOOTH 권한도 꼭 있어야 한다.)

Manifest 파일에 블루투스 권한 등록

AndroidManifest.xml

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

4. 새 Class 파일 생성

MainActivity에는 Button과 TextView만 배치해놓고 BluetoothService라는 Class를 만들도록 한다.

BluetoothService라는 Class에서 블루투스와 관련된 모든 작업을 처리할 예정이다. 

작업이 처리되는 과정은 핸들러를 통해서 MainActivity에 전달되어 상태값을 전달받아 TextView에 보여주거나 버튼의 상태를 변경해 주도록 할 것이다.

BluetoothService 생성자는 메인이 되는 MainActivity로 부터 Activity와 Handler 값을 받는다.

BluetoothService.java

public class BluetoothService {
	// Debugging
	private static final String TAG = "BluetoothService";
	
	private BluetoothAdapter btAdapter;
	
	private Activity mActivity;
	private Handler mHandler;
	
	// Constructors
	public BluetoothService(Activity ac, Handler h) {
		mActivity = ac;
		mHandler = h;
		
		// BluetoothAdapter 얻기
		btAdapter = BluetoothAdapter.getDefaultAdapter();
	}

MainActivity.java

private BluetoothService btService = null;

private final Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
		}
		
	};

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.main);
		
		// BluetoothService 클래스 생성
		if(btService == null) {
			btService = new BluetoothService(this, mHandler);
		}
	}

5. 블루투스 활성화

블루투스를 활성화 하기 위해 BluetoothAdapter클래스를 사용해서 다음 두 단계의 작업을 순서대로 진행한다.

① 블루투스 지원 확인

블루투스 통신을 사용하기 전에 디바이스가 블루투스를 지원하는지 확인할 필요가 있다. 디바이스가 블루투스를 지원하는지 확인해보도록 하자

BluetoothService.java

public boolean getDeviceState() {
		Log.d(TAG, "Check the Bluetooth support");
		
		if(btAdapter == null) {
			Log.d(TAG, "Bluetooth is not available");
			
			return false;
			
		} else {
			Log.d(TAG, "Bluetooth is available");
			
			return true;
		}
	}

getDeviceState() 라는 메소드를 만들어서 기기의 블루투스 지원여부를 확인 한다.

BluetoothAdapter가 null일 경우 블루투스 통신을 지원하지 않는 기기이다. (하지만 이러한 기기는 요즘 없을듯..?)

② 블루투스 활성화

getDeviceState()가 true를 반환할 경우 블루투스 활성화를 요청하도록 해보자.

블루투스 활성화 요청을 위해서 enableBluetooth()라는 메소드를 만들었다. 

BluetoothService.java

public void enableBluetooth() {
		Log.i(TAG, "Check the enabled Bluetooth");
		
		
		if(btAdapter.isEnabled()) {		
			// 기기의 블루투스 상태가 On인 경우
			Log.d(TAG, "Bluetooth Enable Now");
			
			// Next Step
		} else {		
			// 기기의 블루투스 상태가 Off인 경우
			Log.d(TAG, "Bluetooth Enable Request");
			
			Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			mActivity.startActivityForResult(i, REQUEST_ENABLE_BT);
		}
	}

기기의 블루투스 상태가 On일 경우 다음 단계를 실행하면 되고, 

기기의 블루투스 상태가 Off일 경우 블루투스 활성화를 요청하는 알림창을 띄운다.

알림창에서 확인/취소를 선택할 경우 결과는 MainActivityonActivityResult()메소드로 들어온다. 

그 전에 startActivityForResult의 개념에 대해서 알고 있어야 이해하기 편한데, 이 것은 구글링을 통해서 알아보는것을 추천한다.

알림창의 확인/취소 결과는 MainActivity에 onActivityResult()메소드로 전달되는데 소스를 보면 다음과 같다.

MainActivity.java

public void onActivityResult(int requestCode, int resultCode, Intent data) {
        
        switch (requestCode) {          
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
                // 확인 눌렀을 때
                //Next Step
            } else {
                // 취소 눌렀을 때
                Log.d(TAG, "Bluetooth is not enabled");
            }
            break;
        }
}

이제 MainActivity에서 버튼을 클릭했을 때 블루투스 활성화를 위한 이벤트 처리만 해주면 된다.

MainActivity.java

@Override
	public void onClick(View v) {
		if(btService.getDeviceState()) {
			// 블루투스가 지원 가능한 기기일 때
			btService.enableBluetooth();
		} else {
			finish();
		}
	}

여기까지 블루투스를 활성화 하는 방법에 대해서 알아보았다.

블루투스가 활성화 되었을 때(resultCode가 Activity.RESULT_OK일때) 기기 검색을 통해 페어링을 시켜주면 되는데, 이 부분은 다음장에 포스팅 하도록 하겠다.

전체 소스는 다음과 같고, 프로젝트를 첨부파일로 올린다.

안드로이드 블루투스 예제 - andeuloideu beullutuseu yeje
EX_Bluetooth(1).zip

MainActivity.java

public class MainActivity extends Activity implements OnClickListener {
	// Debugging
	private static final String TAG = "Main";
	
	// Intent request code
	private static final int REQUEST_CONNECT_DEVICE = 1;
	private static final int REQUEST_ENABLE_BT = 2;
	
	// Layout
	private Button btn_Connect;
	private TextView txt_Result;
	
	private BluetoothService btService = null;
	
	
	private final Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
		}
		
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.e(TAG, "onCreate");
		
		setContentView(R.layout.main);
		
		/** Main Layout **/
		btn_Connect = (Button) findViewById(R.id.btn_connect);
		txt_Result = (TextView) findViewById(R.id.txt_result);
		
		btn_Connect.setOnClickListener(this);
		
		// BluetoothService 클래스 생성
		if(btService == null) {
			btService = new BluetoothService(this, mHandler);
		}
	}

	@Override
	public void onClick(View v) {
		if(btService.getDeviceState()) {
			// 블루투스가 지원 가능한 기기일 때
			btService.enableBluetooth();
		} else {
			finish();
		}
	}
	
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onActivityResult " + resultCode);
        
        switch (requestCode) {

        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
            	
            } else {

                Log.d(TAG, "Bluetooth is not enabled");
            }
            break;
        }
	}

}

BluetoothService.java

public class BluetoothService {
	// Debugging
	private static final String TAG = "BluetoothService";
	
	// Intent request code
	private static final int REQUEST_CONNECT_DEVICE = 1;
	private static final int REQUEST_ENABLE_BT = 2;
	
	private BluetoothAdapter btAdapter;
	
	private Activity mActivity;
	private Handler mHandler;
	
	// Constructors
	public BluetoothService(Activity ac, Handler h) {
		mActivity = ac;
		mHandler = h;
		
		// BluetoothAdapter 얻기
		btAdapter = BluetoothAdapter.getDefaultAdapter();
	}
	
	/**
	 * Check the Bluetooth support
	 * @return boolean
	 */
	public boolean getDeviceState() {
		Log.i(TAG, "Check the Bluetooth support");
		
		if(btAdapter == null) {
			Log.d(TAG, "Bluetooth is not available");
			
			return false;
			
		} else {
			Log.d(TAG, "Bluetooth is available");
			
			return true;
		}
	}
	
	/**
	 * Check the enabled Bluetooth
	 */
	public void enableBluetooth() {
		Log.i(TAG, "Check the enabled Bluetooth");
		
		
		if(btAdapter.isEnabled()) {		
			// 기기의 블루투스 상태가 On인 경우
			Log.d(TAG, "Bluetooth Enable Now");
			
			// Next Step
		} else {		
			// 기기의 블루투스 상태가 Off인 경우
			Log.d(TAG, "Bluetooth Enable Request");
			
			Intent i = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			mActivity.startActivityForResult(i, REQUEST_ENABLE_BT);
		}
	}
	
}

다음 글 : 블루투스 기기 검색하기 글 보기