为什么 bindService 能和 Activity 的生命周期联动?

栏目: IOS · Android · 发布时间: 6年前

内容简介:之前做一道面试题:startService 和 bindService 有什么不同?为什么 bindService 能和 Activity 的生命周期联动?前一个问题可以很快回答出来:生命周期不同,结束方式不同,交互方式不同。后一个问题也能很快想到应该是 Activity 在销毁的时候顺带把 Service 销毁了。那么为什么 startService 不行呢?具体是怎么实现的呢?如果不对源码研究一番,似乎无法给出让人信服的回答,于是就有了这篇文章。

之前做一道面试题:startService 和 bindService 有什么不同?为什么 bindService 能和 Activity 的生命周期联动?

前一个问题可以很快回答出来:生命周期不同,结束方式不同,交互方式不同。

后一个问题也能很快想到应该是 Activity 在销毁的时候顺带把 Service 销毁了。那么为什么 startService 不行呢?具体是怎么实现的呢?如果不对源码研究一番,似乎无法给出让人信服的回答,于是就有了这篇文章。

启动和绑定的区别

无论是启动 Activity,还是 Service,基本的流程都是 Context -> ActivtityManagerService -> 某些中间类(Activity 是 ActivityStarter、ActivityStack 等,Service 是 ActiveServices) -> ActivityThread。具体的代码流程比较长,而且很多和本文要探讨的主题无关,因此这里不会详细分析启动或绑定的流程,而只会保留与本文相关的部分源码。

startService

从 ContextImpl 的 startService 方法开始说起:

class ContextImpl extends Context {

    @Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }
    
    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            // 检查 Intent
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            // 启动 Service
            ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), requireForeground,
                            getOpPackageName(), user.getIdentifier());
            // 检查结果
            if (cn != null) {
                ...
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    
}
复制代码

ActivityManager.getService() 返回的就是 AMS 本身,而 AMS 只起到一个中转的作用,除了一些参数判断之外,AMS 直接调用了 ActiveServices 的 startServiceLocked:

public final class ActiveServices {

    final ActivityManagerService mAm;

    ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
            throws TransactionTooLargeException {

        // 和 Activity 同样有一个 Record 记录对应的组件
        ServiceRecord r = res.record;
        
        ... // 主要是检查,发现错误则抛出异常,或返回 null 等结果

        // 设置 ServiceRecord 的数据域
        r.lastActivity = SystemClock.uptimeMillis();
        r.startRequested = true;
        r.delayedStop = false;
        r.fgRequired = fgRequired;
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                service, neededGrants, callingUid));
                
        ... // 主要是检查,发现错误则抛出异常,或返回 null 等结果

        // 启动
        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
        return cmp;
    }
}
复制代码

好了,startService 的流程暂时分析到这里,后面也没什么特别的,最后还是会由 ActivityThread 来创建 Service 对象,回调相关的生命周期方法等。

bindService

下面看 bindService 的实现:

class ContextImpl extends Context {

    final @NonNull LoadedApk mPackageInfo;
    private final @Nullable IBinder mActivityToken;

    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }
    
    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        ...
        IServiceConnection sd;
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        try {
            // 这个 token 是 Activity 启动时创建的,对应于 Activity 的 mToken 成员
            IBinder token = getActivityToken();
            ...
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            ...
        } catch (RemoteException e) {
            ...
        }
    }
    
    
    @Override
    public IBinder getActivityToken() {
        return mActivityToken;
    }
    
}
复制代码

可以看到,相比 startService,bindService 还在 ContextImpl 执行的时候就已经显示出了它的不同,除了会获取 Activity 的 token 之外,还有一个很关键的调用是 LoadedApk 的 getServiceDispatcher 方法:

public final class LoadedApk {

    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
        = new ArrayMap<>();

