2

I have a problem with the recycler view and card view. I'm using asynctask to get info from API, and for now I'm getting only a name - which means, I display in my card view only a text view. however, when I'm loading the list, it is awfully slow. in the log cat I can see that the app is getting the data pretty fast, but it takes a lot of time to show it in the recycler view.

I'm adding few samples - from the adapter of the recycler view and the fragment that holds the recycler view in. maybe I did something wrong in the adapter.

Thank you for your help!

Adapter:

public class PlacesListAdapter extends RecyclerView.Adapter<PlacesListAdapter.ListViewHolder>{
ArrayList<PlaceItem> items;
Context context;

public PlacesListAdapter(Context context,ArrayList<PlaceItem> placeItems){
    this.context = context;
    this.items = placeItems;
}

public void swap(ArrayList<PlaceItem> places){
    items.clear();
    items.addAll(places);
    notifyDataSetChanged();
}

@Override
public ListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(context).inflate(R.layout.card_view, parent, false);
    return new ListViewHolder(v);
}

@Override
public void onBindViewHolder(ListViewHolder holder, int position) {
    PlaceItem item = items.get(position);
    holder.bindData(item);
}

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

public class ListViewHolder extends RecyclerView.ViewHolder{
    TextView title;
    PlaceItem placeItem;
    public ListViewHolder(View itemView) {
        super(itemView);
        title = (TextView) itemView.findViewById(R.id.txtTitlePlace);
    }

    public void bindData(PlaceItem item){
        this.placeItem = item;
        title.setText(placeItem.getTitle());
    }
  }
}

Fragment:

public class FragmentListPlaces extends Fragment implements  View.OnClickListener {

ArrayList<PlaceItem> placeItems;
PlacesListAdapter adapter;
RecyclerView list;
EditText editName;

public FragmentListPlaces() {
    // Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View v = inflater.inflate(R.layout.fragment_list_places, container, false);
    editName = (EditText) v.findViewById(R.id.editPlaceName);
    v.findViewById(R.id.btnGetLocations).setOnClickListener(this);
    v.findViewById(R.id.btnSearchByText).setOnClickListener(this);
    placeItems = new ArrayList<>();
    placeItems.add(new PlaceItem("Example"));
    adapter = new PlacesListAdapter(getContext(), placeItems);
    list = (RecyclerView) v.findViewById(R.id.placesList);
    list.setLayoutManager(new LinearLayoutManager(getContext()));
    list.setAdapter(adapter);
    return v;
}

@Override
public void onClick(View v) {
    switch (v.getId()){
        case R.id.btnGetLocations:
            GetUserLocation location = new GetUserLocation();
            location.getLocation(getActivity());
            adapter.swap(placeItems);
            break;
        case R.id.btnSearchByText:
            // this is the method loading data with user input
            String getNameFromUser = editName.getText().toString();
            searchPlaceByText(getNameFromUser);
            adapter.swap(placeItems);
            break;
    }
}

public void searchPlaceByText(String place){
    // instantiate the asynctask here
    LocationDetailsByText locationDetailsByText = new LocationDetailsByText(placeItems);
    locationDetailsByText.execute("http://api.v3.factual.com/t/places-il?q=" + place + "&KEY=AFvDryDJmPkkgXohbpFdqkRQelT9w0HKtyEqXy3G");
}

Loading of data from the web:

public class LocationDetailsByText extends AsyncTask<String, Void, String> {

ArrayList<PlaceItem> placeItems = new ArrayList<>();

public LocationDetailsByText(ArrayList<PlaceItem> places){
    this.placeItems = places;
}
@Override
protected String doInBackground(String... params) {
    StringBuilder result = new StringBuilder();
    BufferedReader reader;
    HttpURLConnection connection = null;
    URL url;

    String query = (params[0]);
    try {
        url = new URL(query);
        connection = (HttpURLConnection)url.openConnection();

        if(connection.getResponseCode() != 200){
            return "Error!";
        }

        reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

        String line = "";
        while((line = reader.readLine())!= null){
            result.append(line);
        }
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        connection.disconnect();
    }
    return result.toString();
}

@Override
protected void onPostExecute(String s) {
    PlaceItem placeItem;
    try {
        JSONObject root = new JSONObject(s);
        JSONObject response = root.getJSONObject("response");
        JSONArray data = response.getJSONArray("data");

        for(int i = 0; i < data.length(); i++){
            JSONObject getData = data.getJSONObject(i);
            String title = getData.getString("name");
            placeItem = new PlaceItem(title);
            placeItems.add(placeItem);
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
}
DarkLeafyGreen
  • 69,338
  • 131
  • 383
  • 601
Eran
  • 19
  • 2
  • 5
  • You can see this question: [Android while using cardview image is loading very lazy](http://stackoverflow.com/questions/33796166/android-while-using-cardview-image-is-loading-very-lazy) – Tunahan Tolga Yıldız Dec 22 '15 at 12:41
  • Hi, this question is mainly for dealing with images..but I'm only using a text view and it still loads very slow..i couldn't find any answer in the forums, so I guess I'm doing something wrong here, but just cant notice it. this problem is slowing my project progress down. – Eran Dec 22 '15 at 12:46
  • I'm also keep getting this line in the log cat: W/View: requestLayout() improperly called by android.support.v7.widget.AppCompatTextView{3c56a6c7 V.ED.... ......ID 222,45-366,118 #7f0c0076 app:id/drawer_item_text} during layout: running second layout pass what does it mean? – Eran Dec 22 '15 at 13:09
  • I'm having something weird..it seems that everything is alright..but the problem is this: in my layout I have 2 buttons. below there is an edit text and above there is the recycler view. when I write a text and click the button, nothing shows up. but if I click on the edit text again to use the keyboard, then the recycler view is loading the items. what can cause this issue? – Eran Dec 22 '15 at 13:35
  • Hi, a few questions: 1) Why do you call the bindData method inside your viewHolder? Why dont you put the code inside onBindViewHolder? 2) Why dont you make the ViewHolder static(it will be more performant)? 3) What is the size of the arraylist? Maybe it would be better to make a new ArrayList instead of clearing it. It all depends of the size. – JpCrow Apr 05 '16 at 02:13

3 Answers3

2

Apart from the issue that @M G pointed out that messes up the behavior in general. you have 2 other major flaws that I can see of which 1) is affecting you mostly.

1) you do all the Json parsing + moving the data to your POJO(PlaceItem[]) onPostExecute. this is wrong!

  • this can be very heavy on computation
  • this way you create 2 many intermediate objects lots of GC

