Search code examples
javaandroidlistviewcrashbaseadapter

My Listview is too slow. and crashes while scrolling up


I have an app that has a ListView with 15 rows. If there is a new query to search, I set adapter to null and clear Allvideos variable and create a new one with

`new ArrayList();

And when user clicks to last row "more results" and I add items to Allvideos. But after some searchings in my app and add that items to list, my app is being slow and when I try to scroll up it crashes. Here is my Adapter

public class VideosAdapter extends BaseAdapter {

// The list of videos to display
public static List<Video> videos;
// An inflator to use when creating rows
private LayoutInflater mInflater;
private Context context;

/**
 * @param context this is the context that the list will be shown in - used to create new list rows
 * @param videos this is a list of videos to display
 */
public VideosAdapter(Context context, List<Video> videos) {
    VideosAdapter.videos = videos;
    this.mInflater = LayoutInflater.from(context);
    this.context = context;
}

@Override
public int getCount() {
    return videos.size();
}

@Override
public Object getItem(int position) {
    return videos.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

public Object getItem(int position) {
    return videos.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final Video video = videos.get(position);
    ViewHolder viewHolder;
    if(video.getTitle()==null && video.getDuration()==-1)
    {
        convertView = mInflater.inflate(R.layout.more_results, null);
        View thumbnail = convertView.findViewById(R.id.thumbnail);
        thumbnail.setVisibility(View.INVISIBLE);
        return convertView;
    }
        //animation = AnimationUtils.loadAnimation(context, R.anim.push_up_in);
            // If convertView wasn't null it means we have already set it to our list_item_user_video so no need to do it again
    else if(convertView == null){
        convertView = mInflater.inflate(R.layout.list_item_user_video, null);
        viewHolder = new ViewHolder();
        viewHolder.thumb = (UrlImageView) convertView.findViewById(R.id.list_image);
        viewHolder.duration=(TextView) convertView.findViewById(R.id.duration);
        viewHolder.title = (TextView) convertView.findViewById(R.id.userVideoTitleTextView);

        convertView.setTag(viewHolder);
    }
    else{
        // we've just avoided calling findViewById() on resource everytime
        // just use the viewHolder
        viewHolder = (ViewHolder) convertView.getTag();
        }
    // We are using a custom imageview so that we can load images using urls
    // For further explanation see: http://blog.blundell-apps.com/imageview-with-loading-spinner/

    //TextView size=(TextView) convertView.findViewById(R.id.size);




    int durationmin=video.getDuration()/60;
    int durationsec=video.getDuration()%60;

    if(durationsec<10) viewHolder.duration.setText(durationmin+":0"+durationsec);
    else{
        viewHolder.duration.setText(durationmin+":"+durationsec);
    }
     // thumb image

    // Get a single video from our list
    // Set the image for the list item
    viewHolder.thumb.setImageDrawable(video.getThumbUrl());
    // Set the title for the list item

    //ytapi.com düzelene kadar böyle kalacak.

    //size.setText(df.format(MainActivity.getSizeOf(MainActivity.getDownloadUrl(videoid)))+" MMB");

    viewHolder.title.setText(video.getTitle());

    /*animation.setDuration(1000);
    convertView.startAnimation(animation);
    animation = null;*/

    return convertView;
}

I add items to list and set adapter with this code.

    if(Allvideos.isEmpty())
    {
        Allvideos=videos;
    }
    else
    {
        //Remove extra more results row 
        Allvideos.remove(Allvideos.size()-1);
        for(int i=0;i<videos.size();i++)
        {
            Allvideos.add(videos.get(i));

        }
    }   

    VideosAdapter adapter = new VideosAdapter(getContext(), Allvideos);
    //adapter.notifyDataSetChanged();
    setAdapter(adapter);

My logcat is like this.

08-22 15:53:40.557: E/InputEventReceiver(619): Exception dispatching input event.
08-22 15:53:40.657: E/AndroidRuntime(619): FATAL EXCEPTION: main
08-22 15:53:40.657: E/AndroidRuntime(619): java.lang.NullPointerException
08-22 15:53:40.657: E/AndroidRuntime(619):  at com.skymaster.tut.ui.adapter.VideosAdapter.getView(VideosAdapter.java:108)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.widget.AbsListView.obtainView(AbsListView.java:2255)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.widget.ListView.makeAndAddView(ListView.java:1769)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.widget.ListView.fillUp(ListView.java:706)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.widget.ListView.fillGap(ListView.java:645)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5040)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.widget.AbsListView.scrollIfNeeded(AbsListView.java:3197)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.widget.AbsListView.onTouchEvent(AbsListView.java:3471)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.View.dispatchTouchEvent(View.java:7127)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2170)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1905)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2176)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1919)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2176)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1919)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2176)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1919)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2176)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1919)
08-22 15:53:40.657: E/AndroidRuntime(619):  at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1925)
08-22 15:53:40.657: E/AndroidRuntime(619):  at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1379)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.app.Activity.dispatchTouchEvent(Activity.java:2396)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.support.v7.app.ActionBarActivityDelegateICS$WindowCallbackWrapper.dispatchTouchEvent(ActionBarActivityDelegateICS.java:268)
08-22 15:53:40.657: E/AndroidRuntime(619):  at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1873)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.View.dispatchPointerEvent(View.java:7307)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3174)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3119)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4155)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4134)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4226)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:171)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:163)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:4205)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:4245)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.Choreographer.doCallbacks(Choreographer.java:555)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.Choreographer.doFrame(Choreographer.java:523)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.os.Handler.handleCallback(Handler.java:615)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.os.Handler.dispatchMessage(Handler.java:92)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.os.Looper.loop(Looper.java:137)
08-22 15:53:40.657: E/AndroidRuntime(619):  at android.app.ActivityThread.main(ActivityThread.java:4745)
08-22 15:53:40.657: E/AndroidRuntime(619):  at java.lang.reflect.Method.invokeNative(Native Method)
08-22 15:53:40.657: E/AndroidRuntime(619):  at java.lang.reflect.Method.invoke(Method.java:511)
08-22 15:53:40.657: E/AndroidRuntime(619):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
08-22 15:53:40.657: E/AndroidRuntime(619):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
08-22 15:53:40.657: E/AndroidRuntime(619):  at dalvik.system.NativeStart.main(Native Method)

And error occurs at this line java:108 I take duration from site(video.getDuration()) and its in second type like 360 sec not 6 min.

duration.setText(durationmin+":"+durationsec);  

For smooth scrolling i used ViewHolder but it didn't solve my problem. As you see in my code there is URLIMAGEVIEW in my layout for each row. With this item I load different images from url for each rows. And While image is loading spinner appears until loading is finished. Also URLIMAGEVIEW is in another Thread, not ui Thread. Can URLIMAGEVIEW be the reason of error or slow my app?


Solution

  • Why have videos as a static field; it is not necessary and it is a bad practice.

    In addition, when displaying items from a list, consider extending ArrayAdapter instead of BaseAdapter; that way you will only need to overide getView; and not getPosition, getCount, etc.

    if(Allvideos.isEmpty())
    {
        Allvideos=videos;
    }
    else
    {
        //Remove extra more results row 
        Allvideos.remove(Allvideos.size()-1);
    

    Consider using a footer here instead of adding and removing the last item every time. As a bonus, it makes your getView method simpler.

        for(int i=0;i<videos.size();i++)
        {
            Allvideos.add(videos.get(i));
    

    You might want to look into the addAll method.

        }
    }   
    
    VideosAdapter adapter = new VideosAdapter(getContext(), Allvideos);
    

    You do not need to recreate the adapter every time. Instead, do the following:

    //fields
    private ArrayList<Video> videos;
    private ArrayAdapter mAdapter;
    private View footer;
    
    protected void onCreate(Bundle savedInstanceState) {
        ListView list = findViewById(R.id.list);
        footer = getLayoutInflater().inflate(R.layout.more_results, null);
        list.addFooterView(footer);
        videos = new ArrayList<Video>
        mAdapter = new VideosAdapter(this, videos);
        list.setAdapter(mAdapter);
    }
    
    public addItemsToList(List<Video> items){
        videos.addAll(items);
        mAdapter.notifyDataSetChanged();
    }
    

    As a bonus, the footer is always at the end of the list. You may toggle the visibility of the footer if you want to remove it, which is also nice.

    Regarding your error; the problem will be fixed when you add the footer. The current problem is caused by your moreResults layout.

    convertView = mInflater.inflate(R.layout.more_results, null);
    View thumbnail = convertView.findViewById(R.id.thumbnail);
    thumbnail.setVisibility(View.INVISIBLE);
    return convertView;
    

    Note that if your adapter tries to recycle this view next time around, convertView will not be null; however, no tag is set here. Thus, the nullPointerException occurs.