android常见bug及解决方案总结

空指针

解决方案
  • 不确定对象在使用前先做是否为空判断
  • 特别注意:fragment getActivity为null处理

数组越界

解决方案
  • 使用索引值获取对象值时,需判断索引值是否小于数据源大小
    example:
if (mData != null && mData.size() !=0 && i < mData.size()){
  Obj obj = mData.get(i);
}

ListView或RecycleView 更新数据源命令不同步

解决方案
  • 保证设置数据源和执行adapter.notifyDataSetChanged在同一个线程并且在Ui线程
    example:
adapter.setData(mData);
adapter.notifyDataSetChanged();

Windows无页面附加(Unable to add window.....is your activity running?)

解决方案
  • 执行windows窗体Dialog或PopupWindow时,先判断当前页面是否销毁,若页面还在则可以执行窗体显示操作,可以通过全局变量或activity自定义堆栈管理判断当前页面是否销毁,在onDestory里面做关闭窗体操作并置空
    example:
@Override
protected void onDestroy() {    
if (mDialog != null && mDialog.isShowing()){        
mDialog.dismiss();        
mDialog = null;    
}    
super.onDestroy();
}

sqlite问题(数据库缺字段或执行过程异常)

解决方案

升级sqlite方法里面添加字段处理,此时记得加入try catch处理方式,防止出现崩溃现象。
example:

FinalDb.DaoConfig daoConfig = new FinalDb.DaoConfig();
daoConfig.setContext(this);
daoConfig.setDbName(ChatDao.DATABASE_NAME);
daoConfig.setDebug(true);
daoConfig.setDbVersion(ChatDao.DATABASE_VERSION_CODE);
daoConfig.setDbUpdateListener(new FinalDb.DbUpdateListener() {
            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                // 当之前版本的数据升级到新版版本的数据,我们需要给对应表增加新的字段
                try {
                    // 添加字段has_appraised字段
                    db.execSQL(ChatDao.VERSION_3_SQL_ADD_COLUMN_HAS_PRAISED);
                }
                catch (Exception e){
                    e.printStackTrace();
                }

                try {
                    // 添加字段receiveDate字段
                    db.execSQL(ChatDao.VERSION_4_SQL_ADD_CLUMN_RECEIVE_DATE);
                }
                catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
FinalDb.create(daoConfig);

OOM(内存溢出)

情况一

处理bitmap资源不得当造成(压缩或者变换得到新bitmap)

解决方案

采用try catch处理方式,若出现异常再做压缩,期间采用弱引用方式处理。
example:

        WeakReference<Bitmap> bitmapWeakReference;
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        try {
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFileDescriptor(fd, null, options);

            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, reqWidth,
                    reqHeight);

            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
            // 弱引用处理图片 add leibing 2016/12/8
            bitmapWeakReference
                    = new WeakReference<Bitmap>(BitmapFactory.decodeFileDescriptor(fd, null, options));
            if (bitmapWeakReference != null && bitmapWeakReference.get() != null)
                return bitmapWeakReference.get();
        }catch (OutOfMemoryError ex){
            // 压缩图片指定值 add by leibing 2016/12/8
            options.inSampleSize = options.inSampleSize + 4;
            options.inJustDecodeBounds = false;
            // 弱引用处理图片 add leibing 2016/12/8
            bitmapWeakReference
                    = new WeakReference<Bitmap>(BitmapFactory.decodeFileDescriptor(fd, null, options));
            if (bitmapWeakReference != null && bitmapWeakReference.get() != null)
                return bitmapWeakReference.get();
        }

情况二

各种内存泄漏问题造成,主要有以下:

单例造成的内存泄露
解决方案

1、将该属性的引用方式改为弱引用;
2、如果传入Context,使用ApplicationContext;
example
泄漏代码片段

    // sington
    private static InstanceClass instance;
    // activity refer
    private Context mContext;
    
    /**
     * set activity refer
     * @author leibing
     * @createTime 2016/12/9
     * @lastModify 2016/12/9
     * @param mContext activity refer
     * @return
     */
    public void setRefer(Context mContext){
        this.mContext = mContext;
    }
    
    /**
     * constructor
     * @author leibing
     * @createTime 2016/12/9
     * @lastModify 2016/12/9
     * @param
     * @return
     */
    private InstanceClass(){
    }

    /**
     * sington
     * @author leibing
     * @createTime 2016/12/9
     * @lastModify 2016/12/9
     * @param
     * @return
     */
    public static InstanceClass getInstance(){
        if (instance == null){
            synchronized (InstanceClass.class){
                if (instance == null)
                    instance = new InstanceClass();
            }
        }

        return instance;
    }

Solution:使用WeakReference

    // sington
    private static InstanceClass instance;
    // activity refer
    private WeakReference<Context> mContextWeakRef;

    /**
     * set activity refer
     * @author leibing
     * @createTime 2016/12/9
     * @lastModify 2016/12/9
     * @param mContext activity refer
     * @return
     */
    public void setRefer(Context mContext){
        mContextWeakRef = new WeakReference<Context>(mContext);
    }
    
    /**
     * constructor
     * @author leibing
     * @createTime 2016/12/9
     * @lastModify 2016/12/9
     * @param
     * @return
     */
    private InstanceClass(){
    }

    /**
     * sington
     * @author leibing
     * @createTime 2016/12/9
     * @lastModify 2016/12/9
     * @param
     * @return
     */
    public static InstanceClass getInstance(){
        if (instance == null){
            synchronized (InstanceClass.class){
                if (instance == null)
                    instance = new InstanceClass();
            }
        }

        return instance;
    }
InnerClass匿名内部类
解决方案

1、将内部类变成静态内部类;
2、如果有强引用Activity中的属性,则将该属性的引用方式改为弱引用;
3、在业务允许的情况下,当Activity执行onDestory时,结束这些耗时任务;
example
泄漏代码片段

public class InnerClassActivity extends Activity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // start a thread to work
        new InnerThread().start();
    }
    
    /**
     * @interfaceName: InnerThread
     * @interfaceDescription: custom thread
     * @author: leibing
     * @createTime: 2016/12/9
     */
    class InnerThread extends Thread{
        @Override
        public synchronized void start() {
            super.start();
        }

    }
}

