我正在创建我的第一个Android应用程序,所以我真的是(android)开发的初学者。
应用程序的第一个特点是实现一个Activity(即BleActivity.java),当单击某个按钮时会调用它。 BleActivity应列出可用的BLE设备(使用tiSensorTag CC2650进行测试),稍后我想从设备中读取数据。
要让它发挥作用是一个相当大的挑战,因为大多数在线教程都是使用已弃用的API编写的。结合几个教程后,该应用程序正在运行!
应用中有一个我无法修复的错误:
当蓝牙关闭并且我触发BleActivity时,onResume()会检查蓝牙是否已启用(情况并非如此),如果不是,则会出现一个对话框请求用户打开蓝牙。
BleActivity.java
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class BleActivity extends ListActivity {
public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
// see nested class LeDeviceListAdapter
private LeDeviceListAdapter mLeDeviceListAdapter;
private BluetoothAdapter mBluetoothAdapter;
private static final int REQUEST_ENABLE_BT = 1;
private static final long SCAN_PERIOD = 10000;
private Handler mHandler;
private boolean mScanning;
private BluetoothLeScanner mLEScanner;
private ScanSettings settings;
private List<ScanFilter> filters;
private static final String INFO = "ZINFO";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Check if BLE is supported
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
} else {
Log.i(INFO, "onCreate: BLE is supported");
}
// Create a Handler to send and process Message Class and Runnable objects associated with a thread's MessageQueue.
mHandler = new Handler();
// Get BluetoothManager and BluetoothAdapter in order to conduct overall Bluetooth Management.
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
}
@Override
protected void onResume() {
super.onResume();
// Initialize list view adapter
mLeDeviceListAdapter = new LeDeviceListAdapter();
setListAdapter(mLeDeviceListAdapter);
//Check if permission for ACCESS_COARSE_LOCATION (AndroidManifest.xml) is granted.
if (checkLocationPermission()) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, R.string.grant_permission, Toast.LENGTH_LONG).show();
finish();
} else {
Log.i(INFO, "onResume: Permission for ACCESS_COARSE_LOCATION is granted");
}
}
// Check if bluetoothAdapter is successfully obtained and if BLE is enabled.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
Log.i(INFO, "onResume: BLE is enabled");
}
// GET getBluetoothLeScanner(): This class provides methods to perform scan related operations for Bluetooth LE devices
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
// Set scan settings
settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).build();
// Set device filter (null is allowed)
filters = new ArrayList<ScanFilter>();
// START SCAN FOR BLE DEVICES!
scanLeDevice(true);
}
// When user denies prompt for enabling Bluetooth, the Activity is closed
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
finish();
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
// Pause scanning for BLE devices */
@Override
protected void onPause() {
super.onPause();
scanLeDevice(false);
mLeDeviceListAdapter.clear();
}
//Methods for permission check
public boolean checkLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_COARSE_LOCATION)) {
new AlertDialog.Builder(this)
.setTitle(R.string.title_location_permission)
.setMessage(R.string.text_location_permission)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
ActivityCompat.requestPermissions(BleActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
}
})
.create()
.show();
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission. ACCESS_COARSE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
}
return false;
} else {
return true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i(INFO, "onRequestPermissionsResult: PERMISSION_GRANTED");
} else {
finish();
}
}
}
}
// Methods for START & STOP scan
private void scanLeDevice(final boolean enable) {
if (enable) {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mLEScanner.stopScan(mScanCallback); //STOP SCANNING
}
}, SCAN_PERIOD); // Stops scanning after a pre-defined scan period.
mScanning = true;
mLEScanner.startScan(filters, settings, mScanCallback); //START SCANNING
} else {
mScanning = false;
mLEScanner.stopScan(mScanCallback); //STOP SCANNING
}
if(mScanning == true) {
//TODO: Implement code for when the app is scanning (green stoplight, turning wheel, etc.)
} else {
//TODO: Implement code for when the app is NOT scanning (red stoplight, idle wheel, etc.)
}
}
// Bluetooth LE scan results are reported using these callbacks.
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice btDevice = result.getDevice();
mLeDeviceListAdapter.addDevice(btDevice);
mLeDeviceListAdapter.notifyDataSetChanged(); }
@Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult sr : results) {
Log.i("ScanResult - Results", sr.toString());
}
}
@Override
public void onScanFailed(int errorCode) {
Log.e("Scan Failed", "Error Code: " + errorCode);
}
};
// ListAdapter for holding devices found through scanning.
private class LeDeviceListAdapter extends BaseAdapter {
private ArrayList<BluetoothDevice> mLeDevices;
private LayoutInflater mInflator;
public LeDeviceListAdapter() {
super();
mLeDevices = new ArrayList<BluetoothDevice>();
mInflator = BleActivity.this.getLayoutInflater();
}
public void addDevice(BluetoothDevice device) {
if(!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
/*
TODO: implement onListItemClick (see example code)
public BluetoothDevice getDevice(int position) {
return mLeDevices.get(position);
}*/
public void clear() {
mLeDevices.clear();
}
@Override
public int getCount() {
return mLeDevices.size();
}
@Override
public Object getItem(int i) {
return mLeDevices.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
// General ListView optimization code.
if (view == null) {
view = mInflator.inflate(R.layout.activity_ble, null);
viewHolder = new ViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
BluetoothDevice device = mLeDevices.get(i);
final String deviceName = device.getName();
if (deviceName != null && deviceName.length() > 0)
viewHolder.deviceName.setText(deviceName);
else
viewHolder.deviceName.setText(R.string.unknown_device);
viewHolder.deviceAddress.setText(device.getAddress());
return view;
}
}
static class ViewHolder {
TextView deviceName;
TextView deviceAddress;
}
}
非常感谢您的帮助,请记住,这是我的第一个应用,我还是业余/初学者。
logcat的:
08-06 16:21:39.152 5489-5489/nl.cargosys.iotcloudconnect E/AndroidRuntime: FATAL EXCEPTION: main
Process: nl.cargosys.iotcloudconnect, PID: 5489
java.lang.RuntimeException: Unable to pause activity {nl.cargosys.iotcloudconnect/nl.cargosys.iotcloudconnect.BleActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.le.BluetoothLeScanner.stopScan(android.bluetooth.le.ScanCallback)' on a null object reference
at android.app.ActivityThread.performPauseActivityIfNeeded(ActivityThread.java:4179)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:4145)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:4119)
at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:4093)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1654)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.bluetooth.le.BluetoothLeScanner.stopScan(android.bluetooth.le.ScanCallback)' on a null object reference
at nl.cargosys.iotcloudconnect.BleActivity.scanLeDevice(BleActivity.java:179)
at nl.cargosys.iotcloudconnect.BleActivity.onPause(BleActivity.java:121)
at android.app.Activity.performPause(Activity.java:7148)
at android.app.Instrumentation.callActivityOnPause(Instrumentation.java:1330)
at android.app.ActivityThread.performPauseActivityIfNeeded(ActivityThread.java:4168)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:4145)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:4119)
at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:4093)
at android.app.ActivityThread.-wrap18(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1654)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
答案 0 :(得分:0)
在您的if条件下 -
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled())
主要问题在于你的 -
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
// Set scan settings
settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).build();
// Set device filter (null is allowed)
filters = new ArrayList<ScanFilter>();
// START SCAN FOR BLE DEVICES!
scanLeDevice(true);
您检查适配器是否可用,如果没有,则启动另一个活动以获得结果,但您继续在扫描仪代码中执行操作并在其他人之后扫描设备。理想情况下,如果您100%确定已启用蓝牙,则应将上述代码放在else语句中,并且只启动扫描设备。
更新1
好的,所以此错误来自您调用onPause()
方法的scanLeDevice(false);
方法。现在在这个方法中你做mLEScanner.stopScan(mScanCallback);
但是你的mLEScanner还没有被初始化,因为初始化代码在你的else语句中或在行中
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
因此,如果您在方法中执行此操作会更好 -
if(mLEScanner!=null)
{
mLEScanner.stopScan(mScanCallback);
}