Android8.0 後臺服務保活的一種思路

原文地址:Android8.0 後臺服務保活的一種思路 | Stars-One的雜貨小窩

項目中有個MQ服務,需要一直連着,接收到消息會發送語音,且手機要在鎖屏也要實現此功能

目前是使用廣播機制實現,每次MQ收到消息,觸發一次啓動服務操作邏輯

在Android11版本測試成功,可實現上述功能

步驟

具體流程:

  1. 進入APP
  2. 開啓後臺服務Service
  3. 後臺服務Service開啓線程,連接MQ
  4. MQ的消費事件,發送廣播
  5. 廣播接收器中,處理啓動服務(若服務已被關閉)和文本語音播放功能

1.廣播註冊

<receiver
    android:name=".receiver.MyReceiver"
    android:enabled="true"
    android:exported="true">
</receiver>
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        //匹配下之前定義的action
        if ("OPEN_SERVICE".equals(action)) {
            if (!ServiceUtils.isServiceRunning(MqMsgService.class)) {
                Log.e("--test", "服務未啓動,先啓動服務");
                Intent myIntent = new Intent(context, MqMsgService.class);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    context.startForegroundService(intent);
                } else {
                    context.startService(intent);
                }

            }

            String text = intent.getStringExtra("text");
            Log.e("--test", "廣播傳的消息"+text);

            EventBus.getDefault().post(new SpeakEvent(text));
        }
    }
}

語音初始化的相關操作都在服務中進行的,這裏不再贅述(通過EventBus轉發時間事件)

這裏需要注意的是,Android8.0版本,廣播不能直接startService()啓動服務,而是要通過startForegroundService()方法,而調用了startForegroundService()方法,則是需要服務在5s內調用一個方法startForeground()

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    Notification notification = NotifyUtil.sendNotification(this, "平板", "後臺MQ服務運行中", NotificationCompat.PRIORITY_HIGH);
    startForeground(1, notification);
}

上面這段代碼,就是寫在Service中的onCreate方法內,之前也是找到有資料說,需要有通知欄,服務纔不會被Android系統給關閉,也不知道有沒有起到作用😂

還需要注意的是,需要聲明權限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
NotifyUtil工具類代碼
public class NotifyUtil {
    private static String channel_id="myChannelId";
    private static String channel_name="新消息";
    private static String description = "新消息通知";
    private static int notifyId = 0;
    private static NotificationManager notificationManager;

    public static void createNotificationChannel(){
        if (notificationManager != null) {
            return;
        }
        //Android8.0(API26)以上需要調用下列方法,但低版本由於支持庫舊,不支持調用
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel channel = new NotificationChannel(channel_id,channel_name,importance);
            channel.setDescription(description);
            notificationManager = (NotificationManager) ActivityUtils.getTopActivity().getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.createNotificationChannel(channel);
        }else{
            notificationManager = (NotificationManager) ActivityUtils.getTopActivity().getSystemService(Context.NOTIFICATION_SERVICE);
        }
    }

    public static void sendNotification(String title,String text){
        createNotificationChannel();
        Notification notification = new NotificationCompat.Builder(ActivityUtils.getTopActivity(),channel_id)
                .setContentTitle(title)
                .setContentText(text)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(ResourceUtils.getMipmapIdByName("ic_launcher"))
                .setLargeIcon(BitmapFactory.decodeResource(ActivityUtils.getTopActivity().getResources(), ResourceUtils.getMipmapIdByName("ic_launcher")))
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .build();
        notificationManager.notify(notifyId++,notification);
    }

    public static Notification sendNotification(Context context,String title,String text,int priority){
        createNotificationChannel();
        Notification notification = new NotificationCompat.Builder(context,channel_id)
                .setContentTitle(title)
                .setContentText(text)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(ResourceUtils.getMipmapIdByName("ic_launcher"))
                .setLargeIcon(BitmapFactory.decodeResource(ActivityUtils.getTopActivity().getResources(), ResourceUtils.getMipmapIdByName("ic_launcher")))
                .setPriority(priority)
                .build();
        notificationManager.notify(notifyId++,notification);
        return notification;
    }

    public static void sendNotification(String title, String text, int priority, PendingIntent pendingIntent){
        createNotificationChannel();
        Notification notification = new NotificationCompat.Builder(ActivityUtils.getTopActivity(),channel_id)
                .setContentTitle(title)
                .setContentText(text)
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(ResourceUtils.getMipmapIdByName("ic_launcher"))
                .setLargeIcon(BitmapFactory.decodeResource(ActivityUtils.getTopActivity().getResources(), ResourceUtils.getMipmapIdByName("ic_launcher")))
                .setPriority(priority)
                .setContentIntent(pendingIntent)
                .build();
        notificationManager.notify(notifyId++,notification);
    }
}

2.服務

聲明一個服務,然後在服務中開啓一個線程,用來連接MQ,MQ的消費事件中,發送廣播

//發出一條廣播
String ALARM_ACTION_CODE = "OPEN_SERVICE";
Intent intent = new Intent(ALARM_ACTION_CODE);
//適配8.0以上(不然沒法發出廣播) 顯式聲明組件
if (DeviceUtils.getSDKVersionCode() > Build.VERSION_CODES.O) {
    intent.setComponent(new ComponentName(context, MyReceiver.class));
}
intent.putExtra("text", msg);
context.sendBroadcast(intent);

之後大體上就是測試了,打開APP,然後直接返回桌面,大概1分鐘後,APP就無法播放語音

而使用了上述的思路,不管是鎖屏還是回到桌面(測試使用的是Android11,谷歌官方系統),都可以實現語音播放,不過未在其他系統的手機上嘗試過

原本現場的設備也就是一個華爲平板,而且是鴻蒙系統的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章