内存泄漏与常见使用工作线程方式

内存泄漏

1.类的静态变量持有大数据对象 静态变量长期维持到大数据对象的引用,阻止垃圾回收。还有一些静态的Activity,静态的View

2.非静态内部类存在静态实例 非静态内部类会维持一个到外部类实例的引用,如果非静态内部类的实例是静态的,就会间接长期维持着外部类的引用,阻止被回收掉。

3.对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。 解决办法使用try catch Finally 里面关闭它,或者用带资源的try()将资源初始化写在括号里面。,不过据了解现在Bitmap不用手动调用recycle,垃圾回收器会自动回收,因为手动调用的话很可能会造成其他地方调用了被回收Bitmap,非要手动的话用引用计数法

4.Handler内存泄漏 Handler作为内部类存在于Activity中,但是Handler生命周期与Activity生命周期往往并不是相同的,比如当Handler对象有Message在排队,则无法释放,进而导致本该释放的Acitivity也没有办法进行回收。 解决办法:声明handler为static类,这样内部类就不再持有外部类的引用了,就不会阻塞Activity的释放 ,如果内部类实在需要用到外部类的对象,可在其内部声明一个弱引用引用外部类。

public class MainActivity extends Activity {
 private CustomHandler mHandler;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     mHandler = new CustomHandler(this);
 }

 static class CustomHandlerextends Handler {
     // 内部声明一个弱引用,引用外部类
     private WeakReference<MainActivity > activityWeakReference;
     public MyHandler(MyActivity activity) {
         activityWeakReference= new WeakReference<MainActivity >(activity);
     }
             // ... ...   
}

5.不要在onDraw里面或者onMeasure创建对象,这样会造成频繁GC
6.单例模式造成的内存泄漏

public class AppManager {

    private static AppManager instance;
    private Context context;

    private AppManager(Context context) {
        this.context = context;
        //应该改成
        this.context = context.getApplicationContext();
    }

    public static AppManager getInstance(Context context) {

        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;

    }

}

由于生命周期不一样,如果传进来的是Activity的Context,会造成Activity无法回收造成内存泄漏,解决办法就是用Application的Context
7.线程造成内存泄漏

        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                SystemClock.sleep(10000);
                return null;
            }
        }.execute();


        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(10000);
            }
        }).start();

上面两种情况都会造成内存泄漏,因为匿名内部类都是默认持有外部引用的,如果再Activity关闭时线程工作还没有做完,将可能造成内存泄漏,和之前handler其实属于差不多算是同类问题,也是使用静态内部类+弱引用来解决

static class MyAsyncTask extends AsyncTask<Void, Void, Void> {

        private WeakReference<Context> weakReference;

        public MyAsyncTask(Context context) {
            weakReference = new WeakReference<>(context);
        }

        @Override
        protected Void doInBackground(Void... params) {
            SystemClock.sleep(10000);
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            MainActivity activity = (MainActivity) weakReference.get();
            if (activity != null) {

                //...
            }
        }
    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            SystemClock.sleep(10000);
        }
    }

    new Thread(new MyRunnable()).start();
    new MyAsyncTask(this).execute();

总结:只要是匿名内部类都会持有外部引用,这时候就要考虑是否会造成内存泄漏了

常见工作线程方式

1直接单个线程

public class MyRunnable implements Runnable {

    @Override
    public void run() {
    //耗时操作
    }

