Android service 启动篇之 bindService

Android service 启动篇之 bindService前言:前面几篇博文Android中service详解Androidservice启动篇之startServiceAndroidservice启动篇之startForegroundService通过sourcecode分析了AMS中service的启动过程,bindService相对复杂一点,主要是多了一些service和app的绑定关系处理。本文继续…

大家好,又见面了,我是你们的朋友全栈君。

系列博文:

Android 中service 详解

Android service 启动篇之 startService

Android service 启动篇之 bindService

Android service 启动篇之 startForegroundService

基于版本:Android O

0. 前言

通过source code 分析了AMS 中service 的启动过程,bindService 相对复杂一点,主要是多了一些service 和app的绑定关系处理。本文继续结合source code 来剖析。

1. 入口函数

1.1 bindService()

    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }

    /** @hide */
    @Override
    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
            UserHandle user) {
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), user);
    }

参数:

  • service 的intent
  • bind 时候的ServiceConnection
  • bind service 用到的flag,例如BIND_AUTO_CREATE
  • 当前的user

1.2 bindServiceCommon()

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
        if (conn == null) {
            throw new IllegalArgumentException("connection is null");
        }
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        validateServiceIntent(service);
        try {
            IBinder token = getActivityToken();
            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                    && mPackageInfo.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                flags |= BIND_WAIVE_PRIORITY;
            }
            service.prepareToLeaveProcess(this);
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, 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();
        }
    }
  • ServiceConnection 不能为null,必须要创建
  • android L 之后的版本不能隐式启动service
  • 调用AMS 的bindService(),calling 的thread,calling 的package 都会传入

注意的是bindService 第 5 个参数sd,传入到AMS 不在是client 的ServiceConnection,而是ServiceDispatcher,详细可以看LoadedApk.ServiceDispatcher,每个应用都有这样的dispatcher 会注册到AMS 中,用于AMS 的回调。

    static final class ServiceDispatcher {
        private final ServiceDispatcher.InnerConnection mIServiceConnection;
        private final ServiceConnection mConnection;
        private final Context mContext;
        private final Handler mActivityThread;
        private final ServiceConnectionLeaked mLocation;
        private final int mFlags;

        private RuntimeException mUnbindLocation;

        private boolean mForgotten;

        private static class ConnectionInfo {
            IBinder binder;
            IBinder.DeathRecipient deathMonitor;
        }

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

            InnerConnection(LoadedApk.ServiceDispatcher sd) {
                mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
            }

            public void connected(ComponentName name, IBinder service, boolean dead)
                    throws RemoteException {
                LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                if (sd != null) {
                    sd.connected(name, service, dead);
                }
            }
        }

        ...
        ...

        public void connected(ComponentName name, IBinder service, boolean dead) {
            if (mActivityThread != null) {
                mActivityThread.post(new RunConnection(name, service, 0, dead));
            } else {
                doConnected(name, service, dead);
            }
        }

        ...
        ...

        public void doConnected(ComponentName name, IBinder service, boolean dead) {
            ServiceDispatcher.ConnectionInfo old;
            ServiceDispatcher.ConnectionInfo info;

            ...
            ...

            // If there was an old service, it is now disconnected.
            if (old != null) {
                mConnection.onServiceDisconnected(name);
            }
            if (dead) {
                mConnection.onBindingDied(name);
            }
            // If there is a new service, it is now connected.
            if (service != null) {
                mConnection.onServiceConnected(name, service);
            }
        }

        ...
        ...
    }

最终会调用到应用中定义的ServiceConnection 的onServiceDisconnected() 和 onServiceConnected(), 分析AMS 的时候会继续说明这里用处。

2. bindServiceLocked

通过上面的bindService,最终会调用到ActiveServices 中的bindServiceLocked()

2.1 retrieveServiceLocked()

