Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

justin z
justin z
11,042 Points

Problem with OkHttp...

I was making an app that uses OkHttp to get Chuck Norris facts from chucknorris.io, but was having some problems using OkHttp. My error is caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference

The code is at https://github.com/zwickers/ChuckNorrisFacts/blob/generate_fact_feature/app/src/main/java/justinzwick/chucknorrisfacts/FactActivity.java

Thanks!

Can you paste the whole error?

6 Answers

aakarshrestha
aakarshrestha
6,509 Points

Make the String permission public, not private.

private String mChuckData;

Change it to:

public String mChuckData;

Also do the debugging by adding Log.v statement on

mChuckData = response.body().string();
Log.v("CheckData", mChuckData);

If you see the data in the Log.v statement in the console, then it is working. From this point on, you should be able to figure out why it is throwing NullPointerException.

Hope it helps!

Happy coding!

justin z
justin z
11,042 Points

I am able to see the Log.v statement in the console.

My console is leading me to believe the mistake is somewhere in the setFact() method, but I can't figure out why there is an issue since mChuckData isn't null.

aakarshrestha
aakarshrestha
6,509 Points

Get rid of this statement, you are just passing string to the method where string does not have any value in it.

try {
            setFact();
        } catch (JSONException e) {
            e.printStackTrace();
        }

Make your onResponse method like this:

public void onResponse(Call call, final Response response) throws IOException {
   mChuckData = response.body().string();
   JSONObject jsonObject = new JSONObject(mChuckData);
   mChuckFact = jsonObject.getString("value");
}

I see that in your setFact() has no String parameter and you are passing

JSONObject jsonObject = new JSONObject(mChuckData); -> mChuckData, which is empty.

Hope it helps!

Happy Coding!

justin z
justin z
11,042 Points

This fix worked, thank you! But I have one more question: why is mChuckData empty? It's a member variable and doGetRequest() is called before setFact(), so I don't see why it was empty.

your get request is on a different, slower thread.

aakarshrestha
aakarshrestha
6,509 Points

Try this, it should solve your problem:

package justinzwick.chucknorrisfacts;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class FactActivity extends AppCompatActivity {
    private OkHttpClient client = new OkHttpClient();
    public String mChuckData;
    private TextView mFactText;
    private String mChuckFact;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fact);
        mFactText = (TextView) findViewById(R.id.factText);
        try {
            doGetRequest("https://api.chucknorris.io/jokes/random");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void doGetRequest(String url) throws IOException{
        Request request = new Request.Builder()
                .url(url)
                .build();

        client.newCall(request)
                .enqueue(new Callback() {
                    @Override
                    public void onFailure(final Call call, IOException e) {
                        // Error

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                // For the example, you can show an error dialog or a toast
                                // on the main UI thread
                                Toast.makeText(FactActivity.this, "there was an error", Toast.LENGTH_SHORT).show();
                            }
                        });
                    }

                    @Override
                    public void onResponse(Call call, final Response response) throws IOException {
                        mChuckData = response.body().string();
                        JSONObject jsonObject = new JSONObject(mChuckData);
                        mChuckFact = jsonObject.getString("value");
                    }
                });
    }
}

Hope it helps!

Happy Coding!

aakarshrestha
aakarshrestha
6,509 Points

String that you are passing in the JSONObject inside your setFact is empty.

In the onResponse method, you have the value stored in mChuckData but it is not passed to the setFact() method because setFact() method is not taking any argument.

Inside the setFact() method, you passed mChuckData, which is declared on the top of your create method and it is private. You are calling setFact() method separately which does not have any value passed in it.

So if you change your setFact() method to setFact(String mChuckData) and call this method inside your onResponse method where you are storing data coming from the webservice call.

Call it in this way:

 public void onResponse(Call call, final Response response) throws IOException {
     mChuckData = response.body().string();
     setFact(mChuckData);
 }

Change your setFact() method to:

 public void setFact(String mchuckdata) throws JSONException {
        JSONObject jsonObject = new JSONObject(mchuckdata);
        mChuckFact = jsonObject.getString("value");
    }

Hope it make sense to you now.

Happy coding!

justin z
justin z
11,042 Points

Philip Gales, here is the whole error:

06-18 13:50:05.756 11626-11626/justinzwick.chucknorrisfacts E/AndroidRuntime: FATAL EXCEPTION: main Process: justinzwick.chucknorrisfacts, PID: 11626 java.lang.RuntimeException: Unable to start activity ComponentInfo{justinzwick.chucknorrisfacts/justinzwick.chucknorrisfacts.FactActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3254) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3350) at android.app.ActivityThread.access$1100(ActivityThread.java:222) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1795) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:158) at android.app.ActivityThread.main(ActivityThread.java:7229) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference at org.json.JSONTokener.nextCleanInternal(JSONTokener.java:116) at org.json.JSONTokener.nextValue(JSONTokener.java:94) at org.json.JSONObject.<init>(JSONObject.java:156) at org.json.JSONObject.<init>(JSONObject.java:173) at justinzwick.chucknorrisfacts.FactActivity.setFact(FactActivity.java:73) at justinzwick.chucknorrisfacts.FactActivity.onCreate(FactActivity.java:36) at android.app.Activity.performCreate(Activity.java:6876) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1135) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3207) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3350)  at android.app.ActivityThread.access$1100(ActivityThread.java:222)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1795)  at android.os.Handler.dispatchMessage(Handler.java:102)  at android.os.Looper.loop(Looper.java:158)  at android.app.ActivityThread.main(ActivityThread.java:7229)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

Is your doGetRequest() getting a response, or is it showing your Toast? I am assuming it either is not getting a response or, because it is on a slower thread that the setFact() is beating it.

Try moving your setFact to your response block of code.

        try {
            setFact();
        } catch (JSONException e) {
            e.printStackTrace();
        }
justin z
justin z
11,042 Points

The app would crash as soon as I would open it, so I would never see the toast. I made the fix that Aakar suggested and now the app works fine, but I'm not completely sure why it works now. Do you know why the mChuckData that I was passing into the JSONObject was null? I don't see how it could be null since I called doGetRequest() before setFact().

It is null, because even though you are calling the doGetRequest() first, it is on a different thread and takes much longer to finish. While it is running on that thread, the main thread has already called setFact(). In the videos they explain threads and how the main thread is faster than the other threads.

justin z
justin z
11,042 Points

Ok, thanks! I will watch the treehouse lessons on threads so I can understand this further.