我正在开发一个需要前台服务来通过蓝牙与计算机同步数据的 Android 应用程序。前台服务在应用程序首次运行的会话期间完美运行。但是,如果我重新启动手机,即使我在 onStartCommand 函数中返回了 START_STICKY,该服务也不会在重新启动时重新启动。我希望它在重新启动后尽快启动,就像我的 VPN 一样。我怎样才能实现这个功能?
这是有问题的代码:
package com.example.app;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Set;
import static com.example.app.App.CONNECTED_DEVICES_CHANNEL_ID;
public class BluetoothSyncService extends Service {
private Utils utils;
private final BluetoothAdapter BLUETOOTH_ADAPTER = BluetoothAdapter.getDefaultAdapter();
private final String CONNECTED_PC_GROUP = "connectedPCS";
private final ArrayList<String> NOTIFIED_PC_ADDRESSES = new ArrayList<>();
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
utils = Utils.getInstance(this);
NotificationCompat.Builder summaryNotificationBuilder =
new NotificationCompat.Builder(this,
CONNECTED_DEVICES_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_placeholder_logo)
.setContentTitle("Sync Service Background")
.setGroup(CONNECTED_PC_GROUP)
.setGroupSummary(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setOngoing(true);
int SUMMARY_NOTIFICATION_ID = 69;
startForeground(SUMMARY_NOTIFICATION_ID, summaryNotificationBuilder.build());
Thread serviceThread = new Thread(() -> {
while (true) {
handleConnectedDevices();
handleNotifications();
}
});
serviceThread.start();
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void handleConnectedDevices() {
Set<BluetoothDevice> pairedDevices = BLUETOOTH_ADAPTER.getBondedDevices();
// Handle connected PCS
for (BluetoothDevice device : pairedDevices) {
if (isConnected(device.getAddress())) {
int deviceClass = device.getBluetoothClass().getDeviceClass();
if (deviceClass == BluetoothClass.Device.COMPUTER_LAPTOP |
deviceClass == BluetoothClass.Device.COMPUTER_DESKTOP) {
if (!utils.inPairedPCS(device.getAddress())) {
utils.addToPairedPCS(new PairedPC(device.getName(),
device.getAddress(), true));
} else {
if (utils.getPairedPCByAddress(device.getAddress()) != null) {
utils.getPairedPCByAddress(device.getAddress()).setConnected(true);
utils.savePairedPCSToDevice();
}
}
}
} else {
if (utils.inPairedPCS(device.getAddress())) {
if (utils.getPairedPCByAddress(device.getAddress()) != null) {
utils.getPairedPCByAddress(device.getAddress()).setConnected(false);
utils.savePairedPCSToDevice();
}
}
}
}
}
private void handleNotifications() {
NotificationManagerCompat notificationManager = NotificationManagerCompat
.from(this);
for (PairedPC pairedPC : utils.getPairedPCS()) {
int CONNECTION_NOTIFICATION_ID = 420;
if (pairedPC.isConnected()) {
if (pairedPC.isActive() && !NOTIFIED_PC_ADDRESSES.contains(pairedPC.getAddress())) {
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(
this, CONNECTED_DEVICES_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_pc)
.setContentTitle("Syncing PC")
.setContentText(pairedPC.getName())
.setGroup(CONNECTED_PC_GROUP)
.setPriority(NotificationCompat.PRIORITY_MIN)
.setOngoing(true);
notificationManager.notify(pairedPC.getAddress(),
CONNECTION_NOTIFICATION_ID, notificationBuilder.build());
NOTIFIED_PC_ADDRESSES.add(pairedPC.getAddress());
} else if (!pairedPC.isActive()) {
notificationManager.cancel(pairedPC.getAddress(), CONNECTION_NOTIFICATION_ID);
NOTIFIED_PC_ADDRESSES.remove(pairedPC.getAddress());
}
} else {
if (NOTIFIED_PC_ADDRESSES.contains(pairedPC.getAddress())) {
notificationManager.cancel(pairedPC.getAddress(), CONNECTION_NOTIFICATION_ID);
NOTIFIED_PC_ADDRESSES.remove(pairedPC.getAddress());
}
}
}
}
private boolean isConnected(String address) {
Set<BluetoothDevice> pairedDevices = BLUETOOTH_ADAPTER.getBondedDevices();
for (BluetoothDevice device : pairedDevices) {
if (device.getAddress().equals(address)) {
Method method = null;
try {
method = device.getClass().getMethod("isConnected", (Class[]) null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
boolean connected = false;
try {
assert method != null;
Object methodInvocation = method.invoke(device, (Object[]) null);
if (methodInvocation != null) {
connected = (boolean) methodInvocation;
} else {
connected = false;
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return connected;
}
}
return false;
}
}
编辑:
所以我尝试按照建议使用广播接收器。然而它仍然无法正常工作。这是接收器的代码:
package com.example.app;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Intent syncServiceIntent = new Intent(context, BluetoothSyncService.class);
context.startService(syncServiceIntent);
}
}
}
这是我的清单:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name=".App"
android:theme="@style/Theme.App">
<receiver android:name=".BootReceiver" android:enabled="true" android:exported="true">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
<activity android:name=".PermissionsActivity" />
<activity android:name=".MainActivity" />
<activity android:name=".StartupActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".BluetoothSyncService"/>
</application>
</manifest>
编辑 2:
解决了!不得不改变 context.startService(syncServiceIntent);到 context.startForegroundService(syncServiceIntent);