在onDestroy之前报告服务泄漏

时间:2016-12-14 14:03:47

标签: java android memory-leaks leakcanary

我有服务,只要应用程序处于活动状态就应该保持活动状态。因此,在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中删除了明确的服务启动/停止和延迟停止,但泄漏仍然是相同的。

1 个答案:

答案 0 :(得分:-1)

看起来这对于有界服务来说是不可避免的。我已经制作了一个示例应用来演示此问题。 https://github.com/KonstantinBerkow/Android-Bound-Service-Leak