2015년 10월 1일 목요일

안드로이드 BluetoothLeScanner 클래스 사용하기



안드로이드에서 Bluetooth LE (Low Energy)를 사용할 때 가장 기본적인 샘플 예제 코드로 https://github.com/googlesamples/android-BluetoothLeGatt 를 많이 사용한다.

하지만 API level 21부터는 API가 약간 바뀌어 LE 디바이스를 스캔할 때 사용하는 BluetoothAdapter 클래스의 startLeScan(), stopLeScan() 메소드가 deprecated 되어 빌드해 실행하는데 문제는 없어도 android studio에서 소스를 보면  mBluetoothAdapter.startLeScan(mLeScanCallback); 과 mBluetoothAdapter.stopLeScan(mLeScanCallback); 부분에 메소드 이름에 빨간색 줄이 쳐져 있는걸 보게 될 것이다.
API level 21부터는 BluetoothAdapter 클래스에서 스캔을 하지 않고 BluetoothLeScanner라는 클래스가 추가되어 이 클래스의 startScan(), stopScan() 메소드를 사용해 BLE 디바이스를 검색하도록 변하였다.
콜백 역시 Bluetooth.LeScanCallback 대신 ScanCallback 클래스를 사용해야 한다.

그러므로 android-BluetoothLeGatt 예제를 API level 21의 변화에 맞게 수정하려면 다음과 같다.

먼저 사용할 클래스  import를 추가한다. DeviceScanActivity.java 소스코드 위쪽의 import 부분에 아래 내용을 추가한다.

import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import java.util.List;

다음 DeviceScanActivity 클래스의 private 멤버 선언부에 밑줄 친 빨간색 내용을 추가한다.

public class DeviceScanActivity extends ListActivity {
    private LeDeviceListAdapter mLeDeviceListAdapter;
    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler; 
    private BluetoothLeScanner mBLEScanner;

다음 onCreate 메소드의 맨 뒤쪽 부분에 다음 줄을 추가해 BluetoothLeScanner 클래스의 인스턴스를 얻는다.

    if (mBluetoothAdapter == null) {
        Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
        finish();
        return;
    }
    mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
    // Checks if Bluetooth LE Scanner is available.    if (mBLEScanner == null) {
        Toast.makeText(this, "Can not find BLE Scanner", Toast.LENGTH_SHORT).show();
        finish();
        return;
    }
}

다음은 onListItemClick() 메소드와 scanLeDevice() 메소드 안에 있는 빨간색 줄이 쳐져 있는 startLeScan(), stopLeScan() 부분을 각각 다음 내용으로 바꿔준다.

mBLEScanner.startScan(mScanCallback); // mBluetoothAdapter.startLeScan() 부분
 
mBLEScanner.stopScan(mScanCallback);  // mBluetoothAdapter.stopLeScan() 부분

그리고 기존의 콜백 정의부분은 없애고 다음의 내용을 추가한다.

즉 아래 부분은 통채로 삭제

// Device scan callback.private BluetoothAdapter.LeScanCallback mLeScanCallback =        new BluetoothAdapter.LeScanCallback() {
    @Override    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {        runOnUiThread(new Runnable() {            @Override            public void run() {                mLeDeviceListAdapter.addDevice(device);                mLeDeviceListAdapter.notifyDataSetChanged();            }        });    }};
 
위쪽 내용을 삭제한 부분에 아래 내용을 추가


    private ScanCallback mScanCallback = new ScanCallback() {
        @Override        public void onScanResult(int callbackType, ScanResult result) {
            processResult(result);
        }

        @Override        public void onBatchScanResults(List<ScanResult> results) {
            for (ScanResult result : results) {
                processResult(result);
            }
        }

        @Override        public void onScanFailed(int errorCode) {
        }

        private void processResult(final ScanResult result) {
            runOnUiThread(new Runnable() {
                @Override                public void run() {
                    mLeDeviceListAdapter.addDevice(result.getDevice());
                    mLeDeviceListAdapter.notifyDataSetChanged();
                }
            });
        }
    };