I suggest move these to background and use Gson.

2) it seems that you do lots of network calls that could happen quite frequently. This needs better managing of concurrent requests, network connections, threads, streams and data arrays obtained form the network stream. This can cause lots of GC.

I would recommend to use some networking library made for this purpose such as retrofit, volley or jus. These all can handle also parsing network data straight to your POJO in the background and minimizing GC and performance in general.

kalin
  • 3,546
  • 2
  • 25
  • 31
1

Few issues with your code

searchPlaceByText(getNameFromUser);
adapter.swap(placeItems);

adapter.swap(placeItems); starts right after you start your AsyncTask but you didn't download anything yet. This is wrong. You should remove adapter.swap(placeItems); from here and do something like this instead:

@Override
public void onClick(View v) {
    switch (v.getId()){
        case R.id.btnGetLocations:
            GetUserLocation location = new GetUserLocation();
            location.getLocation(getActivity());
            adapter.swap(placeItems);//also here probably
            break;
        case R.id.btnSearchByText:
            // this is the method loading data with user input
            String getNameFromUser = editName.getText().toString();
            searchPlaceByText(getNameFromUser);
            break;
    }
}
public void searchPlaceByText(final String place) {
        // instantiate the asynctask here
        LocationDetailsByText locationDetailsByText = new LocationDetailsByText(placeItems) {

            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                adapter.swap(placeItems);
            }
        };
        locationDetailsByText.execute("http://api.v3.factual.com/t/places-il?q=" + place + "&KEY=AFvDryDJmPkkgXohbpFdqkRQelT9w0HKtyEqXy3G");
 }

Next thing is that you clear your list

items.clear();
items.addAll(places);

Which is basically also removing everything from your placeItems because erlier in this class you set this.items = placeItems;. So in PlacesListAdapter just do

public void swap(ArrayList<PlaceItem> places){
     notifyDataSetChanged();
}
M G
  • 1,240
  • 14
  • 26
0

In Your Asynctask, in OnPostExcute, at the end of it notify your adapter about the changes in your data, that's why you cant see your data unless you click on edit text again.

Omid Heshmatinia
  • 5,089
  • 2
  • 35
  • 50