这个在 Android service 启动篇之 startService 的 3.2 节中已经大概解释了下 ,如果app 已经service 在内部进行运行,那么需要满足下面几个条件:

  • service 需要置上flag ServiceInfo.FLAG_EXTERNAL_SERVICE
  • service 需要置上flag ServiceInfo.FLAG_ISOLATED_PROCESS
  • service 的exported 必须为true
  • app 在bind service 的时候置上flag Context.BIND_EXTERNAL_SERVICE
        ComponentName name = new ComponentName(
                sInfo.applicationInfo.packageName, sInfo.name);
        if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
            if (isBindExternal) {
                if (!sInfo.exported) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
                            " is not exported");
                }
                if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
                            " is not an isolatedProcess");
                }
                // Run the service under the calling package's application.
                ApplicationInfo aInfo = AppGlobals.getPackageManager().getApplicationInfo(
                        callingPackage, ActivityManagerService.STOCK_PM_FLAGS, userId);
                if (aInfo == null) {
                    throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " +
                            "could not resolve client package " + callingPackage);
                }
                sInfo = new ServiceInfo(sInfo);
                sInfo.applicationInfo = new ApplicationInfo(sInfo.applicationInfo);
                sInfo.applicationInfo.packageName = aInfo.packageName;
                sInfo.applicationInfo.uid = aInfo.uid;
                name = new ComponentName(aInfo.packageName, name.getClassName());
                service.setComponent(name);
            } else {
                throw new SecurityException("BIND_EXTERNAL_SERVICE required for " +
                        name);
            }

最终获取的ComponentName 的package 为app 的package,name 是service 的name。

2.2 变量AppBindRecord b

 AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);

来看下函数retrieveAppBindingLocked():

    public AppBindRecord retrieveAppBindingLocked(Intent intent,
            ProcessRecord app) {
        Intent.FilterComparison filter = new Intent.FilterComparison(intent);
        IntentBindRecord i = bindings.get(filter);
        if (i == null) {
            i = new IntentBindRecord(this, filter);
            bindings.put(filter, i);
        }
        AppBindRecord a = i.apps.get(app);
        if (a != null) {
            return a;
        }
        a = new AppBindRecord(this, i, app);
        i.apps.put(app, a);
        return a;
    }

 注意,后面在bringUpServiceLocked() 的时候会根据bindings 来确定是否可以bind service。

变量类型为AppBindRecord,如下:

final class AppBindRecord {
    final ServiceRecord service;    // The running service.
    final IntentBindRecord intent;  // The intent we are bound to.
    final ProcessRecord client;     // Who has started/bound the service.

    final ArraySet<ConnectionRecord> connections = new ArraySet<>();
                                    // All ConnectionRecord for this client.

retrieveServiceLocked查找到ServiceRecord之后,生成Service和Client(callerApp)之间的绑定关系AppBindRecord,AppBindRecord的字段包括service,client,intent,确定了他们之间的绑定关系。

2.3 变量 ConnectionRecord c

    ConnectionRecord c = new ConnectionRecord(b, activity,
            connection, flags, clientLabel, clientIntent);

其中的connection 是从app 中传进来的,在上面 1.2 节中已经解析过,相当于一个ServiceDispatcher。记住了每一个需要bind 到该service 的所有信息。在bind 信息有更新的时候也会通过这里的conn 进行connected() 调用。

final class ConnectionRecord {
    final AppBindRecord binding;    // The application/service binding.
    final ActivityRecord activity;  // If non-null, the owning activity.
    final IServiceConnection conn;  // The client connection.
    final int flags;                // Binding options.
    final int clientLabel;          // String resource labeling this client.
    final PendingIntent clientIntent; // How to launch the client.
    String stringName;              // Caching of toString.
    boolean serviceDead;            // Well is it?

2.4 变量 connections

    IBinder binder = connection.asBinder();
    ArrayList<ConnectionRecord> clist = s.connections.get(binder);
    if (clist == null) {
        clist = new ArrayList<ConnectionRecord>();
        s.connections.put(binder, clist);
    }
    clist.add(c);
    b.connections.add(c);
    if (activity != null) {
        if (activity.connections == null) {
            activity.connections = new HashSet<ConnectionRecord>();
        }
        activity.connections.add(c);
    }
    b.client.connections.add(c);
    if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
        b.client.hasAboveClient = true;
    }
    if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
        s.whitelistManager = true;
    }
    if (s.app != null) {
        updateServiceClientActivitiesLocked(s.app, c, true);
    }
    clist = mServiceConnections.get(binder);
    if (clist == null) {
        clist = new ArrayList<ConnectionRecord>();
        mServiceConnections.put(binder, clist);
    }
    clist.add(c);

AppBindRecord的connections字段则保存了这个client的所有ServiceConnection连接ConnectionRecord,ConnectionRecord和IServiceConnection对象是对应的。ServiceRecord也有个connections列表,但ServiceRecord的connections列表存储的是这个Service相关的所有ConnectionRecord,Service和Client之间是多对多的关系,所以其各自维护了一个connections。

