阅读 886

我遇到的那些bug(持续更新中)

此文主要是收集一些自己遇到的一些问题,方便自己总结,也方便他人解决问题!目前比较少,一点一点收集吧,路是一步一步走,坑也要一个一个的踩。

内存泄漏

最近我负责维护的一个老项目遇到一个很奇怪的bug,测试描述说某个Activity的第一次进入正常,第二次进入点击某个按钮的时候会崩溃。检查日志是View空指针异常。我第一反应就是内存导致的,于是开始从这个方向检查。 首先通过Profiler检查,发现没有异常,然后通过LeakCanary继续检查,依然没有发现异常。最后通过方法调用的来分析。这个方法在第一次进入Activity的时候可以执行无数次,第二次进来以后第一次也是正常的,但是当执行第二次的时候就崩溃了。后来在检查这个方法的时候,发现这个方法第二次执行的时候,是通过PopupWindow点击回调来调用的,而PopupWindow只是一个简单的私有成员变量,并不是静态的成员变量。后来在PopupWindow实例化的时候发现PopupWindow通过单例的方法实例化的,从而导致第二次接口回调的时候,使用的还是上一个接口回调,而上一个接口回调指向的是已经被回收的Activity,从而导致使用该PopupWindow的Activity第二次进来使用它的时候,就崩溃了!

修改前:
private static RecommendPopup instance;
public static RecommendPopup getInstance(Activity context, IMapListener iMapListener){
    if (instance == null)
        instance = new RecommendPopup(context,iMapListener);
    return instance;
}

修改后:
public RecommendPopup(Activity context,IMapListener iMapListener) {
        super(context);
        this.mActivity = context;
        this.iMapListener = iMapListener;
    }
复制代码

解决思路: 这种问题,第一步确定初始化和调用的时候同一个对象指针id是否一致,如果不一致就去找异步,静态,单例。如果一致就去找异步或者其它逻辑谁动了它

Adapter刷新

前两天遇到一个奇怪的事情,Adapter刷新没有反应。具体逻辑就是给Adapter增加一个标志位,修改标志位以后刷新发现页面没有变化。添加断点检查确实进入了notifyDataSetChanged方法,后来查看源码发现是因为自身数据没有变化导致adapter没有进入convert方法。查看源码:

  /**
    * Notify any registered observers that the data set has changed.
    *
    * <p>There are two different classes of data change events, item changes and structural
    * changes. Item changes are when a single item has its data updated but no positional
    * changes have occurred. Structural changes are when items are inserted, removed or moved
    * within the data set.</p>
    *
    * <p>This event does not specify what about the data set has changed, forcing
    * any observers to assume that all existing items and structure may no longer be valid.
    * LayoutManagers will be forced to fully rebind and relayout all visible views.</p>
    *
    * <p><code>RecyclerView</code> will attempt to synthesize visible structural change events
    * for adapters that report that they have {@link #hasStableIds() stable IDs} when
    * this method is used. This can help for the purposes of animation and visual
    * object persistence but individual item views will still need to be rebound
    * and relaid out.</p>
    *
    * <p>If you are writing an adapter it will always be more efficient to use the more
    * specific change events if you can. Rely on <code>notifyDataSetChanged()</code>
    * as a last resort.</p>
    *
    * @see #notifyItemChanged(int)
    * @see #notifyItemInserted(int)
    * @see #notifyItemRemoved(int)
    * @see #notifyItemRangeChanged(int, int)
    * @see #notifyItemRangeInserted(int, int)
    * @see #notifyItemRangeRemoved(int, int)
    */
    public final void notifyDataSetChanged() {
    mObservable.notifyChanged();
    }
    
    
    
    static class AdapterDataObservable extends Observable<AdapterDataObserver> {
       ...

        public void notifyChanged() {
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
        
        ...
    }
复制代码

解决思路: 将标志位绑定到数据,而非绑定到Adapter

SharedPreferences数据不能保存问题

最近我负责维护的开源库UpdatePlugin有用户提了一个问题:第二次忽略版本的设置,成功以后在第二次进入应用的时候发现被忽略的版本号还停留在第一次的版本号。经过写demo测试,确认这个是SharedPreferences的锅。具体原因还在分析当中,有点烧脑。

先说一下解决方案吧,用户提供了一个解决方案:

修改前:
public static void saveIgnoreVersion(int versionCode) {
    Set<String> ignoreVersions = getIgnoreVersions();
    if (!ignoreVersions.contains(String.valueOf(versionCode))) {
        ignoreVersions.add(String.valueOf(versionCode));
        getUpdatePref().edit().putStringSet("ignoreVersions",ignoreVersions).apply();
    }
}

// 用户提供修改方法:
public static void saveIgnoreVersion(int versionCode) {
    Set<String> ignoreVersions = getIgnoreVersions();
    if (!ignoreVersions.contains(String.valueOf(versionCode))) {
        ignoreVersions.add(String.valueOf(versionCode));
        getUpdatePref().edit().clear().putStringSet("ignoreVersions",ignoreVersions).apply();
    }
}

// 最后修改方法:

/**
 * 框架内部所提供使用的一些缓存数据存取:如下载进度、忽略版本。
 * @author haoge
 */
public class UpdatePreference {

    private static final String PREF_NAME = "update_preference";

    public static List<String> getIgnoreVersions () {
        String txt =  getUpdatePref().getString("ignoreVersions", "");
        if(TextUtils.isEmpty(txt))return new ArrayList<>();
        txt = txt.replace("[","").replace("]","");
        String[] result = txt.split(",");
        // 杜绝 java.lang.UnsupportedOperationException
        return new ArrayList<>(Arrays.asList(result));
    }

    public static void saveIgnoreVersion(int versionCode) {
        List<String> ignoreVersions = getIgnoreVersions();
        if (!ignoreVersions.contains(String.valueOf(versionCode))) {
            ignoreVersions.add(String.valueOf(versionCode));
            getUpdatePref().edit().putString("ignoreVersions",ignoreVersions.toString()).apply();
        }
    }

    private static SharedPreferences getUpdatePref () {
        return ActivityManager.get().getApplicationContext().getSharedPreferences(PREF_NAME,Context.MODE_PRIVATE);
    }
}
复制代码

选择修改存储方式是因为putStringSet目前的bug暂时还没有分析出具体原因,所以暂时先排除此方法,避免再次发生未知错误。

疑惑: 仔细看了源码关于保存的commitToMemorywriteToFile多遍,始终不明白为什么更新原有的Set<String>以后,再次进入应用的时候数据会丢失?网上的解决方法基本上都是围绕commitToMemory方法来分析,将mcr.changesMade设置为默认的false来避免后期被写入文件,但是如果通过修改mcr.changesMade来避免数据写入文件的话,则mMap无法保存需要存入的值,那么在存成功以后(还没有退出应用期间)是如何取到正确的值呢?

这个问题一直困扰着我,哪位大佬有思路请指点一下,谢谢?

文档提醒:

Note that you must not modify the set instance returned by this call. The consistency of the stored data is not guaranteed if you do, nor is your ability to modify the instance at all.

参考文章:

putStringSet数据不能保存问题


后记

1----本文由苏灿烤鱼整理,将不定期更新。

2----如果有什么想要交流的,欢迎留言。也可以加微信:Vicent_0310

3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正

关注下面的标签,发现更多相似文章
评论