欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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/