Android通知正确创建方式及后台Service实现合理性咨询
Hey there! Let's break down your questions one by one to get your Android app on the right track.
Starting from Android 8.0 (API Level 26), all notifications must belong to a NotificationChannel—otherwise, they won't show up at all. Here's the correct, up-to-date way to create notifications for both Java and Kotlin:
Java Implementation
public class ConnectionService extends Service { private static final String CHANNEL_ID = "BluetoothConnectionChannel"; private static final int NOTIFICATION_ID = 327; @Override public void onCreate() { super.onCreate(); // Create notification channel (required for API 26+) createNotificationChannel(); // Initialize foreground service with notification startForegroundWithNotification(); } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { CharSequence channelName = getString(R.string.bluetooth_channel_name); String channelDesc = getString(R.string.bluetooth_channel_desc); int importance = NotificationManager.IMPORTANCE_LOW; // Low priority to avoid user disruption NotificationChannel channel = new NotificationChannel(CHANNEL_ID, channelName, importance); channel.setDescription(channelDesc); // Register channel with system NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); } } private void startForegroundWithNotification() { Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT // Mandatory for Android 12+ ); Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("Bluetooth Connected") .setContentText("Listening for incoming calls in background") .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_LOW) .build(); startForeground(NOTIFICATION_ID, notification); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } @Override public IBinder onBind(Intent intent) { throw new UnsupportedOperationException("Not yet implemented"); } @Override public void onDestroy() { stopForeground(true); super.onDestroy(); } }
Kotlin Implementation
class ConnectionService : Service() { private val CHANNEL_ID = "BluetoothConnectionChannel" private val NOTIFICATION_ID = 327 override fun onCreate() { super.onCreate() createNotificationChannel() startForegroundWithNotification() } private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelName = getString(R.string.bluetooth_channel_name) val channelDesc = getString(R.string.bluetooth_channel_desc) val importance = NotificationManager.IMPORTANCE_LOW val channel = NotificationChannel(CHANNEL_ID, channelName, importance).apply { description = channelDesc } val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } private fun startForegroundWithNotification() { val notificationIntent = Intent(this, MainActivity::class.java) val pendingIntent = PendingIntent.getActivity( this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) val notification = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("Bluetooth Connected") .setContentText("Listening for incoming calls in background") .setContentIntent(pendingIntent) .setPriority(NotificationCompat.PRIORITY_LOW) .build() startForeground(NOTIFICATION_ID, notification) } override fun onBind(intent: Intent): IBinder? { throw UnsupportedOperationException("Not yet implemented") } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { return START_STICKY } override fun onDestroy() { stopForeground(true) super.onDestroy() } }
Key Notes:
- Use
PendingIntent.FLAG_IMMUTABLE(safer if you don't need to modify the intent later) orFLAG_MUTABLE(required if you need to update the intent) for Android 12+. - Choose
IMPORTANCE_LOWfor background service notifications to avoid interrupting users.
Your core goal is to maintain a Bluetooth connection and listen for incoming calls in both foreground and background. Here's the breakdown:
What's Working Well
START_STICKY: If the system kills your service due to low memory, it will restart the service (without re-delivering the original intent) — perfect for persistent connection services.- Foreground Service: Long-running services need to be foregrounded to avoid being terminated by Android's background process limits. This aligns with your need for constant Bluetooth connectivity.
- Start/Stop on Bluetooth Connect/Disconnect: This logic is efficient, as it prevents the service from running unnecessarily when no Bluetooth connection exists.
Key Improvements Needed
- Missing Bluetooth Connection Logic: Your current service has no code to handle Bluetooth connections. You need to:
- Register broadcast receivers for Bluetooth state changes (
ACTION_ACL_CONNECTED,ACTION_ACL_DISCONNECTED). - Initialize and manage the Bluetooth socket connection in
onCreate().
- Register broadcast receivers for Bluetooth state changes (
- Incoming Call Listening: Add logic to monitor calls:
- Register a broadcast receiver for
ACTION_PHONE_STATE_CHANGEDor useTelephonyManager's callback API. - Request necessary permissions:
READ_PHONE_STATE(Android 10 and below) orREAD_CALL_LOG(Android 11+, more permissive).
- Register a broadcast receiver for
- Service Startup: Use
startForegroundService()instead ofstartService()for Android 8.0+ to avoidIllegalStateException. - Resource Cleanup: In
onDestroy(), unregister broadcast receivers, close the Bluetooth socket, and stop call listeners to prevent memory leaks.
Example Kotlin Snippet for Bluetooth + Call Listening
private lateinit var telephonyManager: TelephonyManager private val phoneStateReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val state = intent?.getStringExtra(TelephonyManager.EXTRA_STATE) if (state == TelephonyManager.EXTRA_STATE_RINGING) { val incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER) // Handle incoming call (e.g., send notification via Bluetooth) } } } override fun onCreate() { super.onCreate() // Initialize call listener telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager registerReceiver(phoneStateReceiver, IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) // Initialize Bluetooth connection (example) val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() if (bluetoothAdapter.isEnabled) { // Connect to your target Bluetooth device here } createNotificationChannel() startForegroundWithNotification() } override fun onDestroy() { unregisterReceiver(phoneStateReceiver) // Close Bluetooth socket connection stopForeground(true) super.onDestroy() }
Final Verdict
Your service framework is a solid foundation. Once you add the Bluetooth connection management, call listening logic, and permission handling, it will fully meet your requirements for persistent Bluetooth connectivity and call monitoring.
内容的提问来源于stack exchange,提问作者João Ghignatti

