Android 安卓-定义一个静态按钮会泄露我的活动吗?我可以避免吗?
我需要按钮是静态的,以便在显示活动的情况下从我的服务中启用/禁用它。我仍然设置了ClickListener,不管怎样,静态视图被认为是危险的。我漏水吗?我可以避免吗Android 安卓-定义一个静态按钮会泄露我的活动吗?我可以避免吗?,android,memory-leaks,android-fragments,android-activity,android-view,Android,Memory Leaks,Android Fragments,Android Activity,Android View,我需要按钮是静态的,以便在显示活动的情况下从我的服务中启用/禁用它。我仍然设置了ClickListener,不管怎样,静态视图被认为是危险的。我漏水吗?我可以避免吗 public class MonitorActivity extends FragmentActivity implements OnClickListener { private static Button updateButton; // static?? @Override prot
public class MonitorActivity extends FragmentActivity implements
OnClickListener {
private static Button updateButton; // static??
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_monitor);
// button
updateButton = (Button) findViewById(R.id.update_data_button);
updateButton.setOnClickListener(this); // oops ?
}
public static void onDataUpdated(Context ctx) {
if (updateButton != null) { //that's why I need it static
updateButton.setEnabled(true); // + set the text etc
}
}
public static void onUpdating() {
if (updateButton != null) {
updateButton.setEnabled(false);
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.update_data_button:
serviceIntent.putExtra(MANUAL_UPDATE_INTENT_KEY, true);
this.startService(serviceIntent);
}
}
@Override
protected void onResume() {
super.onResume();
Boolean isUpdating = AccessPreferences.get(this, updateInProgressKey,
false);
// set the button right
updateButton.setText((isUpdating) ? defaultUpdatingText
: getResources().getString(R.string.update_button_text));
updateButton.setEnabled(!isUpdating);
}
}
我认为您可以在MonitorActivity中创建BroadcastReceiver。并从服务发送额外消息以启用/禁用按钮。我建议您使用
LocalBroadcastManager
在您的
活动中
定义一个广播接收器
并在onStart()
onResume()
中注册广播,然后在onStop()
中注销广播
从服务
将广播发送到活动
如果活动
处于活动状态,它将接收广播并更新用户界面,如果不是没有任何事情发生
在您的服务中定义另一个BroadcastReceiver
,在onCreate()
中注册该广播,并在onDestroy()中注销它
当您的活动启动时,向服务发送广播
,并让服务
使用第一次广播更新UI来回复活动
更新
在做了一些调查之后,我发现你是对的“不鼓励粘性广播”,但是如果你检查一下那篇文章的日期,它是在2008年——在谷歌实施LocalBroadcastManager
之前
我已经检查了,它不是一个真正的广播,它是一个接口,带有广播接收器列表的单例(不是全局的,也没有IPC通信)
我真的很讨厌公共静态,我总是避免使用它们。每个人都应该这样做。所以是的-静态按钮会泄露我的活动。我想出了下面的回调,但它是丑陋的。我最终通过让活动扩展到SharedPreferenceChangeListener解决了这个问题
public final class MonitorActivity extends FragmentActivity implements
OnClickListener, OnSharedPreferenceChangeListener {
private Button updateButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
updateButton = (Button) findViewById(R.id.update_data_button);
updateButton.setOnClickListener(this); //no need to unregister methinks
}
@Override
public synchronized void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
if (updateInProgressKey.equals(key)) {
final Boolean isUpdating = AccessPreferences.get(this,
updateInProgressKey, false);
// set the button right
updateButton.setText((isUpdating) ? sDefaultUpdatingText
: sUpdateButtonTxt);
updateButton.setEnabled(!isUpdating);
}
}
@Override
protected void onStart() {
super.onStart();
AccessPreferences.registerListener(this, this);
AccessPreferences.callListener(this, this, updateInProgressKey);
}
@Override
protected void onStop() {
// may not be called (as onStop() is killable), but no leak,
// see: http://stackoverflow.com/a/20493608/281545
AccessPreferences.unregisterListener(this, this);
super.onStop();
}
}
回拨
onPause()
保证运行-因此我将那里的静态字段设为空,并在onResume()上填充它们。我只读取默认共享首选项,因此在同步块中不会花费很长时间(我必须同步,因为服务可能会在任何奇数时间调用onUpdate()
或onDataUpdate()
)。但不确定是否要注销侦听器
public class MonitorActivity extends FragmentActivity implements
OnClickListener {
private static TextView dataTextView; //null this onPause() to avoid a leak
private static Button updateButton; // null this onPause() to avoid a leak
// ...
public static synchronized void onDataUpdated(Context ctx) {
if (updateButton != null) {
updateButton.setEnabled(true); // + set the text etc
}
}
public static synchronized void onUpdating() {
if (updateButton != null) {
updateButton.setEnabled(false);
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.update_data_button:
serviceIntent.putExtra(MANUAL_UPDATE_INTENT_KEY, true);
this.startService(serviceIntent);
}
}
@Override
protected void onResume() {
super.onResume();
synchronized (MonitorActivity.class) {
Boolean isUpdating = AccessPreferences.get(this,
updateInProgressKey, false);
updateButton = (Button) findViewById(R.id.update_data_button);
updateButton.setOnClickListener(this);
// set the button right
updateButton.setText((isUpdating) ? defaultUpdatingText
: getResources().getString(R.string.update_button_text));
updateButton.setEnabled(!isUpdating);
}
}
@Override
protected void onPause() {
synchronized (MonitorActivity.class) {
updateButton.setOnClickListener(null); // TODO : needed ??
dataTextView = updateButton = null; // to avoid leaking my activity
}
super.onPause();
}
}
有3种解决方案可供您选择:
当上下文被破坏时,Set button=null(在顶部)
对按钮字段使用WeakReference,例如:
私有静态WeakReference更新按钮代码>
不创建静态按钮。它总是保持上下文
是的,很危险。活动已被销毁,但您仍然有一个对按钮的静态引用,而按钮又有一个对活动上下文的引用,因此您泄漏了整个活动(在DDMS中很容易看到)。相反,创建一个接口,从您的服务回调到活动或广播接收器。@Simon:broadcasts应该是粘性的-这是一个nono(IPC等)。如果没有静态成员(onUpdate等都是回调),我如何回调?好的,明白了-那么有回调的接口吗?@Simon:你能举个例子吗?一个界面如何让我不再需要静态按钮?不-它们应该是粘性的,这是错误的(根据Hackborn的说法)“它们应该是粘性的”这意味着什么?请向Diane的帖子提供链接,说明问题所在。@Simon:如果我在活动未激活时广播,广播将丢失-但这不是一个选项-因此BC应该是粘性的-但粘性BC适用于例外情况:“BroadcastReceiver”是您需要的,将其与“LocalBroadcastManager”一起使用在OnStart()中注册广播并在OnStop()中注销广播??无论如何,这有点混乱-如果你按照我上面的链接,你会发现应用程序中不鼓励广播。1-Android在任何地方都使用广播(消息、呼叫、引导、网络更新等)。2-在活动中注册/未注册的广播没有粘性(清单广播是)。3-您可以在“onPuse()”中注销广播,而Android即使在内存不足的情况下也必须调用此函数。4-使用诸如“EventBus”和“Otto”等LIB有更多的选择。1。2.3.这就是我要做的,在简历4上注册。这对我的应用来说是一个过度的杀伤力,但感谢链接-看看我的解决方案,并将其与你的进行比较-我会在某个时候看一看,但不会很快遇到单身汉??另外,“您讨厌公共静态”——您使用的库中有一半是公共静态库,比如Thread.sleep()、Collections.sort()和System.out.println();)提出了一个解决方案-这样我可以在可见的情况下更新活动,但不必担心注销-请参阅我的答案。至于本地广播经理——可能有点棘手:@Simon:这就是我想到的——仍然需要确保它在所有情况下都能正常工作,但我认为它可以。缺点是在某些edhe情况下可能会更新两次UI(onResume()然后onDataUpdated())与LocalBroadcastManager相比,这是一个不错的解决方案,代码更干净