Android面试题:bindService获取代理是同步还是异步

简介: Android面试题:bindService获取代理是同步还是异步

Android中bindService是一个异步的过程,什么意思呢?使用bindService无非是想获得一个Binder服务的Proxy,但这个代理获取到的时机并非由bindService发起端控制,而是由Service端来控制,也就是说bindService之后,APP端并不会立刻获得Proxy,而是要等待Service通知APP端,具体流程可简化如下:


  • APP端先通过bindService去AMS登记,说明自己需要绑定这样一个服务,并留下派送地址
  • APP回来,继续做其他事情,可以看做是非阻塞的
  • AMS通知Service端启动这个服务
  • Service启动,并通知AMS启动完毕
  • AMS跟住之前APP端留下的地址通知APP端,并将Proxy代理传递给APP端


通过代码来看更直接

void test(){
        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
               iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
               Log.v(TAG, "onServiceConnected..." );
            }
           @Override
            public void onServiceDisconnected(ComponentName componentName) {
           }
        }, Context.BIND_AUTO_CREATE);
        Log.v(TAG, "end..." );
    }

bindService的过程中,上面代码的Log应该是怎么样的呢?如果bindService是一个同步过程,那么Log应该如下:


TAG  onServiceConnected ...
TAG  end ...

但是由于是个异步过程,真实的Log如下


TAG  end ...    
TAG  onServiceConnected ...

也就是说bindService不会阻塞等待APP端获取Proxy,而是直接返回,这些都可以从源码获得支持,略过,直接去ActivityManagerNative去看

public int bindService(IApplicationThread caller, IBinder token,
        Intent service, String resolvedType, IServiceConnection connection,
        int flags, int userId) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeStrongBinder(token);
    service.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(connection.asBinder());
    data.writeInt(flags);
    data.writeInt(userId);
    <!--阻塞等待-->
    mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
    reply.readException();
    int res = reply.readInt();
    data.recycle();
    reply.recycle();
    return res;
}

mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0)确实会让APP端调用线程阻塞,等待AMS执行BIND_SERVICE_TRANSACTION请求,不过AMS在执行这个请求的时候并非是唤醒Service才返回,它返回的时机更早,接着看ActivityManagerService,


public int bindService(IApplicationThread caller, IBinder token,
        Intent service, String resolvedType,
        IServiceConnection connection, int flags, int userId) {
    ...
    synchronized(this) {
        return mServices.bindServiceLocked(caller, token, service, resolvedType,
                connection, flags, userId);
    }
}

ActivityManagerService直接调用ActiveServices的函数bindServiceLocked,请求绑定Service,到这里APP端线程依旧阻塞,等待AMS端返回,假定Service所处的进程已经启动但是Service没有启动,这时ActiveServices会进一步调用bindServiceLocked->realStartServiceLocked来启动Service,有趣的就在这里:

