Android Service简析及双进程守护实现

Service

service是Android四大组件之一,和Activity同级别
但是service不能自己运行,只能后台运行没有界面交互,并且可以和其他组件进行交互
比如:控制歌曲后台播放

如何开启Service

有两种方式,看官方图解生命周期示意图


Android Service简析及双进程守护实现_第1张图片
Service生命周期.png

Context.startService(Intent service)

startService方式启动,会走OnCreat---->onStartCommand---->OnDestroy
如果多次调用startService不会每次都执行OnCreat(),OnCreat()只会调用一次,
但是每次都会调用onStartCommand()

Context.bindService(Intent service, ServiceConnection conn,int flags)

bindService方式启动,会走OnCreat---->onBind---->onUnbind---->OnDestroy
单独用bindService方式启动,flags最好用Context.BIND_AUTO_CREATE
总结:

  1. start方式启动,Service和Activity(Context,大多情况是activity)的生命周期是不关联的
    调用者退出后service仍然存在
  2. Bind方式绑定服务,Service和Activity的生命周期是关联的,除了绑定还能操作service,
    Service随着调用者退出而销毁

但是:也可以同时用两种方式启动服务
停止服务的时候需要同时stopService和unbindService,才能停止服务

IntentService和Servicede 区别

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    ......
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }
  ......
}
  1. IntentService 继承自 Service
  2. Service运行在主线程中,不能做耗时处理,否则会ANR
  3. IntentService 是创建一个工作线程处理多线程任务,会自动结束,不需要手动调用stopService
  4. IntentService 重写了onBind()返回null
    重写了onstartCommand()提供了默认实现,将请求的intent添加到队列中

不建议通过bindservice方式启动IntentService
IntentService本质 = Handler + HandlerThread(继承Thread):

跨进程通信AIDL和Binder

要实现不同apk之间通信,服务端和客户端要有相同的aidl,并且包名一致

aidl文件:

package com.qingguoguo.******;

// Declare any non-default types here with import statements

interface MyAidl {
    String getUserName();
    String getUserPwd();
}

客户端演示代码:

public void initData(Bundle bundle) {
        Intent intent = new Intent();
        intent.setAction("com.study.aidl.user");
        // 在Android 5.0之后google出于安全的角度禁止了隐式声明Intent来启动Service.也禁止使用Intent filter.否则就会抛异常
        intent.setPackage("com.qingguoguo.*******");
        //启动服务
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mYAidl = MyAidl.Stub.asInterface(service);
                try {
                    Log.e("TAG", "调试:" + mYAidl.getUserName() + "," + mYAidl.getUserPwd());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
        }, Context.BIND_AUTO_CREATE);
    }

服务端:
需要注册有对应的服务,并设置Action,重写onBind方法

 @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        LogUtils.i(TAG,"MessageService onBind");
        return mIBinder;
    }

    private IBinder mIBinder = new MyAidl.Stub() {
        @Override
        public String getUserName() throws RemoteException {
            return "qingguoguo";
        }

        @Override
        public String getUserPwd() throws RemoteException {
            return "123456";
        }
    };

先打开服务端app,再打开客户端app,onServiceConnected就会打印出相关信息

public interface MyAidl extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.qingguoguo.connotationjoke.MyAidl
{
private static final java.lang.String DESCRIPTOR = "com.qingguoguo.connotationjoke.MyAidl";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.qingguoguo.connotationjoke.MyAidl interface,
 * generating a proxy if needed.
 */
public static com.qingguoguo.connotationjoke.MyAidl asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.qingguoguo.connotationjoke.MyAidl))) {
return ((com.qingguoguo.connotationjoke.MyAidl)iin);
}
return new com.qingguoguo.connotationjoke.MyAidl.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getUserName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getUserName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getUserPwd:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getUserPwd();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.qingguoguo.connotationjoke.MyAidl
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public java.lang.String getUserName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getUserName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public java.lang.String getUserPwd() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getUserPwd, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getUserName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getUserPwd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.lang.String getUserName() throws android.os.RemoteException;
public java.lang.String getUserPwd() throws android.os.RemoteException;
}

这个文件是开发工具帮我们生成的
MyAidl是一个接口
Stub是一个抽象类,继承了Binder,实现了MyAidl接口
Proxy是一个static类,也实现了MyAidl,可以看做是MyAidl接口的代理,实现了里面的抽象方法

通信流程:

客户端通过bindService连接服务端,会调用服务端Service的onBind方法
返回一个UserCalcAIDL.Stub的mBinder实例,然后将该实例返回给客户端的
onServiceConnected()方法里面,有两个参数有一个IBinder就是服务端返回的mBinder,
然后客户端通过该实例建立一个新的AIDL.Stub.Proxy对象,
我们在客户端调用获取信息方法的时候其实就是调用的AIDL.Stub.Proxy里面的getUserName()方法,
通过mBinder的onTransact()方法写入数据,然后获取数据

Android系统进程间通信机制Binder的总体架构,它由
Client、Server、Service Manager和驱动程序Binder四个组件构成。
Service Manager,它是整个Binder机制的守护进程,用来管理开发者创建的各种Server,
并且向Client提供查询Server远程接口的功能。
Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager目录下,主要是由binder.h、binder.c和service_manager.c三个文件组成

Service为什么被杀死

  1. 手机内存不足
  2. 第三方的流氓软件清理
  3. 三方Rom定制,在退出应用的时候做了一些事情

进程的优先级

  • 前台进程
  • 可见进程
  • 服务进程
  • 后台进程
  • 空进程
    越是前台越不容易被杀,越是后面越容易被杀

LowmemoryKiller的工作机制