2.5 bringUpServiceLocked()

    if ((flags&Context.BIND_AUTO_CREATE) != 0) {
        s.lastActivity = SystemClock.uptimeMillis();
        if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                permissionsReviewRequired) != null) {
            return 0;
        }
    }

如果bindService 的时候置上flag Context.BIND_AUTO_CREATE,那么会直接进入bringUpServiceLocked() 进行唤醒。

2.5.1 进入create 流程

在 Android service 启动篇之 startService 的 3.6.1 节中我们对该函数进行的详细的分析,第一次进入唤醒的时候需要通过函数realStartServiceLocked() 进行启动,并且通过函数bumpServiceExecutingLocked(r, execInFg, “create”); 埋下create 流程ANR的炸弹,最后通过scheduleCreateService进入create 流程。

2.5.2 进入bind service 流程

接着会进入bind service 流程:

    private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
            throws TransactionTooLargeException {
        for (int i=r.bindings.size()-1; i>=0; i--) {
            IntentBindRecord ibr = r.bindings.valueAt(i);
            if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
                break;
            }
        }
    }

这里的bindings 就是上面 2.2 节中创建的。

注意:

其实在start service 的时候也会进入这个流程,但是bindings 为空,所以,最终不会进入bind service 的流程。 

详细的函数requestServiceBindingsLocked(r, execInFg); 分析请看下面2.6节。

2.5.3 进入start service 流程

其实在bringUpServiceLocked() 最后会通过函数:

sendServiceArgsLocked(r, execInFg, true);

进入start service 的流程,详细可以看 Android service 启动篇之 startService 的 3.6.1.2 节,但是对于bind service 来说,在函数的最开始条件就不满足return 了。详细看Android service 启动篇之 startService 的 3.4 节和3.6.1.1 节。

2.6 requestServiceBindingsLocked()

上一节,我们看到如果service 第一次启动的时候,会进入realStartServiceLocked()。最终会进入该函数,执行bind 的完成流程。

但是,如果service 已经启动,这个时候在bringUpServiceLocked() 中:

        if (r.app != null && r.app.thread != null) {
            sendServiceArgsLocked(r, execInFg, false);
            return null;
        }

而 sendServiceArgsLocked() 在上面 2.5.3 中知道bind service 是不会执行的。

那最终会跳过 2.5 节的流程继续往下执行:

        if (s.app != null && b.intent.received) {
            // Service is already running, so we can immediately
            // publish the connection.
            try {
                c.conn.connected(s.name, b.intent.binder, false);
            } catch (Exception e) {
                Slog.w(TAG, "Failure sending service " + s.shortName
                        + " to connection " + c.conn.asBinder()
                        + " (in " + c.binding.client.processName + ")", e);
            }

            // If this is the first app connected back to this binding,
            // and the service had previously asked to be told when
            // rebound, then do so.
            if (b.intent.apps.size() == 1 && b.intent.doRebind) {
                requestServiceBindingLocked(s, b.intent, callerFg, true);
            }
        } else if (!b.intent.requested) {
            requestServiceBindingLocked(s, b.intent, callerFg, false);
        }

第一次bind service 的时候requested 为false,最终会调用到requestServiceBindingLocked():

    private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
            boolean execInFg, boolean rebind) throws TransactionTooLargeException {
        if (r.app == null || r.app.thread == null) {
            // If service is not currently running, can't yet bind.
            return false;
        }
        if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                + " rebind=" + rebind);
        if ((!i.requested || rebind) && i.apps.size() > 0) {
            try {
                bumpServiceExecutingLocked(r, execInFg, "bind");
                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                        r.app.repProcState);
                if (!rebind) {
                    i.requested = true;
                }
                i.hasBound = true;
                i.doRebind = false;
            } catch (TransactionTooLargeException e) {
                // Keep the executeNesting count accurate.
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                throw e;
            } catch (RemoteException e) {
                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
                // Keep the executeNesting count accurate.
                final boolean inDestroying = mDestroyingServices.contains(r);
                serviceDoneExecutingLocked(r, inDestroying, inDestroying);
                return false;
            }
        }
        return true;
    }

通过该函数进行bind 的request 申请,通过函数:

bumpServiceExecutingLocked(r, execInFg, "bind");

埋下onBind操作的ANR 炸弹,要求onBind 处理不能超过20s。

并通过:

r.app.thread.scheduleBindService()

