今天在做新模块测试时发现了一个严重的问题,当一个activity开启一个线程时,如果当前activity调用finish()函数不会关闭当前创建的线程。对于每个新建activity,如果activity中的线程发生内存泄漏。在java中线程时垃圾回收机制的根源,也就是说,在运行系统中DVM虚拟机总会使硬件持有运行状态的进程的引用,结果导致处于运行状态的线程将永远不会回收。因此你必须为你的后台线程实现销毁逻辑。
先说下问题出现的场景,我在一个activity中创建一个线程,轮询去发送请求,正常情况下是没什么问题的,先看下问题代码:
@OverridePRotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { while (1 == 1) { try { Thread.sleep(1000); Log.i("-------", "running"); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start();}这时看Log日志是正常打印,然后我按返回键退出当前应用,发现Log日志还在打印,问题出现了,线程被没有被回收,而且当你再次返回到应用时,会再在后台创建一个线程,两个线程同时在跑。在Java中强制关闭线程是非安全性操作,这时我们要为自己的线程添加判断条件,相关代码如下:
private MyThread myThread;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myThread = new MyThread(); myThread.start();}private class MyThread extends Thread { private boolean stop = false; @Override public void run() { super.run(); while (!stop) { try { Thread.sleep(1000); Log.i("-------", "running"); } catch (InterruptedException e) { e.printStackTrace(); } } } public void close() { stop = true; }}@Overrideprotected void onDestroy() { super.onDestroy(); myThread.close();}这样,当activity销毁时走destroy函数然后调用Thread的close,让线程退出轮询,保证了线程安全回收。还有另外一个思路来让线程可以及时回收,我们知道context对象与activity是绑定的,我们可以实例application来暂存当前context与当前context进行比较,我们可以优化上面的代码,具体代码如下:
自定义application用来暂存context对象:
public class MyApplication extends Application { static Context appContext; @Override public void onCreate() { super.onCreate(); } public static void setContext(Context context) { appContext = context; }}让线程去做context比较,这样我们就可以忽略activity的生命周期:private MyThread myThread;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyApplication.setContext(this); myThread = new MyThread(this); myThread.start();}private class MyThread extends Thread { private boolean stop = false; private Context context; public MyThread(Context context) { this.context = context; } @Override public void run() { super.run(); while (context == MyApplication.appContext) { try { Thread.sleep(1000); Log.i("-------", "running"); } catch (InterruptedException e) { e.printStackTrace(); } } }}大功告成。
新闻热点
疑难解答