2011-06-18 31 views
2

我已经确定了我的应用程序中严重的内存泄漏问题,当一个活动快速创建/销毁(或可能很快恢复/暂停;至少这是管理连接到服务我正在考虑基于测试)。内存泄漏管理服务连接在活动onResume/onPause

我想了解一些有助于理解是什么导致了这种情况,以及如何正确解决这个问题。 当我从另一个应用程序切换到我的应用程序时,屏幕方向不同(并且我的活动持有连接对象不在前台)。

我有一堆似乎无法收集垃圾的活动,因为它们似乎仍然被GC引用: 'LoadedApk $ ServiceDispatcher $ DeathMonitor'对象位于GC根目录。

我在onResume()中调用bind并在onPause()中取消绑定。 做了一些实验后,我相信我以某种方式绑定/解除绑定问题的根本原因。例如,如果我停止调用解绑,内存泄漏停止,但我得到一些ConnectionLeaked异常(我明白他们的原因)。 也许在连接对象收到通知之前调用解除绑定会导致此问题(但我在logcat中看不到错误,并且该服务正确接收绑定/解除绑定调用)。

我看到一堆活动实例有以下路径GC根当我使用Eclipse的内存分析器(后我来回切换我的应用程序和其他应用程序之间的几次):

MyActivity

< --mContext android.app.LoadedApk $ @ ServiceDispatcher 0x41059d6

< ----这$ 0 $ android.app.LoadedApk $ ServiceDispatcher @ DeathMonitor本地0x4105b548堆栈

我的源代码基本上是:

public class MyActivity extends Activity { 
    private final String TAG = "MyActivity"; 
    public IREventService mREventService; 
    private ServiceConnection mConnection = new ServiceConnection() { 
     // Called when the connection with the service is established 
     public void onServiceConnected(ComponentName className, IBinder service) { 
      mREventService = IREventService.Stub.asInterface(service); 
      Log.e(TAG, "connected to service:"+MyActivity.this.toString()+ "connection: "+this.toString()); 

     } 

     // Called when the connection with the service disconnects unexpectedly 
     public void onServiceDisconnected(ComponentName className) { 
      Log.e(TAG, "Service has unexpectedly disconnected:"+this.toString()); 
      mREventService = null; 
     } 
    }; 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     Intent intent = new Intent(this, EventService.class); 
     intent.setAction(this.toString()); 

     Log.w(TAG, "bindService:"+this.toString()); 
     bindService(intent, 
       mConnection, Context.BIND_AUTO_CREATE); 
    } 


    @Override 
    protected void onPause() { 
     super.onPause(); 
     Log.w(TAG, "unbinding from service:"+this.toString()); 
     unbindService(mConnection); 

    } 


} 

的logcats日志是这样的:(EventService是服务,我在onBind记录()和onUnBind())

<I switched to another app> 
06-17 17:27:08.965: WARN/MyActivity(5987): MyActivity onStop() 

