Android 12下用Java+Flamingo处理蓝牙/NFC进程及后台监听问题
针对Android 12蓝牙/NFC进程自启问题的解决方案
一、定时停止蓝牙/NFC系统进程(后台每10秒执行)
Android 12对后台任务限制严格,短间隔定时任务需使用前台服务避免被系统回收,同时需申请对应权限:
1. 权限声明(AndroidManifest.xml)
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android 13+ 需添加 --> <service android:name=".ProcessKillService" android:foregroundServiceType="dataSync" />
2. 前台服务实现(ProcessKillService.java)
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Intent; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.app.ActivityManager; import android.content.Context; public class ProcessKillService extends Service { private static final int NOTIFICATION_ID = 1001; private static final String CHANNEL_ID = "ProcessKillChannel"; private Handler handler; private Runnable killRunnable; @Override public void onCreate() { super.onCreate(); createNotificationChannel(); startForeground(NOTIFICATION_ID, getForegroundNotification()); handler = new Handler(Looper.getMainLooper()); killRunnable = new Runnable() { @Override public void run() { killSystemProcesses(); handler.postDelayed(this, 10000); // 每10秒执行一次 } }; handler.post(killRunnable); } private void killSystemProcesses() { ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); // 停止蓝牙进程 am.killBackgroundProcesses("com.android.bluetooth"); // 停止NFC进程 am.killBackgroundProcesses("com.android.nfc"); } private Notification getForegroundNotification() { Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) .setContentTitle("进程监控") .setContentText("正在监控并停止蓝牙/NFC进程") .setSmallIcon(R.drawable.ic_notification); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId(CHANNEL_ID); } return builder.build(); } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "进程监控频道", NotificationManager.IMPORTANCE_LOW ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { super.onDestroy(); if (handler != null && killRunnable != null) { handler.removeCallbacks(killRunnable); } } }
3. 在MainActivity启动服务
import android.content.Intent; import android.os.Build; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 启动前台服务 Intent serviceIntent = new Intent(this, ProcessKillService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } } }
二、解决编译前权限缺失与布局未约束问题
1. 权限缺失修复
- 确保上述权限已在
AndroidManifest.xml中声明 - 动态申请危险权限(Android 6.0+):在MainActivity中添加权限申请逻辑
import android.Manifest; import android.content.pm.PackageManager; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; private static final int REQUEST_PERMISSIONS = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); checkAndRequestPermissions(); } private void checkAndRequestPermissions() { String[] permissions = { Manifest.permission.KILL_BACKGROUND_PROCESSES, Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.POST_NOTIFICATIONS }; boolean needRequest = false; for (String perm : permissions) { if (ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) { needRequest = true; break; } } if (needRequest) { ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSIONS); } else { // 权限已授予,启动服务 startProcessKillService(); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PERMISSIONS) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (allGranted) { startProcessKillService(); } } } private void startProcessKillService() { Intent serviceIntent = new Intent(this, ProcessKillService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } }
2. 布局未约束修复
使用ConstraintLayout确保所有视图都有明确约束,示例activity_main.xml:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <!-- 蓝牙状态日志显示控件 --> <TextView android:id="@+id/bluetooth_log_tv" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" android:textSize="14sp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
所有视图需通过app:layout_constraintXXX属性绑定到父布局或其他视图,避免出现"Unconstrained view"警告。
三、后台持续监听蓝牙状态与触发源记录
1. 蓝牙状态监听(前台服务中注册广播接收器)
在ProcessKillService中添加蓝牙状态广播监听:
import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import androidx.localbroadcastmanager.content.LocalBroadcastManager; private BroadcastReceiver bluetoothStateReceiver; private StringBuilder logBuilder = new StringBuilder(); @Override public void onCreate() { super.onCreate(); // ... 原有代码 ... // 注册蓝牙状态广播接收器 bluetoothStateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); switch (state) { case BluetoothAdapter.STATE_TURNING_ON: logBuilder.append("[" + System.currentTimeMillis() + "] 蓝牙正在启动\n"); // 尝试获取触发源 detectBluetoothTrigger(); sendLogToActivity(); break; case BluetoothAdapter.STATE_ON: logBuilder.append("[" + System.currentTimeMillis() + "] 蓝牙已启动\n"); sendLogToActivity(); break; case BluetoothAdapter.STATE_TURNING_OFF: logBuilder.append("[" + System.currentTimeMillis() + "] 蓝牙正在关闭\n"); sendLogToActivity(); break; case BluetoothAdapter.STATE_OFF: logBuilder.append("[" + System.currentTimeMillis() + "] 蓝牙已关闭\n"); sendLogToActivity(); break; } } } }; IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver(bluetoothStateReceiver, filter); } // 发送日志到MainActivity显示 private void sendLogToActivity() { Intent intent = new Intent("UPDATE_BLUETOOTH_LOG"); intent.putExtra("LOG", logBuilder.toString()); LocalBroadcastManager.getInstance(this).sendBroadcast(intent); } @Override public void onDestroy() { super.onDestroy(); // ... 原有代码 ... if (bluetoothStateReceiver != null) { unregisterReceiver(bluetoothStateReceiver); } }
2. 蓝牙触发源检测(局限性说明)
Android没有公开API直接获取启动蓝牙的应用,普通应用可通过以下方式尝试检测:
- 读取系统日志:需
READ_LOGS权限(系统签名权限,普通应用无法获取),若有系统签名可执行以下代码:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; private void detectBluetoothTrigger() { try { Process process = Runtime.getRuntime().exec("logcat -d -s BluetoothManagerService"); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { if (line.contains("enable")) { // 解析日志中的包名,示例日志格式:"BluetoothManagerService: enable called by com.example.app" int index = line.indexOf("called by "); if (index != -1) { String packageName = line.substring(index + 10); logBuilder.append("触发源:" + packageName + "\n"); } break; } } reader.close(); } catch (IOException e) { e.printStackTrace(); } }
- 替代方案:使用
UsageStatsManager获取蓝牙启动前后的活跃应用,需PACKAGE_USAGE_STATS权限(用户需手动在设置中授权):
import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import java.util.List; import java.util.Calendar; private void detectBluetoothTrigger() { UsageStatsManager usm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); Calendar calendar = Calendar.getInstance(); long endTime = calendar.getTimeInMillis(); calendar.add(Calendar.SECOND, -30); // 检查过去30秒内的应用 long startTime = calendar.getTimeInMillis(); List<UsageStats> usageStatsList = usm.queryUsageStats(UsageStatsManager.INTERVAL_BEST, startTime, endTime); if (usageStatsList != null && !usageStatsList.isEmpty()) { UsageStats recentStats = usageStatsList.get(0); logBuilder.append("疑似触发源:" + recentStats.getPackageName() + "\n"); } }
3. MainActivity接收日志并显示
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.widget.TextView; private TextView bluetoothLogTv; private BroadcastReceiver logUpdateReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bluetoothLogTv = findViewById(R.id.bluetooth_log_tv); // 注册本地广播接收器接收日志 logUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String log = intent.getStringExtra("LOG"); bluetoothLogTv.setText(log); } }; LocalBroadcastManager.getInstance(this).registerReceiver(logUpdateReceiver, new IntentFilter("UPDATE_BLUETOOTH_LOG")); checkAndRequestPermissions(); } @Override protected void onDestroy() { super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(logUpdateReceiver); }
内容的提问来源于stack exchange,提问作者Ho Yeung - Lee-Maiva
相关产品推荐
相关产品推荐

