Android中内存泄漏的相关因素分析(一)
程序员文章站
2022-04-19 16:07:13
...
前言
内存泄漏是由于程序所占用的内存没有及时收回所造成,这是应用程序中很常见的问题,而造成泄漏的因素也很多,在这篇文章中会对这些因素做详细的分析
非静态的内部类因素
非静态内部类包括:成员内部类、局部内部类、匿名内部类。内部类虽然和外部类是写在同一个java文件中,但是编译完之后,还是生成各自的class文件,内部类通过this访问外部类的成员。编译器会自动为内部类添加一个成员变量,这个成员变量就是指向外部类对象的引用;同时编译器会自动为内部类的构造方法添加一个参数,参数的类型就是外部类的类型,在构造方法内部使用这个参数为内部类中添加的成员变量赋值,在调用内部类的构造方法初始化内部类对象时,会默认传入外部类的引用,所以说内部类会持有外部类的引用,在内部类中做耗时操作,并频繁的退出重启相关的界面很容易造成内存泄漏。那我们首先来了解一下这几种内部类的区别吧。
成员内部类
//成员内部类是一种与成员变量、方法、构造器和初始化块相似的类成员,局部内部类和匿名内部类则不是
public class Person {
private String name;
private int age;
private class Student{
private String sex;
private String number;
...
}
}
局部内部类
/**
注意无论是局部变量还是局部内部类,它的上一级程序单元都是方法而不是类,使用static是没有任
中,其他的程序单元是不可能访问另一个方法中的局部成员变量的,因此所有的局部成员都不能用访问
控制符修饰
*/
public class Person{
public static void main(String[] args) {
//方法中定义局部内部类
class InnerClass{
int name;
}
InnerClass inner = new InnerClass();
inner.name = "月白";
...
}
}
匿名内部类
/**
1、匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立刻创建匿名内部类的对象
*/
interfac Person{
public String getName();
}
public class Student{
public void test(Person person) {}
public static void main(String[] args) {
Student student = new Student();
student.test(new Person() {
public String getName{} {
return "月白";
}
});
}
}
常见的内部类导致的内存泄漏实例
1、Runnable
泄漏版
new Thread(new Runnable() {
@Override
public void run() {
try {
//模拟耗时操作
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
优化版
new Thread(new MyRunnable()).start();
//将非静态内部类改为静态非匿名内部类
private static class MyRunnable implements Runnable {
@Override
public void run() {
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2、Handler
泄漏版
/**
下例中Handler作为非静匿名内部类,隐式的持有外部HandlerActivity的引用,当前的活动执行
finish()时,由于存储在MessageQueue中的一些Message不能被马上处理,导致引用Handler的
活动或者Service不能被回收
*/
public class HandlerActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
button = (Button) findViewById(R.id.bt_next);
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mHandler.sendMessageDelayed(Message.obtain(), 60000);
finish();
}
});
}
}
优化版
/**
使用一个静态的Handler内部类,Handler持有的对象要使用弱引用,并且在Activity的Destroy
方法中移除MessageQueue中的消息
*/
public class HandlerActivity extends AppCompatActivity {
private Button button;
private MyHandler myHandler = new MyHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
button = (Button) findViewById(R.id.bt_next);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
myHandler.sendMessageDelayed(Message.obtain(), 60000);
finish();
}
});
}
//使用静态的内部类
private static class MyHandler extends Handler {
//弱引用的方式持有外界Activity
private final WeakReference<HandlerActivity> mActivity;
public MyHandler(HandlerActivity activity) {
mActivity = new WeakReference<HandlerActivity2>(activity);
}
@Override
public void handleMessage(Message msg) {
//判断是否被回收
if (mActivity != null && mActivity.get() == null) {
if(msg.what == 1) {
//逻辑操作
}
}
}
}
@Override
public void onDestroy() {
if (myHandler != null) {
//取消MessageQueue中所有的消息
myHandler.removeCallbacksAndMessages(null);
}
super.onDestroy();
}
}
3、AsyncTask
泄漏版
public class AsyncTaskActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task);
button = (Button) findViewById(R.id.bt_next);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startAsyncTask();
finish();
}
});
}
void startAsyncTask() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
while (true) ;
}
}.execute();
}
@Override
protected void onDestroy() {
super.onDestroy();
myAsyncTask.cancel(true);
}
}
优化版
/**
当需要更新UI时,因为此处用的是static修饰,为避免使用静态成员变量,采用如下方法,值得注意的是如下
的textView仍然是持有一个context的强引用,阻止GC的回收,因此在这里使用的是WeakReference方式
*/
private static class MyAsyncTask extends AsyncTask<Void,Void,String> {
private TextView textView;
private WeakReference<TextView> textViewReference;
public MyAsyncTask(TextView textView) {
this.textViewReference = new WeakReference<TextView>(textView);
}
@Override
protected String doInBackground(Void... params) {
//耗时操作
return null;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if (textViewReference != null && textViewReference.get() != null) {
textViewReference.get().setText("测试");
}
}
}
4、TimerTask
泄漏版
new Timer().schedule(new TimerTask() {
@Override
public void run() {
while (true) ;
}
}, 1000); // 1秒后启动一个任务
优化版
private TimerTask timerTask ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timerTask = new MyTimerTask() ;
new Timer().schedule( timerTask ,1000 ); // 1秒后启动一个任务
}
private static class MyTimerTask extends TimerTask {
@Override
public void run() {
while(true);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消定时任务
if ( timerTask != null ){
timerTask.cancel() ;
}
}
https://blog.csdn.net/sinat_31057219/article/details/74533647
https://blog.csdn.net/itachi85/article/details/73522042?locationNum=6&fps=1
https://www.jianshu.com/p/ac00e370f83d
https://blog.csdn.net/guolin_blog/article/details/42238627/
上一篇: VUE 各种遇到的问题以及面试题
下一篇: vue的双向数据绑定的原理