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)


相关推荐

  • c++ 优先级队列_kafka优先级队列

    c++ 优先级队列_kafka优先级队列C++优先级队列解析优先级队列:是零个或多个元素的集合,优先级队列中每一个元素都有一个优先级,元素的先后的出队顺序是由优先级的高低决定的。优先级高的先出队,优先级低的后出队。优先级队列的主要特点:从一个集合中能够快速的查找到和删除最大值和最小值的元素。1.入队解释图:2.出队解释图:3.代码:PriorityQueue.h#pragmaonce#ifndefMYPRIORITYQUEUE_H#defineMYPRIORITYQUEUE_H#include<iostre

  • jdk的动态代理机制_jdk动态代理

    jdk的动态代理机制_jdk动态代理摘要:    代理模式为其他对象提供了一种代理以控制对这个对象的访问,具体实现包括两大类:静态代理和动态代理。Java动态代理机制的出现使得Java开发人员只需要简单地指定一组接口及委托类对象便能动态地获得代理类,并且其所生成的代理类在将所有的方法调用分派到委托对象上反射执行的同时,还可以对方法进行增强,这也正是SpringAOP的实现基础。通过阅读本文,读者将会对代理模式和Java动态…

    2022年10月16日
  • matplotlib无法显示图片_python运行程序后不显示图像

    matplotlib无法显示图片_python运行程序后不显示图像在学习Matplotlib的时候,在Pycharm中运行的时候不会弹出画的图像。首先你运行之后最小化pycharm,看看是不是已经出来了,只是没有自己弹到最顶层。 代码如下: importnumpyasnpimportmatplotlib.pyplotaspltdefmain():#线的绘制x=np.linspace(-np.p…

  • linux启动时开启screen

    linux启动时开启screen

  • openwrt使用linux内核版本,openwrt更换原有Linux内核版本「建议收藏」

    openwrt使用linux内核版本,openwrt更换原有Linux内核版本「建议收藏」openwrt更换原有Linux内核版本1;将openwrt14.07中的内核版本从3.10.49更换成3.10.102a;更改文件include/kernel-version.mkb;修改文件?target/Linux/<路由器架构>下的Makefile,将KERNEL_PATCHVER:=××那一行的xx改成自己希望的版本系列即可,但不能具体为3.10.102,…

  • 为什么要做深度学习而不是宽度学习?「建议收藏」

    为什么要做深度学习而不是宽度学习?「建议收藏」点击此处返回总目录 前面在深度学习介绍的最后有留下一个疑问。我们为什么要dee…

发表回复

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

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