BindServiceが起動したサービスがActivity破棄時に自動的に破棄される理由(サービス破棄時も同様)
11550 ワード
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の流れを見て、どこにサービス情報が保存されているかを見て、ContextImplに直接bindServiceの方法を探します.
bindServiceCommonメソッド:
コードが少なく、理解しやすいので、ServiceConnectionがどこで使われているかを見て、どこで保存されているかを知るだけです.1行のコードだけが送信されました.
私たちは引き続き点を入れて点を発見して入らないで、このmPackageInfoを見てみましょう:
このクラスの一番上には2つの異常があります.1つはIntentReceiverが逆登録していないこと、1つはServiceConnectionがバインドを解除していないことです.すべての答えはこのクラスにあります.
LoadedApkクラスの先頭にコメントがあります.
現在ロードされているapkのローカルステータスメンテナンスを見てみましょう.emmmには何があるのでしょうか.
bindServiceのプロセスに続いて、見てみましょう.
mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
1
何があったの?
コードの中ではっきり説明しました.map.put(c, sd); ServiceConnectionをmServicesに保存します.
それからActivityが破棄する時の処理を見て、ここで私は直接コードの位置を言って、みんなは実際にActivityの関連する流れを研究することができます.Activity ThreadのhandleDestroyActivityには、次のコードがあります.
mPackageInfoはおなじみですか?ぐるっと一周してまたLoadApkにやってきました.removeContextRegistrationsを見てみましょう
明らかにここでは、私たちが縛っていないReceiverとServiceを縛って、異常を出力してくれます.
ここで基本的に終わります.今、言語を整理します.-なぜbindServiceはActivityライフサイクルと連動できるのですか.答え:bindServiceではLoadApkがServiceConnectionをmapで保存しているため、ActivityがdestroyになったときにremoveContextRegistrationsを実行してcontextの関連登録を消去します.したがってActivityが終了するとサービスも解放される.
残りの変数の用途は、ソースコードを参照してください.-----------------------------作者:风卷云飞会黑暗来源:CSDN原文:https://blog.csdn.net/castledrv/article/details/80311502
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の流れを見て、どこにサービス情報が保存されているかを見て、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がどこで使われているかを見て、どこで保存されているかを知るだけです.1行のコードだけが送信されました.
IServiceConnection sd;
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
私たちは引き続き点を入れて点を発見して入らないで、このmPackageInfoを見てみましょう:
final @NonNull LoadedApk mPackageInfo;
このクラスの一番上には2つの異常があります.1つはIntentReceiverが逆登録していないこと、1つは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);
1
何があったの?
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の関連する流れを研究することができます.Activity Threadの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を縛って、異常を出力してくれます.
ここで基本的に終わります.今、言語を整理します.-なぜbindServiceはActivityライフサイクルと連動できるのですか.答え:bindServiceではLoadApkがServiceConnectionをmapで保存しているため、ActivityがdestroyになったときにremoveContextRegistrationsを実行してcontextの関連登録を消去します.したがってActivityが終了するとサービスも解放される.
残りの変数の用途は、ソースコードを参照してください.-----------------------------作者:风卷云飞会黑暗来源:CSDN原文:https://blog.csdn.net/castledrv/article/details/80311502