我是靠谱客的博主 健壮墨镜,最近开发中收集的这篇文章主要介绍BindService启动的Service为何在Activity销毁时自动解绑(Service销毁时也是一样处理),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

以bindService启动服务后,直接退出Activity,日志会输出:

    ActivityThread: Activity com.example.administrator.layoutmanager.MainActivity has leaked ServiceConnection com.example.administrator.layoutmanager.MainActivity1@52816718thatwasoriginallyboundhereandroid.app.ServiceConnectionLeaked:Activitycom.example.administrator.layoutmanager.MainActivityhasleakedServiceConnectioncom.example.administrator.layoutmanager.MainActivity

    1@52816718 that was originally bound here

紧接着就是我们服务的解绑与销毁

    MyService: call onUnbind…
    call onDestroy…

既然我们没去主动解绑,系统会帮我们解绑,那么它一定缓存了服务的connection。

所以我们先看下bindService的流程,看看在哪里保存了我们的Service信息,直接到ContextImpl中找bindService方法

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

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 在哪里用到了 便知道 在哪里保存了。只有一行代码将其传了进去:

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

我们继续点进去 发现点不进去,来看看这个mPackageInfo:

   final @NonNull LoadedApk mPackageInfo;

这个类最上面还有两个异常:
一个是IntentReceiver没有反注册,一个是ServiceConnection没有解绑。一切都答案都在这个类里了。

final class IntentReceiverLeaked extends AndroidRuntimeException {
    public IntentReceiverLeaked(String msg) {
        super(msg);
    }
}

final class ServiceConnectionLeaked extends AndroidRuntimeException {
    public ServiceConnectionLeaked(String msg) {
        super(msg);
    }
}

LoadedApk类开头有一句注释:

/**
 * Local state maintained about a currently loaded .apk.
 * @hide
 */

当前加载的apk的本地状态维护
来看看里面有些什么,emmm 应有尽有…
 

static final String TAG = "LoadedApk";
    static final boolean DEBUG = false;

    private final ActivityThread mActivityThread;
    final String mPackageName;
    private ApplicationInfo mApplicationInfo;
    private String mAppDir;
    private String mResDir;
    private String[] mOverlayDirs;
    private String[] mSharedLibraries;
    private String mDataDir;
    private String mLibDir;
    private File mDataDirFile;
    private File mDeviceProtectedDataDirFile;
    private File mCredentialProtectedDataDirFile;
    private final ClassLoader mBaseClassLoader;
    private final boolean mSecurityViolation;
    private final boolean mIncludeCode;
    private final boolean mRegisterPackage;
    private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
    /** WARNING: This may change. Don't hold external references to it. */
    Resources mResources;
    private ClassLoader mClassLoader;
    private Application mApplication;

    private String[] mSplitNames;
    private String[] mSplitAppDirs;
    private String[] mSplitResDirs;

    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<>();

我们接着 bindService的流程走,看看

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

    1

里面发生了什么

   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) {
                if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
                sd = map.get(c);
            }
            if (sd == null) {
                sd = new ServiceDispatcher(c, context, handler, flags);
                if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
                if (map == null) {
                    map = new ArrayMap<>();
                    mServices.put(context, map);
                }
                map.put(c, sd);
            } else {
                sd.validate(context, handler);
            }
            return sd.getIServiceConnection();
        }
    }

代码中很清楚的的交代了, map.put(c, sd); 将ServiceConnection 存到了mServices当中。

然后看Activity在销毁时的处理,这里我就直接说代码位置,大家可以实际 去研究下Activity相关流程。
ActivityThread 的 handleDestroyActivity 中有一段代码:

 if (c instanceof ContextImpl) {
                ((ContextImpl) c).scheduleFinalCleanup(
                        r.activity.getClass().getName(), "Activity");
            }
//接下来
  final void scheduleFinalCleanup(String who, String what) {
        mMainThread.scheduleContextCleanup(this, who, what);
    }
   final void scheduleContextCleanup(ContextImpl context, String who,
            String what) {
        ContextCleanupInfo cci = new ContextCleanupInfo();
        cci.context = context;
        cci.who = who;
        cci.what = what;
        sendMessage(H.CLEAN_UP_CONTEXT, cci);
    }

//这个消息
      case CLEAN_UP_CONTEXT:
                    ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj;
                    cci.context.performFinalCleanup(cci.who, cci.what);
                    break;
 //最后  来到
  final void performFinalCleanup(String who, String what) {
        //Log.i(TAG, "Cleanup up context: " + this);
        mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
    }

mPackageInfo熟悉吗?绕了一圈 又来到了 LoadApk。我们来看removeContextRegistrations

    public void removeContextRegistrations(Context context,
            String who, String what) {
        final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
        synchronized (mReceivers) {
            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(ActivityThread.TAG, leak.getMessage(), leak);
                    if (reportRegistrationLeaks) {
                        StrictMode.onIntentReceiverLeaked(leak);
                    }
                    try {
                        ActivityManager.getService().unregisterReceiver(
                                rd.getIIntentReceiver());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
            mUnregisteredReceivers.remove(context);
        }

        synchronized (mServices) {
            //Slog.i(TAG, "Receiver registrations: " + mReceivers);
            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(ActivityThread.TAG, leak.getMessage(), leak);
                    if (reportRegistrationLeaks) {
                        StrictMode.onServiceConnectionLeaked(leak);
                    }
                    try {
                        ActivityManager.getService().unbindService(
                                sd.getIServiceConnection());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                    sd.doForget();
                }
            }
            mUnboundServices.remove(context);
            //Slog.i(TAG, "Service registrations: " + mServices);
        }
    }

很明显 这里去 帮我们解绑 我们没有解绑的Receiver和Service,并输出异常。

到这里就基本结束了,现在来组织下语言:
- 为什么bindService可以跟Activity生命周期联动?
答: 因为bindService时LoadApk将ServiceConnection用map保存了起来,当Activity被destroy时会执行removeContextRegistrations来清除 该context的相关注册。所以Activity退出时服务也被解绑。

其余变量的用处可以自行参阅源码。
---------------------
作者:风卷云飞会天黑
来源:CSDN
原文:https://blog.csdn.net/castledrv/article/details/80311502

最后

以上就是健壮墨镜为你收集整理的BindService启动的Service为何在Activity销毁时自动解绑(Service销毁时也是一样处理)的全部内容,希望文章能够帮你解决BindService启动的Service为何在Activity销毁时自动解绑(Service销毁时也是一样处理)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(32)

评论列表共有 0 条评论

立即
投稿
返回
顶部