    public final IServiceConnection getServiceDispatcher(ServiceConnection c,
            Context context, Handler handler, int flags) {
        synchronized (mServices) {
            LoadedApk.ServiceDispatcher sd = null;
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
            if (map != null) {
                sd = map.get(c);
            }
            if (sd == null) {
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (map == null) {
                    map = new ArrayMap<>();
                    mServices.put(context, map); // 记录
                }
                map.put(c, sd); // 记录 ServiceConnection
            } else {
                sd.validate(context, handler);
            }
            return sd.getIServiceConnection();
        }
    }
    
}
复制代码

ServiceDispatcher 可以忽略,主要关注 mServices 这个成员,它记录了即将绑定到 Activity 的 ServiceConnection。

接着看后面的绑定流程,AMS 同样跳过,直接看 ActiveServices 的实现:

public final class ActiveServices {

    final ActivityManagerService mAm;
    final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections = new ArrayMap<>();

    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
            String resolvedType, final IServiceConnection connection, int flags,
            String callingPackage, final int userId) throws TransactionTooLargeException {
        
        // 获取应用进程的信息
        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
        if (callerApp == null) {
            throw new SecurityException(
                    "Unable to find app for caller " + caller
                    + " (pid=" + Binder.getCallingPid()
                    + ") when binding service " + service);
        }

        // 获取绑定的 Activity 信息
        ActivityRecord activity = null;
        if (token != null) {
            activity = ActivityRecord.isInStackLocked(token);
            if (activity == null) {
                Slog.w(TAG, "Binding with unknown activity: " + token);
                return 0;
            }
        }

        // 获取 ServiceRecord
        ServiceRecord s = res.record;

        boolean permissionsReviewRequired = false;

        // 启动 Activity,成功启动后再启动 Service
        if (mAm.mPermissionReviewRequired) {
            if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                    s.packageName, s.userId)) {

                RemoteCallback callback = new RemoteCallback(
                        new RemoteCallback.OnResultListener() {
                    @Override
                    public void onResult(Bundle result) {
                        synchronized(mAm) {
                            final long identity = Binder.clearCallingIdentity();
                            try {
                                ...
                                if (...) {
                                    try {
                                        // 启动 Service
                                        bringUpServiceLocked(...);
                                    } catch (RemoteException e) {
                                        /* ignore - local call */
                                    }
                                } else {
                                    ...
                                }
                            } finally {
                                ...
                            }
                        }
                    }
                });

                final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
                // 注意 callback
                intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
                
                // 启动 Activity,成功启动后回调 callback 
                mAm.mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
                    }
                });
            }
        }

        final long origId = Binder.clearCallingIdentity();

        try {
            ...
            // 注意参数 activity(ActivityRecord)
            ConnectionRecord c = new ConnectionRecord(b, activity,
                    connection, flags, clientLabel, clientIntent);

            // connection 参数为 IServiceConnection
            IBinder binder = connection.asBinder();
            
            // 让 ActivityRecord 记录 connections 信息
            if (activity != null) {
                if (activity.connections == null) {
                    activity.connections = new HashSet<ConnectionRecord>();
                }
                activity.connections.add(c);
            }

            // 让 ServiceRecord 记录 connections 信息
            ArrayList<ConnectionRecord> clist = s.connections.get(binder);
            if (clist == null) {
                clist = new ArrayList<ConnectionRecord>();
                s.connections.put(binder, clist);
            }
            clist.add(c);

            // 让自身的成员变量 mServiceConnections 记录 connections 信息
            clist = mServiceConnections.get(binder);
            if (clist == null) {
                clist = new ArrayList<ConnectionRecord>();
                mServiceConnections.put(binder, clist);
            }
            clist.add(c);
            
            ...

            if ((flags&Context.BIND_AUTO_CREATE) != 0) { // 如果设置了绑定后自动启动
                s.lastActivity = SystemClock.uptimeMillis();
                // 启动 Service
                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
                        permissionsReviewRequired) != null) {
                    return 0;
                }
            }

            if (s.app != null && b.intent.received) { // Service 已经在运行中,直接回调 onServiceConnected 即可
                // Service is already running, so we can immediately
                // publish the connection.
                try {
                    // 回调 onServiceConnected
                    c.conn.connected(s.name, b.intent.binder, false);
                } catch (Exception e) {
                    ...
                }

                ...
            } else if (!b.intent.requested) { 
                // 绑定 onBind,内部调用了 scheduleBindService
                requestServiceBindingLocked(s, b.intent, callerFg, false);
            }

        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        return 1;
    }
    
}
复制代码

