okhttp使用错误引发的一例crash

class NetMainActivity : AppCompatActivity() {
    var TAG = "NetMainActivity"

    var client :OkHttpClient  = OkHttpClient()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_net_main)
        btn1.setOnClickListener {
            testGet()
        }
    }

    fun testGet() {
        var request = Request.Builder().url("http://www.baidu.com").build()
        thread {
            //错误写法
            Log.e(TAG, "testGet: Threadname1=${Thread.currentThread().name}")
            var response = client.newCall(request).execute()
            textView.post {
                Log.e(TAG, "testGet: Threadname2=${Thread.currentThread().name}")
                textView.text = response.body?.string()
            }

            //正确写法
            Log.e(TAG, "testGet: Threadname1=${Thread.currentThread().name}")
            var response = client.newCall(request).execute()
            var str = response.body?.string()
            textView.post {
                Log.e(TAG, "testGet: Threadname2=${Thread.currentThread().name}")
                textView.text = str
            }
        }
    }
}

        上面是简单使用okHttp访问网络获取返回值,然后把返回结果文本展示到textview中。

        很简单的代码,最开始写的是上面错误代码,安装app后,有时候运行能访问网络并成功展示,有时候多点几下就出现crash,并提示android.os.NetworkOnMainThreadException异常。异常堆栈如下:

2022-07-21 17:36:36.260 8066-8066/com.example.helloworld E/NetMainActivity: testGet: Threadname2=main
2022-07-21 17:36:36.280 8066-8066/com.example.helloworld E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.helloworld, PID: 8066
    android.os.NetworkOnMainThreadException
        at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1513)
        at java.net.SocketInputStream.read(SocketInputStream.java:175)
        at java.net.SocketInputStream.read(SocketInputStream.java:144)
        at okio.InputStreamSource.read(JvmOkio.kt:90)
        at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:129)
        at okio.RealBufferedSource.request(RealBufferedSource.kt:206)
        at okio.RealBufferedSource.require(RealBufferedSource.kt:199)
        at okio.RealBufferedSource.readHexadecimalUnsignedLong(RealBufferedSource.kt:381)
        at okhttp3.internal.http1.Http1ExchangeCodec$ChunkedSource.readChunkSize(Http1ExchangeCodec.kt:429)
        at okhttp3.internal.http1.Http1ExchangeCodec$ChunkedSource.read(Http1ExchangeCodec.kt:408)
        at okhttp3.internal.connection.Exchange$ResponseBodySource.read(Exchange.kt:276)
        at okio.RealBufferedSource.read(RealBufferedSource.kt:189)
        at okio.RealBufferedSource.exhausted(RealBufferedSource.kt:197)
        at okio.GzipSource.read(GzipSource.kt:88)
        at okio.Buffer.writeAll(Buffer.kt:1642)
        at okio.RealBufferedSource.readString(RealBufferedSource.kt:95)
        at okhttp3.ResponseBody.string(ResponseBody.kt:187)
        at com.example.helloworld.activity.net.NetMainActivity$testGet$1.invoke$lambda-0(NetMainActivity.kt:34)
        at com.example.helloworld.activity.net.NetMainActivity$testGet$1.lambda$o7kqc81vUHfiH5ZjT9CxIqj9Fhs(Unknown Source:0)
        at com.example.helloworld.activity.net.-$$Lambda$NetMainActivity$testGet$1$o7kqc81vUHfiH5ZjT9CxIqj9Fhs.run(Unknown Source:4)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:201)
        at android.app.ActivityThread.main(ActivityThread.java:6820)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:922)
    	Suppressed: android.os.NetworkOnMainThreadException
        at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1513)
        at java.net.SocketInputStream.read(SocketInputStream.java:175)
        at java.net.SocketInputStream.read(SocketInputStream.java:144)
        at okio.InputStreamSource.read(JvmOkio.kt:90)
        at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:129)
        at okio.RealBufferedSource.indexOf(RealBufferedSource.kt:427)
        at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.kt:320)
        at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.kt:105)
        at okhttp3.internal.http1.Http1ExchangeCodec$ChunkedSource.readChunkSize(Http1ExchangeCodec.kt:426)
        at okhttp3.internal.http1.Http1ExchangeCodec$ChunkedSource.read(Http1ExchangeCodec.kt:408)
        at okhttp3.internal.Util.skipAll(Util.kt:337)
        at okhttp3.internal.Util.discard(Util.kt:358)
        at okhttp3.internal.http1.Http1ExchangeCodec$ChunkedSource.close(Http1ExchangeCodec.kt:450)
        at okio.ForwardingSource.close(ForwardingSource.kt:34)
        at okhttp3.internal.connection.Exchange$ResponseBodySource.close(Exchange.kt:309)
        at okio.RealBufferedSource.close(RealBufferedSource.kt:477)
        at okio.RealBufferedSource.close(RealBufferedSource.kt:477)
        at okio.InflaterSource.close(InflaterSource.kt:136)
        at okio.GzipSource.close(GzipSource.kt:171)
        at okio.RealBufferedSource.close(RealBufferedSource.kt:477)
        at kotlin.io.CloseableKt.closeFinally(Closeable.kt:60)
        at okhttp3.ResponseBody.string(ResponseBody.kt:186)
        		... 10 more
