Search code examples
androidandroid-fragmentsmvpsmackmosby

Android Smack with MVP fragment


I am using smack (XMPP library) and Mosby's MvpFagment to show the roster of a user in a listview (his/her connections).

I got the following code which works in a different fragment just doing a network call using the Retrofit library:

public void loadListData(final List<NeighbourListItemModel> itemsList) {
    Log.e("list", itemsList.size() + "");
    listViewAdapter.clear();
    listViewAdapter.addThemAll(itemsList);
    listViewAdapter.notifyDataSetChanged();
}

Which gets called by a RosterListener using roster.addRosterListener(new RosterListener() { .. });

The NeighbourListItemModel is just a simple POJO class having some getters and setts.

However, this gives a AbstractXMPPConnection﹕ Exception in async packet listener android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. error, probably because the XMP connection runs in its own thread.

Now if I change the code to the following:

@Override
public void loadListData(final List<NeighbourListItemModel> itemsList) {
    Log.e("list", itemsList.size() + "");
    getActivity().runOnUiThread(new Runnable() {
        @Override
        public void run() {
            listViewAdapter.clear();
            listViewAdapter.addThemAll(itemsList);
            listViewAdapter.notifyDataSetChanged();
        }
    });
}

I get a java.lang.NullPointerException: Attempt to invoke virtual method 'int getLayout()' on a null object reference error. Where getLayout is defined in:

@Override
    public View getView(final int position, View convertView, ViewGroup parent) {

        ViewHolderAbstractClass viewHolder;
        if (null == convertView || null == convertView.getTag()) {
            viewHolder = getItem(position).getDefaultViewHolder(mItemType);
            convertView = LayoutInflater.from(getContext()).inflate(viewHolder.getLayout(), null);
            viewHolder.findViewsById(convertView);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolderAbstractClass) convertView.getTag();
        }

        BaseModel previousItem = position > 0 ? getItem(position - 1) : null;
        viewHolder.setup(getItem(position), previousItem, mCaller.getContext(), position);

        return convertView;
    }

The view holder abstract class is nothing special:

public abstract class ViewHolderAbstractClass {

    abstract public int getLayout();

    abstract public void findViewsById(View view);

    abstract public void initializeComponentBehavior(BaseModel item, Context context, int position);

    public void setup(BaseModel item, BaseModel previous_item, Context context, int position) {
        initializeComponentBehavior(item, context, position);
    }

}

So obviously the viewHolder variable is null, but I have no idea why. My adapter is defined as new BaseListAdapter(this, R.layout.neighbours_list_item, new ArrayList<ChatItemModel>(), Constants.DATA_TYPE.CHAT);

Like I said, the former code is working when I make a call using Retrofit, but I suspect XMPP running in its own thread is giving me headaches.


Solution

  • The issue was that the Constants.DATA_TYPE itemType was referring to another model instead of a NeighbourListItemModel which caused a null return. The stacktrace did not show this clearly, but at least it is solved now.