无法触发扫描蓝牙设备的警报管理器
我是一名初学者,拥有Android和Java(早在八十年代,我在Z80机器语言中已经相当先进)。我想创建一个应用程序,每隔5分钟在后台扫描蓝牙设备。目的是监视我家周围各种设备的使用情况。我有3个类:具有用于UI的代码的MainActivity,用于扫描蓝牙设备的代码的MbtScanner和具有用于在警报发生时调用扫描的代码的BluetoothTScheduler。警报设置为每60秒重复出于测试目的,但一旦代码正常工作,我会将其设置为每5分钟一次。结果然后写入文件。无法触发扫描蓝牙设备的警报管理器
当从MainActivity调用时,扫描工作正常,但由于警报管理器而未从BluetoothTScheduler调用时,扫描工作正常。
我已经搜索了stackoverflow和互联网的答案,并没有运气。我认为问题在于我传递给MbtScanner活动的上下文,这是错误的上下文类型,但我不太了解该问题,也不知道如何解决该问题。
这是我的MainActivity
public class MainActivity extends AppCompatActivity {
AlarmManager alarmManager;
Intent intent;
public PendingIntent pendingIntent;
MbtScanner mbtscanner;
BluetoothTScheduler bluetoothTScheduler;
public static ArrayAdapter<String> mArrayAdapter;
ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listView);
mArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, 0);
listView.setAdapter(mArrayAdapter);
mbtscanner = new MbtScanner(this);
setScheduler();
mbtscanner.mbtScannerInit();
mbtscanner.scanResult();
setBtReceiver();
}
// SCAN FOR BLUETOOTH DEVICES - CALLED BY UI SCAN BUTTON
public void btScan(View v) {
if (!mbtscanner.stillScanning) {
mArrayAdapter.clear();
}
mbtscanner.mbtScan();
}
// ASK USER TO SWITCH ON BLUETOOTH (CALLED IF BLUETOOTH OFF)
private void turnOnBT() {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 1);
}
// SET BLUETOOTH RECEIVER AND IF FOUND CAPTURE DATA TO FILE AND TO LISTVIEW
private void setBtReceiver() {
//CHECK FOR BLUETOOTH CAPABILITY AND IF IT IS SWITHCED ON
if (mbtscanner.mBluetoothAdapter == null) {
Toast.makeText(getApplicationContext(), "Bluetooth not supported", Toast.LENGTH_SHORT).show();
finish();
} else {
if (!mbtscanner.mBluetoothAdapter.isEnabled()) {
turnOnBT();
}
if (mbtscanner.mBluetoothAdapter.isDiscovering()) mbtscanner.mBluetoothAdapter.cancelDiscovery();
}
}
public static void updateListView(String s){
mArrayAdapter.add(s);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// IF USER REFUSES TO SWITCH ON BLUETOOTH THEN TERMINATE APPLICATION
if (resultCode == RESULT_CANCELED) {
Toast.makeText(getApplicationContext(), "Bluetooth must be enabled to Continue", Toast.LENGTH_SHORT).show();
finish();
}
}
@Override
protected void onPause() {
Toast.makeText(this, "On Pause", Toast.LENGTH_SHORT).show();
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
// init();
// setBtReceiver();
}
@Override
protected void onDestroy() {
Toast.makeText(this, "On Destroy", Toast.LENGTH_SHORT).show();
if (mbtscanner.mBluetoothAdapter.isDiscovering()) {
mbtscanner.mBluetoothAdapter.cancelDiscovery();
}
unregisterReceiver(mbtscanner.mReceiver);
super.onDestroy();
}
// THIS IS CALLED FROM A UI BUTTON "CANCEL ALARM"
public void cancelAlarm(View view) {
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
intent = new Intent(this, BluetoothTScheduler.class);
pendingIntent = PendingIntent.getBroadcast(
this.getApplicationContext(), 234324243, intent, 0);
alarmManager.cancel(pendingIntent);
Toast.makeText(this, "Alarm cancelled", Toast.LENGTH_LONG).show();
}
public void setScheduler() {
bluetoothTScheduler = new BluetoothTScheduler();
alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
intent = new Intent(this, BluetoothTScheduler.class);
pendingIntent = PendingIntent.getBroadcast(
this.getApplicationContext(), 234324243, intent, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ (30 * 1000), 60 * 1000, pendingIntent);
Toast.makeText(this, "Alarm set in 10 seconds for every 60 seconds", Toast.LENGTH_LONG).show();
}
}
代码这是我的MbtScanner类的代码:
public class MbtScanner {
public String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/btTracker";
FileWriter fileWriter;
File file;
ProgressDialog progress;
Context mContext;
BluetoothAdapter mBluetoothAdapter;
IntentFilter filter;
BroadcastReceiver mReceiver;
boolean stillScanning = false;
String s;
int count =0;
public MbtScanner(Context mContext) {
this.mContext = mContext;
}
public void mbtScan(){
mBluetoothAdapter.startDiscovery();
stillScanning = true;
// DIALOG BOX SAYING PLEASE WAIT
progress = new ProgressDialog(mContext);
progress.setTitle("Scanning");
progress.setMessage("Please wait...");
// progress.setCancelable(false);
progress.show();
}
public void mbtScannerInit(){
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
File dir = new File(path);
dir.mkdirs();
file = new File(path + "/savedfile.csv");
}
public void scanResult() {
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
// IF DEVICE FOUND
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
// CAPTURE DEVICE DATA TO LISTVIEW
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
count++;
int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
String sName, sAddress, sUuids, sDate, sFile;
int iBondState, iMajorClass;
sName = device.getName();
sAddress = device.getAddress();
iBondState = device.getBondState();
iMajorClass = device.getBluetoothClass().getMajorDeviceClass();
sUuids = "" + device.getUuids();
sDate = DateFormat.getDateTimeInstance().format(new Date());
s = "Count= " + count + "\n";
s += "Name= " + sName + "\n";
s += "Address= " + sAddress + "\n";
s += "Bond state= " + iBondState + "; ";
s += "Major class= " + iMajorClass + "\n";
s += "Uuids= " + sUuids + "; ";
s += "RSSI= " + rssi + "dBm" + "\n";
s += "Date= " + sDate + "\n";
MainActivity.updateListView(s);
// CREATE STRING OF DEVICE DATA TO SAVE TO FILE
sFile = count + "," + sName + "," + sAddress + "," + iBondState + "," + iMajorClass + "," + sUuids + "," + rssi + "," + sDate;
// SAVE DEVICE DATA TO FILE
try {
commitToFile(sFile);
} catch (IOException e) {
e.printStackTrace();
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
Toast.makeText(mContext, "Discovery started", Toast.LENGTH_SHORT).show();
// SCANNING FINISHED SO INFORM USER
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
progress.dismiss();
Toast.makeText(mContext, "Discovery finished. Last device found: " + s, Toast.LENGTH_SHORT).show();
stillScanning = false;
// IF SOMEHOW BLUETOOTH HAS BEEN SWITCHED OFF THEN TRY TO SWITCH IT BACK ON
} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
if (mBluetoothAdapter.getState() == mBluetoothAdapter.STATE_OFF) {
// Toast.makeText(mActivity, "Bluetooth state changed", Toast.LENGTH_SHORT).show();
// turnOnBT(); ALTERNATIVE CODE TO NOTIFY USER THAT APP CAN NO LONGER WORK BCAUSE BLUTOOTH IS OFF, AND CANCEL ALARM
}
}
}
};
mContext.registerReceiver(mReceiver, filter);
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
mContext.registerReceiver(mReceiver, filter);
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
mContext.registerReceiver(mReceiver, filter);
filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(mReceiver, filter);
}
// SAVE DATA TO FILE
private void commitToFile(String str) throws IOException {
fileWriter = new FileWriter(file, true);
BufferedWriter bufferWriter = new BufferedWriter(fileWriter);
PrintWriter printWriter = new PrintWriter(bufferWriter);
printWriter.print(str + "\n");
printWriter.close();
Toast.makeText(mContext, "written to file: ", Toast.LENGTH_SHORT).show();
}
}
这是代码我为这是由Alarmmanager称为BluetoothTScheduler类:
public class BluetoothTScheduler extends BroadcastReceiver {
public static final String MyPREFERENCES = "MyPrefs" ;
SharedPreferences sharedpreferences;
int brCounter;
MbtScanner sbtscanner;
@Override
public void onReceive(Context context, Intent intent) {
sharedpreferences = context.getSharedPreferences(MyPREFERENCES, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
brCounter = sharedpreferences.getInt("counter", 0);
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(1000);
Toast.makeText(context, "Alarm...." + brCounter, Toast.LENGTH_LONG).show();
brCounter = brCounter +1;
editor.putInt("counter", brCounter);
editor.commit();
sbtscanner = new MbtScanner(context);
sbtscanner.mbtScannerInit();
if (sbtscanner.mBluetoothAdapter.isDiscovering()) sbtscanner.mBluetoothAdapter.cancelDiscovery();
sbtscanner.scanResult();
}
}
这是我的清单文件:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="BluetoothTScheduler" >
</receiver>
</application>
这里是logcat的:
D/InputMethodManager: windowDismissed mLockisused = false
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
E/AndroidRuntime: Process: com.orinocosolutions.bluetracker2, PID: 29905
E/AndroidRuntime: java.lang.RuntimeException: Unable to start receiver com.orinocosolutions.bluetracker2.BluetoothTScheduler: android.content.ReceiverCallNotAllowedException: BroadcastReceiver components are not allowed to register to receive intents
E/AndroidRuntime: at android.app.ActivityThread.handleReceiver(ActivityThread.java:3508)
E/AndroidRuntime: at android.app.ActivityThread.access$1900(ActivityThread.java:218)
E/AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1795)
E/AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime: at android.os.Looper.loop(Looper.java:145)
E/AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6917)
E/AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime: at java.lang.reflect.Method.invoke(Method.java:372)
E/AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
E/AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
E/AndroidRuntime: Caused by: android.content.ReceiverCallNotAllowedException: BroadcastReceiver components are not allowed to register to receive intents
E/AndroidRuntime: at android.app.ReceiverRestrictedContext.registerReceiver(ContextImpl.java:275)
E/AndroidRuntime: at android.app.ReceiverRestrictedContext.registerReceiver(ContextImpl.java:264)
E/AndroidRuntime: at com.orinocosolutions.bluetracker2.MbtScanner.scanResult(MbtScanner.java:166)
E/AndroidRuntime: at com.orinocosolutions.bluetracker2.BluetoothTScheduler.onReceive(BluetoothTScheduler.java:42)
E/AndroidRuntime: at android.app.ActivityThread.handleReceiver(ActivityThread.java:3501)
E/AndroidRuntime: at android.app.ActivityThread.access$1900(ActivityThread.java:218)
E/AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1795)
E/AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime: at android.os.Looper.loop(Looper.java:145)
E/AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:6917)
E/AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime: at java.lang.reflect.Method.invoke(Method.java:372)
任何帮助是非常赞赏。
它看起来像所有你需要做什么来修复错误就是改变这样的:
sbtscanner = new MbtScanner(context.getApplicationContext());
这是因为在MbtScanner的scanResult()
方法中,使用的是:
sbtscanner = new MbtScanner(context);
为了这此Context在运行时注册BroadcastReceiver,传入onReceive()
方法BluetoothTScheduler
的上下文不允许用于注册其他BroadcastReceiver。
此异常从registerReceiver(广播接收器, IntentFilter的),并且当被从广播接收器组件中使用 这些方法bindService(意向,ServiceConnection,INT)抛出。在 这种情况下,从 收到Intent返回时,组件将不再处于活动状态,因此使用异步API无效。
在上面的修复程序中,您的MbtScanner类将仅使用从Alarm中触发的BroadcastReceiver调用的应用程序上下文。
太棒了!似乎已经解决了这个问题。解决这个问题已经在我的代码中发现了其他一些小错误,但我相信我可以解决这些问题。再次感谢! – OrinocoBoyo