问题
I've got an Android Daydream that displays a stream of tweets using Twitter4j's streaming implementation. This works fine on Android 4.2 and 4.3. On 4.4 however, I cannot quickly close the stream (in onDreamingStopped
).
I'm getting this stacktrace but the NetworkOnMainThreadException
isn't the problem.
The problem appears to be related to this issue, around connection reuse. This OkHttp changeset (which gets merged into Android here) changes the way that close
behaves on ChunkedInputStream
. Instead of simply marking itself as "closed" and then disconnecting the socket if there was still more data to be read, it now attempts to discard the stream first to enable quick reuse of the socket. If it fails to discard the stream, it then disconnects the socket as before.
The reason I'm now getting a NetworkOnMainThreadException
is because (as you see from the stack trace) discarding the stream now attempts to read from the stream. That's easy enough to fix - I just drop it in an AsyncTask
when closing my Daydream and forget about it.
The problem is that the stream isn't being discarded within the timeout set. Looking at the HttpTransport#discardStream method of the latest version of the source, it specifies a 100ms timeout on the socket (the original commit specified 30ms), then tries to read from the stream (Util.skipAll
) to empty the buffer. However I'm seeing a multiple-second delay around the BufferedInputStream.read() call. The length of this delay seems to vary.
This isn't a huge problem - since I now have to close this stream off the UI thread anyway, I'm not causing the onDreamingStopped
call to take a long time to return (which was causing the daydream to stay on the screen for a long time after pressing back/home - my initial bug report which caused me to follow this rabbit hole). However, it does leave this connection hanging around for a while after it's supposed to be closed.
I've tested how long it takes to close the stream with two Twitter accounts of varying activity levels. The first one didn't see any activity in the time I was trying to close the stream, and I was consistently seeing the call take around 30 seconds. The second account has much more activity on it, and the time to close the stream was much more varied on this one - anywhere from 1.5 to 30 seconds. It appears to close instantly when a new tweet comes in (a new chunk is written to the stream).
Why am I seeing this delay in closing a stream on KitKat? Why does it not respect the 100ms timeout that's being set?
This is similar to Android KitKat HttpURLConnection disconnect AsyncTask - although that's presumably using a FixedLengthInputStream
under the hood, the same change has been applied to the close
method of that class.
回答1:
This was a bug in OkHttp. Fix is here. If you don't mind including the OkHttp jar in your application, you can work-around this problem until AOSP is updated to include the fix.
OkHttpClient okHttpClient = new OkHttpClient();
URL.setURLStreamHandlerFactory(okHttpClient);
The fix was not merged in time for OkHttp 1.3; you'll need to wait for a later release or build the jar
yourself.
来源:https://stackoverflow.com/questions/20306498/unable-to-close-chunkedinputstream-quickly-on-android-4-4-kitkat