可以看到,相比 startService,bindService 在启动 Service 之前做了一些额外的工作:

  1. 通知 LoadedApk 记录 ServiceConnection
  2. 根据 ActivityToken 获取 ActivityRecord
  3. 添加 ConnectionRecord 到 ActivityRecord 及 ServiceRecord 上

上面就是 startService 和 bindService 在源码实现上的主要区别了,下面开始分析 Activity 的 finish 方法,看看 Service 是怎么随着 Activity 的销毁而销毁的。

Service 是怎么随着 Activity 的销毁而销毁的?

和启动流程类似,finish 的执行流程为 Activity -> ActivityManager -> ActivitiyStack -> ActivityThread,因为代码量有些大,而且前三步和本文关系不大,因此这里直接看 ActivityThread 的实现即可:

public final class ActivityThread {

    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();

    private void handleDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance) {
        // 回调生命周期方法
        ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance);
        if (r != null) {
            // 清理 window 资源
            cleanUpPendingRemoveWindows(r, finishing);
            // 删除 DecorView
            WindowManager wm = r.activity.getWindowManager();
            View v = r.activity.mDecor;
            if (v != null) {
                IBinder wtoken = v.getWindowToken();
                if (r.activity.mWindowAdded) {
                    if (r.mPreserveWindow) {
                        r.window.clearContentView();
                    } else {
                        wm.removeViewImmediate(v);
                    }
                }
                // 清除记录,这个记录可以参考 ViewRootImpl 的 setView 方法
                if (wtoken != null && r.mPendingRemoveWindow == null) {
                    WindowManagerGlobal.getInstance().closeAll(wtoken,
                            r.activity.getClass().getName(), "Activity");
                } else if (r.mPendingRemoveWindow != null) {
                    WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
                            r.activity.getClass().getName(), "Activity");
                }
                r.activity.mDecor = null;
            }
            
            if (r.mPendingRemoveWindow == null) {
                WindowManagerGlobal.getInstance().closeAll(token,
                        r.activity.getClass().getName(), "Activity");
            }

            // 使用 Base Context 执行最后的清理步骤
            Context c = r.activity.getBaseContext();
            if (c instanceof android.app.ContextImpl) {
                ((ContextImpl) c).scheduleFinalCleanup(
                        r.activity.getClass().getName(), "Activity");
            }
        }
        // 通知 AMS
        if (finishing) {
            try {
                ActivityManager.getService().activityDestroyed(token);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
        mSomeActivitiesChanged = true;
    }
}
复制代码

可以看到,在 ActivityThread 中,Activity 的销毁流程共有 4 步:

  1. 回调 onPause、onStop、onDestroy 等生命周期方法
  2. 关闭 Window、移除 DecorView、清理 WindowManager 的记录
  3. 调用 ContextImpl 执行最后的清理步骤
  4. 通知 AMS Activity 已被销毁

Service 的解绑逻辑就隐藏在 ContextImpl 里面,下面看它的实现:

class ContextImpl extends Context {

    final @NonNull ActivityThread mMainThread;
    final @NonNull LoadedApk mPackageInfo;

    // 回调 ActivityThread
    final void scheduleFinalCleanup(String who, String what) {
        mMainThread.scheduleContextCleanup(this, who, what);
    }

    // ActivityThread 最终又回调了该方法
    final void performFinalCleanup(String who, String what) {
        //Log.i(TAG, "Cleanup up context: " + this);
        mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
    }
    
}
复制代码

