I'm trying to program an App to establish a basic communication over BLE between an Android Phone and an RFDuino. The RFDuino is just advertising and can be detected by the "BLE Scanner" App.
Below is the draft code for the activity, that is supposed to detect BLE devices.
The problem is, that no devices are detected. The Debug Log shows, that the method "onLeScan" is not even called once...
I'm running out of Ideas and would be thankful for every hint :-)
package de.tuhh.et5.serialcommunicator;
import (...)
public class BLEActivity extends AppCompatActivity {
private static final long SCAN_PERIOD = 10000;
private int REQUEST_ENABLE_BT = 1;
public final static String TAG = "BLE_Activity"; // Tag for the LOG
private BluetoothAdapter ble_adapter;
private boolean scanning;
private Handler ble_handler;
private BLE_DeviceListAdapter BLE_DeviceListAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
d("on Create");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ble);
TabHost tabHost=(TabHost)findViewById(R.id.tabHost);
tabHost.setup();
TabHost.TabSpec t1=tabHost.newTabSpec("Tab1");
t1.setContent(R.id.tab1_layout);
t1.setIndicator("Devices");
tabHost.addTab(t1);
BLE_DeviceListAdapter = new BLE_DeviceListAdapter();
ListView ble_list = (ListView)findViewById(R.id.BLE_DeviceListView);
ble_list.setAdapter(BLE_DeviceListAdapter);
ble_handler = new Handler();
// get a bluetooth adapter from the bluetooth manager
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
ble_adapter = bluetoothManager.getAdapter();
// Check if BLE is supported by the Android device
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "BLE Not Supported",
Toast.LENGTH_SHORT).show();
finish();
}
// Enable bluetooth
if (ble_adapter == null || !ble_adapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
// menu stuff
@Override
public boolean onCreateOptionsMenu(Menu menu) {
d("menu inflates");
// Inflate the menu; this adds items to the action bar if it is present.
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.ble_menu, menu);
if (scanning) {
menu.findItem(R.id.scanning_start).setVisible(false);
} else {
menu.findItem(R.id.scanning_start).setVisible(true);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.scanning_start:
d("scan pressed");
BLE_DeviceListAdapter.clear();
ble_scan(true);
break;
}
return true;
}
private void ble_scan(final boolean en){
d("Scan start");
// stopLeScan and startLeScan are deprecated since Android 5.0, but still work.
// to offer a compatibility down to Android 4.3 the old methods are used
if (en){
ble_handler.postDelayed(new Runnable() {
@Override
public void run() {
d("scan timer finished");
scanning = false;
ble_adapter.stopLeScan(ble_callback);
invalidateOptionsMenu();
}
},SCAN_PERIOD);
scanning = true;
ble_adapter.startLeScan(ble_callback);
}else{
scanning = false;
ble_adapter.stopLeScan(ble_callback);
}
invalidateOptionsMenu();
}
private BluetoothAdapter.LeScanCallback ble_callback = new BluetoothAdapter.LeScanCallback(){
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord){
d("onLeScan");
runOnUiThread(new Runnable(){
@Override
public void run(){
BLE_DeviceListAdapter.addDevice(device);
BLE_DeviceListAdapter.notifyDataSetChanged();
}
});
}
};
// This class lists the elements in an ble device entry as defined in "ble_device_entry.cml"
// It is needed in the BLE_DeviceListAdapter class
private class ViewHolder {
TextView name;
TextView device_id;
TextView device_adress;
}
// #####################################################
// private Class BLE_DeviceListAdapter
//
// Adapter to handle found BLE Devices
// #####################################################
private class BLE_DeviceListAdapter extends BaseAdapter{
private ArrayList<BluetoothDevice> ble_devices; // holds found devices
private LayoutInflater ble_inflater; // layout inflater
public BLE_DeviceListAdapter(){
super();
// Init
ble_devices = new ArrayList<>();
ble_inflater = BLEActivity.this.getLayoutInflater();
}
// method to add a device
public void addDevice(BluetoothDevice ble_device){
// device is added, if it is not already in the list
if (!ble_devices.contains(ble_device)){
ble_devices.add(ble_device);
}
}
// Mandatory, returns device at position ...
@Override
public BluetoothDevice getItem(int position){
return ble_devices.get(position);
}
// clear list..
public void clear(){
ble_devices.clear();
}
// Mandatory: Not needed, therefore just a dummy
@Override
public long getItemId(int i) {
return i;
}
// Mandatory: returns size of list
@Override
public int getCount() {
return ble_devices.size();
}
// Mandatory: creates and returns view with a device entry
@Override
public View getView(int i, View view, ViewGroup viewGroup ){
ViewHolder viewHolder; // create view holder
// if the view does not already exist:
if (view == null){
view = ble_inflater.inflate(R.layout.ble_device_entry, null);// inflate view
viewHolder = new ViewHolder(); // create a ViewHolder
// Connenct the ViewHolder elements to layout elements
viewHolder.name = (TextView)findViewById(R.id.BLE_sName);
viewHolder.device_adress = (TextView)findViewById(R.id.BLE_sAdress);
viewHolder.device_id = (TextView)findViewById(R.id.BLE_sID);
view.setTag(viewHolder); // Add the ViewHolder as a Tag to the view
}else{
// if the view already exists
viewHolder = (ViewHolder) view.getTag(); // get Tag from view to populate ViewHolder
}
// get BLE Device
Log.d("test", "hier so....");
BluetoothDevice device = ble_devices.get(i);
// get device name
final String deviceName = device.getName();
// assign device name to ViewHolder Object
if (deviceName != null && deviceName.length() > 0){
viewHolder.device_id.setText(deviceName);
}else viewHolder.device_id.setText(R.string.unknown_ble_device);
// assign more values
viewHolder.device_adress.setText(device.getAddress());
viewHolder.name.setText(R.string.unknown_ble_device);
d("before return view");
return view;
}
}
// #####################################################
// End Adapter
// #####################################################
public void d(Object msg) {
Log.d(TAG, ">==< " + msg.toString() + " >==<");
}
public void e(Object msg) {
Log.e(TAG, ">==< " + msg.toString() + " >==<");
}
}
答案 0 :(得分:1)
好吧,似乎我搞砸了权限。我没有该位置的运行时权限。我认为基于清单的许可是足够的,如果一个人没有排除目标Android 6.xx。现在我对视图持有者和东西有问题,但那是另一个故事: - )。
无论如何,感谢解决方案@MarkusKauppinen!
答案 1 :(得分:0)
我认为扫描代码看起来不错。
要检查的一些事项:
您是否调试/登录以检查选项菜单是否有效并且ble_scan()
被调用?
您是否调试/登录以检查startLeScan()
是否返回true
?
如果未找到任何设备,则不会调用onLeScan()
回调。
由于您正在扫描10秒钟,因此RFDuino应该经常宣传自己/足够长时间,以至于在10秒窗口内始终至少发生一个广告周期。
所有Android版本都需要这些权限:
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
另外Android 6.x(Marhsmallow)以及后来还需要:
android.permission.ACCESS_COARSE_LOCATION
如果您正在编写仅针对6.x及更高版本的代码,则需要使用新的运行时权限模型,而不是基于旧的清单文件。
在某些运行Android 6.x的设备上,系统范围内&#34;位置&#34;设置必须为ON。请参阅:onLeScan from BluetoothAdapter.LeScanCallback not called in Android Marshmallow
然后当然some of the older discussions可能会有一些有用的提示。