Service启动流程总结-bind和unbind

本文详细介绍了Android中Service的绑定(bind)和解绑(unbind)过程,包括 Caller 发起bind请求,AMS处理bind,Service处理bind请求,AMS发布Service,以及 Caller 处理连接回调等步骤。同时,文章还分析了解绑过程,包括从Caller发起unbind请求,AMS解绑Service,以及Service执行onUnbind回调等。整个流程涉及AMS、Service、Binder通信等多个关键环节。
摘要由CSDN通过智能技术生成

回顾

Service启动系列总结:

《Service启动流程总结-start和stop service》

概述

在开发中我们使用Context#bindService和Context#unbindService来绑定和解绑Service,通过绑定来启动Service,可以方便的调用Service提供的API。Service的bind、unbind过程和start、stop过程类似,比较大的区别是多了Binder的传递过程。

基本使用

  1. 定义AIDL文件
// ISpeaker.aidl
package com.cdh.study;

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

interface ISpeaker {
   
    String sayHello();
}
  1. 定义Server端
public class SpeakerService extends Service {
   

    // 创建由aidl文件自动生成的Stub实例
    private final ISpeaker.Stub mSpeakerBinder = new ISpeaker.Stub() {
   
        @Override
        public String sayHello() throws RemoteException {
   
            return "hello!";
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
   
        // 返回Stub实例
        return mSpeakerBinder;
    }
}

在Service的onBind回调中返回创建的ISpeaker.Stub实例,Stub继承Binder和ISpeaker。Client端在成功连接后便可通过其调用Server端的sayHello方法。

  1. 定义Client端
// Server端API接口,若Client和Server处于同一进程则返回的即是Server端binder实例,否则是Server端binder代理
private ISpeaker mISpeaker;

// 创建ServiceConnection实例,用作回调接口
private final ServiceConnection mConnection = new ServiceConnection() {
   
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
   
        // 连接回调,service即Server端onBind返回的IBinder
        mISpeaker = ISpeaker.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
   
        // 断连回调
        mISpeaker = null;
    }
};

public void bind() {
   
    // 绑定Server
    mContext.bindService(new Intent(mContext, SpeakerService.class), mConnection, Context.BIND_AUTO_CREATE);
}

public void unbind() {
   
    // 解绑Server,需要传入绑定时的ServiceConnection接口(内部以ServiceConnection作为key)
    mContext.unbindService(mConnection);
}
  1. 调用Server端提供的API
public void requestHello() {
   
    if (mISpeaker != null) {
   
        try {
   
            // 调用Service方法,会返回"hello!"
            mISpeaker.sayHello();
        } catch (RemoteException e) {
   
            e.printStackTrace();
        }
    }
}

Client端首先进行绑定Service,成功绑定后会回调ServiceConnection的onServiceConnected方法,在这里获取Server端binder实例或binder代理,之后便可通过binder来调用Server端API。

以上是一个bind service的简单例子,接下来跟踪源码调用看看bind service的整体流程。

源码探究

文中源码基于 Android 10.0

bind过程

Caller发起bind

Caller指调用方,发起bind service,将会调用ContextImpl的bindService方法:

[ContextImpl#bindService]

public boolean bindService(Intent service, ServiceConnection conn, int flags) {
   
    warnIfCallingFromSystemProcess();
    // 传入意图、ServiceConnection实例、BIND_AUTO_CREATE(表示目标Service未启动时自动创建)和主线程Handler和当前用户具柄
    return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null,
            getUser());
}

[ContextImpl#bindServiceCommon]

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
        String instanceName, Handler handler, Executor executor, UserHandle user) {
   
    // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
    // binder通信接口,用于AMS向Caller进程通信
    IServiceConnection sd;
    // ···
    if (mPackageInfo != null) {
   
        if (executor != null) {
   
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
        } else {
   
            // 此时executor是null,执行这个case获取IServiceConnection
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        }
    } else {
   
        throw new RuntimeException("Not supported in system context");
    }
    // 省略显示意图校验 ···
    try {
   
        IBinder token = getActivityToken();
        // ···
        // 请求AMS进行bind流程
        int res = ActivityManager.getService().bindIsolatedService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
        if (res < 0) {
   
            throw new SecurityException(
                    "Not allowed to bind to service " + service);
        }
        return res != 0;
    } catch (RemoteException e) {
   
        throw e.rethrowFromSystemServer();
    }
}

该方法中先获取IServiceConnection,然后调用AMS的bindIsolatedService方法。

IServiceConnection说明

在前面的基本使用例子中,Client端通过ServiceConnection接口回调获取Server端binder。但ServiceConnection只是一个普通接口,不支持跨进程通信。因此这里利用IServiceConnection进行AMS向Caller进程调用时的binder通信接口。

sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);

上文通过这个方法获取IServiceConnection,mPackageInfo是LoadedApk,进入方法看看。

[LoadedApk#getServiceDispatcher]

public final IServiceConnection getServiceDispatcher(ServiceConnection c,
        Context context, Handler handler, int flags) {
   
    return getServiceDispatcherCommon(c, context, handler, null, flags);
}

[LoadedApk#getServiceDispatcherCommon]

private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
        Context context, Handler handler, Executor executor, int flags) {
   
    synchronized (mServices) {
   
        LoadedApk.ServiceDispatcher sd = null;
        // ···
        if (sd == null) {
   
            if (executor != null) {
   
                sd = new ServiceDispatcher(c, context, executor, flags);
            } else {
   
                // 创建ServiceDispatcher
                sd = new ServiceDispatcher(c, context, handler, flags);
            }
            // ···
        } else {
   
            // ···
        }
        // 返回其中的mIServiceConnection成员
        return sd.getIServiceConnection();
    }
}

这里省略了判断缓存的部分,若没有缓存,则新建ServiceDispatcher包装传入的参数。

接着看ServiceDispatcher构造方法:

ServiceDispatcher(ServiceConnection conn,
        Context context, Handler activityThread, int flags) {
   
    mIServiceConnection = new InnerConnection(this);
    mConnection = conn;
    mContext = context;
    mActivityThread = activityThread;
    mActivityExecutor = null;
    mLocation = new ServiceConnectionLeaked(null);
    mLocation.fillInStackTrace();
    mFlags = flags;
}

ServiceDispatcher会持有InnerConnection和ServiceConnection。

InnerConnection继承IServiceConnection.Stub,它实现了IServiceConnection的connected方法:

private static class InnerConnection extends IServiceConnection.Stub {
   
    @UnsupportedAppUsage
    final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

    InnerConnection(LoadedApk.ServiceDispatcher sd) {
   
        // 利用弱引用持有ServiceDispatcher
        mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值