LowmemoryKiller会在内存不足的时候扫描所有的用户进程,
找到不是太重要的进程杀死,至于LowmemoryKiller杀进程够不够狠,
要看当前的内存使用情况,内存越少,下手越狠,相同级别的,占用内存大,先杀死

针对于此我们可以做以下优化
1.提高进程的优先级,其实就是减小进程的p->oomkilladj(越小越重要),
如启动Service调用startForeground()尽量提高进程的优先级;

2.当应用退到后台适当释放资源然后降低APP的内存占用量,因为在oom_adj相同的时候,
会优先干掉内存消耗大的进程;

3.对于要一直在后台运行的Service,占用内存要小。

双进程守护实现

双进程守护相互唤醒以及5.0以上的JobSheduler

public class GuardService extends Service {
    public final static String TAG = "GuardService";
    private final static int GUARD_SERVICE_ID = 2;

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

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

    private IBinder mIBinder = new GuardServiceAidl.Stub() {
    };

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //提高进程的优先级
        startForeground(GUARD_SERVICE_ID, new Notification());
        //绑定建立连接
        bindService(new Intent(this, MessageService.class),
                mConnection, Context.BIND_IMPORTANT);
        return START_STICKY;

    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //连接上
            ToastUtils.showShort("建立连接 MessageService");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //断开连接,重新启动,绑定
            startService(new Intent(GuardService.this, MessageService.class));
            bindService(new Intent(GuardService.this, MessageService.class), mConnection, Context.BIND_IMPORTANT);
        }
    };

JobService 必须要在5.0以上
并在xml中添加权限

//注册清单中添加权限




if (Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP){
       startService(new Intent(this, JobWakeUpService.class));
}

JobScheduler API为你的应用执行一个操作。与AlarmManager不同的是这个执行时间是不确定的。除此之外,JobScheduler API允许同时执行多个任务。这允许你的应用执行某些指定的任务时不需要考虑时机控制引起的电池消耗


@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class JobWakeUpService extends JobService {
    private final static int JOB_WAKEUP_SERVICE_ID = 2;
    private static final java.lang.String TAG = "JobWakeUpService";

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //开启轮寻
        ComponentName componentName = new ComponentName(this, JobWakeUpService.class);
        JobInfo.Builder jobBuilder = new JobInfo.Builder(JOB_WAKEUP_SERVICE_ID, componentName);
        //设置轮寻时间
        jobBuilder.setPeriodic(5000);
        JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobScheduler.schedule(jobBuilder.build());
        return START_STICKY;
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        LogUtils.i(TAG,"onStartJob:"+params.toString());
        //开启定时任务,轮寻,看MessageService有没有被杀死
        if (!serviceAlive(MessageService.class.getName())) {
            startService(new Intent(this, MessageService.class));
        }
        return false;
    }


    /**
     * 判断服务是否还活着
     *
     * @param serviceName 包名+服务的类名
     */
    private boolean serviceAlive(String serviceName) {
        boolean isWork = false;
        ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        List runningServices = activityManager.getRunningServices(100);
        if (runningServices.isEmpty()) {
            return false;
        }
        for (int i = 0; i < runningServices.size(); i++) {
            String className = runningServices.get(i).service.getClassName();
            if (serviceName.equals(className)) {
                isWork = true;
                break;
            }
        }
        return isWork;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

服务Service与线程Thread的区别

  • 两者概念的迥异

    • Thread 是程序执行的最小单元,它是分配CPU的基本单位
      android系统中UI线程也是线程的一种,当然Thread还可以用于执行一些耗时异步的操作。

    • Service是Android的一种机制,服务是运行在主线程上的,它是由系统进程托管。
      它与其他组件之间的通信类似于client和server,是一种轻量级的IPC通信,这种通信的载体是binder
      它是在linux层交换信息的一种IPC,而所谓的Service后台任务只不过是指没有UI的组件罢了。

  • 两者的执行任务迥异

    • 在android系统中,线程一般指的是工作线程(即后台线程),而主线程是一种特殊的工作线程,它负责将事件分派给相应的用户界面小工具,如绘图事件及事件响应,因此为了保证应用 UI 的响应能力主线程上不可执行耗时操作。如果执行的操作不能很快完成,则应确保它们在单独的工作线程执行。

    • Service 则是android系统中的组件,一般情况下它运行于主线程中,因此在Service中是不可以执行耗时操作的,否则系统会报ANR异常,之所以称Service为后台服务,大部分原因是它本身没有UI,用户无法感知(当然也可以利用某些手段让用户知道),但如果需要让Service执行耗时任务,可在Service中开启单独线程去执行。

  • 两者使用场景

    • 当要执行耗时的网络或者数据库查询以及其他阻塞UI线程或密集使用CPU的任务时,都应该使用工作线程(Thread),这样才能保证UI线程不被占用而影响用户体验。

    • 在应用程序中,如果需要长时间的在后台运行,而且不需要交互的情况下,使用服务。比如播放音乐,通过Service+Notification方式在后台执行同时在通知栏显示着。

  • 两者的最佳使用方式

    在大部分情况下,Thread和Service都会结合着使用
    比如下载文件,一般会通过Service在后台执行+Notification在通知栏显示+Thread异步下载
    再如应用程序会维持一个Service来从网络中获取推送服务。
    在Android官方看来也是如此,所以官网提供了一个Thread与Service的结合
    来方便我们执行后台耗时任务,它就是IntentService,当然 IntentService并不适用于所有的场景
    但它的优点是使用方便、代码简洁,不需要我们创建Service实例并同时也创建线程
    某些场景下还是非常赞的!
    由于IntentService是单个worker thread,所以任务需要排队,因此不适合大多数的多任务情况。

  • 两者的真正关系

    • 两者没有半毛钱关系。

你可能感兴趣的:(Android Service简析及双进程守护实现)