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


    本文为作者原创,转载请注明出处,谢谢配合
    作者:stars-one
    链接:https://www.cnblogs.com/stars-one/p/16149722.html

    本篇大约有4861个字,阅读预计需要6.08分钟


    原文地址: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,谷歌官方系统),都可以实现语音播放,不过未在其他系统的手机上尝试过

    原本现场的设备也就是一个华为平板,而且是鸿蒙系统的

    补充参考

  • 相关阅读:
    全栈测试工程师是怎么样练成的?
    go 使用 odbc 报错 SQLSetEnvUIntPtrAttr
    智能优化算法Matlab源码大礼包领取
    为什么说Java不适合做游戏开发,劣势在哪里?
    Bandizip批量解压遇到的问题
    15天获取20万用户,小程序如何做到?
    【随笔】Git 高级篇 -- 分离 HEAD(十一)
    调用 sap.ui.base.ManagedObject 的构造函数时,如何传递绑定路径进去
    如何设置网络访问 SQL Express?
    数据查询必备技能SQL调优:Mysql什么情况下不走索引
  • 原文地址:https://www.cnblogs.com/stars-one/p/16149722.html