<this is where i switch back to my app> 
06-17 17:27:09.415: INFO/ActivityManager(154): Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=169} 
06-17 17:29:44.855: WARN/MyActivity(5987): MyActivity onDestroy() 
06-17 17:29:44.855: INFO/TabletStatusBar(205): DISABLE_BACK: no 
06-17 17:29:45.015: WARN/MyActivity(5987): MyActivity onCreate() 
06-17 17:29:45.015: WARN/MyActivity(5987): MyActivity onResume() 
06-17 17:29:45.015: WARN/MyActivity(5987): bindService:[email protected] 
06-17 17:29:45.015: WARN/EventService(6009): onBind() [email protected] 
06-17 17:29:45.025: WARN/MyActivity(5987): MyActivity onPause() 
06-17 17:29:45.025: WARN/MyActivity(5987): unbinding from service:[email protected] 
06-17 17:29:45.025: WARN/EventService(6009): onUnBind() [email protected] 
06-17 17:29:45.025: ERROR/MyActivity(5987): connected to service:[email protected]: [email protected] 
06-17 17:29:45.065: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 32K, 3% free 31936K/32839K, paused 34ms 
06-17 17:29:45.125: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 65K, 4% free 32895K/33927K, paused 36ms 
06-17 17:29:45.205: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 803K, 5% free 33550K/35079K, paused 30ms 
06-17 17:29:45.245: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 65K, 4% free 34109K/35335K, paused 27ms 
06-17 17:29:45.255: INFO/dalvikvm-heap(5987): Grow heap (frag case) to 36.976MB for 3722256-byte allocation 
06-17 17:29:45.285: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed <1K, 4% free 37744K/38983K, paused 25ms 
06-17 17:29:45.335: DEBUG/dalvikvm(5987): GC_CONCURRENT freed <1K, 4% free 37744K/38983K, paused 2ms+3ms 
06-17 17:29:45.645: INFO/WindowManager(154): Setting rotation to 3, animFlags=1 
06-17 17:29:45.665: INFO/ActivityManager(154): Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/2 orien=P layout=0x10000014 uiMode=0x11 seq=170} 
06-17 17:29:45.685: DEBUG/FlurryAgent(5770): Ending session 
06-17 17:29:45.755: WARN/MyActivity(5987): MyActivity onStop() 
06-17 17:29:45.755: WARN/MyActivity(5987): MyActivity onDestroy() 
06-17 17:29:45.755: WARN/IInputConnectionWrapper(5770): showStatusIcon on inactive InputConnection 
06-17 17:29:45.865: WARN/MyActivity(5987): MyActivity onCreate() 
06-17 17:29:45.865: WARN/MyActivity(5987): MyActivity onResume() 
06-17 17:29:45.865: WARN/MyActivity(5987): bindService:[email protected] 
06-17 17:29:45.875: WARN/EventService(6009): onBind() [email protected] 
06-17 17:29:45.875: WARN/MyActivity(5987): MyActivity onPause() 
06-17 17:29:45.875: WARN/MyActivity(5987): unbinding from service:[email protected] 
06-17 17:29:45.875: WARN/EventService(6009): onUnBind() [email protected] 
06-17 17:29:45.875: ERROR/MyActivity(5987): connected to service:[email protected]: [email protected] 
06-17 17:29:45.925: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 6130K, 18% free 32743K/39815K, paused 24ms 
06-17 17:29:46.005: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 883K, 16% free 33737K/39815K, paused 23ms 
06-17 17:29:46.035: DEBUG/dalvikvm(5987): GC_FOR_ALLOC freed 1K, 15% free 33970K/39815K, paused 23ms 

感谢您的帮助。

回答

4

看起来你已经遇到了ServiceConnection.onServiceConnected和Context.unbindService之间的一个已知的Android-4.0竞争条件。

在Android 4.0之前版本的Android上,如果您在接收到对绑定服务的服务连接的ServiceConnection.onServiceConnected方法的调用之前调用Context.unbindService,则框架将通过JNI引用向您的上下文泄漏android.app.LoadedApk $ ServiceDispatcher $ DeathMonitor对象的方式。

这很可能是你打这个问题 - 如果你看一下你的日志,你会看到正在该onConnected消息之前打印您的onUnbind消息:

06-17 17:29:45.875: WARN/EventService(6009): onUnBind() [email protected] 
06-17 17:29:45.875: ERROR/MyActivity(5987): connected to service:[email protected]: [email protected] 

此bug已被修正为Android 4.0。这里的承诺是固定的bug:

https://github.com/android/platform_frameworks_base/commit/5a6ef737edbf57577443ac056613afe6cb121519

对于早期版本的Android,一个解决办法是推迟调用unbindService直到你收到ServiceConnection.onServiceConnected一个电话之后。对于它的价值,可以在服务连接的onServiceConnected回调方法中调用Context.unbindService。

+0

想补充一点这个答案。在http://developer.android.com/guide/components/bound-services.html中,示例代码使用在onServiceConnected中设置为true的变量mBound;因此,只有在mBound为真时才调用解除绑定。但是,我认为如果我们遵循示例代码的方法,即使在活动停止或销毁之后,最终仍会调用onServiceConnected(非常小)的机会;所以也许在onServiceConnected中,有一个布尔值来检查onStop(或onDestroy)是否被调用,并从那里调用解除绑定。 –