private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app) throws RemoteException {
        ...
        <!--请求Service端启动Service-->
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
        ...
        <!--请求绑定Service-->
        requestServiceBindingsLocked(r);

app.thread.scheduleCreateService也是一个Binder通信过程,他其实是AMS异步请求ActivityThread中的ApplicationThread服务,系统服务请求客户端的本地服务一般都是异步的:



// 插入消息,等待主线程执行
public final void scheduleCreateService(IBinder token,
        ServiceInfo info, CompatibilityInfo compatInfo) {
    CreateServiceData s = new CreateServiceData();
    s.token = token;
    s.info = info;
    s.compatInfo = compatInfo;
    <!--向Loop的MessagerQueue插入一条消息就返回-->
    queueOrSendMessage(H.CREATE_SERVICE, s);
}


不过,这个请求直接向Service端的ActivityThread线程中直接插入一个消息就返回了,而并未等到该请求执行,因为AMS使用的非常频繁,不可能老等待客户端完成一些任务,所以AMS端向客户端发送完命令就直接返回,这个时候其实Service还没有被创建,也就是这个请求只是完成了一半,onServiceConnected也并不会执行,onServiceConnected什么时候执行呢?app.thread.scheduleCreateService向APP端插入第一条消息,是用来创建Service的, requestServiceBindingsLocked其实就是第二条消息,用来处理绑定的


private final boolean requestServiceBindingLocked(ServiceRecord r,
            IntentBindRecord i, boolean rebind) {
                ...
           <!-- 第二个消息,请求处理绑定-->
            r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);

第二条消息是处理一些绑定需求,Android的Hanlder消息处理机制保证了第二条消息一定是在第一条消息之后执行,

public final void scheduleBindService(IBinder token, Intent intent,
        boolean rebind) {
    BindServiceData s = new BindServiceData();
    s.token = token;
    s.intent = intent;
    s.rebind = rebind;
    queueOrSendMessage(H.BIND_SERVICE, s);
}

以上两条消息插入后,AMS端被唤醒,进而重新唤醒之前阻塞的bindService端,而这个时候,Service并不一定被创建,所以说这是个未知的异步过程,Service端处理第一条消息的时会创建Service,


private void handleCreateService(CreateServiceData data) {
    ...
    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
   ...

执行第二条消息的时候, 会向AMS请求publishService,其实就是告诉AMS,服务启动完毕,可以向之前请求APP端派发代理了。

 private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    if (s != null) {
       try {
        data.intent.setExtrasClassLoader(s.getClassLoader());
        try {
            if (!data.rebind) {
                IBinder binder = s.onBind(data.intent);
                ActivityManagerNative.getDefault().publishService(
                        data.token, data.intent, binder);
            ...                       

AMS端收到publishService消息之后,才会向APP端发送通知,进而通过Binder回调APP端onServiceConnected函数,同时传递Proxy Binder服务代理


void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    ...
     try {
    <!--通过binder 回到APP端的onServiceConnected--> 
        c.conn.connected(r.name, service);
    } catch (Exception e) {

到这里,onServiceConnected才会被回调,不过,至于Service端那两条消息什么时候执行,谁也不能保证,也许因为特殊原因,那两条消息永远不被执行,那onServiceConnected也就不会被回调,但是这不会影响AMS与APP端处理其他问题,因为这些消息是否被执行已经不能阻塞他们两个了,简单流程如下:


image.png

最后,其实startService也是异步。


目录
打赏
0
0
0
1
4
分享
相关文章
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
47 6
面试官最爱的面试题:wait() 和 notify() 为什么需要同步?
大家好,我是小米。今天来探讨一个常见的Java面试题:为什么线程通信的 `wait()`、`notify()` 和 `notifyAll()` 方法被定义在 Object 类里,且必须在同步方法或同步块中调用?通过小明和小红的工作场景,我们理解了这些方法的核心思想——线程间的协调与通信。它们依赖于对象锁,确保线程按预期顺序执行,避免资源争抢和死锁。掌握这些知识点,能帮助你更好地应对多线程相关的面试问题。如果你对线程同步等话题感兴趣,欢迎继续交流。
40 12
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
**Kotlin中的`by lazy`和`lateinit`都是延迟初始化技术。`by lazy`用于只读属性,线程安全,首次访问时初始化;`lateinit`用于可变属性,需手动初始化,非线程安全。`by lazy`支持线程安全模式选择,而`lateinit`适用于构造函数后初始化。选择依赖于属性特性和使用场景。**
207 5
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
[go 面试] 同步与异步:程序执行方式的不同之处
[go 面试] 同步与异步:程序执行方式的不同之处
Android经典面试题之Kotlin中常见作用域函数
**Kotlin作用域函数概览**: `let`, `run`, `with`, `apply`, `also`. `let`安全调用并返回结果; `run`在上下文中执行代码并返回结果; `with`执行代码块,返回结果; `apply`配置对象后返回自身; `also`附加操作后返回自身
75 8
面试题Kafka问题之Kafka的副本消息同步如何解决
面试题Kafka问题之Kafka的副本消息同步如何解决
116 4
|
7月前
|
Android经典面试题之Kotlin中object关键字实现的是什么类型的单例模式?原理是什么?怎么实现双重检验锁单例模式?
Kotlin 单例模式概览 在 Kotlin 中,`object` 关键字轻松实现单例,提供线程安全的“饿汉式”单例。例如: 要延迟初始化,可使用 `companion object` 和 `lazy` 委托: 对于参数化的线程安全单例,结合 `@Volatile` 和 `synchronized`
87 6
Android经典面试题之SurfaceView和TextureView有什么区别?
分享了`SurfaceView`和`TextureView`在Android中的角色。`SurfaceView`适于视频/游戏,独立窗口低延迟,但变换受限;`TextureView`支持复杂变换,视图层级中渲染,适合动画/视频特效,但性能略低。两者在性能、变换、使用和层级上有差异,开发者需按需选择。
199 1
|
7月前
|
Android经典面试题之Java中获取时间戳的方式有哪些?有什么区别?
在Java中获取时间戳有多种方式,包括`System.currentTimeMillis()`(毫秒级,适用于日志和计时)、`System.nanoTime()`(纳秒级,高精度计时)、`Instant.now().toEpochMilli()`(毫秒级,ISO-8601标准)和`Instant.now().getEpochSecond()`(秒级)。`Timestamp.valueOf(LocalDateTime.now()).getTime()`适用于数据库操作。选择方法取决于精度、用途和时间起点的需求。
96 3
Android面试题之kotlin中怎么限制一个函数参数的取值范围和取值类型等
在Kotlin中,限制函数参数可通过类型系统、泛型、条件检查、数据类、密封类和注解实现。例如,使用枚举限制参数为特定值,泛型约束确保参数为Number子类,条件检查如`require`确保参数在特定范围内,数据类封装可添加验证,密封类限制为一组预定义值,注解结合第三方库如Bean Validation进行校验。
119 6

热门文章

最新文章

  • 1
    即时通讯安全篇(一):正确地理解和使用Android端加密算法
    12
  • 2
    escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
    29
  • 3
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    41
  • 4
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    30
  • 5
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    43
  • 6
    Android历史版本与APK文件结构
    134
  • 7
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    36
  • 8
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    30
  • 9
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    61
  • 10
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    40