1

I've been working on an app that downloads thumbnails and text from Parse and adds it to ArrayLists which are then displayed through a custom adapter for a ListView. The issue here is, the thumbnails for the required content is sometimes misplaced. For example: In my profile, instead of my pic another pic downloaded from parse would be placed. How to fix it ?

MainActivity.java ( Downloads the content )

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

 if (isParseInitialized == false) {

        Parse.initialize(new Parse.Configuration.Builder(this)
                        .applicationId("AppId")
                .clientKey("ClientKey")
                .server("https://parseapi.back4app.com")

                .build()
        );

        isParseInitialized = true;

    }

    catchVideos();

    progressBar = (ProgressBar) findViewById(R.id.progressBar);

    context = this;
    listView = (ListView) findViewById(R.id.listView);

    customAdapter = new CustomAdapter(MainActivity.this, titles, thumbnails, channel);

    //progressBar.setVisibility(View.INVISIBLE);

    final Handler handler = new Handler();
    Runnable run = new Runnable() {
        @Override
        public void run() {

            handler.postDelayed(this, 1000);

            if (tapped == true) {

                ParseQuery<ParseObject> query = new ParseQuery<ParseObject>("Content");
                query.whereEqualTo("Title", title);
                query.findInBackground(new FindCallback<ParseObject>() {
                    @Override
                    public void done(List<ParseObject> objects, ParseException e) {
                        if(e == null) {

                            for (ParseObject object : objects) {

                               Log.i("Info", object.getString("url"));

                                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(object.getString("url")));
                                startActivity(intent);
                            }
                        }
                    }
                });

                tapped = false;

            }
        }
    };

    handler.post(run);



}

public void catchVideos(){

    ParseQuery<ParseObject> query = new ParseQuery<ParseObject>("Content");
    query.whereNotEqualTo("Status", null);
    query.orderByDescending("createdAt");
    query.findInBackground(new FindCallback<ParseObject>() {
        @Override
        public void done(List<ParseObject> objects, ParseException e) {
            if(e == null) {

                if (!titles.isEmpty()) {
                    titles.clear();
                }

                if (!channel.isEmpty()) {
                    channel.clear();
                }

                if (!thumbnails.isEmpty()) {
                    thumbnails.clear();
                }

                for (ParseObject object : objects) {

                    titles.add(object.getString("Title"));
                    channel.add(object.getString("Channel"));

                    ParseFile file = (ParseFile) object.get("Thumbnail");
                    file.getDataInBackground(new GetDataCallback() {
                        @Override
                        public void done(byte[] data, ParseException e) {

                            if (e == null) {

                                Bitmap thumbnail = BitmapFactory.decodeByteArray(data, 0, data.length);
                                thumbnails.add(thumbnail);

                                listView.setAdapter(customAdapter);

                                progressBar.setVisibility(View.INVISIBLE);

                            }

                        }
                    });

                    customAdapter.notifyDataSetChanged();

                    Log.i("Info", object.getString("Title"));
                    Log.i("Info", object.getString("url"));
                }

            }
        }
    });
}

CustomAdapter.java

        public class CustomAdapter extends BaseAdapter{

ArrayList<String> result;
ArrayList<String> channelName;
Context context;
ArrayList<Bitmap> imageId;
private static LayoutInflater inflater=null;
public CustomAdapter(MainActivity mainActivity, ArrayList<String> titles, ArrayList<Bitmap> thumbnails, ArrayList<String> channel) {
    // TODO Auto-generated constructor stub
    result=titles;
    channelName=channel;
    context=mainActivity;
    imageId=thumbnails;
    inflater = ( LayoutInflater )context.
            getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
    // TODO Auto-generated method stub
    return result.size();
}

@Override
public Object getItem(int position) {
    // TODO Auto-generated method stub
    return position;
}

@Override
public long getItemId(int position) {
    // TODO Auto-generated method stub
    return position;
}

public class Holder
{
    TextView tv;
    TextView channelText;
    ImageView img;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    Holder holder=new Holder();
    View rowView;
    rowView = inflater.inflate(R.layout.custom_row, null);
    holder.tv=(TextView) rowView.findViewById(R.id.textView1);
    holder.channelText = (TextView) rowView.findViewById(R.id.channel);
    holder.img=(ImageView) rowView.findViewById(R.id.imageView1);
    try  {
        holder.img.setImageBitmap(imageId.get(position));
    } catch (Exception e) {
        e.printStackTrace();
    }
    holder.tv.setText(result.get(position));
    holder.channelText.setText(channelName.get(position));

    rowView.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            //Toast.makeText(context, "You Clicked "+ result.get(position), Toast.LENGTH_LONG).show();
            //Launch URL
            MainActivity.tapped = true;
            MainActivity.title = result.get(position);

        }
    });

    return rowView;

    }

  }
  • 2
    Please explain what you mean by "the required content is sometimes misplaced". If you don't explain the problem properly, nobody will be able to help. – Sufian Jul 17 '16 at 15:22

1 Answers1

0

You're adding each image to a list once it is downloaded. Later, you're iterating over the images in order. However, the order that they appear in the list won't necessarily be the same as the order you requested them. They're being loaded asynchronously, meaning that the code doesn't wait for the image to finish loading before moving on to the next one. If you start loading a large image and then start loading a small image immediately afterwards, the small one might finish downloading first. In the resulting list, the small image would appear before the larger one, even though it was requested second.

To fix the order of the list of images, you could use Futures. Instead of declaring thumbnails as a List<Bitmap>, make it a List<Future<Bitmap>>. Then, add all three items to the list at the same time.

    titles.add(object.getString("Title"));
    channel.add(object.getString("Channel"));
    CompletableFuture<Bitmap> futureThumbnail = new CompletableFuture<>();
    thumbnails.add(futureThumbnail);

That guarantees all three lists will be in the same order, regardless of how long the images take to download.

The next step is to fill in each future with the corresponding image.

    ParseFile file = (ParseFile) object.get("Thumbnail");
    file.getDataInBackground(new GetDataCallback() {
        @Override
        public void done(byte[] data, ParseException e) {
            if (e == null) {
                Bitmap thumbnail = BitmapFactory.decodeByteArray(data, 0, data.length);
                future.complete(thumbnail);
                ...

This method has the added benefit that you can wait for the images to finish downloading. To wait for all the images to download and put them in a list in the right order:

List<Bitmap> thumbnails = new ArrayList<>();
for (Future<Bitmap> future : futureThumbnails) {
    thumbnails.add(future.get());
}

Or, if you prefer not to wait:

List<Bitmap> thumbnails = new ArrayList<>();
Bitmap defaultValue = null; // or preferably some other default value
for (Future<Bitmap> future : futureThumbnails) {
    thumbnails.add(future.isDone() ? future.get() : defaultValue); 
}
Sam
  • 8,330
  • 2
  • 26
  • 51
  • There is an issue with the code I think for (Future future : futureThumbnails) { thumbnails.add(future.get()); } - Can't loop through this and I get this error Error:(202, 54) error: for-each not applicable to expression type required: array or java.lang.Iterable found: CompletableFuture – Vignesh Raja Jul 17 '16 at 16:23