    public void startThread() {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

2.线程池
常见有四种线程池

Executors.newFixedThreadPool(3);//固定大小线程池
Executors.newCachedThreadPool();//可变大小线程池
Executors.newSingleThreadExecutor();//串行线程池
Executors.newScheduledThreadPool(int corePoolSize)//定期循环执行任务线程池

当然也可以定义线程池

public class SimpleExecutor implements Executor {
    @Override
    public void execute(Runnable command) {
        new Thread(command).start();
    }
}

3.HandlerThread是一个集成了Looper和MessageQueue的线程,当启动HandlerThread时,会同时生成Looper和MessageQueue,然后等待消息进行处理.常见用法

public class ThreadDemo extends Activity {  
 private static final String TAG = "bb";    
    private int count = 0;    
    private Handler mHandler ;    
        
    private Runnable mRunnable = new Runnable() {    
        public void run() {    
            //为了方便 查看,我们用Log打印出来    
            Log.e(TAG, Thread.currentThread().getId() + " " +count);    
            count++;    
//            setTitle("" +count);    
            //每2秒执行一次    
            mHandler.postDelayed(mRunnable, 2000);    
        }    
    };    
    @Override    
    public void onCreate(Bundle savedInstanceState) {    
     Log.e(TAG, "Main id    "+Thread.currentThread().getId() + " " +count);    
        super.onCreate(savedInstanceState);    
        setContentView(R.layout.main);     
        //通过Handler启动线程    
        HandlerThread handlerThread = new HandlerThread("threadone");//创建一个handlerThread线程  
        handlerThread.start();//启动该线程  
        mHandler =  new Handler(handlerThread.getLooper());//设计handler的looper
        mHandler.post(mRunnable); //加入mRunnable线程体到子线程的消息队列  
          
          
    }    
    @Override    
    protected void onDestroy() {    
        //将线程与当前handler解除  
        mHandler.removeCallbacks(mRunnable);    
        super.onDestroy();    
    }    
}  
  1. AsyncQueryHandler是用于在ContentProvider上面执行异步的CRUD操作的工具类,CRUD操作会被放到一个单独的子线程中执行,当操作结束获取到结果后,将通过消息的方式传递给调用AsyncQueryHandler的线程,通常就是主线程。AsyncQueryHandler是一个抽象类,集成自Handler,通过封装ContentResolver、HandlerThread、AsyncQueryHandler等实现对ContentProvider的异步操作。
    最简单的demo
AsyncQueryHandler asyncQueryHandler = new AsyncQueryHandler(activity.getContentResolver()){

        @Override
        protected void onInsertComplete(int token, Object cookie, Uri uri) {
            super.onInsertComplete(token, cookie, uri);
            Log.d(TAG, "onInsertComplete  return uri: " + uri);
        }

 };

 asyncQueryHandler.startInsert(-1, null, mUri, values);

查询,更新也是一样的,第一个参数为识别码,只要对应的在OnXXXXCompelete方法里面对应switch就可以了

  1. IntentService具有Service一样的生命周期,同时也提供了在后台线程中处理异步任务的机制
    继承OnHandleIntent方法,通过intent来携带数据
ublic class SimpleIntentService extends IntentService {

     
    public SimpleIntentService() {
        super(SimpleIntentService.class.getName());
        setIntentRedelivery(true);
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        //该方法在后台调用,这里可以拿到intent所携带的数据
    }
}
  1. AsyncTask是在Executor框架基础上进行的封装,官方文档说这个不适合长时间的操作,简单用法如下
public class FullTask extends AsyncTask<String,String,String> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        //主线程执行
    }

    @Override
    protected String doInBackground(String... params) {
        return null;
        //子线程执行
    }

    @Override
    protected void onProgressUpdate(String... values) {
        super.onProgressUpdate(values);
        //主线程执行
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        //主线程执行
    }

    @Override
    protected void onCancelled() {
        super.onCancelled();
        //主线程执行
    }

}
  1. Loader是Android3.0开始引入的一个异步数据加载框架
    这里写图片描述

    官方文档详见:Loader官方文档

区别:


这里写图片描述

切换回主线程方法##

EventBus,runOnUithread()

主要参考链接

链接1
链接2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容

  • 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,...
    宇宙只有巴掌大阅读 2,360评论 0 12
  • Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏...
    _痞子阅读 1,624评论 0 8
  • Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏...
    apkcore阅读 1,216评论 2 7
  • 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,...
    DreamFish阅读 790评论 0 5
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,478评论 25 707