android技术我爱编程

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

2018-04-12  本文已影响0人  青果果

Service

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

如何开启Service

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


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中添加权限

//注册清单中添加权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<service
     android:name=".doublesevice.JobWakeUpService"
     android:enabled="true"
     android:permission="android.permission.BIND_JOB_SERVICE"/>

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<ActivityManager.RunningServiceInfo> 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的区别

上一篇下一篇

猜你喜欢

热点阅读