Android服务导致内存问题

时间:2015-08-12 14:28:03

标签: android sqlite xamarin azure-mobile-services

我的Android应用程序遇到了一些问题。在应用程序中,我有一个长期运行的任务,用于在用户成功登录后将用户数据从Azure服务器同步到本地SQL。因此,我设计了一个服务,它在启动时与活动绑定,以通知用户多少这项任务将花费更长的时间。

应用程序必须从在线数据库复制到本地SQLlite的数据非常大,这会导致内存非常高,因为AzureService& SqlService必须请求相当大的数据列表。它从+ - 30mb到60mb ram。完成所有这些操作后,负责同步的活动将停止服务,并启动 SecondActivity

我的问题是,即使在 FirstActivity (处理sycing)已杀死所有内容(连接,服务,活动本身......)后,内存也不会掉入 SecondActivity 即可。我尝试过不同的技巧让它掉线(在显示器中点击'导致gc',打开其他需要内存的应用程序)但它似乎不起作用。我不知道我在 FirstActivity 中泄漏了什么导致内存在 SecondActivity 中保持高位,据我所知,通过调试,一切都被破坏了需要被摧毁。 我做错了什么?

FirstActivity

[Activity (Label = "Dvit.Apps.MemoryManager", MainLauncher = true, Icon = "@drawable/icon")]
    public class FirstActivity : Activity
    {
        BirdyService _service;
        IServiceConnection _connection;
        TextView _serviceOutput;
        Boolean _isBound = false;

        public FirstActivity ()
        {
            _connection = new SyncServiceConnection (this);
        }

        protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);

            // Set our view from the "main" layout resource
            SetContentView (Resource.Layout.Main);

            Button btnStartService = FindViewById<Button> (Resource.Id.myButton);
            Button NextPage = new Button (this);
            _serviceOutput = FindViewById<TextView> (Resource.Id.txtServiceOutput);
            NextPage.LayoutParameters = new ViewGroup.LayoutParams (WindowManagerLayoutParams.MatchParent, 100);
            NextPage.Gravity = GravityFlags.CenterHorizontal;
            NextPage.Text = "Next Page";
            NextPage.Click += (object sender, EventArgs e) => {
                UnboundService();
                this.Finish();
                StartActivity(typeof(SecondActivity));
            };

            (btnStartService.Parent as LinearLayout).AddView (NextPage);

            btnStartService.Click += delegate {
                BindService ();
                Intent syncLoginInfoIntent = new Intent (this, typeof(BirdyService));
                syncLoginInfoIntent.PutExtra ("commandType", "LoginSync");
                StartService (syncLoginInfoIntent);
            };
        }

        void BindService ()
        {
            Console.WriteLine ("Bind service");
            base.BindService (new Intent (this, typeof(BirdyService)), _connection, Bind.AutoCreate);
            _isBound = true;
        }

        void UnboundService ()
        {
            Console.WriteLine ("Unbinding service");
            if (_isBound) {
                ((SyncServiceConnection)_connection).OnDisconnect ();
                base.UnbindService (_connection);
                _isBound = false;
            }
        }

        void HandleSyncFeedback (object sender, BirdyService.SyncProgressEventArgs e)
        {
            this.RunOnUiThread(() =>{_serviceOutput.Text += "\n" + e.Extra;});
        }

        protected override void OnDestroy ()
        {
            base.OnDestroy ();
            Console.WriteLine ("FirstActivity Destroyed!");
             _connection = null;
            _serviceOutput = null;
        }

        class SyncServiceConnection: Java.Lang.Object, IServiceConnection
        {
            FirstActivity _self;

            public SyncServiceConnection (FirstActivity self)
            {
                _self = self;               
            }

            public void OnServiceConnected (ComponentName className, IBinder service)
            {
                Console.WriteLine ("~~CONNECTING SERVICE!!!~~");
                _self._service = ((BirdyService.SyncBinder)service).Service;
                _self._service.SyncProgressEvent += _self.HandleSyncFeedback;
            }

            public void OnDisconnect ()
            {
                Console.WriteLine ("~~DISCONECTING SERVICE!!!~~");
                if (_self._service != null) {
                    _self._service.SyncProgressEvent -= _self.HandleSyncFeedback;
                    _self._service = null;
                }
            }

            public void OnServiceDisconnected (ComponentName className)
            {   

            }
        }
    }

SecondActivity

[Activity (Label = "Dvit.Apps.MemoryManager", Icon = "@drawable/icon")]
public class SecondActivity : Activity
{
    int count = 1;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Set our view from the "main" layout resource
        SetContentView (Resource.Layout.Main);