最终调用handleBindService函数:

    private void handleBindService(BindServiceData data) {
        Service s = mServices.get(data.token);
        ...
        if (s != null) {
            try {
                ...
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
                    } else {
                        s.onRebind(data.intent);
                        ActivityManager.getService().serviceDoneExecuting(
                                data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                    }
                    ensureJitEnabled();
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            } catch (Exception e) {
                ...
            }
        }
    }

onBind 接口调用完成后会调用AMS.publishService,最终会调用到ActiveServices.publishServiceLocked 进行onBind 的后期处理,最终会调用serviceDoneExecutingLocked 解除ANR炸弹

至此,bindService的流程基本完成,总结下:

  • 注意两个核心变量AppBindRecord 和ConnectionRecord;
  • 如果没有启动service,会顺序进入create、bind、start 流程,并且在每个流程都会埋下ANR炸弹,要求每个流程在20s 内处理完成,在处理结束后ActiveServices 会拆除炸弹;
  • 如果已经启动service,会进入onStartCommand流程,同样伴随着ANR炸弹;
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/132854.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 解决iframe参数过长无法加载问题小记

    解决iframe参数过长无法加载问题小记项目中用到了iframe,传参的时候使用的src属性,默认采用的get方式,此种方式在参数较长的时候就会报错(404无法找到资源),为了解决这种情况,改为采用post方式提交。解决方法:结合form表单,利用表单的post请求方式达到目的。实现方式 增加一个form表单的标签,method设置为post,target设置一个标识,假如target=”target1” 在iframe设置na…

  • 阿里面试失败后,一气之下我图解了Java中18把锁「建议收藏」

    目录乐观锁和悲观锁独占锁和共享锁互斥锁和读写锁公平锁和非公平锁可重入锁自旋锁分段锁锁升级(无锁|偏向锁|轻量级锁|重量级锁)锁优化技术(锁粗化、锁消除)乐观锁和悲观锁悲观锁悲观锁对应于生活中悲观的人,悲观的人总是想着事情往坏的方向发展。举个生活中的例子,假设厕所只有一个坑位了,悲观锁上厕所会第一时间把门反锁上,这样其他人上厕所只能在门外等候,这种状态就是「阻塞」了。回到代码世界中,一个共享数据加了悲观锁,那线程每次想操作这个数据前都会假设其他线程.

  • gradle下载慢的问题[通俗易懂]

    gradle下载慢的问题[通俗易懂]gradle下载慢的问题开发工具:IntelliJIDEA&AndroidStudio问题:新建项目下载gradle慢的问题最好的解决办法,翻墙。有些问题,在Google和stackoverflow上很容搜索出来(万恶的百度),还有很多项目文件Github学习到很多。推荐买一个,推荐一个我从大二就开始用的便宜稳定的,里面还有教程。获需要的可以关注公众号:Gremlin回复:v即可获取…

  • LoadRunner 11 安装及激活成功教程

    LoadRunner 11 安装及激活成功教程注意事项:  安装前,把所有的杀毒软件和防火墙关闭。  若以前安装过LoadRunner,则将其卸载。  安装路径不要带中文字符。  如果系统为WIN7,旗舰版才能安装。  安装完毕,需激活成功教程

  • 实时系统动态内存算法分析dsa(二)——TLSF代码分析

    实时系统动态内存算法分析dsa(二)——TLSF代码分析上一篇我们看了dsa的分类和简单的内存管理算法实现,这篇文档我们来看TLSF的实现,一种更加高级的内存管理算法;1、实现原理基本的Segregated Fit算法是使用一组链表,每个链表只包含特定长度范围来的空闲块的方式来管理空闲块的,这样链表数组的长度可能会很大。TLSF为了简化查找定位过程,使用了两层链表。第一层,将空闲内存块的大小根据2的幂进行分类,如(16、32、64.

  • Java web实现简登录页面(MyBatis+jsp+servlet+html+css+javascript)附源码「建议收藏」

    Java web实现简登录页面(MyBatis+jsp+servlet+html+css+javascript)附源码「建议收藏」本文利用MyBatis+jsp+servlet+html+css+javascript实现了一个简单的登录页面。对用户输入的用户名和密码就行校验,校验通过则登录成功,密码和用户信息保存在mysql表中,通过MyBatis访问(MyBatis相关知识可参考先前的文章MyBatis测试)。先给出最终的效果图:登录成功则进入如下页面:登录失败,则进入如下页面:如果输入的密码或者用…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号