静态活动
这种泄漏
private static MainActivity activity;
void setStaticActivity() {
activity = this;
}
被构造以揭示在静态类变量中持有对您的活动的引用的结果,该静态类变量将超过活动的任何特定实例。活动的类对象是app-global并且将保持在无限期的内存中的活动。有合理的理由,开发人员可能选择这样做,因此我们需要提出一个解决方案,不会阻止活动被垃圾收集一旦它准备好被销毁。Android提供了一组特殊的对象https://developer.android.com/reference/java/lang/ref/package-summary.html#classes,允许开发人员控制引用的“强度”。该活动正在泄漏,因为它继续被强烈引用,即使在意图是破坏它并释放它从内存。只要此引用存在,垃圾回收器就无法清除活动的内存。因此,我们可以使用WeakReferencehttps://developer.android.com/reference/java/lang/ref/WeakReference.html解决泄漏。弱引用不会阻止对象的内存被回收,因此如果只有对对象的弱引用仍然存在,它将有资格进行垃圾回收。
private static WeakReference<MainActivity> activityReference;
void setStaticActivity() {
activityReference = new WeakReference<MainActivity>(this);
}
静态视图
静态维护对视图的引用
private static View view;
void setStaticView() {
view = findViewById(R.id.sv_button);
}
从一个活动是一样有问题的直接引用活动本身,因为视图包含对它们所在的活动的引用。因此,一个WeakReference在解决这个泄漏同样有效。但是,当我们清楚Activity对象处于它的生命周期结束时,我们也可以手动清除引用,并希望与垃圾回收器会面。为此,我们简单地覆盖Activity的onDestroy()方法,该方法保证在生命周期结束时调用,并将引用设置为null。
private static View view;
@Override
public void onDestroy() {
super.onDestroy();
if (view != null) {
unsetStaticView();
}
}
void unsetStaticView() {
view = null;
}
3.内在类
泄漏
private static Object inner;
void createInnerClass() {
class InnerClass {
}
inner = new InnerClass();
}
我们创建的是非常类似于上面两个。开发人员经常警告避免非静态嵌套类,称为内部类https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html,因为它们对外部类有隐含的引用,因此这是很容易无意中泄漏你的活动。然而,使用内部类是有好处的,例如能够访问外部类的私有成员,只要我们知道引用的生命周期,我们就可以防止泄漏。再一次,我们天真地创建了一个静态引用,这次是我们内部类的一个实例。为了解决泄漏,我们可以很容易地避免声明引用为静态,并像往常一样继续业务。
private Object inner;
void createInnerClass() {
class InnerClass {
}
inner = new InnerClass();
}
4-7。匿名类
到目前为止,我们看到的每个内存泄漏的根本原因是一个应用程序全局静态引用,直接或间接通过其他引用的链保存到Activity对象,并防止它被垃圾回收。我们使用AsyncTask创建的泄漏:
void startAsyncTask() {
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}.execute();
}
处理程序:
void createHandler() {
new Handler() {
@Override public void handleMessage(Message message) {
super.handleMessage(message);
}
}.postDelayed(new Runnable() {
@Override public void run() {
while(true);
}
}, Long.MAX_VALUE >> 1);
}
主题:
void spawnThread() {
new Thread() {
@Override public void run() {
while(true);
}
}.start();
}
和TimerTask:
void scheduleTimer() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
while(true);
}
}, Long.MAX_VALUE >> 1);
}
都是由声明一个匿名类https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html引起的。一个匿名类实际上只是一个专门的内部类,其主要优点是允许代码被简洁地编写。每当特定任务只需要一个特定类的一次性子类化时,Java提供了语法糖,它允许子类被声明为具有最小语法的表达式。这是伟大的写干净的代码,但可以导致一个新的,但密切相关的内存泄漏集。正如我们在上面看到的内部类,它们的实例是完全无害的,只要你不创建一个引用它们的活动的生命周期,它们被声明。但是,这些特定的匿名类都被用于产生应用程序的后台线程。这些java线程是app-global,并且维护对创建它们的对象的引用,匿名类实例,它反过来持有对外部类的引用,因为它是一个非静态内部类。线程可以永久运行,因此将持久性的内存链保存到垃圾收集器不执行其工作,即使在活动的生命周期完成后。这一次我们不能避免声明引用为静态,因为线程是全局的应用程序的状态。相反,为了避免Activity泄漏,我们必须放弃匿名类的简洁性,并将每个子类声明为静态嵌套类。一旦嵌套类是静态的,它不再维护对外部类实例的引用,并打破了我们的引用链。没有什么本质上区分这些特定的类,我们可以应用相同的技术AsyncTask:
private static class NimbleTask extends AsyncTask<Void, Void, Void> {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}
void startAsyncTask() {
new NimbleTask().execute();
}
处理程序:
private static class NimbleHandler extends Handler {
@Override public void handleMessage(Message message) {
super.handleMessage(message);
}
}
private static class NimbleRunnable implements Runnable {
@Override public void run() {
while(true);
}
}
void createHandler() {
new NimbleHandler().postDelayed(new NimbleRunnable(), Long.MAX_VALUE >> 1);
}
和TimerTask:
private static class NimbleTimerTask extends TimerTask {
@Override public void run() {
while(true);
}
}
void scheduleTimer() {
new Timer().schedule(new NimbleTimerTask(), Long.MAX_VALUE >> 1);
}
但是,如果你坚持使用一个匿名类,你总是可以选择终止保持对象活动时间比活动更长的java线程。以下是为匿名地声明的Thread完成此操作的许多方法之一。由于我们希望线程与Activity一起结束,我们所要做的就是设计运行循环,以依赖线程的中断标志,然后在Activity的onDestroy()方法中设置标志。
private Thread thread;
@Override
public void onDestroy() {
super.onDestroy();
if (thread != null) {
thread.interrupt();
}
}
void spawnThread() {
thread = new Thread() {
@Override public void run() {
while (!isInterrupted()) {
}
}
}
thread.start();
}
8.传感器管理器
这个例子
void registerListener() {
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}
只是使用Android系统服务可能泄露您的活动的许多方法之一。为了方便系统服务和Activity之间的通信,我们将Activity注册为侦听器,因此在服务的传感器管理器事件队列和我们的Activity之间创建一个引用链。只要我们的活动仍然在传感器管理器注册,引用将不会被释放,泄漏将持续。一旦活动在其生命周期结束,真的没有任何理由,它应该继续从任何传感器侦听事件。为了解决泄漏,我们所需要做的就是在活动结束时注销监听器。
private SensorManager sensorManager;
private Sensor sensor;
@Override
public void onDestroy() {
super.onDestroy();
if (sensor != null) {
unregisterListener();
}
}
void unregisterListener() {
sensorManager.unregisterListener(this, sensor);
}
活动泄漏都是植根于我们在这里看到的和那些非常相似的特殊情况。现在,我们已经建议如何解决这些具体问题,您可以应用相同的技术,以任何未来的泄漏可能会出现。内存泄漏一旦被识别就很容易压缩,只要你经常检查它们,你可以早点赶上它们,为用户创造最好的体验。