2022-07-21 17:36:36.343 1030-1116/? E/InputDispatcher: channel 'b58ae29 com.example.helloworld/com.example.helloworld.activity.net.NetMainActivity (server)' ~ Channel is unrecoverably broken and will be disposed!

NetworkOnMainThreadException异常代表有在主线程执行网络请求相关操作。可是

var response = client.newCall(request).execute()

网络请求的这部分代码明明是在子线程执行的啊。

接着我在代码中打印了两行日志。日志内容为

2022-07-21 17:36:36.122 8066-8111/com.example.helloworld E/NetMainActivity: testGet: Threadname1=Thread-2
2022-07-21 17:36:36.260 8066-8066/com.example.helloworld E/NetMainActivity: testGet: Threadname2=main
2022-07-21 17:36:36.280 8066-8066/com.example.helloworld E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.helloworld, PID: 8066
    android.os.NetworkOnMainThreadException
        at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1513)
...

可以看到client的excute()是在子线程执行的。并且当我们获取到response对象,把response的body展示到textview上是在主线程执行的,这一点从log和代码都能看出来。

        那究竟是哪里的网络操作在主线程中操作了呢?

        我尝试了下先在子线程中把response.body.string()字符串先在子线程中获取到了,再在主线程中设置textview。就是上面正确的代码。

        结果我发现怎么点击都能正常访问网络并成功展示了。那看来response.body.string()中应该是有一些网络操作了。之前以为只要拿到Response对象就以为已经执行完网络操作返回结果了,看来是自己天真了。

        来看Response#string()的源码,里面有一句话特别重要:This method loads entire response body into memory.If the response body is very large thismay trigger an [OutOfMemoryError]意思是这个函数是用来加载整个response结果到内存中,response结果可能会很大,所以重点来啦,这是一个耗时动作!!!耗时动作是不能在主线程中执行的,所以我们上面在主线程中执行string()引发了crash。

/**
 * Returns the response as a string.
 *
 * If the response starts with a
 * [Byte Order Mark (BOM)](https://en.wikipedia.org/wiki/Byte_order_mark), it is consumed and
 * used to determine the charset of the response bytes.
 *
 * Otherwise if the response has a `Content-Type` header that specifies a charset, that is used
 * to determine the charset of the response bytes.
 *
 * Otherwise the response bytes are decoded as UTF-8.
 *
 * This method loads entire response body into memory. If the response body is very large this
 * may trigger an [OutOfMemoryError]. Prefer to stream the response body if this is a
 * possibility for your response.
 */
@Throws(IOException::class)
fun string(): String = source().use { source ->
  source.readString(charset = source.readBomAsCharset(charset()))
}

        response.body.string()具体怎样引发的crash后面有时间再分析吧!!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值