Android8.0 后台服务保活的一种思路

Stars-one 2022年05月05日 48次浏览 本篇字数为5,625字

本文为作者原创,转载请注明出处,谢谢配合
作者:Stars-one
链接:https://stars-one.site/2022/05/05/android-service-background


项目中有个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,谷歌官方系统进行测试),不过未在其他系统的手机上尝试过

原本现场的设备也就是一个华为平板,而且是鸿蒙系统的,不过后续反馈是可以的,就先这样吧