Solution:使用WeakReference + static

public class InnerClassActivity extends Activity{
    // 图片资源
    private Drawable mDrawable;
    // inner thread
    private InnerThread mInnerThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // init drawable
        mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
        // start a thread to work
        mInnerThread = new InnerThread(mDrawable);
        mInnerThread.start();
    }
    
    @Override
    protected void onDestroy() {
        if (mInnerThread != null)
            mInnerThread.setIsRun(false);
        super.onDestroy();
    }
    
    /**
     * @interfaceName: InnerThread
     * @interfaceDescription: custom thread
     * @author: leibing
     * @createTime: 2016/12/9
     */
    static class InnerThread extends Thread{
        // weak ref
        public WeakReference<Drawable> mDrawableWeakRef;
        // is run
        private boolean isRun = true;

        /**
         * constructor
         * @author leibing
         * @createTime 2016/12/9
         * @lastModify 2016/12/9
         * @param mDrawable 图片资源(存在对页面的引用风险)
         * @return
         */
        public InnerThread(Drawable mDrawable){
          mDrawableWeakRef = new WeakReference<Drawable>(mDrawable);
        }
        
        /**
         *  set is run
         * @author leibing
         * @createTime 2016/12/9
         * @lastModify 2016/12/9
         * @param isRun
         * @return
         */
        public void setIsRun(boolean isRun){
            this.isRun = isRun;
        }

        @Override
        public void run() {
            while (isRun){
                // do work
                try {
                    // sleep one second
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public synchronized void start() {
            super.start();
        }
    }
}
Activity Context 的不正确使用
解决方案

1、使用ApplicationContext代替ActivityContext,因为ApplicationContext会随着应用程序的存在而存在,而不依赖于activity的生命周期;
2、对Context的引用不要超过它本身的生命周期,慎重的对Context使用“static”关键字。Context里如果有线程,一定要在onDestroy()里及时停掉。
example
泄漏代码片段

public class DrawableActivity extends Activity{
    // static drawable 
    private static Drawable leakDrawable;
    
    @Override
    protected void onCreate(Bundle state) {
        super.onCreate(state);
        TextView label = new TextView(this);
        // init drawable
        if (leakDrawable == null) {
            leakDrawable = getResources().getDrawable(R.drawable.ic_launcher);
        }
        // view set drawable
        label.setBackgroundDrawable(leakDrawable);
        
        setContentView(label);
    }
}

Solution:

public class DrawableActivity extends Activity{
    // static drawable
    private static Drawable leakDrawable;

    @Override
    protected void onCreate(Bundle state) {
        super.onCreate(state);
        TextView label = new TextView(this);
        // init drawable
        if (leakDrawable == null) {
            leakDrawable = getApplicationContext().getResources().getDrawable(R.drawable.ic_launcher);
        }
        // view set drawable
        label.setBackgroundDrawable(leakDrawable);

        setContentView(label);
    }
}
Handler引起的内存泄漏
解决方案

1、可以把Handler类放在单独的类文件中,或者使用静态内部类便可以避免泄露;
2、如果想在Handler内部去调用所在的Activity,那么可以在handler内部使用弱引用的方式去指向所在Activity.使用Static + WeakReference的方式来达到断开Handler与Activity之间存在引用关系的目的。
example
泄漏代码片段

public class HandlerActivity extends Activity{
    // custom handler
    private CustomHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // init custom handler
        mHandler = new CustomHandler();
        // sendMsg
        Message msg = new Message();
        mHandler.sendMessage(msg);
    }
    
    /**
     * @interfaceName: CustomHandler
     * @interfaceDescription: custom handler
     * @author: leibing
     * @createTime: 2016/12/9
     */
    class CustomHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    }
}

