我有服务,只要应用程序处于活动状态就应该保持活动状态。因此,在LoginActivity用户未登录时,我调用stopService,当用户登录时,我启动服务显式查看startService。
public class LoginActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
if (accountHelper.isLoggedIn()) {
proceedToMainActivity();
} else if (savedInstanceState == null) {
Fragment fragment = LoginFragment.newInstance();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.LoginContainer, fragment)
.commit();
}
}
public void proceedToMainActivity() {
final Intent intent = MainActivity.createIntent(this);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
//...
}
然后在其他活动中,我在onStart中绑定并在onStop中取消绑定,在Service:
中@Override
public boolean onUnbind(Intent intent) {
Timber.d("onUnbind: %s", intent);
handler.scheduleStop(KEEP_ALIVE);
return false;
}
这将在KEEP_ALIVE时间之后安排stopSelf()(现在是500(半秒),但60000(一分钟)就可以了)) 当用户长时间离开应用程序并成功重新创建时服务被破坏,但是在onCreate和onBind LeakCanary的非常短暂的时间之后报告此服务的内存泄漏。请注意,onUnbind和onDestroy都没有被调用。 LeakCanary提供以下转储:
In <app-package>:1.0:7.
* <app-package>.service.SocketService has leaked:
* GC ROOT static android.app.ActivityThread.sCurrentActivityThread
* references android.app.ActivityThread.mServices
* references android.util.ArrayMap.mArray
* references array java.lang.Object[].[1]
* leaks <app-package>.service.SocketService instance
* Retaining: 9.6 KB.
* Reference Key: 525376d3-87b7-4522-9c9a-2d4547bc4f8d
* Device: unknown Android Android SDK built for x86_64 sdk_google_phone_x86_64
* Android Version: 7.1 API: 25 LeakCanary: 1.5 00f37f5
* Durations: watch=5900ms, gc=126ms, heap dump=5628ms, analysis=22007ms
* Details:
* Class android.app.ActivityThread
| static DEBUG_BACKUP = false
| static sPackageManager = android.content.pm.IPackageManager$Stub$Proxy@717230928 (0x2ac01350)
| static LOG_AM_ON_STOP_CALLED = 30049
| static MIN_TIME_BETWEEN_GCS = 5000
| static DEBUG_MESSAGES = false
| static $classOverhead = byte[719]@1885346289 (0x706019f1)
| static SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003
| static sCurrentBroadcastIntent = java.lang.ThreadLocal@1884894056 (0x70593368)
| static HEAP_COLUMN = java.lang.String@1886211480 (0x706d4d98)
| static SERVICE_DONE_EXECUTING_ANON = 0
| static ACTIVITY_THREAD_CHECKIN_VERSION = 4
| static DEBUG_ORDER = false
| static TWO_COUNT_COLUMNS = java.lang.String@1886211728 (0x706d4e90)
| static DEBUG_PROVIDER = false
| static TAG = java.lang.String@1886274008 (0x706e41d8)
| static DEBUG_CONFIGURATION = false
| static DEBUG_MEMORY_TRIM = false
| static SERVICE_DONE_EXECUTING_STOP = 2
| static DEBUG_SERVICE = false
| static DEBUG_BROADCAST = false
| static HEAP_FULL_COLUMN = java.lang.String@1886211560 (0x706d4de8)
| static localLOGV = false
| static sMainThreadHandler = android.app.ActivityThread$H@717234304 (0x2ac02080)
| static THUMBNAIL_FORMAT = android.graphics.Bitmap$Config@1884894032 (0x70593350)
| static REPORT_TO_ACTIVITY = true
| static DEBUG_RESULTS = false
| static DONT_REPORT = 2
| static ONE_COUNT_COLUMN_HEADER = java.lang.String@1886211784 (0x706d4ec8)
| static SERVICE_DONE_EXECUTING_START = 1
| static sCurrentActivityThread = android.app.ActivityThread@717242624 (0x2ac04100)
| static USER_LEAVING = 1
| static LOG_AM_ON_PAUSE_CALLED = 30021
| static ONE_COUNT_COLUMN = java.lang.String@1886211696 (0x706d4e70)
| static LOG_AM_ON_RESUME_CALLED = 30022
* Instance of android.app.ActivityThread
| static DEBUG_BACKUP = false
| static sPackageManager = android.content.pm.IPackageManager$Stub$Proxy@717230928 (0x2ac01350)
| static LOG_AM_ON_STOP_CALLED = 30049
| static MIN_TIME_BETWEEN_GCS = 5000
| static DEBUG_MESSAGES = false
| static $classOverhead = byte[719]@1885346289 (0x706019f1)
| static SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003
| static sCurrentBroadcastIntent = java.lang.ThreadLocal@1884894056 (0x70593368)
| static HEAP_COLUMN = java.lang.String@1886211480 (0x706d4d98)
| static SERVICE_DONE_EXECUTING_ANON = 0
| static ACTIVITY_THREAD_CHECKIN_VERSION = 4
| static DEBUG_ORDER = false
| static TWO_COUNT_COLUMNS = java.lang.String@1886211728 (0x706d4e90)
| static DEBUG_PROVIDER = false
| static TAG = java.lang.String@1886274008 (0x706e41d8)
| static DEBUG_CONFIGURATION = false
| static DEBUG_MEMORY_TRIM = false
| static SERVICE_DONE_EXECUTING_STOP = 2
| static DEBUG_SERVICE = false
| static DEBUG_BROADCAST = false
| static HEAP_FULL_COLUMN = java.lang.String@1886211560 (0x706d4de8)
| static localLOGV = false
| static sMainThreadHandler = android.app.ActivityThread$H@717234304 (0x2ac02080)
| static THUMBNAIL_FORMAT = android.graphics.Bitmap$Config@1884894032 (0x70593350)
| static REPORT_TO_ACTIVITY = true
| static DEBUG_RESULTS = false
| static DONT_REPORT = 2
| static ONE_COUNT_COLUMN_HEADER = java.lang.String@1886211784 (0x706d4ec8)
| static SERVICE_DONE_EXECUTING_START = 1
| static sCurrentActivityThread = android.app.ActivityThread@717242624 (0x2ac04100)
| static USER_LEAVING = 1
| static LOG_AM_ON_PAUSE_CALLED = 30021
| static ONE_COUNT_COLUMN = java.lang.String@1886211696 (0x706d4e70)
| static LOG_AM_ON_RESUME_CALLED = 30022
| mActivities = android.util.ArrayMap@718216736 (0x2acf1e20)
| mAllApplications = java.util.ArrayList@718233784 (0x2acf60b8)
| mAppThread = android.app.ActivityThread$ApplicationThread@717234816 (0x2ac02280)
| mAvailThumbnailBitmap = null
| mBackupAgents = android.util.ArrayMap@718216768 (0x2acf1e40)
| mBoundApplication = android.app.ActivityThread$AppBindData@718080384 (0x2acd0980)
| mCompatConfiguration = android.content.res.Configuration@718125024 (0x2acdb7e0)
| mConfiguration = android.content.res.Configuration@718125128 (0x2acdb848)
| mCoreSettings = android.os.Bundle@718233808 (0x2acf60d0)
| mCurDefaultDisplayDpi = 480
| mDensityCompatMode = false
| mGcIdler = android.app.ActivityThread$GcIdler@718198224 (0x2aced5d0)
| mGcIdlerScheduled = false
| mH = android.app.ActivityThread$H@717234304 (0x2ac02080)
| mInitialApplication = <app-package>.ILSApplication@717705912 (0x2ac752b8)
| mInstrumentation = android.app.Instrumentation@717931136 (0x2acac280)
| mInstrumentationAppDir = null
| mInstrumentationLibDir = null
| mInstrumentationPackageName = null
| mInstrumentationSplitAppDirs = null
| mInstrumentedAppDir = null
| mInstrumentedLibDir = null
| mInstrumentedSplitAppDirs = null
| mJitEnabled = true
| mLastAssistStructures = java.util.ArrayList@718233832 (0x2acf60e8)
| mLastSessionId = 0
| mLifecycleSeq = 3
| mLocalProviders = android.util.ArrayMap@718216800 (0x2acf1e60)
| mLocalProvidersByName = android.util.ArrayMap@718216832 (0x2acf1e80)
| mLooper = android.os.Looper@717234272 (0x2ac02060)
| mMainThreadConfig = android.content.res.Configuration@718125232 (0x2acdb8b0)
| mNewActivities = null
| mNumVisibleActivities = 1
| mOnPauseListeners = android.util.ArrayMap@718216864 (0x2acf1ea0)
| mPackages = android.util.ArrayMap@718216896 (0x2acf1ec0)
| mPendingConfiguration = null
| mProfiler = android.app.ActivityThread$Profiler@718233856 (0x2acf6100)
| mProviderMap = android.util.ArrayMap@718216928 (0x2acf1ee0)
| mProviderRefCountMap = android.util.ArrayMap@718216960 (0x2acf1f00)
| mRelaunchingActivities = java.util.ArrayList@718233880 (0x2acf6118)
| mResourcePackages = android.util.ArrayMap@718216992 (0x2acf1f20)
| mResourcesManager = android.app.ResourcesManager@717554016 (0x2ac50160)
| mServices = android.util.ArrayMap@718217024 (0x2acf1f40)
| mSomeActivitiesChanged = true
| mSystemContext = null
| mSystemThread = false
| mThumbnailCanvas = null
| mThumbnailHeight = -1
| mThumbnailWidth = -1
| mUpdatingSystemConfig = false
| shadow$_klass_ = android.app.ActivityThread
| shadow$_monitor_ = 0
* Instance of android.util.ArrayMap
| static DEBUG = false
| static BASE_SIZE = 4
| static EMPTY = android.util.ArrayMap@1884928752 (0x7059baf0)
| static mBaseCache = java.lang.Object[8]@717433776 (0x2ac32bb0)
| static mBaseCacheSize = 2
| static $classOverhead = byte[453]@1885300129 (0x705f65a1)
| static EMPTY_IMMUTABLE_INTS = int[0]@1884928784 (0x7059bb10)
| static mTwiceBaseCacheSize = 0
| static mTwiceBaseCache = null
| static CACHE_SIZE = 10
| static TAG = java.lang.String@1887082792 (0x707a9928)
| mArray = java.lang.Object[8]@718274320 (0x2acfff10)
| mCollections = null
| mHashes = int[4]@718411680 (0x2ad217a0)
| mIdentityHashCode = false
| mSize = 1
| shadow$_klass_ = android.util.ArrayMap
| shadow$_monitor_ = 0
* Array of java.lang.Object[]
| [0] = android.os.BinderProxy@718299104 (0x2ad05fe0)
| [1] = <app-package>.service.SocketService@718331328 (0x2ad0ddc0)
| [2] = null
| [3] = null
| [4] = null
| [5] = null
| [6] = null
| [7] = null
* Instance of <app-package>.service.SocketService
| static MESSAGE = java.lang.String@1880325920 (0x70137f20)
| static TEMP_ID = java.lang.String@718413760 (0x2ad21fc0)
| static $change = null
| static SESSION_ID = java.lang.String@718449288 (0x2ad2aa88)
| static LIMIT = java.lang.String@1880322712 (0x70137298)
| static $classOverhead = byte[1708]@718645249 (0x2ad5a801)
| static KEEP_ALIVE = 500
| static USER_CONNECTED = java.lang.String@718550736 (0x2ad436d0)
| static TO_USER_ID = java.lang.String@718413792 (0x2ad21fe0)
| static CHAT_USER_ID = java.lang.String@719910160 (0x2ae8f510)
| static ACCESS_TOKEN = java.lang.String@717227480 (0x2ac005d8)
| static ON_SOCKET_CONNECTED = 100
| static serialVersionUID = 0
| static USER_ID = java.lang.String@1886968720 (0x7078db90)
| static USER_MESSAGE = java.lang.String@719910320 (0x2ae8f5b0)
| static CHAT_MESSAGE = java.lang.String@719910120 (0x2ae8f4e8)
| static FROM_MESSAGE_ID = java.lang.String@719984336 (0x2aea16d0)
| static MESSAGE_TYPE = java.lang.String@719910200 (0x2ae8f538)
| static USER_TYPING = java.lang.String@719910360 (0x2ae8f5d8)
| static PAY_MESSAGE = java.lang.String@719910240 (0x2ae8f560)
| compositeSubscription = rx.subscriptions.CompositeSubscription@718283888 (0x2ad02470)
| handler = <app-package>.service.SocketServiceHandler@719621440 (0x2ae48d40)
| messenger = android.os.Messenger@718283872 (0x2ad02460)
| onChatMessage = <app-package>.service.SocketService$$Lambda$5@718283824 (0x2ad02430)
| onPayMessage = <app-package>.service.SocketService$$Lambda$7@718824184 (0x2ad862f8)
| onUserConnected = <app-package>.service.SocketService$$Lambda$4@718283840 (0x2ad02440)
| onUserMessage = <app-package>.service.SocketService$$Lambda$6@718283808 (0x2ad02420)
| onUserTyping = <app-package>.service.SocketService$$Lambda$1@718824216 (0x2ad86318)
| ready = true
| referenceWatcher = com.squareup.leakcanary.RefWatcher@717705992 (0x2ac75308)
| sessionId = java.lang.String@719458016 (0x2ae20ee0)
| slavicSocketApi = $Proxy1@718286432 (0x2ad02e60)
| socket = io.socket.client.Socket@719769456 (0x2ae6cf70)
| storIOSQLite = com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite@719770408 (0x2ae6d328)
| taskScheduler = <app-package>.util.SimpleTaskScheduler@718283856 (0x2ad02450)
| mActivityManager = android.app.ActivityManagerProxy@717230672 (0x2ac01250)
| mApplication = <app-package>.ILSApplication@717705912 (0x2ac752b8)
| mClassName = java.lang.String@718406088 (0x2ad201c8)
| mStartCompatibility = false
| mThread = android.app.ActivityThread@717242624 (0x2ac04100)
| mToken = android.os.BinderProxy@718299104 (0x2ad05fe0)
| mBase = android.app.ContextImpl@719377344 (0x2ae0d3c0)
| shadow$_klass_ = <app-package>.service.SocketService
| shadow$_monitor_ = -2028143957
* Excluded Refs:
| Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always)
| Thread:FinalizerWatchdogDaemon (always)
| Thread:main (always)
| Thread:LeakCanary-Heap-Dump (always)
| Class:java.lang.ref.WeakReference (always)
| Class:java.lang.ref.SoftReference (always)
| Class:java.lang.ref.PhantomReference (always)
| Class:java.lang.ref.Finalizer (always)
| Class:java.lang.ref.FinalizerReference (always)
为何服务标记为泄露? UPD:我已经从onUnbind中删除了明确的服务启动/停止和延迟停止,但泄漏仍然是相同的。
答案 0 :(得分:-1)
看起来这对于有界服务来说是不可避免的。我已经制作了一个示例应用来演示此问题。 https://github.com/KonstantinBerkow/Android-Bound-Service-Leak