android recycler view adapters, viewpagers, databa

2019-07-12 13:53发布

hello im loading lots of little images (ex: 180x180 10.21kb) from a LOCAL database into lots of different recycler views in fragments in viewpagers in tab layouts (nested fragments.) for instance like this each tab has a new fragment with a recycler view with these little cards and images everything works fine but eventually it all seems to get a bit much for the system and my app is force closed with an out of memory error the error points to my adapters onBindViewHolder, which looks like this

@Override
public void onBindViewHolder(MyViewHolder holder, int position){
    addNewCard cardmaker = cardMakerListDB.get(position);
    holder.cardText.setText(cardmaker.getCardName());
    holder.speechText.setText(cardmaker.getCardSpeech());
    Drawable drawable = new    
    BitmapDrawable(Utility.getPhoto(cardmaker.getCardIcon()));
    holder.cardImage.setImageDrawable(drawable);
}

specifically its pointing at this line

BitmapDrawable(Utility.getPhoto(cardmaker.getCardIcon()));

this is getting a byte array from the database and converting it into a bitmap here is the method from my Utility class

 public static Bitmap getPhoto(byte[] image) {
    return BitmapFactory.decodeByteArray(image, 0, image.length);
}

can anyone see something that i cant, or that i could be doing to avoid this here im quite new to all this, or is there a different workaround any suggestions welcome

EDIT

Thought id try and be clever and have all the images load from an async task, I dont actually know if this would resolve my issue or not but i get this error

07-10 14:18:28.594 18486-18832/ss.sealstudios.com.socialstories D/skia: --- SkImageDecoder::Factory returned null

i understand it i just dont know what i need to be passing the method to fix it heres what ive tried

MY ENTIRE VIEW HOLDER TRYING TO IMPLEMENT ASYNC TASK

public class CardAdapterDB extends    
RecyclerView.Adapter<CardAdapterDB.MyViewHolder> {
private List<addNewCard> cardMakerListDB;

public class MyViewHolder extends RecyclerView.ViewHolder{

    public TextView cardText, speechText;
    public ImageView cardImage;
    public ProgressBar progress;


    public MyViewHolder(View view){
        super(view);
        cardText = (TextView) view.findViewById(R.id.cardText);
        speechText = (TextView) view.findViewById(R.id.speechText);
        cardImage = (ImageView) view.findViewById(R.id.cardimage);
        progress = (ProgressBar) view.findViewById(R.id.progressBarFetch);

    }

}
public CardAdapterDB(List<addNewCard> cardMakerListDB){
    this.cardMakerListDB = cardMakerListDB;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
    View itemView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card, parent, false);
    return new MyViewHolder(itemView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position){
    addNewCard cardmaker = cardMakerListDB.get(position);
    holder.cardText.setText(cardmaker.getCardName());
    holder.speechText.setText(cardmaker.getCardSpeech());
    holder.progress.setVisibility(View.VISIBLE);
    startNewAsyncTask(cardmaker.getCardIcon(),holder.progress,holder);

}

private class MyAsyncTask extends AsyncTask<Bitmap, Void, Bitmap> {

    private byte[] data;
    private ProgressBar progress;
    private MyViewHolder holder;

    public MyAsyncTask(byte[] data,ProgressBar progress,MyViewHolder holder)   
    {
        this.data = data;
        this.progress = progress;
        this.holder = holder;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        progress.setVisibility(View.VISIBLE);
        progress.setIndeterminate(true);
    }

    @Override
    protected Bitmap doInBackground(Bitmap... data) {

        return BitmapFactory.decodeByteArray(this.data, 0, data.length);
    }
    @Override
    protected void onPostExecute(Bitmap response) {
        super.onPostExecute(response);
        {
        Drawable drawable = new BitmapDrawable(response);
        holder.cardImage.setImageDrawable(drawable);
           // progress.setVisibility(View.GONE);
        }
    }
}
public void startNewAsyncTask(byte[] image,ProgressBar progress,MyViewHolder 
holder) {
    MyAsyncTask asyncTask = new MyAsyncTask(image,progress,holder);
    asyncTask.execute();
}

@Override
public int getItemCount(){
    return cardMakerListDB.size();
}
}

2条回答
冷血范
2楼-- · 2019-07-12 14:19

Try using Picasso
Dependency : compile 'com.squareup.picasso:picasso:2.5.2

Picasso
  .with(context)
  .load(your_image)
  .fit()
  // call .centerInside() or .centerCrop() to avoid a stretched image
  .into(your_imageview);
查看更多
我只想做你的唯一
3楼-- · 2019-07-12 14:33

Apparently the huge number of images causes your app to crash with OutOfMemoryError, and unfortunately you can do nothing if your app got this error, it will forced to be closed.

So, all you can do is to avoid the OutOfMemoryError, and this can be done by a lot of ways:

1. assign a largeHeap for your application:

you can do that by adding android:largeHeap="true" to the <application> tag inside your manifest.xml file.

2. override the onLowMemory method of your activity, which will enable you to take an action if the system feels like the memory is very low at some point:

@Override
    public void onLowMemory() {
        // you can here remove the Bitmaps or stopping the process of generating images or do whatever you want to survive being trapped into 'OutOfMemoryError'.
    };

3. you can use any of the Image Libraries to changing the settings of your retrieved images, like reducing its resolution, reducing its size, and even reducing its color set, and the most common ones that are being used in this manner will be Universal-Image-Loader and Picasso, for example in Universal-Image-Loader you can use it in any process of downloading and displaying your Bitmap:

enter image description here


In your case, you have the Bitmap already loaded and all you want is to use an Image library to edit its options(here we will use UniversalImageLoader), in this case you can save the image as mentioned is this answer and after that load it from the memory with the options you gave to it:

// Saving image to disk
String filename = "some_name.jpg";
File sd = Environment.getExternalStorageDirectory();
File dest = new File(sd, filename);

Bitmap bitmap = (Bitmap)data.getExtras().get("data");
try {
     FileOutputStream out = new FileOutputStream(dest);
     bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
     out.flush();
     out.close();
} catch (Exception e) {
     e.printStackTrace();
}

// this is an example of the used options
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
options.inJustDecodeBounds = true;
options.inDither = false;
options.inPurgeable = true;
options.inInputShareable = true;

// create ImageLoader instance
ImageLoader loader = ImageLoader.getInstance();
loader.loadImageSync(dest.getAbsolutePath(), options);
查看更多
登录 后发表回答