Android之bindService启动服务与Activity生命周期关系

您所在的位置:网站首页 安卓中服务的生命周期和activity的生命周期一样吗 Android之bindService启动服务与Activity生命周期关系

Android之bindService启动服务与Activity生命周期关系

2024-07-01 23:32| 来源: 网络整理| 查看: 265

问:为什么bindService可以跟Activity生命周期联动? 答: 因为bindService时LoadApk将ServiceConnection用map保存了起来,当Activity被destroy时会执行removeContextRegistrations来清除 该context的相关注册。所以Activity退出时服务也被解绑。

启动service有两种方法:                  1、 Context.startService():调用者与服务之间没有关联,即使调用者退出,服务仍可运行                  2、 Context.bindService():调用者与服务绑定在一起,调用者一旦退出,服务也就终止

 

以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.MainActivity1@52816718thatwasoriginallyboundhereandroid.app.ServiceConnectionLeaked:Activitycom.example.administrator.layoutmanager.MainActivityhasleakedServiceConnectioncom.example.administrator.layoutmanager.MainActivity1@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 mReceivers = new ArrayMap(); private final ArrayMap mUnregisteredReceivers = new ArrayMap(); private final ArrayMap mServices = new ArrayMap(); private final ArrayMap mUnboundServices = new ArrayMap();

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

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

里面发生了什么

public final IServiceConnection getServiceDispatcher(ServiceConnection c, Context context, Handler handler, int flags) { synchronized (mServices) { LoadedApk.ServiceDispatcher sd = null; ArrayMap 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 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 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,并输出异常。

其余变量的用处可以自行参阅源码。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3