or_*_*vir 6 java android memory-leaks inner-classes
最近我一直在研究java/android中的内存泄漏,几乎无处不在,它说我应该使用带有弱引用的静态内部类而不是匿名类.
所以,在我的Android应用程序中,我开始这样做,但很快就厌倦了它,因为它是很多样板代码...我认为有一个替代解决方案,我更喜欢使用,但我不确定它是在防止内存泄漏方面,静态内部类的有效替代方法.正如我之前所说,我还没有看到其他任何地方建议的解决方案(都说使用静态内部类)所以这就是为什么我不确定我的替代方案是否有效.
使用我的应用程序中的一个简单示例:
我有一个名为WebClient的类来处理异步Web请求,它接受一个名为iCallback的接口,它将响应从服务器返回给调用者,在我的活动中,一旦我得到这个回调,我需要关闭一个对话框,并可能执行一些与活动相关的事情(比如触发onBackPressed()和setResult()).
所以这是我创建的静态内部类:
private static class CallBack implements WebClient.ICallback
{
private WeakReference<ProgressDialog> mProgDiag;
private WeakReference<BaseActivity> mActivity;
public CallBack(BaseActivity activity, ProgressDialog progDiag)
{
this.mProgDiag = new WeakReference<>(progDiag);
this.mActivity = new WeakReference<>(activity);
}
@Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
final BaseActivity parentActivity = mActivity.get();
ProgressDialog dialog = mProgDiag.get();
if(dialog != null)
{
dialog.dismiss();
}
if (responseAsString == null)
{
if(parentActivity != null)
{
Utils.makeServerErrorDialog(parentActivity,
new iDialogButtonClickedListener()
{
@Override
public void onDialogButtonClicked()
{
parentActivity.onBackPressed();
}
});
}
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
if(parentActivity != null)
{
Intent result = new Intent();
result.putExtra(...);
parentActivity.setResult(Activity.RESULT_OK, result);
}
}
else
{
Utils.reportErrorToServer(...);
if(parentActivity != null)
{
parentActivity.setResult(Activity.RESULT_CANCELED);
}
}
if(parentActivity != null)
{
parentActivity.onBackPressed();
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以对于我需要在这个静态内部类中的每个变量,我必须创建一个新的弱引用,然后检索对象本身,然后每次我想要访问它我需要检查它是否为null ...这似乎很多代码给我.
这是我建议的替代方案:
public abstract class BaseActivity extends AppCompatActivity
implements WebClient.ICallback
{
private static final String TAG = "BaseActivity";
WebClient.ICallback mCallBack;
ProgressDialog mProgDiag;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(...);
mCallBack = this;
//some code to invoke a server request on button click
//and passing mCallBack to the request
}
@Override
public void onCallback(String data)
{
String responseAsString = Utils.extractStringFromResponse(...);
mProgDiag.dismiss();
if (responseAsString == null)
{
Utils.makeServerErrorDialog(this,
new iDialogButtonClickedListener()
{
@Override
public void onDialogButtonClicked()
{
onBackPressed();
}
});
return;
}
//everything is ok
if (responseAsString.equals("1"))
{
Intent result = new Intent();
result.putExtra(...);
setResult(Activity.RESULT_OK, result);
}
else
{
Utils.reportErrorToServer(...);
setResult(Activity.RESULT_CANCELED);
}
onBackPressed();
}
@Override
protected void onPause()
{
mCallBack = null;
super.onPause();
}
@Override
protected void onResume()
{
super.onResume();
mCallBack = this;
}
}
Run Code Online (Sandbox Code Playgroud)
对我来说这看起来更清晰:没有为我需要访问的每个变量创建和检索弱引用的实例,我可以直接调用活动方法(例如onBackPressed()),并且不会在任何地方检查null.
在调用callBack方法之前,我现在必须检查null的唯一地方是WebClient类.
所以我的问题是,这种方法在防止内存泄漏方面是否达到了相同的效果?它是静态内部类的"有价值"替代品吗?
不幸的是,你的方法不起作用。通过在您的活动中实现WebClient.ICallback而不是内部类,您并不能消除泄漏。发生泄漏并不是因为对活动和对话框的引用隐式存在于匿名类、lambda 或非静态内部类实例中;而是因为对活动和对话框的引用隐含在匿名类、lambda 或非静态内部类实例中。当 WebClient 在活动消失时保留此引用(它不会被销毁,因为有对它的强引用)时,就会发生这种情况。
当活动暂停时,您设置为 null 的特殊mCallBack不会获得任何结果。同样,您可以简单地将活动实例传递给 WebClient。现在有对您的活动的强引用,该活动由不受您控制的某人(WebClient 的异步处理程序)管理。如果不幸的话,异步处理程序将卡在某个地方并且永远不会释放此引用。
请阅读此详细说明。
请注意,如果不采取特殊措施,WebView本身可能会导致内存泄漏!