[译] 内存泄露的八种花样 (1),音视频开发工程师前景
不幸的是,由于内部类可以直接访问到它的外部类的变量,这个特性意味着内部类会隐式的持有一个对它的外部类的引用,这间接导致了我们不小心又泄露了 Activity。
4. 匿名类
同样的,匿名类也持有一个指向它申明的地方所在的类的引用。如果你在[Activity 内定义和实例化一个 Asyn
cTask 匿名类](
),那也可能发生内存泄露
void startAsyncTask() {new AsyncTask<Void, Void, Void>() {@Override protected Void doInBackground(Void... params) {while(true);}}.execute();}
super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);View aicButton = findViewById(R.id.at_button);aicButton.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {startAsyncTask();nextActivity();}});
AsyncTask 的内存泄露
5. Handler
同样的原则也适用于后台任务:[定义一个匿名的 Runnable,然后将它加入 Handler 的处理队列里](
)。这个 Runnable 对象会隐含的持有一个指向它定义的时候所在的 Activity 的引用,然后它会作为一个消息对象加入到 Handler 的消息队列里去。在 Activity 生命周期结束之后,只要这个消息还没被 Activity 处理,那就有一条引用链指向我们的 Activity 对象,使得 Activity 对象无法被回收,进而泄露。
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);}
View hButton = findViewById(R.id.h_button);hButton.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {createHandler();nextActivity();}});
Handler 导致的内存泄露
6. 线程
类似的问题我们可以在[线程](
)、[定时任务(TimerTask)](
)里发现。
void spawnThread() {new Thread() {@Override public void run() {while(true);}}.start();}
View tButton = findViewById(R.id.t_button);tButton.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {spawnThread();nextActivity();}});
线程使用不当导致内存泄露
7. 定时任务
只要它们是通过匿名类的方式定义和实例化的,即便是工作在另外的线程,依旧会在 Activity 被 destroy 之后,存在一条指向 Activity 的引用链,导致 Activity 泄露。
void scheduleTimer() {new Timer().schedule(new TimerTask() {@Overridepublic void run() {while(true);}}, Long.MAX_VALUE >> 1);}
View ttButton = findViewById(R.id.tt_button);ttButton.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {scheduleTimer();nextActivity();}});
TimerTask 导致的内存泄露
8. 系统服务
最后,还有一些系统服务可以通过上下文 Context 对象上的[getSystemService](
))方法获取到。这些服务运行在他们各自的进程里,协助应用执行某种类型的的后台任务,或者和设备的硬件进行交互。如果 Context 对象需要系统服务内的某个事件发生的时候通知到这个 Context,那么它需要把自身作为一个[监听器](
)注册给系统服务。系统服务也由此持有了一个对 Activity 对象的应用。如果我们在 Activity 的生命周期结束的时候忘了去反注册这个监听器,就会发生泄露。
void registerListener() {SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST);}View smButton = findViewById(R.id.sm_button);smButton.setOnClickListener(new View.OnClickListener() {@Override public void onClick(View v) {registerListener();nextActivity();}
评论