22

I'm using the OkHttp library in my android app to make web requests to a weather API. I've already implemented my code and I'm getting a FATAL EXCEPTION when doing the request.

I've already added INTERNET permissions in my manifest too.

MainActivity.java:

private CurrentWeather currentWeather;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final ActivityMainBinding binding = DataBindingUtil.setContentView(MainActivity.this, R.layout.activity_main);

        String apiKey = "xxx";
        double latitude = 37.8267;
        double longitude = -122.4233;
        String forecastURL = String.format("https://api.darksky.net/forecast/%s/%f,%f", apiKey, latitude, longitude);

        if (isNetworkAvailable()) {
            OkHttpClient client = new OkHttpClient();

            Request request = new Request.Builder()
                    .url(forecastURL)
                    .build();

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

                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    try {
                        Log.v(TAG, response.body().string());
                        String jsonData = response.body().string();
                        if (response.isSuccessful()) {
                            currentWeather = getCurrentDetails(jsonData);
                        }

                    } catch (IOException e) {
                        Log.e(TAG, e.getLocalizedMessage());
                    } catch (JSONException e) {
                        Log.e(TAG, e.getLocalizedMessage());
                    }
                }
            });
        }
        Log.d(TAG, "Main UI code is running");
    }

    private CurrentWeather getCurrentDetails(String jsonData) throws JSONException {

        JSONObject forecast = new JSONObject(jsonData);
        String timezone = forecast.getString("timezone");

        JSONObject currently = forecast.getJSONObject("currently");

        String icon = currently.getString("icon");
        String locationLabel = "Alcatraz Island";
        String summary = currently.getString("summary");
        long time = currently.getLong("time");
        double humidity = currently.getDouble("humidity");
        double precipProbability = currently.getDouble("precipProbability");
        double temperature = currently.getDouble("temperature");

        return new CurrentWeather(locationLabel, icon, time, temperature, humidity, precipProbability, summary, timezone);
    }

Gradle:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.squareup.okhttp3:okhttp:3.12.0'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

Then, here's the exception I'm getting:

2018-12-04 20:55:49.969 3314-3330/com.test.starmie E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: com.test.starmie, PID: 3314
    java.lang.IllegalStateException: closed
        at okio.RealBufferedSource.rangeEquals(RealBufferedSource.java:407)
        at okio.RealBufferedSource.rangeEquals(RealBufferedSource.java:401)
        at okhttp3.internal.Util.bomAwareCharset(Util.java:471)
        at okhttp3.ResponseBody.string(ResponseBody.java:175)
        at com.test.starmie.MainActivity$1.onResponse(MainActivity.java:66)
        at okhttp3.RealCall$AsyncCall.execute(RealCall.java:206)
        at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
        at java.lang.Thread.run(Thread.java:761)

I don't know what to do at this point. I've read around and found a few posts regarding this topic. From what I've gathered, UI changes must be made in the runOnUiThread() block. But I'm not making any UI changes here in my code and I still get the exception.

I've also already tried putting my JSON parsing code in the runOnUiThread() and got the same FATAL EXCEPTION result. Anyone got any ideas?

fishhau
  • 459
  • 1
  • 3
  • 15

7 Answers7

45

Response body can be consumed only once. You make it twice

Log.v(TAG, response.body().string());
String jsonData = response.body().string();

More info in docs

Stanislav Bondar
  • 6,056
  • 2
  • 34
  • 46
11

This is caused by using different versions of OkHttp. Ensure all your dependencies from com.squareup.okhttp3 are using the same version.

example:

implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
implementation 'com.squareup.okhttp3:okhttp:3.8.0'
Houssin Boulla
  • 2,687
  • 1
  • 16
  • 22
10

I had the same issue and got it resolved by switching to Java 8 compatibility. Add compileOptions under your build.gradle file.

android {

    ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}
Dejan Atanasov
  • 1,202
  • 10
  • 13
5

I tried all answers, even migrate okhttp from 3.14.9 to 4.4.0, but none worked for my case: I had a response interceptor inherited from Interceptor,

class ResponseInterceptor : Interceptor {
    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val response = chain.proceed(request)
        if (!isSuccessful(response.code)) {
            handleErrorResponse(response)
        }
        return response
    }

    private fun handleErrorResponse(response: Response) {
        throw ApiException(response.body?.string()) // only called one time!
    }
}

I finally fixed it by change

data class ApiException(val errorMessage: String?) : Exception(errorMessage)
// `Exception` is `typealias Exception = java.lang.Exception`

to

import java.io.IOException
data class ApiException(val errorMessage: String?) : IOException(errorMessage)

Really don't know why java.lang.Exception caused such issue.

Weiyi
  • 1,843
  • 2
  • 22
  • 34
  • Thank you, this is what it got it fixed for me. I used RuntimeException but the error was the same until I changed to IOException. Strange... – kodartcha May 02 '23 at 11:36
5

in my case, i already called response.body.string() once but still got the crash. got it fixed with

ResponseBody responseBodyCopy = response.peekBody(Long.MAX_VALUE);
responseBodyCopy.string();

from this comment on github : https://github.com/square/okhttp/issues/1240#issuecomment-330813274

icgoogo
  • 401
  • 5
  • 7
1

Assign the response to a variable then you will be able to reuse it

0
dependencies {
 implementation "com.squareup.okhttp3:okhttp:4.2.1"
 compile "com.squareup.okhttp3:logging-interceptor:4.2.1"
 compile "com.squareup.okhttp3:okhttp-urlconnection:4.2.1"
}
Leonardo Alves Machado
  • 2,747
  • 10
  • 38
  • 53
GUGAN RAJ
  • 101
  • 7