你的Android应用程序可以阻止泄漏内存的八种方式

来源:互联网 发布:红警2共和国之辉mac版 编辑:程序博客网 时间:2024/05/20 18:19

静态活动

这种泄漏

    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);    }

活动泄漏都是植根于我们在这里看到的和那些非常相似的特殊情况。现在,我们已经建议如何解决这些具体问题,您可以应用相同的技术,以任何未来的泄漏可能会出现。内存泄漏一旦被识别就很容易压缩,只要你经常检查它们,你可以早点赶上它们,为用户创造最好的体验。

0 0
原创粉丝点击