        // Get our button from the layout resource,
        // and attach an event to it

    }

    protected override void OnDestroy ()
    {
        base.OnDestroy ();
        Console.WriteLine ("SecondActivity Destroyed!");
    }
}

服务

[Service (Enabled = true)]
public class BirdyService:Service
{
    IAzureService _azureSvc;
    ISqlService _sqlSvc;
    ISyncService _syncSvc;
    IGlobalSettings _settings;
    AndroidSqlteConnection _connection;

    #region Service 'IsSyncing?' Checkers

    private Boolean _syncLoginSync = false;



    public Boolean IsServiceSyncing {
        get {
                if (_syncLoginSync == true)
                    return true;
                else
                    return false;               
        }
    }

    #endregion

    public class SyncProgressEventArgs:EventArgs
    {
        public SyncProgressEventArgs ()
        {
            this.Progress = 0;
            this.IsCompleted = false;
        }

        public bool IsCompleted {
            get;
            set;
        }

        public string Extra{ get; set; }

        public Int16 Progress {
            get;
            set;
        }
    }

    public event EventHandler<SyncProgressEventArgs> SyncProgressEvent;


    #region Binder => communication between starting activity & service

    private IBinder binder;


    public class SyncBinder:Binder
    {
        BirdyService _service;

        public SyncBinder (BirdyService service)
        {
            _service = service;
        }

        public BirdyService Service {
            get{ return _service; }              
        }
    }

    public override IBinder OnBind (global::Android.Content.Intent intent)
    {
        return binder;
    }

    #endregion

    public BirdyService () : base ()
    {
        binder = new SyncBinder (this);
    }

    public override void OnCreate ()
    {
        base.OnCreate ();

        var appContext = this.ApplicationContext as MemoryManagerApplication;

        _settings = appContext.GlobalSettings;
        _azureSvc = new AzureService (new MobileServiceClient (_settings.AzureMobileServiceUrl, _settings.AzureMobileServicePassword));
        _connection = new AndroidSqlteConnection ();
        _sqlSvc = new SqlService (_connection);
        _syncSvc = new SyncService (_azureSvc, appContext.WebApiClient, _sqlSvc, _settings);
    }

    public override StartCommandResult OnStartCommand (global::Android.Content.Intent intent, StartCommandFlags flags, int startId)
    {
        if (intent != null) {
            var command = intent.GetStringExtra ("commandType");
            var keepAlive = intent.GetBooleanExtra ("keepAlive", false);

            Console.WriteLine ("~~>SERVICE TASK RECEIVED COMMMANDNAME: " + command + "<~~");

            switch (command) {
            case "LoginSync":
                {
                    LoginSync (intent, keepAlive);
                }
                break;
            }
        }

        return StartCommandResult.Sticky;
    }

    #region LoginSync

    private void LoginSync (global::Android.Content.Intent intent, bool keepAlive = false)
    {
        new Task (async () => {
            try {
                var progressEventArg = new SyncProgressEventArgs ();
                _syncLoginSync = true;
                _settings.IsSyncingOrganisations = true;

                progressEventArg.Extra = "Fetching master data...";
                progressEventArg.Progress = 10;

                if (SyncProgressEvent != null)
                    this.SyncProgressEvent (null, progressEventArg);

                await _syncSvc.SyncMasterData (true);

                var requestUserInfoFragment = await Authenticate (progressEventArg);

                _settings.IsSyncingOrganisations = false;
                progressEventArg.Extra = "Completed!";
                progressEventArg.Progress = 100;
                progressEventArg.IsCompleted = true;

                if (SyncProgressEvent != null)
                    SyncProgressEvent (requestUserInfoFragment, progressEventArg);

                _syncLoginSync = false;

                if (!IsServiceSyncing && !keepAlive)
                    this.StopSelf ();

            } catch (Exception ex) {
                _syncLoginSync = false;
                //_Logger.Trace (ex.Message);
                _settings.IsSyncingOrganisations = false;

                if (!IsServiceSyncing)
                    this.StopSelf ();

                throw;
            }
        }).Start ();

    }