Solution(static + weakRef):

public class HandlerActivity extends Activity{
    // custom handler
    private CustomHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // init custom handler
        mHandler = new CustomHandler(this);
        // sendMsg
        Message msg = new Message();
        mHandler.sendMessage(msg);
    }

    /**
     * @interfaceName: CustomHandler
     * @interfaceDescription: custom handler
     * @author: leibing
     * @createTime: 2016/12/9
     */
    static class CustomHandler extends Handler{
        // weak ref
        private WeakReference<Context> mContextWeakRef;
        
        /**
         * constructor
         * @author leibing
         * @createTime 2016/12/9
         * @lastModify 2016/12/9
         * @param mContext activity ref
         * @return
         */
        public CustomHandler(Context mContext){
            mContextWeakRef = new WeakReference<Context>(mContext);    
        }
        
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (mContextWeakRef != null && mContextWeakRef.get() != null){
                // do work
            }
        }
    }
}
注册监听器的泄漏
解决方案

1、使用ApplicationContext代替ActivityContext;
2、在Activity执行onDestory时,调用反注册;

Cursor,Stream没有close,View没有recyle;
解决方案

资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。因为有些资源性对象,比如SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该调用它的close()函数,将其关闭掉,然后才置为null. 在我们的程序退出时一定要确保我们的资源性对象已经关闭。

集合中对象没清理造成的内存泄漏
解决方案

在Activity退出之前,将集合里的东西clear,然后置为null,再退出程序。

private List<String> mData;    
public void onDestory() {        
    if (mData!= null) {
        mData.clear();
        mData= null;
    }
}
WebView造成的泄露
解决方案

为webView开启另外一个进程,通过AIDL与主线程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。

类型转换错误

解决方案

首先判断类型转换前数据是否符合转化后的数据再做处理。
example:
字符串转整型数字
首先判断字符串是否为数字字符串,然后再转换,此时最好加上try catch处理,防止崩溃。

以上是笔者在项目中遇到的常见bug以及解决方案,如有不足,请补充。

关于作者

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

推荐阅读更多精彩内容