背景
嗨,我对Android编程比较陌生。我还在学习。
我有一个包含两个表的数据库:Item和Type。 Item包含以下列:_id,code,type。 Type包含以下列:_id,name。
在我的活动中,我会显示各种类型的微调器以及项目的网格视图。微调器应该过滤结果并更新gridview。
gridview项目仅包含图像。列代码给出了图像文件的名称。 我有一个数据库助手DatabaseHelper,它以不同的方式打开,关闭和访问数据库。
在我的活动ItemList的开头,调用getContents(),getContents()调用数据库,并根据selectedType的值更新listOfIds和listOfCodes(初始化为0)。然后创建gridview。
对于gridview,我在显示图像和滚动(内存不足)时遇到了内存问题。所以我遵循了Android教程:https://developer.android.com/training/displaying-bitmaps/process-bitmap.html现在,每个项目都是异步显示的。它工作正常。
问题
当我在微调器中选择一个类型时,selectedType被更新,调用getContents()然后调用notifyDataSetChanged()。有时它工作正常,有时会崩溃。我认为当我选择一个类型时,尚未终止的旧线程正在更新时访问listOfIds和listOfCodes。
如何杀死所有thoses线程(在更新列表之前)并阻止gridview在更新期间调用getView? 我在考虑创建一个包含线程的列表,更新它,并在更新列表之前终止所有线程。但我无法找到阻止gridview适配器在更新期间创建视图的正确方法。
感谢您的帮助。
代码
以下是我的代码的一部分:
ItemList.java
public class ItemList extends Activity {
private ImageAdapter mAdapter;
private Bitmap mPlaceHolderBitmap;
private DatabaseHelper myHelper;
public static int column = 3;
public static int MARGIN = 5; //margin in dp
public static int width;
public GridView gallery;
public SpinnerAdapter spinnerAdapter;
public Spinner mySpinner;
public int defaultImageID;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.item_list);
initialiseValues();
createHelper();
resetPositions();
getContents();
createLists();
createSpinnerListener();
}
@Override
protected void onResume() {
super.onResume();
}
public void createHelper() {
myHelper = new DatabaseHelper(getApplicationContext());
myHelper.getSpinnerIds();
}
public void initialiseValues(){
myHelper.selectedType = 0;
//determines the width of the displayed image
Point size = new Point();
Display display = getWindowManager().getDefaultDisplay();
display.getSize(size);
width = size.x - dpToPx(2*column*MARGIN);
//determines the default image to display
defaultImageID = ...; //here I get the id of the default image;
mPlaceHolderBitmap = decodeSampledBitmapFromResource(getResources(), defaultImageID, null, null), width, width);
}
public void createList() {
spinnerAdapter = new SpinnerAdapter(this, myHelper, myHelper.spinnerNames);
spinnerAdapter.setDropDownViewResource(R.layout.spinner_item);
mySpinner = (Spinner) findViewById(R.id.spinner);
mySpinner.setAdapter(spinnerAdapter);
gallery = (GridView) findViewById(R.id.gallery);
mAdapter = new ImageAdapter(this);
gallery.setAdapter(mAdapter);
}
public void getContents() {
myHelper.getLists();
}
public void createSpinnerListener() {
mySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
myHelper.selectedType = myHelper.spinnerIds.get(position);
getContents();
mAdapter.notifyDataSetChanged();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
private class ImageAdapter extends BaseAdapter {
private final Context mContext;
public ImageAdapter(Context context) {
super();
mContext = context;
}
@Override
public int getCount() {
return myHelper.listOfCodes.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup container) {
View view;
if (convertView == null) { // if it's not recycled, initialize some attributes
LayoutInflater inflater = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.grid_item, null);
}
else {view = convertView;}
int image_id = getApplicationContext().getResources().getIdentifier(".../" + myHelper.listOfCodes.get(position), null, null);
if (image_id == 0) {image_id = R.drawable.item_unknown;}
loadBitmap(image_id, imageView);
return view;
}
}
public void loadBitmap(int resId, ImageView imageView) {
if (cancelPotentialWork(resId, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}
public static boolean cancelPotentialWork(int data, ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final int bitmapData = bitmapWorkerTask.data;
if (bitmapData != data) {
bitmapWorkerTask.cancel(true); // Cancel previous task
}
else {
return false; // The same work is already in progress
}
}
return true; // No task associated with the ImageView, or an existing task was cancelled
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
public BitmapWorkerTask(View view) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return decodeSampledBitmapFromResource(getResources(), data, width, width);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
}
DatabaseHelper.java
public class DatabaseHelper extends SQLiteOpenHelper{
...
public int currentID = 0;
public int numberOfItems = 0;
public List<Integer> listOfIds = new ArrayList<>();
public List<String> listOfCodes = new ArrayList<>();
public List<Integer> spinnerIds = new ArrayList<>();
public List<String> spinnerNames = new ArrayList<>();
public int selectedType = 0;
private Context context;
public SQLiteDatabase myDataBase;
...
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, 1);
this.context = context;
}
...
public void getSpinnerIds() {
spinnerIds.clear();
spinnerNames.clear();
/*
updates spinnerIds by accessing the db, first one is "All types"
*/
}
public void getLists() {
listOfIds.clear();
listOfCodes.clear();
numberOfItems = 0;
String where = " WHERE code <> '0'";
if (selectedPosition != 0) {where += " AND Type = " + String.valueOf(selectedType);}
openDataBase();
try {
Cursor myCursor = myDataBase.rawQuery("SELECT _id, code, type FROM Item" + where + " ORDER BY _id ASC", null);
myCursor.moveToFirst();
do {
listOfIds.add(myCursor.getInt(0));
listOfCodes.add(myCursor.getString(1));
}
while (myCursor.moveToNext());
myCursor.close();
numberOfItems = listOfIds.size();
}
catch (Exception CursorIndexOutOfBoundsException) {}
myDataBase.close();
}
public String getName(int id) {
String name;
openDataBase();
Cursor myCursor = myDataBase.rawQuery("SELECT name FROM Item WHERE _id = " + String.valueOf(id), null);
myCursor.moveToFirst();
name = myCursor.getString(0);
myCursor.close();
myDataBase.close();
return name;
}
...
}
SpinnerAdapter.java
public class SpinnerAdapter extends ArrayAdapter<String> {
private Context context;
private DatabaseHelper helper;
public SpinnerAdapter(Context context, DatabaseHelper helper, List<String> list) {
super(context, R.layout.spinner_item_small, R.id.text, list);
this.context = context;
this.helper = helper;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
return getCustomView(position, convertView);
}
@Override
public View getView(int position, View convertView, ViewGroup prnt) {
return getCustomView(position, convertView);
}
public View getCustomView(int position, View convertView) {
View view;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.spinner_item_small, null);
}
else {view = convertView;}
TextView textView = (TextView) view.findViewById(R.id.text);
textView.setText(helper.spinnerNames.get(position));
return view;
}
}