Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Android Build a Self-Destructing Message Android App Sending Messages Sending the Message

Julio Martin Hernandez
Julio Martin Hernandez
6,355 Points

Sending Video Error

Why do I always get an exception: "06-23 17:34:23.780 28942-28942/com.martyawesome.ribbit.app E/AndroidRuntime? FATAL EXCEPTION: main java.lang.NullPointerException at com.martyawesome.ribbit.app.FileHelper.getByteArrayFromFile(FileHelper.java:27) at com.martyawesome.ribbit.app.RecipientsActivity.createMessage(RecipientsActivity.java:148) at com.martyawesome.ribbit.app.RecipientsActivity.onOptionsItemSelected(RecipientsActivity.java:109) at android.app.Activity.onMenuItemSelected(Activity.java:2640) at com.android.internal.policy.impl.PhoneWindow.onMenuItemSelected(PhoneWindow.java:1171) at com.android.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:735) at com.android.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152) at com.android.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:874) at com.android.internal.view.menu.ActionMenuView.invokeItem(ActionMenuView.java:630) at com.android.internal.view.menu.ActionMenuItemView.onClick(ActionMenuItemView.java:200) at android.view.View.performClick(View.java:4475) at android.view.View$PerformClick.run(View.java:18786) at android.os.Handler.handleCallback(Handler.java:730) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:176) at android.app.ActivityThread.main(ActivityThread.java:5419) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:525) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862) at dalvik.system.NativeStart.main(Native Method)"

This is my RecipientsActivity.java:

"package com.martyawesome.ribbit.app;

import android.app.AlertDialog; import android.app.ListActivity; import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast;

import com.parse.FindCallback; import com.parse.ParseException; import com.parse.ParseFile; import com.parse.ParseObject; import com.parse.ParseQuery; import com.parse.ParseRelation; import com.parse.ParseUser; import com.parse.SaveCallback;

import java.util.ArrayList; import java.util.List;

public class RecipientsActivity extends ListActivity {

public static final String TAG = RecipientsActivity.class.getSimpleName();

protected ParseRelation<ParseUser> mFriendsRelation;
protected List<ParseUser> mFriends;
protected ParseUser mCurrentUser;
protected Uri mMediaUri;
protected String mFileType;

protected MenuItem mSendMenuItem;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
    setContentView(R.layout.activity_recipients);

    getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    mMediaUri = getIntent().getData();
    mFileType = getIntent().getExtras().getString(ParseConstants.KEY_FILE_TYPE);
}

@Override
public void onResume() {
    super.onResume();

    mCurrentUser = ParseUser.getCurrentUser();
    mFriendsRelation = mCurrentUser.getRelation(ParseConstants.KEY_FRIENDS_RELATION);
    setProgressBarIndeterminateVisibility(true);
    ParseQuery<ParseUser> query = mFriendsRelation.getQuery();
    query.addAscendingOrder(ParseConstants.KEY_USERNAME);
    query.findInBackground(new FindCallback<ParseUser>() {
        @Override
        public void done(List<ParseUser> friends, ParseException e) {
            setProgressBarIndeterminateVisibility(false);

            if (e == null) {
                mFriends = friends;

                String[] usernames = new String[mFriends.size()];
                int i = 0;
                for (ParseUser user : mFriends) {
                    usernames[i] = user.getUsername();
                    i++;
                }

                ArrayAdapter<String> adapter = new ArrayAdapter<String>(getListView().getContext(),
                        android.R.layout.simple_list_item_checked, usernames);
                setListAdapter(adapter);
            } else {
                Log.e(TAG, e.getMessage());
                AlertDialog.Builder builder = new AlertDialog.Builder(RecipientsActivity.this);
                builder.setMessage(e.getMessage())
                        .setTitle(R.string.error_title)
                        .setPositiveButton(android.R.string.ok, null);
                AlertDialog dialog = builder.create();
                dialog.show();
            }
        }
    });
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.recipients, menu);

    mSendMenuItem = menu.getItem(0);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_send) {
        ParseObject message = createMessage();
        if (message == null) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage(R.string.error_selecting_file)
                    .setTitle(R.string.error_selecting_file_title)
                    .setPositiveButton(android.R.string.ok, null);
            AlertDialog dialog = builder.create();
            dialog.show();
        } else {
            send(message);
            finish();
        }

        return true;
    }
    return super.onOptionsItemSelected(item);
}

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
    super.onListItemClick(l, v, position, id);

    if (l.getCheckedItemCount() > 0) {
        mSendMenuItem.setVisible(true);
    } else {
        mSendMenuItem.setVisible(false);
    }

}