可以看到,ContextImpl 只是起到了一个中转的作用,最终是交给 LoadedApk 执行的。从方法 removeContextRegistrations 的名字可以推测出,它的作用是清理注册到 Context 上的资源:

public final class LoadedApk {

    private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
        = new ArrayMap<>();
    private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
        = new ArrayMap<>();
    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
        = new ArrayMap<>();
    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
        = new ArrayMap<>();

    public void removeContextRegistrations(Context context,
            String who, String what) {
        final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
        synchronized (mReceivers) {
            // 获取注册到 Context 上的 BroadcastReceiver
            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
                    mReceivers.remove(context);
            if (rmap != null) {
                // 遍历,逐个注销
                for (int i = 0; i < rmap.size(); i++) {
                    LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
                    // 打印异常信息
                    IntentReceiverLeaked leak = new IntentReceiverLeaked(
                            what + " " + who + " has leaked IntentReceiver "
                            + rd.getIntentReceiver() + " that was " +
                            "originally registered here. Are you missing a " +
                            "call to unregisterReceiver()?");
                    leak.setStackTrace(rd.getLocation().getStackTrace());
                    Slog.e(android.app.ActivityThread.TAG, leak.getMessage(), leak);
                    if (reportRegistrationLeaks) {
                        StrictMode.onIntentReceiverLeaked(leak);
                    }
                    // 通知 AMS 注销 BroadcastReceiver
                    try {
                        ActivityManager.getService().unregisterReceiver(
                                rd.getIIntentReceiver());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
            mUnregisteredReceivers.remove(context);
        }

        synchronized (mServices) {
            // 获取绑定到 Context 上的 ServiceConnection
            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
                    mServices.remove(context);
            if (smap != null) {
                // 遍历,逐个解绑
                for (int i = 0; i < smap.size(); i++) {
                    LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
                    // 打印异常信息
                    ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
                            what + " " + who + " has leaked ServiceConnection "
                            + sd.getServiceConnection() + " that was originally bound here");
                    leak.setStackTrace(sd.getLocation().getStackTrace());
                    Slog.e(android.app.ActivityThread.TAG, leak.getMessage(), leak);
                    if (reportRegistrationLeaks) {
                        StrictMode.onServiceConnectionLeaked(leak);
                    }
                    // 通知 AMS 解绑 ServiceConnection
                    try {
                        // 具体实现参考 
                        ActivityManager.getService().unbindService(
                                sd.getIServiceConnection());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                    sd.doForget();
                }
            }
            mUnboundServices.remove(context);
        }
    }
    
}
复制代码

果然,removeContextRegistrations 的作用就是把注册/绑定到 Context 上的 BroadcastReceiver、ServiceConnection 给注销/解绑,并抛出异常信息,告诉用户应该主动地注销/解绑。unbindService、unregisterReceiver 的流程忽略,无非还是从相关的列表中删除一些记录(比如 activity.connections),并通知 ActivityThread 执行最后的注销逻辑。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网络多人游戏架构与编程

网络多人游戏架构与编程

格雷泽 (Joshua Glazer)、马达夫 (Sanjay Madhav) / 王晓慧、张国鑫 / 人民邮电出版社 / 2017-10-1 / CNY 109.00

本书是一本深入探讨关于网络多人游戏编程的图书。 全书分为13章,从网络游戏的基本概念、互联网、伯克利套接字、对象序列化、对象复制、网络拓扑和游戏案例、延迟、抖动和可靠性、改进的延迟处理、可扩展性、安全性、真实世界的引擎、玩家服务、云托管专用服务器等方面深入介绍了网络多人游戏开发的知识,既全面又详尽地剖析了众多核心概念。 本书的多数示例基于C++编写,适合对C++有一定了解的读者阅读。本......一起来看看 《网络多人游戏架构与编程》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具