提问人:Rob Garner 提问时间:7/23/2023 更新时间:7/23/2023 访问量:38
Android Studio:调用方法“Message m = myThread.ThreadHandler.obtainMessage();”时出现空指针异常
Android Studio: Null pointer exception when calling method "Message m = myThread.ThreadHandler.obtainMessage();"
问:
我对 Android Studio 和一般的应用程序开发相当陌生。我的背景是嵌入式固件。 总而言之,我正在编写一个应用程序,该应用程序将连接到支持 BLE 的硬件设备。该应用程序将允许在设备上进行一些设置,然后应用程序将继续在后台运行服务,以便定期扫描硬件设备。
我按如下方式设置了服务(以下代码中仅包含相关部分):
public class MyBLEService extends Service {
public MyBLEService() {
}
/**
* Stops scanning after 8 seconds.
*/
private static final long SCAN_PERIOD = 8000;
private static boolean isRunning;
private static boolean BackgroundScan;
private static int ActivityStatus;
public static int LastCommand;
public static int NumBytesToSend;
private char CommandErrorCode;
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
private Bundle myBundle = new Bundle();
private static BluetoothAdapter myBluetoothAdapter;
private static ScanCallback mScanCallback;
private static BluetoothGatt mBluetoothGATT;
private static BluetoothLeScanner mBluetoothLeScanner;
private static BluetoothDevice mBluetoothDevice;
private static BluetoothGattCharacteristic DE_Characteristic;
public static String ServiceID;
private static final String NOTIFICATION_CHANNEL_ID ="notification_channel_id";
private static final String NOTIFICATION_Service_CHANNEL_ID = "service_channel";
private Runnable ScanDelayRunnable;
private LooperThread myThread;
private static Handler mHandler;
private byte[] HashInput = new byte[100]; // Unencrypted data for encryption algorithm
private byte[] HashOutput = new byte[100]; // Output buffer for encrypted or decrypted data.
private byte[] PayloadData = new byte[100]; // Raw payload data (if required)
private final byte[] EmptyArray = new byte[100]; // Raw payload data (if required)
private static char temp_16;
private static char CheckSum;
private static FragmentManager fm;
private void startInForeground() {
int icon = R.mipmap.ic_launcher;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
icon = R.mipmap.ic_launcher;
}
Intent notificationIntent = new Intent(this, MyBLEService.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(icon)
.setContentIntent(pendingIntent)
.setContentTitle("Got-U BLE Service")
.setContentText("Running");
Notification notification=builder.build();
if(Build.VERSION.SDK_INT>=26) {
NotificationChannel channel = new NotificationChannel(NOTIFICATION_Service_CHANNEL_ID, "Sync Service", NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("Service Name");
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
notification = new Notification.Builder(this,NOTIFICATION_Service_CHANNEL_ID)
.setContentTitle("Got-U BLE Service")
.setContentText("Running")
.setSmallIcon(icon)
.setContentIntent(pendingIntent)
.build();
}
startForeground(2566, notification);
}
private static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
public void BLEServiceSetBluetoothAdapter(BluetoothAdapter btAdapter) {
myBluetoothAdapter = btAdapter;
mBluetoothLeScanner = myBluetoothAdapter.getBluetoothLeScanner();
}
public void BLEServiceSetFragmentManager(FragmentManager myFM) {
fm = myFM;
}
private void sendDataActivity(String action, String name, String value)
{
Intent sendLevel = new Intent();
sendLevel.setAction(action);
sendLevel.putExtra(name, value);
sendBroadcast(sendLevel);
}
class LooperThread extends Thread {
public Handler ThreadHandler;
public void run() {
super.run();
Looper.prepare();
ThreadHandler = new Handler(Looper.myLooper()) {
@SuppressLint("MissingPermission")
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what){
case Constants.ServiceDoNothing:
Log.d("BLE Service", "serviceHandler Value in ServiceDoNothing in Handler: " + myThread.ThreadHandler);
break;
case Constants.ServiceScanForHub:
startScanning(false);
break;
case Constants.ServiceGetCharacteristics:
mBluetoothGATT.discoverServices();
break;
case Constants.ServiceDisconnect:
mBluetoothGATT.close();
break;
case Constants.ServiceEnableNotifications:
setCharacteristicNotification(DE_Characteristic, true);
break;
case Constants.ServiceSendCommand:
// The BLE WriteCharacteristic needs a buffer with the exact
// size of bytes to send. Since HashOutput has a fixed size, we
// need to create another temporary buffer that is the size
// of NumReturnBytes, and copy the data in HashOutput to that buffer:
byte[] CommandSendBuffer = new byte[NumBytesToSend];
System.arraycopy(msg.obj,0,CommandSendBuffer,0,NumBytesToSend);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
mBluetoothGATT.writeCharacteristic(DE_Characteristic, CommandSendBuffer, BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
}
else{
DE_Characteristic.setValue(CommandSendBuffer);
boolean success = mBluetoothGATT.writeCharacteristic(DE_Characteristic);
}
break;
default: break;
}
}
};
Looper.loop();
}
}
@Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work doesn't disrupt our UI.
Log.d("BLE Service", "Creating service thread");
myThread = new LooperThread();
myThread.start();
isRunning = false;
ActivityStatus = Constants.ServiceDoNothing;
MainActivity.ServiceIsActive = false;
mHandler = new Handler();
mScanCallback = null;
startInForeground();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Toast.makeText(this, "service started", Toast.LENGTH_SHORT).show();
Log.d("BLE Service", "Starting service");
isRunning = true;
Log.d("BLE Service", "serviceHandler Value in OnStart: " + myThread.ThreadHandler);
SetActivityStatus(Constants.ServiceDoNothing);
Log.d("BLE Service", "serviceHandler Value in OnStart after sendMessage: " + myThread.ThreadHandler);
MainActivity.ServiceIsActive = true;
sendDataActivity(Constants.MessageFromBLEService, Constants.BLEServiceState, Constants.BLEServiceReady);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// We don't provide binding, so return null
return null;
}
@Override
public void onDestroy() {
MainActivity.ServiceIsActive = false;
isRunning = false;
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
private void DelayedMessageHandler(int what, Object object) {
new Handler().postDelayed(new Runnable() {
public void run() {
Message m = myThread.ThreadHandler.obtainMessage();
m.what = what;
if(object != null){
m.obj = object;
}
myThread.ThreadHandler.sendMessage(m);
}
}, 300);
}
public int GetActivityStatus() {
return ActivityStatus;
}
public void SubmitPassword(String password, int type) {
// Copy password characters into PayloadData:
char[] array;
// Split up password into array of char. since we cannot
// directly index the string as an array:
array = password.toCharArray();
for(int i =0;i<array.length;i++){
PayloadData[i] = (byte) array[i];
}
// Now set/submit the password:
if(type == Constants.NEW_PASSWORD)
LastCommand = Constants.DE_CMD_SET_PASSWORD;
else
LastCommand = Constants.DE_CMD_SUBMIT_PASSWORD;
NumBytesToSend = AssembleCommandResponse(Constants.DATA_PACKET_TYPE_COMMAND,
Constants.ERROR_NO_ERROR,
(byte) LastCommand,
(char) array.length,
(char) 0xFFFF,
(char) array.length,
true);
SetActivityStatus(Constants.ServiceSendCommand);
}
public void SetActivityStatus(int status) {
ActivityStatus = status;
//Log.d("BLE Service", "serviceHandler Value in SetActivityStatus: " + serviceHandler);
switch(status){
case Constants.ServiceDoNothing:
Log.d("BLE Service", "serviceHandler Value in ServiceDoNothing: " + myThread.ThreadHandler);
break;
case Constants.ServiceScanForHub:
DelayedMessageHandler(Constants.ServiceScanForHub, null);
break;
case Constants.ServiceGetCharacteristics:
DelayedMessageHandler(Constants.ServiceGetCharacteristics, null);
break;
case Constants.ServiceEnableNotifications:
DelayedMessageHandler(Constants.ServiceEnableNotifications, null);
break;
case Constants.ServiceSendCommand:
DelayedMessageHandler(Constants.ServiceSendCommand, HashOutput);
break;
default: break;
}
}
...
}
我创建了一个类 LooperThread。然后,我在服务的 onCreate 中创建线程 myThread。 我通过调用启动服务
startService(new Intent(this, MyBLEService.class));
在 MainActivity 中。
在服务的 onStartCommand 中,我初始化了几个参数,然后调用本地过程
sendDataActivity(Constants.MessageFromBLEService, Constants.BLEServiceState, Constants.BLEServiceReady);
此过程通知 MainActivity 服务已启动并正在运行。Main 现在将启用一个显示活动微调器的片段,并开始通过 BLE 与硬件设备通信。执行此操作的方式是在 Service 中调用此过程,案例为 Constants.ServiceScanForHub:
public void SetActivityStatus(int status) {
ActivityStatus = status;
//Log.d("BLE Service", "serviceHandler Value in SetActivityStatus: " + serviceHandler);
switch(status){
case Constants.ServiceDoNothing:
Log.d("BLE Service", "serviceHandler Value in ServiceDoNothing: " + myThread.ThreadHandler);
break;
case Constants.ServiceScanForHub:
DelayedMessageHandler(Constants.ServiceScanForHub, null);
break;
case Constants.ServiceGetCharacteristics:
DelayedMessageHandler(Constants.ServiceGetCharacteristics, null);
break;
case Constants.ServiceEnableNotifications:
DelayedMessageHandler(Constants.ServiceEnableNotifications, null);
break;
case Constants.ServiceSendCommand:
DelayedMessageHandler(Constants.ServiceSendCommand, HashOutput);
break;
default: break;
}
}
这反过来又调用 DelayedMessageHandler(Constants.ServiceScanForHub, null);
private void DelayedMessageHandler(int what, Object object) {
new Handler().postDelayed(new Runnable() {
public void run() {
Message m = myThread.ThreadHandler.obtainMessage();
m.what = what;
if(object != null){
m.obj = object;
}
myThread.ThreadHandler.sendMessage(m);
}
}, 300);
}
这是我遇到问题的地方,特别是在这一行中:
Message m = myThread.ThreadHandler.obtainMessage();
ThreadHandler 的值为 null,即使它在创建线程时不为 null。因此,当然,会引发 Null Pointer 异常,并且应用程序崩溃! 我做了很多阅读和调试,但我无法弄清楚发生了什么。以下是 logcat 信息:
2023-07-23 10:40:42.243 30762-30762 AndroidRuntime com.example.got_u E FATAL EXCEPTION: main
Process: com.example.got_u, PID: 30762
java.lang.NullPointerException: Attempt to read from field 'android.os.Handler com.example.got_u.MyBLEService$LooperThread.ThreadHandler' on a null object reference
at com.example.got_u.MyBLEService$1.run(MyBLEService.java:350)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
2023-07-23 10:40:42.266 30762-30762 Process com.example.got_u I Sending signal. PID: 30762 SIG: 9
---------------------------- PROCESS ENDED (30762) for package com.example.got_u ----------------------------
这是清单信息:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Request legacy Bluetooth permissions on older devices. -->
<uses-permission
android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission
android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" /> <!-- Before Android 12 (but still needed location, even if not requested) -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <!-- From Android 12 -->
<uses-permission
android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.GotU"
tools:targetApi="31">
<service
android:name=".MyBLEService"
android:foregroundServiceType="connectedDevice"
android:process=":connectedDevice_process"
android:enabled="true"
android:exported="false">
</service>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
我真的一直在为这个问题而苦苦挣扎,我希望一个善良的灵魂能够提出一个解决方案。 谢谢大家!
我尝试向 logcat 添加大量调试信息。此外,在线阅读大量内容(主要是在 stackOverflow 中),不幸的是到目前为止没有任何结果。
答: 暂无答案
评论