【问题标题】:Closing cursor in onPostExecute renders nothing在 onPostExecute 中关闭光标不会呈现任何内容
【发布时间】:2016-03-23 09:12:25
【问题描述】:

我正在使用 AsyncTask 从 SQLite 数据库加载数据,然后使用 StaggeredLayoutManager 和 CustomListAdapter 将数据提供给 Recycler View。

如果我不关闭onPostExecuteMethod中doInBackground方法的游标,从前一个activity的过渡是滞后的,并且android logcat说“跳过52帧”。

如果我关闭光标,在 Post Execute 方法结束时,不会发生延迟,但在 Activity 内不会呈现任何内容。 (Recycler View 不呈现任何内容。)

 @Override
    protected void onPostExecute(Void Void) {             
        c.close();
        swipeRefreshLayout.setRefreshing(false);
        swipeRefreshLayout.setOnRefreshListener(
          new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                new MyTask().execute();
            }
        });
        swipeRefreshLayout.setColorSchemeColors(getResources().getIntArray(R.array.swipeRefreshColors));

    }

doInBackground 方法

       try {           
                myDb = new SavedData(getApplicationContext());
                writeableDatabase = myDb.getReadableDatabase();
                final String[] projection = {SavedData.COLUMN_NAME_ENTRY_ID,
                        SavedData.COLUMN_NAME_PRICE,
                        SavedData.COLUMN_NAME_SHIPPING,
                        SavedData.COLUMN_NAME_WEIGHT,
                        SavedData.COLUMN_NAME_PRODUCT};
                sortOrder = BookSave.COLUMN_NAME_ENTRY_ID + " DESC";
                c = writeableDatabase.query(
                        SavedData.TABLE_NAME,  
                        projection,
                        null,
                        null,
                        null, 
                        null,
                        sortOrder
                );
                m = new MyListCursorAdapter(getApplicationContext(), c);
                gaggeredGridLayoutManager = new StaggeredGridLayoutManager(NUM, 1);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {

            }

BaseAdapter 类

import android.content.Context;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.support.v7.widget.RecyclerView;

public abstract class CursorRecyclerViewAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {

    private Context mContext;

    private Cursor mCursor;

    private boolean mDataValid;

    private int mRowIdColumn;

    private DataSetObserver mDataSetObserver;

    public CursorRecyclerViewAdapter(Context context, Cursor cursor) {
        mContext = context;
        mCursor = cursor;
        mDataValid = cursor != null;
        mRowIdColumn = mDataValid ? mCursor.getColumnIndex("_id") : -1;
        mDataSetObserver = new NotifyingDataSetObserver();
        if (mCursor != null) {
            mCursor.registerDataSetObserver(mDataSetObserver);
        }
    }

    public Cursor getCursor() {
        return mCursor;
    }

    @Override
    public int getItemCount() {
        if (mDataValid && mCursor != null) {
            return mCursor.getCount();
        }
        return 0;
    }

    @Override
    public long getItemId(int position) {
        if (mDataValid && mCursor != null && mCursor.moveToPosition(position)) {
            return mCursor.getLong(mRowIdColumn);
        }
        return 0;
    }

    @Override
    public void setHasStableIds(boolean hasStableIds) {
        super.setHasStableIds(true);
    }

    public abstract void onBindViewHolder(VH viewHolder, Cursor cursor);

    @Override
    public void onBindViewHolder(VH viewHolder, int position) {
        if (!mDataValid) {
            throw new IllegalStateException("this should only be called when the cursor is valid");
        }
        if (!mCursor.moveToPosition(position)) {
            throw new IllegalStateException("couldn't move cursor to position " + position);
        }
        onBindViewHolder(viewHolder, mCursor);
    }

    /**
     * Change the underlying cursor to a new cursor. If there is an existing cursor it will be
     * closed.
     */
    public void changeCursor(Cursor cursor) {
        Cursor old = swapCursor(cursor);
        if (old != null) {
            old.close();
        }
    }

    /**
     * Swap in a new Cursor, returning the old Cursor.  Unlike
     * {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
     * closed.
     */
    public Cursor swapCursor(Cursor newCursor) {
        if (newCursor == mCursor) {
            return null;
        }
        final Cursor oldCursor = mCursor;
        if (oldCursor != null && mDataSetObserver != null) {
            oldCursor.unregisterDataSetObserver(mDataSetObserver);
        }
        mCursor = newCursor;
        if (mCursor != null) {
            if (mDataSetObserver != null) {
                mCursor.registerDataSetObserver(mDataSetObserver);
            }
            mRowIdColumn = newCursor.getColumnIndexOrThrow("_id");
            mDataValid = true;
            notifyDataSetChanged();
        } else {
            mRowIdColumn = -1;
            mDataValid = false;
            notifyDataSetChanged();
            //There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
        }
        return oldCursor;
    }

    private class NotifyingDataSetObserver extends DataSetObserver {
        @Override
        public void onChanged() {
            super.onChanged();
            mDataValid = true;
            notifyDataSetChanged();
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            mDataValid = false;
            notifyDataSetChanged();
            //There is no notifyDataSetInvalidated() method in RecyclerView.Adapter
        }
    }
}

扩展的适配器类

import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;


public class MyListCursorAdapter extends CursorRecyclerViewAdapter<MyListCursorAdapter.ViewHolder> {

    private static final String TAG = "MyListCursorAdapter";

    public MyListCursorAdapter(Context context, Cursor cursor) {
        super(context, cursor);
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.productList, parent, false);
        ViewHolder vh = new ViewHolder(itemView);

        return vh;
    }

    @Override
    public void onBindViewHolder(final ViewHolder viewHolder, final Cursor cursor) {
        Book myListItem = Book.fromCursor(cursor);
        viewHolder.titleTextView.setText(myListItem.getTitle());
        viewHolder.shippingTextView.setText(myListItem.getAShipping());
        Log.d(TAG, "onBindViewHolder: "+viewHolder.isRecyclable());
    }



    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView shippingTextView;
        public TextView titleTextView;
        public LinearLayout insideCard;
        public Button payButton;
        public View seperator;
        public CardView cv;
        public View view;

        public ViewHolder(final View view) {
            super(view);
            WindowManager wm = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
            Display display = wm.getDefaultDisplay();
            DisplayMetrics DM = new DisplayMetrics();
            display.getMetrics(DM);
            payButton = (Button) view.findViewById(R.id.readButton);
            seperator = view.findViewById(R.id.separator);
            titleTextView = (TextView) view.findViewById(R.id.titleTextView);
            insideCard = (LinearLayout) view.findViewById(R.id.insideCard);
            insideCard.getLayoutParams().width = (DM.widthPixels / 2) - 24;
            shippingTextView = (TextView) view.findViewById(R.id.shippingTextView);


//typeface
            Typeface light = Typeface.createFromAsset(titleTextView.getResources().getAssets(), "fonts/light.ttf");
            titleTextView.setTypeface(light);
            Typeface regular = Typeface.createFromAsset(authorTextView.getResources().getAssets(), "fonts/thinItalic.ttf");
            shippingTextView.setTypeface(regular);

            payButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent goToCheckout = new Intent(view.getContext(), ShowReader.class);
                    goToCheckout.putExtra("PRODUCT_NAME", titleTextView.getText());
                    view.getContext().startActivity(goToCheckout);
                }
            });


            setWidth((DM.widthPixels / 2) - 24);
        }

        public void setWidth(int a) {
            a = a - 8 * 4;
            payButton.getLayoutParams().width = a;
            seperator.getLayoutParams().width = a;

        }
    }

}

