service是在后台运行的,没有界面的android组件之一。
1.方法调用顺序:开启:(Activity)startSercice()–>(Service)onCreate()–>(Service)onStartCommand(){内部调用onStart()};关闭:stopService()–>onDestroy(); 2.多次调用startSercice(),onCreate()只执行一次,但onStartCommand()+onStart()执行多次 3.服务一直会在后台运行,除非调用调用stopService或手动停止。
1.方法的调用顺序:bindService()–>onCreate()–>onBind();unBindService()–>onUnbind()–>onDestroy()。 2.多次调用bindService(),onCreate() + onBind()只会执行一次。 3.可以绑定多次,但解绑只有一次,若多次解绑,会报错:没有注册service。 4.service与绑定者是同生共死的关系。 5.通过bindService()开启服务,在手机设置界面是找不到的。 6.如果onBind()方法返回null,那么onServiceConnection()是不执行的。
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
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
在实际开发中,我们会遇到一些要求后台运行的service,但是现在Android手机型号太多,而且国内的系统都是各大厂商定制过得,加入了自己的一些优化设置(比如省电方面的)。造成的的问题是:当service在后台运行一段时间后,如果是比较耗电,会被系统kill,service里的逻辑操作也就停止了,对我们的业务造成困扰。那么该如何处理呢?
主要有2种方法:手动设置和代码设置混合使用
在手机设置界面中,有锁屏保护和省电管理或者手机管家中的内存优化都要进行处理。
在360F4手机中,在设置–>电池和省电–>锁屏受保护应用:选中我们自己的app,就想选中微信一样,如果不选中微信的话,锁屏30分钟后,微信也会被kill掉。不同的手机锁屏后kill掉app的时间是不同的,有的没有这一项设置,有的是几分钟,360F4手机是30分钟。见下图:
这类App一般都是系统自带的,都是品牌商自己的软件,为了时间省电优化,通常都会把后台运行的app杀死,而且权限还挺高。不过也有用于自己安装的,比如:360手机卫士、腾讯手机管家、猎豹清理大师等,各种app的优化设置不同,这里介绍几个常见的
我–>设置–>清理加速–>内存加速优化名单–>把我们的app加入优化忽略名单中
待定
这也有2种方法: 1. 给serviceA在配置一个守护它的serviceB,当我们的serviceA被杀死后,我们可以serviceB再重新开启A, 2. 注册BroadcastReceiver,锁屏时获取“电源锁”,屏幕解锁是释放“电源锁”
原理:在这个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; } }注册广播,当锁屏时获取电源锁,当屏幕解锁时释放电源锁,
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设置成前台进程:
Notification notification = new Notification.Builder(this).build(); startForeground(1, notification);把service的前台进程销毁:
stopForeground(true);Android进程保活招式大全 Demo: http://git.oschina.net/ServiceDemo/WakeLock01 其他相关: Android的PowerManager和PowerManager.WakeLock用法简析 使用WakeLock使Android应用程序保持后台唤醒 Service在后台长期运行的解决方案