protected ParseObject createMessage() {
    ParseObject message = new ParseObject(ParseConstants.CLASS_MESSAGES);
    message.put(ParseConstants.KEY_SENDER_ID, ParseUser.getCurrentUser().getObjectId());
    message.put(ParseConstants.KEY_SENDER_NAME, ParseUser.getCurrentUser().getUsername());
    message.put(ParseConstants.KEY_RECIPIENT_IDS, getRecipientIds());
    message.put(ParseConstants.KEY_FILE_TYPE, mFileType);

    byte[] fileBytes = FileHelper.getByteArrayFromFile(this, mMediaUri);

    if (fileBytes == null) {
        return null;
    } else {
        if (mFileType.equals(ParseConstants.TYPE_IMAGE)) {
            fileBytes = FileHelper.reduceImageForUpload(fileBytes);
        }

        String fileName = FileHelper.getFileName(this, mMediaUri, mFileType);
        ParseFile file = new ParseFile(fileName, fileBytes);
        message.put(ParseConstants.KEY_FILE, file);
        return message;
    }
}

protected ArrayList<String> getRecipientIds() {
    ArrayList<String> recipientIds = new ArrayList<String>();
    for (int i = 0; i < getListView().getCount(); i++) {
        if (getListView().isItemChecked(i)) {
            recipientIds.add(mFriends.get(i).getObjectId());
        }
    }

    return recipientIds;
}

protected void send(ParseObject message) {
    message.saveInBackground(new SaveCallback() {
        @Override
        public void done(ParseException e) {

            if (e == null) {
                //success
                Toast.makeText(RecipientsActivity.this, getString(R.string.success_message),Toast.LENGTH_LONG).show();
            } else {
                AlertDialog.Builder builder = new AlertDialog.Builder(RecipientsActivity.this);
                builder.setMessage(R.string.error_sending_message)
                        .setTitle(R.string.error_selecting_file_title)
                        .setPositiveButton(android.R.string.ok, null);
                AlertDialog dialog = builder.create();
                dialog.show();

            }
        }
    });
}

}"

Note: I am using Android Studio

3 Answers

You could try updating. I'm using 0.6.1.

I would also go into your emulator's app settings and force stop, clear the data and uninstall the app then try again.

I was suddenly getting some really weird exceptions and crashes and I hadn't changed any code! After I reset the genymotion emulator everything was working again.

It looks like line 46 is throwing a null pointer exception.

I would check and make sure you included this line in your MessageAdapter getView method:

 holder.iconImageView = (ImageView)convertView.findViewById(R.id.message_icon);

Here's my full MessageAdapter class:

package com.spacecasestudios.messagemonster;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.parse.ParseObject;

import java.util.List;

/**
 * Created by Ryan on 6/21/2014.
 */
public class MessageAdapter extends ArrayAdapter<ParseObject> {

    protected Context mContext;
    protected List<ParseObject> mMessages;
    protected TextView mView;

    public MessageAdapter(Context context, List<ParseObject> messages){
        super(context, R.layout.message_item, messages);
        mContext = context;
        mMessages = messages;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if(convertView == null){
            convertView = LayoutInflater.from(mContext).inflate(R.layout.message_item, null);
            holder = new ViewHolder();
            convertView.setTag(holder);
            holder.iconImageView = (ImageView)convertView.findViewById(R.id.message_icon);
            holder.nameLabel = (TextView)convertView.findViewById(R.id.sender_label);
            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder)convertView.getTag();
        }

        ParseObject message = mMessages.get(position);

        if (message.getString(ParseConstants.KEY_FILE_TYPE).equals(ParseConstants.TYPE_IMAGE)){
            holder.iconImageView.setImageResource(R.drawable.ic_action_picture);
        }
        else {
            holder.iconImageView.setImageResource(R.drawable.ic_action_play_over_video);
        }

        holder.nameLabel.setText(message.getString(ParseConstants.KEY_SENDER_NAME));
        return convertView;
    }

    private static class ViewHolder{
        ImageView iconImageView;
        TextView nameLabel;
    }

    public void refill(List<ParseObject> messages){
        mMessages.clear();
        mMessages.addAll(messages);
        notifyDataSetChanged();
    }
}
Julio Martin Hernandez
Julio Martin Hernandez
6,355 Points

I was missing the line "convertView.setTag(holder);". Thank you so much for the help! :)

Same here. I wonder how Ben's app was able to run without setTag ...

Harry James
Harry James
14,780 Points

EDIT2: Sorry about that Ben! I didn't realise that you fixed this in a later video! Apologies!

Ben Jakuben you should probably take a look at this :)


It seems that without this:

...
holder = new ViewHolder(); // Placeholder so users know where to write the line below.
convertView.setTag(holder); // Missing line
...

then the app will crash once returning from the Video Intent to the app. I assume this is because you're trying to get a non-existent tag.

EDIT: I tried this for myself to see what's going on. I'm going to keep this here for those who don't understand what's happening but just put the line in to fix the issue. So, I imagined I was a computer, going through the code. This is what happens WITHOUT the tag: When the activity is first created, we run getView()'s first if statement. Here, we create the ViewHolder, ignore the else statement and carry on. We're then going to set the appropriate drawable file for each item in the list. That's it. We're done for now.

