我的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;
}
}
答案 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介绍如何使用内存监视器,分配跟踪器和堆查看器来查找特定问题。
真的,这些工具可以让您清楚地了解您的记忆力。我会从那里开始,窥探你的第二个活动仍在处理的事情。