반응형
공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!! 포스팅 내용이 찾아주신 분들께 도움이 되길 바라며 더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^
|
이번 포스팅에서는 BLE Beacon Scanner 를 구현하는 방법에 대해 알아보겠습니다.
기존 안드로이드에서 제공하는 BluetoothAdapter 만으로는 iBeacon 같은 비콘들을 스캔할 수가 없는데요.
안드로이드에서 API 21버전 이상의 기기에서 사용가능한 BluetoothLeScanner 를 사용하면
스캔할 수 있습니다.
1. Manifest.xml 설정 ( FINE_LOCATION 과 COARSE_LOCATION 중 하나만 선언해도 됩니다 )
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
2. API 23버전 이상을 위한 동적 권한 요청 코드.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSIONS);
PERMISSIONS 상수의 값은 정수형 값으로 1이상의 아무 숫자나 사용하면 됩니다. 저는 100을 사용했어요.
저는 원하는 특정 비콘만 스캔해서 ListView에 추가시키는 기능을 만들어 봤어요.
아래에서 ScanFilter, ScanFilter.Builder, ScanSettings, ScanSetting.Builder 만 제거하면
모든 BLE Beacon들을 검색할 수 있습니다.
3. MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Vector; public class MainActivity extends AppCompatActivity { BluetoothAdapter mBluetoothAdapter; BluetoothLeScanner mBluetoothLeScanner; BluetoothLeAdvertiser mBluetoothLeAdvertiser; private static final int PERMISSIONS = 100; Vector<Beacon> beacon; BeaconAdapter beaconAdapter; ListView beaconListView; ScanSettings.Builder mScanSettings; List<ScanFilter> scanFilters; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss", Locale.KOREAN); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, PERMISSIONS); beaconListView = (ListView) findViewById(R.id.beaconListView); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner(); mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); beacon = new Vector<>(); mScanSettings = new ScanSettings.Builder(); mScanSettings.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); // 얘는 스캔 주기를 2초로 줄여주는 Setting입니다. // 공식문서에는 위 설정을 사용할 때는 다른 설정을 하지 말고 // 위 설정만 단독으로 사용하라고 되어 있네요 ^^ // 위 설정이 없으면 테스트해 본 결과 약 10초 주기로 스캔을 합니다. ScanSettings scanSettings = mScanSettings.build(); scanFilters = new Vector<>(); ScanFilter.Builder scanFilter = new ScanFilter.Builder(); scanFilter.setDeviceAddress("특정 기기의 MAC 주소"); //ex) 00:00:00:00:00:00 ScanFilter scan = scanFilter.build(); scanFilters.add(scan); mBluetoothLeScanner.startScan(scanFilters, scanSettings, mScanCallback); // filter와 settings 기능을 사용하지 않을 때는 mBluetoothLeScanner.startScan(mScanCallback); 처럼 사용하시면 돼요. } ScanCallback mScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); try { ScanRecord scanRecord = result.getScanRecord(); Log.d("getTxPowerLevel()",scanRecord.getTxPowerLevel()+""); Log.d("onScanResult()", result.getDevice().getAddress() + "\n" + result.getRssi() + "\n" + result.getDevice().getName() + "\n" + result.getDevice().getBondState() + "\n" + result.getDevice().getType()); final ScanResult scanResult = result; new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { beacon.add(0,new Beacon(scanResult.getDevice().getAddress(), scanResult.getRssi(), simpleDateFormat.format(new Date()))); beaconAdapter = new BeaconAdapter(beacon, getLayoutInflater()); beaconListView.setAdapter(beaconAdapter); beaconAdapter.notifyDataSetChanged(); } }); } }).start(); } catch (Exception e) { e.printStackTrace(); } } @Override public void onBatchScanResults(List<ScanResult> results) { super.onBatchScanResults(results); Log.d("onBatchScanResults", results.size() + ""); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); Log.d("onScanFailed()", errorCode+""); } }; @Override protected void onDestroy() { super.onDestroy(); mBluetoothLeScanner.stopScan(mScanCallback); } } |
4. Beacon.java
public class Beacon {
private String address;
private int rssi;
private String now;
public Beacon(String address, int rssi, String now) {
this.address = address;
this.rssi = rssi;
this.now = now;
}
public String getAddress() {
return address;
}
public int getRssi() {
return rssi;
}
public String getNow() {
return now;
}
}
5. BeaconAdapter.java
public class BeaconAdapter extends BaseAdapter {
private Vector<Beacon> beacons;
private LayoutInflater layoutInflater;
public BeaconAdapter(Vector<Beacon> beacons, LayoutInflater layoutInflater) {
this.beacons = beacons;
this.layoutInflater = layoutInflater;
}
@Override
public int getCount() {
return beacons.size();
}
@Override
public Object getItem(int position) {
return beacons.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
BeaconHolder beaconHolder;
if (convertView == null) {
beaconHolder = new BeaconHolder();
convertView = layoutInflater.inflate(R.layout.item_beacon, parent, false);
beaconHolder.address = convertView.findViewById(R.id.address);
beaconHolder.rssi = convertView.findViewById(R.id.rssi);
beaconHolder.time = convertView.findViewById(R.id.time);
convertView.setTag(beaconHolder);
} else {
beaconHolder = (BeaconHolder)convertView.getTag();
}
beaconHolder.time.setText("시간 :" + beacons.get(position).getNow());
beaconHolder.address.setText("MAC Addr :"+beacons.get(position).getAddress());
beaconHolder.rssi.setText("RSSI :"+beacons.get(position).getRssi() + "dBm");
return convertView;
}
private class BeaconHolder {
TextView address;
TextView rssi;
TextView time;
}
}
6. item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="22sp"/>
<TextView
android:id="@+id/rssi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="20sp"/>
<TextView
android:id="@+id/time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="18sp"/>
</LinearLayout>
7. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="altong.mon.mybeaconscanner.MainActivity">
<ListView
android:id="@+id/beaconListView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</android.support.constraint.ConstraintLayout>
이상입니다.
궁금하신 점이 있으시다면 댓글 달아주세요.
프로젝트는 깃허브에 공유해 놓았습니다~^^
반응형
'안드로이드' 카테고리의 다른 글
안드로이드 RecylerView에 SnapHelper를 사용해서 스냅(snap)적용시키기! (2) | 2017.10.25 |
---|---|
안드로이드 TextView, EditText 에 java 코드로 Bold 속성 주기! (0) | 2017.10.23 |
안드로이드 구글 이메일 로그인 구현하기 android google email login (0) | 2017.10.18 |
안드로이드 카카오계정 로그인 구현하기 android kakao login (0) | 2017.10.18 |
안드로이드 네이버 아이디 로그인 구현하기 android naver id login (0) | 2017.10.17 |