【问题标题】:Can't get an alarmmanager to trigger scanning of bluetooth devices无法让警报管理器触发蓝牙设备的扫描
【发布时间】:2016-08-15 21:28:57
【问题描述】:

我是 Android 和 Java 的初学者(我在八十年代的 Z80 机器语言方面相当先进)。我想创建一个应用程序在后台每 5 分钟扫描一次蓝牙设备。目的是监控我家周围各种设备的使用情况。我有 3 个类:MainActivity 具有 UI 代码,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();
}

}

这是我的 BluetoothTScheduler 类的代码,由 Alarmmanager 调用:

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>

这里是日志:

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)

非常感谢任何帮助。

【问题讨论】:

    标签: android bluetooth broadcastreceiver alarmmanager


    【解决方案1】:

    看起来你需要做的就是修改这个错误:

    sbtscanner = new MbtScanner(context);
    

    到这里:

    sbtscanner = new MbtScanner(context.getApplicationContext());
    

    这是因为在MbtScanner的scanResult()方法内部,你是在运行时使用这个Context来注册一个BroadcastReceiver,而传入BluetoothTScheduler的onReceive()方法的Context是不允许用来注册另一个的广播接收器。

    来自the documentation:

    这个异常是从 registerReceiver(BroadcastReceiver, IntentFilter) 和 bindService(Intent, ServiceConnection, int) 时 这些方法是从 BroadcastReceiver 组件中使用的。在 在这种情况下,组件在从返回时将不再处于活动状态 接收 Intent,因此使用异步 API 无效。

    通过上述修复,您的 MbtScanner 类将仅在从警报触发的 BroadcastReceiver 调用时使用应用程序上下文。

    【讨论】:

    • 太棒了!这似乎已经解决了这个问题。修复这个问题已经在我的代码中发现了其他几个小错误,但我相信我可以解决这些问题。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 2022-01-04
    相关资源
    最近更新 更多