Android基础:Service

本文深入讲解 Android 中 Service 的概念、启动方式及生命周期特点,并探讨如何实现 Service 的进程保活策略,包括手动设置与代码实现的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Service简介

1 服务的介绍

service是在后台运行的,没有界面的android组件之一。

2 进程的分类

进程分为5类,按照级别的高低依次为:
1 前台进程:Foreground Process,正在进行交互的进程,相当于activity执行了onResume()。
2 可视进程:Visible Process 可见不可交互,相当于activity执行了onPause()。
3 服务进程:Service Process 开启了startService()
4 后台进程:Background Process 相当于activity执行了onStop()。
5 空进程:Empty Process 进程中没有任何组件在运行。

3 service的开启方式

2种:startService() + bindService()

3-1 startSercice()方式开启服务,其生命周期的特点

1.方法调用顺序:开启:(Activity)startSercice()–>(Service)onCreate()–>(Service)onStartCommand(){内部调用onStart()};关闭:stopService()–>onDestroy();
2.多次调用startSercice(),onCreate()只执行一次,但onStartCommand()+onStart()执行多次
3.服务一直会在后台运行,除非调用调用stopService或手动停止。

3-2 bindService()方式开启服务,其生命周期的特点

1.方法的调用顺序:bindService()–>onCreate()–>onBind();unBindService()–>onUnbind()–>onDestroy()。
2.多次调用bindService(),onCreate() + onBind()只会执行一次。
3.可以绑定多次,但解绑只有一次,若多次解绑,会报错:没有注册service。
4.service与绑定者是同生共死的关系。
5.通过bindService()开启服务,在手机设置界面是找不到的。
6.如果onBind()方法返回null,那么onServiceConnection()是不执行的。

这里写图片描述

4 为什么引入bindService()这种方法

为了引用service里面的方法。

Demo验证

验证上面的3-1

startService(intent)方式开启service,可以多次调用startService(intent),service的onCreate()只会执行一次,但onStartCommon()和onStart()会执行多次。stopService(intent)也可以被调用多次,但onDestroy()只会执行一次。

这里写图片描述

   private void initViews() {
    btn1.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            startService(intent);
        }
    });
    btn2.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            stopService(intent);
        }
    });
}
public class DemoService extends Service {

    private static final String TAG = "DemoService";

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Log.d(TAG, "onStart");
        super.onStart(intent, startId);
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

源码:https://git.oschina.net/ServiceDemo/StartServiceDemo

验证上面的3-2

bindService(intent):绑定
unBindService(intent):解绑

bindService(intent)方式开启service,可以绑定多次,但解绑只有一次,多次解绑会报错。
多次绑定,onCreate()+onBind()只执行一次。

这里写图片描述

   private void initViews() {
    btn1.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            bindService(intent, conn, BIND_AUTO_CREATE);
        }
    });
    btn2.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            unbindService(conn);
        }
    });
}

   private ServiceConnection conn = new ServiceConnection() {

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {

    }
};
public class DemoService extends Service {

    private static final String TAG = "DemoService";

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Log.d(TAG, "onStart");
        super.onStart(intent, startId);
    }


    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return null;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind");
        return super.onUnbind(intent);
    }
}

源码:https://git.oschina.net/ServiceDemo/BindServiceDemo

怎么判断service1在不在运行中?

/**
 * 判断某个服务是否正在运行的方法
 * @param mContext
 * @param serviceName 是包名+服务的类名(例如:com.cqc.servicedemo01.Service1)
 * @return true代表正在运行,false代表服务没有正在运行
 */