    async Task<Boolean> Authenticate (SyncProgressEventArgs args)
    {
        try {

            args.Extra = "Fetching user...";
            args.Progress = 30;
            if (this.SyncProgressEvent != null)
                this.SyncProgressEvent (null, args);

            Account account = await _azureSvc.AccountByGoogleId (_settings.LoginUserId);
            if (account != null) {
                var party = await _azureSvc.GetItemById<Party> (account.PartyId);

                args.Extra = "User found, initializing user data ...";
                args.Progress = 50;
                if (this.SyncProgressEvent != null)
                    this.SyncProgressEvent (null, args);

                _sqlSvc.Insert<Party> (party);
                _sqlSvc.Insert<Account> (account);

                args.Extra = "User found, fetching organisation data...";
                args.Progress = 50;
                if (this.SyncProgressEvent != null)
                    this.SyncProgressEvent (null, args);
                //Fetch organisations
                await _syncSvc.SyncPartyInitData (party.Id, true, true);

                //Fetch appointments & orders
                args.Extra = "Organisation data completed, fetching party data...";
                args.Progress = 80;
                if (this.SyncProgressEvent != null)
                    this.SyncProgressEvent (null, args);

                await _syncSvc.SyncPartyDetailData (party.Id);

                //do not request user info
                return false;

            } else {

                //FILL IN ACCOUNT
                account = new Account ();
                Party party = new Party ();
                party.Id = Guid.NewGuid ().ToString ();
                account.Id = Guid.NewGuid ().ToString ();
                account.IdentityProvider = _settings.LoginPlatform;
                account.IdentityToken = _settings.LoginUserId;
                account.PartyId = party.Id;
                account.InfoProvider = _settings.UserInfo.RawData;
                account.LoginName = _settings.LoginUserId;

                //FILL IN PARTY 
                bool requestUserInfoFragment = false;

                if (_settings.UserInfo != null) {

                    if (!String.IsNullOrWhiteSpace (_settings.UserInfo.FirstName)) {
                        party.FirstName = _settings.UserInfo.FirstName;
                    } else
                        requestUserInfoFragment = true;

                    if (!String.IsNullOrWhiteSpace (_settings.UserInfo.LastName)) {
                        party.LastName = _settings.UserInfo.LastName;

                    } else
                        requestUserInfoFragment = true;

                    if (!String.IsNullOrWhiteSpace (_settings.UserInfo.Email)) {
                        party.Email = _settings.UserInfo.Email;
                    } else
                        requestUserInfoFragment = true;

                }

                party.LocalIsNew = true;
                account.LocalIsNew = true;

                _sqlSvc.Insert<Party> (party);
                _sqlSvc.Insert<Account> (account);


                if (!requestUserInfoFragment) {
                    args.Extra = "Adding new users to the server";
                    args.Progress = 80;
                    if (this.SyncProgressEvent != null)
                        this.SyncProgressEvent (null, args);

                    await _syncSvc.SyncAllFromType<Party> ();
                    await _syncSvc.SyncAllFromType<Account> ();

                }
                //ELSE sync in newuserfragment


                return requestUserInfoFragment;
            }
        } catch (Exception ex) {
            throw;
        }
    }

    #endregion

    public override void OnDestroy ()
    {
        base.OnDestroy ();
        Console.WriteLine ("Destroying BirdyService");
        _connection.Close ();
        _connection = null;
        _azureSvc = null;
        _sqlSvc = null;
        _syncSvc = null;
        _settings = null;
    }

}

2 个答案:

答案 0 :(得分:1)

首先,首先打开Strict模式,通过将此添加到应用首次活动的OnCreate来检测活动泄漏:

StrictMode.SetVmPolicy (new StrictMode.VmPolicy.Builder ().DetectAll ().PenaltyLog ().Build ());  

每次检测到多于一个类型的活动实例时,这将向logcat转储警告。它看起来像这样:

[StrictMode] class md5962cfe7bc03e689322450e055e633b77.FirstActivity; instances=2; limit=1
[StrictMode] android.os.StrictMode$InstanceCountViolation: class md5962cfe7bc03e689322450e055e633b77.FirstActivity; instances=2; limit=1
[StrictMode]    at android.os.StrictMode.setClassInstanceLimit(StrictMode.java:1)

接下来要切断活动的对等对象连接:

    protected override void OnDestroy ()
    {
        base.OnDestroy ();
        base.Dispose ();
        GC.Collect (GC.MaxGeneration);
    }  

base.Dispose ()打破了C#对象和Java Vm对象之间的对等对象连接。 http://developer.xamarin.com/guides/android/advanced_topics/garbage_collection/#Disposing_of_Peer_instances

一般来说,这应足以修复样品的泄漏。您还可以更进一步,然后处理null从Java.Lang.Object派生的Activity中的所有对象。例如:

    protected override void OnDestroy ()
    {
        button.Dispose ();
        button = null;

        base.OnDestroy ();
        base.Dispose ();
        GC.Collect (GC.MaxGeneration);
    }

答案 1 :(得分:1)

您的问题的核心是您不知道仍在引用哪些内容,以及引用它的内容。

使用Android Studio中的工具跟踪内存分配的位置应该很简单。 Memory Profiling 101介绍如何使用内存监视器,分配跟踪器和堆查看器来查找特定问题。

真的,这些工具可以让您清楚地了解您的记忆力。我会从那里开始,窥探你的第二个活动仍在处理的事情。