Now, the user clicks on a video, is brought to their chosen app and, when they're brought back, the activities getView() method will run again: This time, the ViewHolder already exists in cache so, we're going to run the else statement which sets the holder variable equal to the tag from convertView (convertView.getTag()) but, wait. We never gave convertView a tag? Ah. So, I put in a debug message, logging the value of holder and, it turns out it's equal to null (That is what getTag() returns if there is no tag). Now, the computer doesn't notice anything's wrong here so, it carries on. Next, it will run one of the if or else statements below (Depending on whether your first item in the list is an image or a video) and will try to do holder.iconImageView. Now, wait. holder is still equal to null so, what we're actually doing is null.iconImageView! That won't work! And that's why we get the exception.

Hope this explains things :)

Michael Rogers
Michael Rogers
93 Points

What/where is this MessageAdapter class you speak of? It seems like the code was refactored at some point to remove this class/error, though coincidentally I'm running into the same original problem that Marty ran into. Specifically, the NullPointerException at line 46 of getByteArrayFromFile in the FileHelper class.

You might try changing your onOptionsSelected method by using a switch statement instead of an if. I'm also using android studio and I was was having some errors in this class, but using the switch fixed my problems.

 @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch(item.getItemId()){
            case android.R.id.home:
                // Handle action bar item clicks here. The action bar will
                // automatically handle clicks on the Home/Up button, so long
                // as you specify a parent activity in AndroidManifest.xml.
                NavUtils.navigateUpFromSameTask(this);
                return true;

            case R.id.action_send:
                ParseObject message = createMessage();
                if(message == null){
                    AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setMessage(R.string.error_selecting_file)
                            .setTitle(R.string.error_selecting_file_title)
                            .setPositiveButton(android.R.string.ok, null);
                    AlertDialog dialog = builder.create();
                    dialog.show();
                }
                else{
                    send(message);
                    Toast.makeText(RecipientsActivity.this, getString(R.string.uploading_message),    
                              Toast.LENGTH_LONG).show();
                    finish();
                }
                return true;
        }
        return super.onOptionsItemSelected(item);
    }
Julio Martin Hernandez
Julio Martin Hernandez
6,355 Points

Thank you! But now I have a new weird error after capturing a video:

06-24 05:37:39.397  19909-19909/com.martyawesome.ribbit.app E/AndroidRuntime? FATAL EXCEPTION: main
    java.lang.NullPointerException
            at com.martyawesome.ribbit.app.MessageAdapter.getView(MessageAdapter.java:46)
            at android.widget.AbsListView.obtainView(AbsListView.java:2608)
            at android.widget.ListView.makeAndAddView(ListView.java:1852)
            at android.widget.ListView.fillSpecific(ListView.java:1333)
            at android.widget.ListView.layoutChildren(ListView.java:1645)
            at android.widget.AbsListView.onLayout(AbsListView.java:2447)
            at android.view.View.layout(View.java:15204)
            at android.view.ViewGroup.layout(ViewGroup.java:4793)
            at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1076)
            at android.view.View.layout(View.java:15204)
            at android.view.ViewGroup.layout(ViewGroup.java:4793)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:15204)
            at android.view.ViewGroup.layout(ViewGroup.java:4793)
            at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1589)
            at android.view.View.layout(View.java:15204)
            at android.view.ViewGroup.layout(ViewGroup.java:4793)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:15204)
            at android.view.ViewGroup.layout(ViewGroup.java:4793)
            at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:349)
            at android.view.View.layout(View.java:15204)
            at android.view.ViewGroup.layout(ViewGroup.java:4793)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:15204)
            at android.view.ViewGroup.layout(ViewGroup.java:4793)
            at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2260)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2007)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1249)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6364)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:791)
            at android.view.Choreographer.doCallbacks(Choreographer.java:591)
            at android.view.Choreographer.doFrame(Choreographer.java:561)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:777)
            at android.os.Handler.handleCallback(Handler.java:730)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:176)
            at android.app.ActivityThread.main(ActivityThread.java:5419)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
            at dalvik.system.NativeStart.main(Native Method)

My code for the MessageAdapter:

42       ParseObject message = mMessages.get(position);
43       if (message.getString(ParseConstants.KEY_FILE_TYPE).equals(ParseConstants.TYPE_IMAGE)) {
44           holder.iconImageView.setImageResource(R.drawable.ic_action_picture);
45        } else {
46            holder.iconImageView.setImageResource(R.drawable.ic_action_play_over_video);
47        }
48        holder.nameLabel.setText(message.getString(ParseConstants.KEY_SENDER_NAME));
Julio Martin Hernandez
Julio Martin Hernandez
6,355 Points

Maybe this a sign that I have to update my Android Studio? My Android Studio version is 0.5.2.