public boolean isServiceWork(Context mContext, String serviceName) {
    boolean isWork = false;
    ActivityManager myAM = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningServiceInfo> myList = myAM.getRunningServices(80);
    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保活

在实际开发中,我们会遇到一些要求后台运行的service,但是现在Android手机型号太多,而且国内的系统都是各大厂商定制过得,加入了自己的一些优化设置(比如省电方面的)。造成的的问题是:当service在后台运行一段时间后,如果是比较耗电,会被系统kill,service里的逻辑操作也就停止了,对我们的业务造成困扰。那么该如何处理呢?

主要有2种方法:手动设置和代码设置混合使用

手动设置

在手机设置界面中,有锁屏保护和省电管理或者手机管家中的内存优化都要进行处理。

锁屏保护

在360F4手机中,在设置–>电池和省电–>锁屏受保护应用:选中我们自己的app,就想选中微信一样,如果不选中微信的话,锁屏30分钟后,微信也会被kill掉。不同的手机锁屏后kill掉app的时间是不同的,有的没有这一项设置,有的是几分钟,360F4手机是30分钟。见下图:
这里写图片描述

省电管理

这类App一般都是系统自带的,都是品牌商自己的软件,为了时间省电优化,通常都会把后台运行的app杀死,而且权限还挺高。不过也有用于自己安装的,比如:360手机卫士、腾讯手机管家、猎豹清理大师等,各种app的优化设置不同,这里介绍几个常见的

360手机卫士

我–>设置–>清理加速–>内存加速优化名单–>把我们的app加入优化忽略名单中
这里写图片描述

OPPO手机管家

待定

代码设置

这也有2种方法:
1. 给serviceA在配置一个守护它的serviceB,当我们的serviceA被杀死后,我们可以serviceB再重新开启A,
2. 注册BroadcastReceiver,锁屏时获取“电源锁”,屏幕解锁是释放“电源锁”

增加一个守护的service

原理:在这个serviceB中开启一个Thread,每个1分钟查找正在运行的service,如果没有serviceA,那么久开启serviceA;如果有serviceA,那么就做处理。

public class MonitorService extends Service {

    protected static boolean isCheck = false;

    protected static boolean isRunning = false;

    private static final String SERVICE_NAME = "com.cqc.walelock01.WakeLockService";

    @Override
    public void onCreate() {
        Log.d("MonitorService2", "MonitorService onCreate");
        super.onCreate();

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("MonitorService2", "MonitorService onStartCommand");
        new Thread() {
            @Override
            public void run() {
                while (isCheck) {
                    try {
                        Thread.sleep(5 * 60 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        Log.d("MonitorService2", "thread sleep failed" );
                    }

                    if (!isServiceWork(getApplicationContext(), SERVICE_NAME)) {
                        Log.d("MonitorService2", "WakeLockService轨迹服务已停止,正在重启轨迹服务");
                        startService(new Intent(MonitorService.this, WakeLockService.class));
                    } else {
                        Log.d("MonitorService2", "WakeLockService轨迹服务正在运行");
                    }
                }
            }

        }.start();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * 判断某个服务是否正在运行的方法
     *
     * @param mContext
     * @param serviceName 是包名+服务的类名(例如:com.baidu.trace.LBSTraceService)
     * @return true代表正在运行,false代表服务没有正在运行
     */
    public boolean isServiceWork(Context mContext, String serviceName) {
        boolean isWork = false;
        ActivityManager myAM = (ActivityManager) mContext
                .getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningServiceInfo> myList = myAM.getRunningServices(80);
        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;
    }
}

WakeLock :电源锁

注册广播,当锁屏时获取电源锁,当屏幕解锁时释放电源锁,

public class TrackReceiver extends BroadcastReceiver {

    private static final String TAG = "TrackReceiver";

    @SuppressLint("Wakelock")
    @Override
    public void onReceive(final Context context, final Intent intent) {
        final String action = intent.getAction();
        if (Intent.ACTION_SCREEN_OFF.equals(action)) {
            Log.d(TAG,"screen off,acquire wake lock!");
            if (null != WakeLockService.wakeLock && !(WakeLockService.wakeLock.isHeld())) {
                WakeLockService.wakeLock.acquire();
            }
        } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
            Log.d(TAG,"screen on,release wake lock!");
            if (null != WakeLockService.wakeLock && WakeLockService.wakeLock.isHeld()) {
                WakeLockService.wakeLock.release();
            }
        }
    }
}

获取电源锁是需要权限的

<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.DEVICE_POWER"/>
第二个权限AS会红色提示“这是系统app需要的权限”,这是提示不是报错,添加上就可以了

这里写图片描述
BroadcastReceiver有2种注册方法:静态注册和动态注册。
我么这里采用动态注册的方法,原因是:

receiver应该跟随serviceA的生命周期,当serviceA被销毁后,将不再获取电源锁;service开启后,再根据屏幕的变化判断是获取还是释放电源锁

public class WakeLockService extends Service {
    //cpu保活
    private static boolean isRegister = false;
    protected static PowerManager pm = null;
    public static PowerManager.WakeLock wakeLock = null;
    public static TrackReceiver trackReceiver = new TrackReceiver();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //动态注册
        if (!isRegister) {
            if (null == pm) {
                pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
            }
            if (null == wakeLock) {
                wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "track upload");
            }
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_SCREEN_OFF);
            filter.addAction(Intent.ACTION_SCREEN_ON);
            registerReceiver(trackReceiver, filter);
            isRegister = true;
        }
        return super.onStartCommand(intent, flags, startId);
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        if (isRegister) {
            try {//销毁广播
                unregisterReceiver(trackReceiver);
                isRegister = false;
            } catch (Exception e) {
            }
        }
    }
}

这个service保活是从百度鹰眼官方Demo中提取的。
网址:http://lbsyun.baidu.com/index.php?title=android-yingyan/guide/tracelive
这里写图片描述

让service在前台运行

把service设置成前台进程:

Notification notification = new Notification.Builder(this).build();
startForeground(1, notification);

把service的前台进程销毁:

stopForeground(true);

Android进程保活招式大全

Android进程保活招式大全
Demo: http://git.oschina.net/ServiceDemo/WakeLock01
其他相关:
Android的PowerManager和PowerManager.WakeLock用法简析
使用WakeLock使Android应用程序保持后台唤醒
Service在后台长期运行的解决方案

Service与Activity之间的传值

  • 把值放在Application中
  • 用SharePreferences存取值
  • activity中利用intent存值,在service的onStartCommand(…)中取值
intent.putExtra("orderCode", "123456");
startService(intent);
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    String orderCode = intent.getStringExtra("orderCode");
    return super.onStartCommand(intent, flags, startId);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值