【问题讨论】:

  • doInBackground() 方法中移动 c.close();
  • 还是一样的结果。
  • 显示你的数据库代码。
  • 这有什么关系?
  • 可以添加doInBackground()代码吗?

标签: android sqlite android-layout android-recyclerview


【解决方案1】:

这是你的问题:

//typeface
            Typeface light = Typeface.createFromAsset(titleTextView.getResources().getAssets(), "fonts/light.ttf");
            titleTextView.setTypeface(light);
            Typeface regular = Typeface.createFromAsset(authorTextView.getResources().getAssets(), "fonts/thinItalic.ttf");
            shippingTextView.setTypeface(regular);

每次您要创建新的ViewHolder 时,您都会从文件中读取。当您多次执行此操作时,这是一项繁重的操作。因此,一种解决方案是只读取一次并缓存字体。那么让我们看一些代码:

public class MyFontProvider {

    private static final String TAG = MyFontProvider.class.getSimpleName();

    private Map<String, Typeface> mCache;

    private static volatile MyFontProvider sInstance;

    public static MyFontProvider getInstance() {
        if (sInstance == null) {
            synchronized (MyFontProvider.class) {
                if (sInstance == null) {
                    sInstance = new MyFontProvider();
                }
            }
        }
        return sInstance;
    }

    private MyFontProvider() {
        mCache = new Hashtable<>();
    }

    public synchronized Typeface getFont(String assetPath, Context context) {
        if (!mCache.containsKey(assetPath)) {
            try {
                Typeface t = Typeface.createFromAsset(context.getAssets(), assetPath);
                mCache.put(assetPath, t);
            } catch (Exception e) {
                Log.e(TAG, "Error getting typeface (" + assetPath + " ) " + e.getMessage());
                return null;
            }
        }
        return mCache.get(assetPath);
    }
}

现在你可以改变

//typeface
            Typeface light = Typeface.createFromAsset(titleTextView.getResources().getAssets(), "fonts/light.ttf");
            titleTextView.setTypeface(light);
            Typeface regular = Typeface.createFromAsset(authorTextView.getResources().getAssets(), "fonts/thinItalic.ttf");
            shippingTextView.setTypeface(regular);

//typeface
            titleTextView.setTypeface(MyFontProvider.getInstance().getFont("fonts/light.ttf",this));
            shippingTextView.setTypeface(MyFontProvider.getInstance().getFont("fonts/thinItalic.ttf",this));

将字体路径存储为public final static String 也是一个好主意,这样您就可以在一个地方更改它,而不必修改多个文件。

【讨论】:

  • 太棒了。解决问题。我猜,文件系统是有代价的。
【解决方案2】:

在完全完成之前,您不应该关闭光标。一旦你这样做了,你就丢失了你的数据源,所以这就是为什么你在关闭它之后什么都看不到。

您是否分析过您的适配器?鉴于您在不关闭光标时存在延迟,但您没有关闭光标,因此您总是有可能无法有效地创建视图

【讨论】:

  • 如何有效地创建视图?
  • 您可以发布更多代码吗?仅使用 sn-ps 很难猜出发生了什么
猜你喜欢
  • 1970-01-01
  • 2020-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多