精华内容
下载资源
问答
  • 高版本Service保活

    2019-04-12 10:33:47
    Service保活思路,分两步: 一、优化Service优先级,降低被杀死的概率 二、杀死后拉起 Service优化: 1、增加通知,设置为前台服务 NotificationCompat.Builder nb = new NotificationCompat.Builder(this); nb....

    Service保活思路,分两步:
    一、优化Service优先级,降低被杀死的概率
    二、杀死后拉起

    Service优化:
    1、增加通知,设置为前台服务
    NotificationCompat.Builder nb = new NotificationCompat.Builder(this);
    nb.setOngoing(true);
    nb.setContentTitle(getString(R.string.app_name));
    nb.setContentText(getString(R.string.app_name));
    nb.setSmallIcon(R.drawable.icon);
    PendingIntent pendingintent =PendingIntent.getActivity(this, 0, new Intent(this, Main.class), 0);
    nb.setContentIntent(pendingIntent);
    startForeground(1423, nb.build());

    2、Manifest属性设置
    android:priority=“1000”//优先级
    android:enabled=“true”//是否可被系统启动

    3、START_STICKY
    在Services的onStartCommand方法return START_STICKY
    Services在被杀死后,可被系统拉起,第一次5s/第二次10s/时间依次翻倍
    1)START_STICKY:在Service被关闭后,重新开启
    2)Service
    START_NOT_STICKY:服务被异常杀掉后,系统将会被设置为started状态,系统不会重启该服务,直到startService(Intent intent)方法再次被调用。
    3)
    START_REDELIVER_INTENT:重传Intent,使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。

    4)START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

    4、使用android:process属性
    让service运行在独立进程当中,使得应用后台被kill时,不会影响service
    android:process=":location"
    查看进程命令:
    1)先进入shell模式
    adb shell
    2)grep命令过滤包名
    ps|grep net.xfxb.blissdelivery

    私有进程和公有进程的区别:
    私有进程,android:process的值以冒号开头的话,那么该进程就是私有进程;其他应用的组件不可以和它跑在同一个进程中。
    全局进程,进程名称不以“:”开头的进程都可以叫全局进程,如android:process=“com.secondProcess”,以小写字母开头,表示运行在一个以这个名字命名的全局进

    程中,其他应用通过设置相同的ShareUID可以和它跑在同一个进程。
    --------------------以上优化方案,Service在后台运行不锁屏状态下正常运行,锁屏后台运行状态大概能持续15分钟左右--------

    Service拉起:
    待续

    展开全文
  • Android之service保活

    千次阅读 2017-03-20 18:02:17
    这段时间做的项目需要service保活,也就是实时监听设备的通知栏消息,并可以捕获到通知的内容,然后进行对应的操作。之前尝试过很多方式,最后感觉前台service对于服务保活相对好使(据说这个微信也用过的方案),...

    这段时间做的项目需要service保活,也就是实时监听设备的通知栏消息,并可以捕获到通知的内容,然后进行对应的操作。之前尝试过很多方式,最后感觉前台service对于服务保活相对好使(据说这个微信也用过的方案),知情者可能要问了:前台service不是有个通知栏一直显示么?这样对用户来说不是很好。我们这里可以使用两个service互调来实现不显示通知栏,原理如下:

    对于 API level < 18 :调用startForeground(ID, new Notification()),发送空的Notification ,图标则不会显示。
    对于 API level >= 18:在需要提优先级的service A启动一个InnerService,两个服务同时startForeground,且绑定同样的 ID。Stop 掉InnerService ,这样通知栏图标即被移除。
    前台服务类:

    public class ForeService extends Service {
    
        private final int FORESERVICE_PID = android.os.Process.myPid();
        private AssistServiceConnection mConnection;
    
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            /**
             * 之前的额前台service(会显示通知栏)
             */
    /*        //定义一个notification
            Notification.Builder builder1 = new Notification.Builder(this);
            builder1.setSmallIcon(R.mipmap.ic_launcher); //设置图标
    //        builder1.setTicker("新消息");
            builder1.setContentTitle("My title"); //设置标题
            builder1.setContentText("My content"); //消息内容
    //        builder1.setContentInfo("");//补充内容
    //        builder1.setWhen(System.currentTimeMillis()); //发送时间
    //        builder1.setDefaults(Notification.DEFAULT_ALL); //设置默认的提示音,振动方式,灯光
    //        builder1.setAutoCancel(true);//打开程序后图标消失
            Intent intent = new Intent(this, MainActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
            builder1.setContentIntent(pendingIntent);
            Notification notification1 = builder1.build();
            //把该service创建为前台service
            startForeground(1, notification1);*/
    
            setForeground();
        }
    
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            return super.onStartCommand(intent, flags, startId);
        }
    
        private void setForeground() {
            //如果sdk<18 , 直接调用startForeground即可,不会在通知栏创建通知
            if (Build.VERSION.SDK_INT < 18) {
                this.startForeground(FORESERVICE_PID, getNotification());
                return;
            }
    
            if (null == mConnection) {
                mConnection = new AssistServiceConnection();
            }
    
            this.bindService(new Intent(this, AssistService.class), mConnection,
                    Service.BIND_AUTO_CREATE);
        }
    
        public Notification getNotification() {
            //定义一个notification
            Notification.Builder builder1 = new Notification.Builder(this);
            builder1.setSmallIcon(R.mipmap.ic_launcher); //设置图标
    //        builder1.setTicker("新消息");
            builder1.setContentTitle("My title"); //设置标题
            builder1.setContentText("My content"); //消息内容
    //        builder1.setContentInfo("");//补充内容
    //        builder1.setWhen(System.currentTimeMillis()); //发送时间
    //        builder1.setDefaults(Notification.DEFAULT_ALL); //设置默认的提示音,振动方式,灯光
    //        builder1.setAutoCancel(true);//打开程序后图标消失
            Intent intent = new Intent(this, MainActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
            builder1.setContentIntent(pendingIntent);
            Notification notification1 = builder1.build();
            return notification1;
        }
    
    
        private class AssistServiceConnection implements ServiceConnection {
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder binder) {
                // sdk >=18 的,会在通知栏显示service正在运行,这里不要让用户感知,所以这里的实现方式是利用2个同进程的service,利用相同的notificationID,
                // 2个service分别startForeground,然后只在1个service里stopForeground,这样即可去掉通知栏的显示
                Service assistService = ((AssistService.LocalBinder) binder)
                        .getService();
                ForeService.this.startForeground(FORESERVICE_PID, getNotification());
                assistService.startForeground(FORESERVICE_PID, getNotification());
                assistService.stopForeground(true);
    
                ForeService.this.unbindService(mConnection);
                mConnection = null;
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        }
    }
    
    辅佐服务类:

    public class AssistService extends Service {
    
        public class LocalBinder extends Binder {
            public AssistService getService() {
                return AssistService.this;
            }
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new LocalBinder();
        }
    }
    然后开启前台service即可。


    下面给介绍一种新的保活方案:NotificationListenerService。NotificationListenerService就是一个监听通知的服务,只要手机收到了通知,NotificationListenerService都能监听到,即时用户把进程杀死,也能重启。

    NotificationListenerService 的使用范围也挺广的,比如我们熟知的抢红包,智能手表同步通知,通知栏去广告工具等,都是利用它来完成的。所以,我也想赶时髦地好好利用这把“利器”。最后方案也就出来了:在 Android 4.3 以下(API < 18)使用 AccessibilityService 来读取新通知,在 Android 4.3 及以上(API >= 18)使用 NotificationListenerService 来满足需求。

    NotificationListenerService

    在这里,我们就做一个小需求:实时检测微信的新通知,如果该通知是微信红包的话,就进入微信聊天页面。

    首先创建一个 WeChatNotificationListenerService 继承 NotificationListenerService 。然后在 AndroidManifest.xml 中进行声明相关权限和 <intent-filter> :

    1
    2
    3
    4
    5
    6
    7
    <service android:name="com.yuqirong.listenwechatnotification.WeChatNotificationListenerService"
    android:label="@string/app_name"
    android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">

    <intent-filter>
    <action android:name="android.service.notification.NotificationListenerService" />
    </intent-filter>
    </service>

    然后一般会重写下面这三个方法:

    • onNotificationPosted(StatusBarNotification sbn) :当有新通知到来时会回调;
    • onNotificationRemoved(StatusBarNotification sbn) :当有通知移除时会回调;
    • onListenerConnected() :当 NotificationListenerService 是可用的并且和通知管理器连接成功时回调。

    onNotificationPosted(StatusBarNotification sbn)

    下面我们来看看 NotificationListenerService 中的重点: onNotificationPosted(StatusBarNotification sbn) 方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
    // 如果该通知的包名不是微信,那么 pass 掉
    if (!"com.tencent.mm".equals(sbn.getPackageName())) {
    return;
    }
    Notification notification = sbn.getNotification();
    if (notification == null) {
    return;
    }
    PendingIntent pendingIntent = null;
    // 当 API > 18 时,使用 extras 获取通知的详细信息
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    Bundle extras = notification.extras;
    if (extras != null) {
    // 获取通知标题
    String title = extras.getString(Notification.EXTRA_TITLE, "");
    // 获取通知内容
    String content = extras.getString(Notification.EXTRA_TEXT, "");
    if (!TextUtils.isEmpty(content) && content.contains("[微信红包]")) {
    pendingIntent = notification.contentIntent;
    }
    }
    } else {
    // 当 API = 18 时,利用反射获取内容字段
    List<String> textList = getText(notification);
    if (textList != null && textList.size() > 0) {
    for (String text : textList) {
    if (!TextUtils.isEmpty(text) && text.contains("[微信红包]")) {
    pendingIntent = notification.contentIntent;
    break;
    }
    }
    }
    }
    // 发送 pendingIntent 以此打开微信
    try {
    if (pendingIntent != null) {
    pendingIntent.send();
    }
    } catch (PendingIntent.CanceledException e) {
    e.printStackTrace();
    }
    }

    从上面的代码可知,对于分析 Notification 的内容分为了两种:

    • 当 API > 18 时,利用 Notification.extras 来获取通知内容。extras 是在 API 19 时被加入的;
    • 当 API = 18 时,利用反射获取 Notification 中的内容。具体的代码在下方。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    public List<String> getText(Notification notification) {
    if (null == notification) {
    return null;
    }
    RemoteViews views = notification.bigContentView;
    if (views == null) {
    views = notification.contentView;
    }
    if (views == null) {
    return null;
    }
    // Use reflection to examine the m_actions member of the given RemoteViews object.
    // It's not pretty, but it works.
    List<String> text = new ArrayList<>();
    try {
    Field field = views.getClass().getDeclaredField("mActions");
    field.setAccessible(true);
    @SuppressWarnings("unchecked")
    ArrayList<Parcelable> actions = (ArrayList<Parcelable>) field.get(views);
    // Find the setText() and setTime() reflection actions
    for (Parcelable p : actions) {
    Parcel parcel = Parcel.obtain();
    p.writeToParcel(parcel, 0);
    parcel.setDataPosition(0);
    // The tag tells which type of action it is (2 is ReflectionAction, from the source)
    int tag = parcel.readInt();
    if (tag != 2) continue;
    // View ID
    parcel.readInt();
    String methodName = parcel.readString();
    if (null == methodName) {
    continue;
    } else if (methodName.equals("setText")) {
    // Parameter type (10 = Character Sequence)
    parcel.readInt();
    // Store the actual string
    String t = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel).toString().trim();
    text.add(t);
    }
    parcel.recycle();
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    return text;
    }

    凭着 onNotificationPosted(StatusBarNotification sbn) 方法就已经可以完成监听微信通知并打开的动作了。下面我们来看一下其他关于 NotificationListenerService 的二三事。

    取消通知

    有了监听,NotificationListenerService 自然提供了可以取消通知的方法。取消通知的方法有:

    • cancelNotification(String key) :是 API >= 21 才可以使用的。利用 StatusBarNotification 的 getKey() 方法来获取 key 并取消通知。
    • cancelNotification(String pkg, String tag, int id) :在 API < 21 时可以使用,在 API >= 21 时使用此方法来取消通知将无效,被废弃。

    最后,取消通知的方法:

    1
    2
    3
    4
    5
    6
    7
    public void cancelNotification(StatusBarNotification sbn) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    cancelNotification(sbn.getKey());
    } else {
    cancelNotification(sbn.getPackageName(), sbn.getTag(), sbn.getId());
    }
    }

    检测通知监听服务是否被授权

    1
    2
    3
    4
    5
    6
    7
    public boolean isNotificationListenerEnabled(Context context) {
    Set<String> packageNames = NotificationManagerCompat.getEnabledListenerPackages(this);
    if (packageNames.contains(context.getPackageName())) {
    return true;
    }
    return false;
    }

    打开通知监听设置页面

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public void openNotificationListenSettings() {
    try {
    Intent intent;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
    intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
    } else {
    intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
    }
    startActivity(intent);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    被杀后再次启动时,监听不生效的问题

    这个问题来源于知乎问题: NotificationListenerService不能监听到通知,研究了一天不知道是什么原因?

    从问题的回答中可以了解到,是因为 NotificationListenerService 被杀后再次启动时,并没有去 bindService ,所以导致监听效果无效。

    最后,在回答中还给出了解决方案:利用 NotificationListenerService 先 disable 再 enable ,重新触发系统的 rebind 操作。代码如下:

    1
    2
    3
    4
    5
    6
    7
    private void toggleNotificationListenerService() {
    PackageManager pm = getPackageManager();
    pm.setComponentEnabledSetting(new ComponentName(this, com.fanwei.alipaynotification.ui.AlipayNotificationListenerService.class),
    PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    pm.setComponentEnabledSetting(new ComponentName(this, com.fanwei.alipaynotification.ui.AlipayNotificationListenerService.class),
    PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
    }

    该方法使用前提是 NotificationListenerService 已经被用户授予了权限,否则无效。另外,在自己的小米手机上实测,重新完成 rebind 操作需要等待 10 多秒(我的手机测试过大概在 13 秒左右)。幸运的是,官方也已经发现了这个问题,在 API 24 中提供了 requestRebind(ComponentName componentName) 方法来支持重新绑定。

    AccessibilityService

    讲完了 NotificationListenerService 之后,按照前面说的那样,在 API < 18 的时候使用 AccessibilityService 。

    同样,创建一个 WeChatAccessibilityService ,并且在 AndroidManifest.xml 中进行声明:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <service
    android:name="com.yuqirong.listenwechatnotification.WeChatAccessibilityService"
    android:label="@string/app_name"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
    <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>
    <meta-data
    android:name="android.accessibilityservice"
    android:resource="@xml/accessible_service_config" />
    </service>

    声明之后,还要对 WeChatAccessibilityService 进行配置。需要在 res 目录下新建一个 xml 文件夹,在里面新建一个 accessible_service_config.xml 文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version="1.0" encoding="utf-8"?>
    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeNotificationStateChanged"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagIncludeNotImportantViews"
    android:canRetrieveWindowContent="true"
    android:description="@string/app_name"
    android:notificationTimeout="100"
    android:packageNames="com.tencent.mm" />

    最后就是代码了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class WeChatAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    if (Build.VERSION.SDK_INT < 18) {
    Notification notification = (Notification) event.getParcelableData();
    List<String> textList = getText(notification);
    if (textList != null && textList.size() > 0) {
    for (String text : textList) {
    if (!TextUtils.isEmpty(text) &&
    text.contains("[微信红包]")) {
    final PendingIntent pendingIntent = notification.contentIntent;
    try {
    if (pendingIntent != null) {
    pendingIntent.send();
    }
    } catch (PendingIntent.CanceledException e) {
    e.printStackTrace();
    }
    }
    break;
    }
    }
    }
    }

    @Override
    public void onInterrupt() {

    }

    }

    看了一圈 WeChatAccessibilityService 的代码,发现和 WeChatNotificationListenerService 在 API < 18 时处理的逻辑是一样的,getText(notification) 方法就是上面那个,在这里就不复制粘贴了,基本没什么好讲的了。

    有了 WeChatAccessibilityService 之后,在 API < 18 的情况下也能监听通知啦。\(^ο^)/

    我们终于实现了当初许下的那个需求了。 cry …

    总结

    除了监听通知之外,AccessibilityService 还可以进行模拟点击、检测界面变化等功能。具体的可以在 GitHub 上搜索抢红包有关的 Repo 进行深入学习。

    而 NotificationListenerService 的监听通知功能更加强大,也更加专业。在一些设备上,如果 NotificationListenerService 被授予了权限,那么可以做到该监听进程不死的效果,也算是另类的进程保活。

    源码下载:ListenWeChatNotification.rar

    展开全文
  • Android Service保活

    2021-02-02 10:59:10
    保活Service我们需要做什么: 1.在应用被关闭后保活 2.在内用占用过大,系统自动释放内存时保活(优先杀死占用较高的Service) 3.重启手机后自动开启Service 4.手机息屏后不被释放内存 5.手动清理内存时保活 ...

    保活Service我们需要做什么:

    1.在应用被关闭后保活

    2.在内用占用过大,系统自动释放内存时保活(优先杀死占用较高的Service)

    3.重启手机后自动开启Service

    4.手机息屏后不被释放内存

    5.手动清理内存时保活

    Android进程的生命周期

    与大家比较熟悉的Activity生命周期相比,Android进程的生命周期实质更为简单,越核心的东西越简单嘛,Android将一个进程分为五种不同的状态:

    一、前台进程 Foreground process
    二、可见进程 Visible process
    三、服务进程 Service process
    四、后台进程 Background process
    五、空进程 Empty process

    后台进程常驻的实现

    进程提权

    adj值越小的进程越不容易被杀死,相对普通进程来说能让adj去到0显然是最完美的,可是我们如何才能让一个完全没有可见元素的后台进程拥有前台进程的状态呢?Android给了Service这样一个功能:startForeground,它的作用就像其名字一样,将我们的Service置为前台,不过你需要发送一个Notification:

    public class DaemonService extends Service {
        @Override
        public void onCreate() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                Notification.Builder builder = new Notification.Builder(this);
                builder.setSmallIcon(R.mipmap.ic_launcher);
                startForeground(250, builder.build());
            } else {
                startForeground(250, new Notification());
            }
        }
    }

    双进程保护

    1.创建aidl实现跨进程通信(新建一个aidl)

    interface ProcessConnection {
        /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
        //删除不必要方法
     }

    2.创建主服务

    /**
     * 主进程 双进程通讯
     * Created by db on 2018/1/11.
     */
     
    public class StepService extends Service{
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new ProcessConnection.Stub() {};
        }
     
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            startForeground(1,new Notification());
            //绑定建立链接
            bindService(new Intent(this,GuardService.class),
                    mServiceConnection, Context.BIND_IMPORTANT);
            return START_STICKY;
        }
     
        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                //链接上
                Log.d("test","StepService:建立链接");
            }
     
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                //断开链接
                startService(new Intent(StepService.this,GuardService.class));
                //重新绑定
                bindService(new Intent(StepService.this,GuardService.class),
                        mServiceConnection, Context.BIND_IMPORTANT);
            }
        };
     
    }

    3.创建守护服务

     /**
     * 守护进程 双进程通讯
     * Created by db on 2018/1/11.
     */
     
    public class GuardService extends Service{
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return new ProcessConnection.Stub() {};
        }
     
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            startForeground(1,new Notification());
            //绑定建立链接
            bindService(new Intent(this,StepService.class),
                    mServiceConnection, Context.BIND_IMPORTANT);
            return START_STICKY;
        }
     
        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                //链接上
                Log.d("test","GuardService:建立链接");
            }
     
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                //断开链接
                startService(new Intent(GuardService.this,StepService.class));
                //重新绑定
                bindService(new Intent(GuardService.this,StepService.class),
                        mServiceConnection, Context.BIND_IMPORTANT);
            }
        };
     
    }
     

    返回参数含义:

    • START_STICKY:在Service被关闭后,重新开启Service
    • START_NOT_STICKY:服务被异常杀掉后,系统将会被设置为started状态,系统不会重启该服务,直到startService(Intent intent)方法再次被调用。
    • START_REDELIVER_INTENT:重传Intent,使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
    • START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

    使用JobService来实现应用退出后重启Service

    什么是JobService

    JobService也是一个service,和普通的service不同的是,JobService是一个任务回调类,通过JobScheduler设置任务给系统,系统来调用JobService中的方法,具体处理什么任务需要我们自己在JobService中的回调方法中实现。那么关于任务的管理和进程的维护、调度当然是由系统来统一管理。

    Google从Android SDK 21之后添加了JobScheduler来执行一些满足特定条件但不紧急的后台任务,我们可以利用JobScheduler来执行这些特殊的后台任务时来减少电量的消耗。

    使用JobService来实现APP进程防杀。 
    1.首先声明权限

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <service
                android:name=".MyJobDaemonService"
                android:enabled="true"
                android:exported="true"
                android:permission="android.permission.BIND_JOB_SERVICE" />

    2.自定义一个Service类,继承自JobService

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public class MyJobDaemonService extends JobService {
        private int kJobId = 0;
     
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("MyJobDaemonService", "jobService启动");
            scheduleJob(getJobInfo());
            return START_NOT_STICKY;
        }
     
        @Override
        public boolean onStartJob(JobParameters params) {
            Log.i("MyJobDaemonService", "执行了onStartJob方法");
            boolean isLocalServiceWork = isServiceWork(this, "com.marswin89.marsdaemon.demo.Service1");
            boolean isRemoteServiceWork = isServiceWork(this, "com.marswin89.marsdaemon.demo.Service2");
            if(!isLocalServiceWork||
                    !isRemoteServiceWork){
                this.startService(new Intent(this,Service1.class));
    //            this.startService(new Intent(this,Service2.class));
    //            Toast.makeText(this, "进程启动", Toast.LENGTH_SHORT).show();
                Log.i("onStartJob", "启动service1");
            }
            return true;
        }
     
        @Override
        public boolean onStopJob(JobParameters params) {
            Log.i("MyJobDaemonService", "执行了onStopJob方法");
            scheduleJob(getJobInfo());
            return true;
        }
     
        //将任务作业发送到作业调度中去
        public void scheduleJob(JobInfo t) {
            Log.i("MyJobDaemonService", "调度job");
            JobScheduler tm =
                    (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
            tm.schedule(t);
        }
     
        public JobInfo getJobInfo(){
            JobInfo.Builder builder = new JobInfo.Builder(kJobId++, new ComponentName(this, MyJobDaemonService.class));
            builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE);
            builder.setPersisted(true);
            builder.setRequiresCharging(false);
            builder.setRequiresDeviceIdle(false);
            //间隔1000毫秒
            builder.setPeriodic(1000);
            return builder.build();
        }
     
        // 判断服务是否正在运行
        public boolean isServiceWork(Context mContext, String serviceName) {
            boolean isWork = false;
            ActivityManager myAM = (ActivityManager) mContext
                    .getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningServiceInfo> myList = myAM.getRunningServices(100);
            if (myList.size() <= 0) {
                return false;
            }
            for (int i = 0; i < myList.size(); i++) {
                String mName = myList.get(i).service.getClassName().toString();
                if (mName.equals(serviceName)) {
                    isWork = true;
                    break;
                }
            }
            return isWork;
        }
     
    }

    保证Service在开机后自动启动

    (1)注册广播

      <receiver android:name=".modle.mReceiver">
                <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED"/>
                </intent-filter>
      </receiver>
    /**
     * 开机完成广播
     */
     
    public class mReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent){
            Intent mIntent = new Intent(context,StepService.class);
            context.startService(mIntent);
        }
    }

    保证息屏后不被释放资源杀死(WakeLock的使用)

    (1)添加权限

        <uses-permission android:name="android.permission.WAKE_LOCK" />

    需求:要在后台跑一个Service执行轮询,屏幕熄灭或锁屏后,仍然需要保持Service一直处于轮询状态。

     应用程序中如果要在待机前保存数据状态的话,要保证此过程中不会进入待机。可以在 onResume() 或者 onStart() 中申请 wakelock 锁,即调用getLock()方法。

    在 onPause() 或者 onDistroy() 中处理应用待机后再释放掉 wakelock 锁,此时调用releaseLock()方法

       /**
         * 同步方法   得到休眠锁
         * @param context
         * @return
         */
        synchronized private void getLock(Context context){
            if(mWakeLock==null){
                PowerManager mgr=(PowerManager)context.getSystemService(Context.POWER_SERVICE);
                mWakeLock=mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,StepService.class.getName());
                mWakeLock.setReferenceCounted(true);
                Calendar c=Calendar.getInstance();
                c.setTimeInMillis((System.currentTimeMillis()));
                int hour =c.get(Calendar.HOUR_OF_DAY);
                if(hour>=23||hour<=6){
                    mWakeLock.acquire(5000);
                }else{
                    mWakeLock.acquire(300000);
                }
            }
            Log.v(TAG,"get lock");
        }
    synchronized private void releaseLock()
        {
            if(mWakeLock!=null){
                if(mWakeLock.isHeld()) {
                    mWakeLock.release();
                    Log.v(TAG,"release lock");
                }
     
                mWakeLock=null;
            }
        }

     

     

    展开全文
  • 程序退出后service保活 先擤两把鼻涕,我的内心在哭泣。。。。。。(目前还在处于探索状态(百度ing…)) **运行手机:**华为nova 3i ,好像是安卓9.0 回合一: PowerManager.WakeLock m_wklk; 在onCreate()中 m_...

    程序退出后service保活

    先擤两把鼻涕,我的内心在哭泣。。。。。。(目前还在处于探索状态(百度ing…))
    **运行手机:**华为nova 3i ,好像是安卓9.0

    回合一:
    PowerManager.WakeLock m_wklk;
    在onCreate()中

     m_wklk = null;
            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
            m_wklk = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TestService.class.getName());
            if (null != m_wklk){
                m_wklk.acquire();
            }
    

    在onDestroy中

    if (m_wklk != null) {
            m_wklk.release();
            m_wklk = null;
        }
    

    AndroidManifest中

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    

    测试结果:点击home,运行一小会会,我看到了亲切的 onDestory >_<、

    回合二:
    在onCreate()中

     AlarmManager manager;= (AlarmManager) getSystemService(ALARM_SERVICE);
     int aMiniute =  2*60*1000; // 这是一分钟的毫秒数
      long triggerAtTime = SystemClock.elapsedRealtime() + aMiniute;
    
     Intent i = new Intent(this, AlarmReceiver.class);
     PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
     manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
    

    在onDestroy中

    manager.cancel(pi);
    

    AlarmReceiver :

    public class AlarmReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("Test", "c重启服务onStart TestService .......... ");
              Intent i = new Intent(context, TestService.class);
            context.startService(i);
        } }
    

    记得注册AlarmReceiver 和 Service

    参考:Android 开发 AlarmManager 定时器
    https://blog.csdn.net/qq_37217804/article/details/82665097

    Intent立即响应; pendingintent 类似发送短信,邮件等

    *PendingIntent的第四个参数的说明:

    • 常量:
    • FLAG_CANCEL_CURRENT 生成一个新的对象
    • FLAG_NO_CREATE若不存在,则创建一个新的对象
    • FLAG_ONE_SHOT创建的对象只能使用一次
    • FLAG_UPDATE_CURRENT已存在则直接使用

    PendingIntent 的使用 以及其中参数的理解
    https://blog.csdn.net/qq_38340601/article/details/82930108

    隔一段时间AlarmReceiver 可以startService(),但是一段时间(几分钟后)还是onDestory 了。。。。。。。。。。。

    我不晓得啥子,方方的。。。。。。。
    哪位大神路过,求指教,跪谢。。。。。

    经过一下午的摸索,终于找到了解决办法。
    参考这位大佬的文章 已经毕业的C先生
    定时AlarmManager循环执行后台任务和多个定时循环后台任务写在一起

    依旧是用AlarmManager,原因猜测应该是安卓版本导致的 问题。目前实现的程度应该是,点击home键,程序中的service可以在后台运行,点击一键清理应该就结束了。
    版本判断如下:(具体参考上面的链接)

           int apiLevel = getApiLevel();
            if (apiLevel < Build.VERSION_CODES.KITKAT) {
                Log.d("api<19", "setExactAlarmCompat ");
                alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, mCalendar.getTimeInMillis(), INTERVAL_TIME, pIntent);
            } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    alarmManager.setExact(AlarmManager.RTC_WAKEUP, mCalendar.getTimeInMillis(), pIntent);
                }
                Log.d("19<api<23", "setExactAlarmCompat ");
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, mCalendar.getTimeInMillis(), pIntent);
                Log.d("api>23", "setExactAlarmCompat ");
            }
    ————————————————
    版权声明:本文为CSDN博主「已经毕业的C先生」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/cqx13763055264/article/details/79146187
    
    展开全文
  • 进程保活,service保活

    2018-12-28 12:33:17
    一、onStartCommand方法,返回START_STICKY ...不久后service就会再次尝试重新创建,因为保留在开始状态,在创建 service后将保证调用onstartCommand。如果没有传递任何开始命令给service,那...
  • Android service保活

    2018-12-13 16:39:00
    最近项目要实现这样一个效果:运行后,要有一个service始终保持在后台运行,不管用户作出什么操作,都要保证service不被kill,这可真是一个难题。参考了现今各种定制版的系统和安全厂商牛虻软件,如何能保证自己的...
  • Android Service保活(线程保活)

    千次阅读 2018-11-19 13:34:07
    当系统觉得内存的资源非常有限的时候,为了保证一些优先级高的程序能运行,就会杀掉一些他认为不重要的程序或者服务来释放内存, 比如项目需求需要Service在后台实时监听网络连接状态,或者其他用Service执行的后台...
  • Android之Service保活

    千次阅读 2020-01-20 14:26:40
    临近年关,这一年的...要求很简单:定位、地图划线,然后就是后台服务的保活操作了。 今天,在此记录GitHub上好用的轮子:DaemonLibrary。 集成方式: Step 1. Add the JitPack repository to your build file...
  • IPC - Service保活

    2019-12-11 12:08:04
    1.Service为什么会被杀死? 1>手机内存不足时,Service有可能被杀死; 2>360,手机管家等优化内存时,Service可能被杀死; 2.进程的优先级 1.前台进程 用户当前操作所必需的进程。如果一个进程满足以下...
  • Android Service保活攻防

    2018-09-21 14:31:52
    一、为什么要保活保活的源头是由于我们希望自己服务或者进程能够一直在后台运行,但是总有各种各样的原因导致我们的希望破灭,主要原因有如下几种:1、Android系统回收;2、手机厂商定制管理系统,如电源管理、...
  • 目录 说下冷启动与热启动是什么...Service 保活 IntentService原理分析 说下冷启动与热启动是什么,区别,如何优化,使用场景等。 app冷启动: 当应用启动时,后台没有该应用的进程,这时系统会重新创建一个...
  • Android Service保活最佳实践

    千次阅读 2019-04-28 17:38:08
    最近的项目因为服务在后台被杀,导致重新进入页面时遇到若干问题,网上搜了好多方案,都不可行,基本都过时了,所以自己总结了一下,我的保活策略就是不保活。 顺便提一下网上现有的保活方法,我试过了是没有成功的...
  • service保活那些坑

    2019-07-12 15:00:06
    最近在做一个悬浮标签,应用到安卓智能电视上,遇到了service在后台容易被杀的情况,网上各种查,各种尝试,各种方式,都试过了,都不好使,比如: 1,设置优先级。 2,提升为前台进程(电视上无效) 3,添加到系统...
  • Android Service保活方法总结

    千次阅读 2018-08-25 22:51:25
    在Android开发中我们经常会需要让一个Service长久的存活下去,直到海枯石烂,但是总有一些刁民想干掉朕的服务,比如...下面介绍几种常用的服务保活的方法。 1 onStartCommand方法,返回START_STICKY 在运行onStar...
  • android:service保活

    千次阅读 2018-08-02 14:20:44
    1、修改Service的onStartCommand 方法返回值 当服务被异常终止,是否能重启服务? 一般的做法是修改返回值,返回START_ STICKY。 onStartCommand()返回一个整型值,用来描述系统在杀掉服务后是否要继续启动服务,...
  • Android组件保活,service保活

    千次阅读 2016-06-21 18:20:14
    首先介绍一下android的进程. Android 进程分为两种 :其一,java层的进程,也就是在虚拟机里运行的进程,他们的父进程是zygote进程,我们平时调用的AM里面的类似getrunningprocess()方法,返回的就是这类进程信息,这...
  • Android 7.0 Service保活总结

    万次阅读 2017-11-06 15:36:41
    我先说一个最近研究得出来的结论,在7.0这个版本,包括三星和国内的这些原生rom,如果不通过用户或厂家设置,至少service是绝对没有任何办法保活的,绝对,除非你还能找到未知的BUG。虽然我也很头疼,但我真的很赞同...
  • JobSchedule与Service保活

    千次阅读 2018-12-02 11:35:11
    Android Service保活主要在两个层面 提高service优先级,降低service被杀死的概率。 在service被杀死后进行拉活。。 3.1 灰色保活方案简介 startForeground(ID, new Notification()),可以将Service...
  • PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); if (powerManager != null) { isIgnoring = powerManager.isIgnoringBatteryOptimizations(getPackageName()); } return ...
  • 保活Service我们需要做什么: 1.在应用被关闭后保活(最难) 2.在内用占用过大,系统自动释放内存时保活(优先杀死占用较高的Service) 3.重启手机后自动开启Service 4.手机息屏后不被释放内存 5.手动清理内存...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 437
精华内容 174
关键字:

service保活