一、代码示例
如下代码监听开机广播,启动一个Service
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.demo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.android.demo.broadcast.BootBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name="com.android.demo.service.AppService"
android:process=":remote" >
</service>
</application>
</manifest>
AppService
package com.android.demo.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import com.android.demo.util.ShellUtils;
import java.util.ArrayList;
import java.util.List;
public class AppService extends Service {
private final static String TAG = "AppService";
private boolean isServiceStarted = false;
private Intent startIntent = null;
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "AppService onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "AppService onStartCommand");
//onStart(intent, startId);
//return super.onStartCommand(intent, flags, startId);
//START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。
//随后系统会尝试重新创建service,由于服务状态为开始状态,
//所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。
//如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null
return START_STICKY;
}
@Override
public void onStart(Intent intent, int startId) {
// TODO Auto-generated method stub
startIntent = intent;
Log.e(TAG, "AppService, onStart");
if (!isServiceStarted) {
isServiceStarted = true;
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
Log.e(TAG, "AppService, onDestroy");
isServiceStarted = false;
//在销毁的时候,重新开启
if (startIntent != null) {
startService(startIntent);
}
}
}
BootBroadcastReceiver
package com.android.demo.broadcast;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.android.demo.service.AppService;
public class BootBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "BootBroadcastReceiver";
private static Context mContext;
@Override
public void onReceive(Context context, Intent intent) {
mContext = context;
String action = intent.getAction();
Log.e(TAG, "BootBroadcastReceiver action = " + action);
if (action == null) {
return;
}
if (action.equals("android.intent.action.BOOT_COMPLETED")) {
Log.i(TAG, "BOOT_COMPLETED");
Intent i = new Intent(context, AppService.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(i);
}
}
}
二、 Service简介
Service是Android 系统中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的级别差不多,但不能自己运行只能后台运行,并且可以和其他组件进行交互。service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后台的。
Service的启动有两种方式:context.startService() 和 context.bindService()
三、 Service启动流程
context.startService() 启动流程:
context.startService() -> onCreate() -> onStart() -> Service running -> context.stopService() -> onDestroy() -> Service stop
onstart()方法是在android2.0一下的版本中使用。而在android2.0以上则使用onstartCommand()方法。
如果Service还没有运行,则android先调用onCreate(),然后调用onStart();
如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。
如果stopService的时候会直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行,该Service的调用者在启动起来后可以通过stopService关闭Service。
所以调用startService的生命周期为:onCreate --> onStart (可多次调用) --> onDestroy
context.bindService()启动流程:
context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop
onBind()将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service的实例、运行状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。
所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。
四、 Service生命周期
Service的生命周期并不像Activity那么复杂,它只继承了onCreate()、onStart()、onDestroy()三个方法
当我们第一次启动Service时,先后调用了onCreate()、onStart()这两个方法;当停止Service时,则执行onDestroy()方法。
这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法。
它可以通过Service.stopSelf()方法或者Service.stopSelfResult()方法来停止自己,只要调用一次stopService()方法便可以停止服务,无论调用了多少次的启动服务方法。
五、 关于怎样让服务不被杀死
① 添加android:persistent=“true”
添加android:persistent=“true"到AndroidManifest.xml,
Google文档描述如下:Whether or not the application should remain running at all times-true” if it should, and "false"if not. The default value is “false”. Applications should not normally set this flag; persistence mode is intended only for certain system applications.可见这个属性不能随便用,到目前为止,我所发现使用该属性的应用只有Phone,而且使用是要求权限的,所以这个属性对第三方应用来说意义不是很大;常驻内存属性对第三方app无效
② 重写service的onStartCommand方法
设置onStartCommand()的返回值 。
@Override public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
简单介绍下这个方法,在Android开发的过程中,每次调用startService(Intent)的时候,都会调用该Service对象的onStartCommand(Intent,int,int)方法,然后在onStartCommand方法中做一些处理。然后我们注意到这个函数有一个int的返回值,这篇文章就是简单地讲讲int返回值的作用。
从Android官方文档中,我们知道onStartCommand有4种返回值:
START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
③ 在onDestory方法中重启Service服务
一般来说,这样做是可以的。但是如果这样----》设置–>下载–>强制停止。则不会执行ondestory方法,或者通过别人应用,如360直接kill掉我的应用时,也是不会调用Service的ondestory方法的。onDestroy方法里重启serviceservice +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service;
<receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
<action android:name="com.dbjtech.waiqin.destroy" />//这个就是自定义的action
</intent-filter>
</receiver>
在onDestroy时:
@Override
public void onDestroy() {
stopForeground(true);
Intent intent = new Intent("com.dbjtech.waiqin.destroy");
sendBroadcast(intent);
super.onDestroy();
}
在BootReceiver里
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.dbjtech.waiqin.destroy")) {
//在这里写重新启动service的相关操作
startUploadService(context);
}
}
}
也可以直接在onDestroy()里startService
@Override
public void onDestroy() {
Intent sevice = new Intent(this, MainService.class);
this.startService(sevice);
super.onDestroy();
}
【结论】当在setting里-应用-强制停止时,APP进程可能就直接被干掉了,onDestroy方法都进不来,所以还是无法保证.
④ 修改AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:sharedUserId="android.uid.system">
<application android:icon="@drawable/icon"
android:label="@string/app_name" android:allowClearUserData="false"
android:process="system"
android:killAfterRestore="false">
如果在加入了此部分代码,表示该程序运行在system进程组中,system进程组是没有权限访问sd卡的,而且service是不会自动重启的。
⑤ 提高service的优先级别
不管你service的优先级别有多高,用户都是可以手动杀死的.
提升service优先级
在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。
<service
android:name="com.dbjtech.acbxt.waiqin.UploadService"
android:enabled="true" >
<intent-filter android:priority="1000" >
<action android:name="com.dbjtech.myservice" />
</intent-filter>
</service>
【结论】目前看来,priority这个属性貌似只适用于broadcast,对于Service来说可能无效
⑥ 提升service进程优先级
Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是:
1.前台进程( FOREGROUND_APP)
2.可视进程(VISIBLE_APP )
3.次要服务进程(SECONDARY_SERVER )
4.后台进程 (HIDDEN_APP)
5.内容供应节点(CONTENT_PROVIDER)
6.空进程(EMPTY_APP)
当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground 将service放到前台状态。这样在低内存时被kill的几率会低一些。
在onStartCommand方法内添加如下代码:
Notification notification = new Notification(R.drawable.ic_launcher,
getString(R.string.app_name), System.currentTimeMillis());
PendingIntent pendingintent = PendingIntent.getActivity(this, 0,
new Intent(this, AppMain.class), 0);
notification.setLatestEventInfo(this, "uploadservice", "请保持程序在后台运行",
pendingintent);
startForeground(0x111, notification);
注意在onDestroy里还需要stopForeground(true),运行时在下拉列表会看到自己的APP
⑦ 可以监听Intent.ACTION_TIME_TIC系统时钟广播
启动、网络切换、耳机插入、锁屏,亮屏等系统广播,系统每隔一段时间发送这个广播,当service被杀死的时候,隔一段时间通过广播启动,动态注册android.intent.action.TIME_TICK监听
判断service是否启动
public boolean isServiceRunning(String serviceName)
{
ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service :manager.getRunningServices(Integer.MAX_VALUE))
{
if(serviceName.equals(service.service.getClassName()))
{
return true;
}
}
return false;
}
接受到广播后判断是否启动该service 若没有启动就启动它
if(intent.getAction().equals(Intent.ACTION_TIME_TICK))
{
if (!isServiceRunning(name)){
Intent mIntent = new Intent(context, MyService.class);
context.startService(mIntent);
}
}
⑧ 增加系统白名单,防止重要应用低内存时被误杀
联系厂商,加入白名单,有童鞋验证把demo工程的包名改成手机QQ的,编译运行在华为的机子上,发现进程杀不死,退到后台oom_adj的值同样不发生变化,而恢复原来的包名就不行了,可见很多大型软件,尤其聊天类的都会和厂商合作,将相关应用加入白名单。