Просмотр исходного кода

Merge remote-tracking branch 'origin/master'

kunfei 4 лет назад
Родитель
Сommit
1019739ec2

+ 50 - 71
app/src/main/java/io/legado/app/help/http/cronet/CronetRequestCallback.kt → app/src/main/java/io/legado/app/help/http/cronet/AbsCallBack.kt

@@ -1,6 +1,5 @@
 package io.legado.app.help.http.cronet
 
-import android.os.ConditionVariable
 import io.legado.app.help.http.okHttpClient
 import io.legado.app.utils.DebugLog
 import okhttp3.*
@@ -11,46 +10,28 @@ import okio.Buffer
 import org.chromium.net.CronetException
 import org.chromium.net.UrlRequest
 import org.chromium.net.UrlResponseInfo
-
 import java.io.IOException
 import java.nio.ByteBuffer
 import java.util.*
 
-class CronetRequestCallback @JvmOverloads internal constructor(
-    private val originalRequest: Request,
-    private val mCall: Call,
-    eventListener: EventListener? = null,
-    responseCallback: Callback? = null
+abstract class AbsCallBack(
+    val originalRequest: Request,
+    val mCall: Call,
+    private val eventListener: EventListener? = null,
+    private val responseCallback: Callback? = null
+
 ) : UrlRequest.Callback() {
 
-    private val eventListener: EventListener?
-    private val responseCallback: Callback?
+    val buffer = Buffer()
+
+    var mResponse: Response
+
+    var mException: IOException? = null
     private var followCount = 0
-    private var mResponse: Response
-    private var mException: IOException? = null
-    private val mResponseCondition = ConditionVariable()
-    private val mBuffer = Buffer()
-
-    @Throws(IOException::class)
-    fun waitForDone(urlRequest: UrlRequest): Response {
-        //获取okhttp call的完整请求的超时时间
-        val timeOutMs: Long = mCall.timeout().timeoutNanos() / 1000000
-        if (timeOutMs > 0) {
-            mResponseCondition.block(timeOutMs)
-        } else {
-            mResponseCondition.block()
-        }
-        //ConditionVariable 正常open或者超时open后,检查urlRequest是否完成
-        if (!urlRequest.isDone) {
-            urlRequest.cancel()
-            mException = IOException("Cronet timeout after wait " + timeOutMs + "ms")
-        }
 
-        if (mException != null) {
-            throw mException as IOException
-        }
-        return this.mResponse
-    }
+
+    abstract fun waitForDone(urlRequest: UrlRequest): Response
+
 
     override fun onRedirectReceived(
         request: UrlRequest,
@@ -59,6 +40,10 @@ class CronetRequestCallback @JvmOverloads internal constructor(
     ) {
         if (followCount > MAX_FOLLOW_COUNT) {
             request.cancel()
+            mException = IOException("Too many redirect")
+        }
+        if (mCall.isCanceled()) {
+            mException = IOException("Request Canceled")
         }
         followCount += 1
         val client = okHttpClient
@@ -66,26 +51,18 @@ class CronetRequestCallback @JvmOverloads internal constructor(
             request.followRedirect()
         } else if (!originalRequest.url.isHttps && newLocationUrl.startsWith("https://") && client.followSslRedirects) {
             request.followRedirect()
-        } else if (client.followRedirects) {
+        } else if (okHttpClient.followRedirects) {
             request.followRedirect()
         } else {
+            mException = IOException("Too many redirect")
             request.cancel()
         }
     }
 
+
+    //UrlResponseInfo可能为null
     override fun onResponseStarted(request: UrlRequest, info: UrlResponseInfo) {
         this.mResponse = responseFromResponse(this.mResponse, info)
-//        用于调试
-//        val sb: StringBuilder = StringBuilder(info.url).append("\r\n")
-//        sb.append("[Cached:").append(info.wasCached()).append("][StatusCode:")
-//            .append(info.httpStatusCode).append("][StatusText:").append(info.httpStatusText)
-//            .append("][Protocol:").append(info.negotiatedProtocol).append("][ByteCount:")
-//            .append(info.receivedByteCount).append("]\r\n");
-//        val httpHeaders=info.allHeadersAsList
-//        httpHeaders.forEach { h ->
-//            sb.append("[").append(h.key).append("]").append(h.value).append("\r\n");
-//        }
-//        Log.e("Cronet", sb.toString())
         //打印协议,用于调试
         DebugLog.i(javaClass.name, info.negotiatedProtocol)
         if (eventListener != null) {
@@ -95,6 +72,7 @@ class CronetRequestCallback @JvmOverloads internal constructor(
         request.read(ByteBuffer.allocateDirect(32 * 1024))
     }
 
+
     @Throws(Exception::class)
     override fun onReadCompleted(
         request: UrlRequest,
@@ -103,27 +81,34 @@ class CronetRequestCallback @JvmOverloads internal constructor(
     ) {
 
 
+        if (mCall.isCanceled()) {
+            request.cancel()
+            mException = IOException("Request Canceled")
+        }
+
         byteBuffer.flip()
 
         try {
-            mBuffer.write(byteBuffer)
+            buffer.write(byteBuffer)
         } catch (e: IOException) {
             DebugLog.i(javaClass.name, "IOException during ByteBuffer read. Details: ", e)
+            mException = IOException("IOException during ByteBuffer read. Details:", e)
             throw e
         }
         byteBuffer.clear()
         request.read(byteBuffer)
     }
 
+
     override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
         eventListener?.responseBodyEnd(mCall, info.receivedByteCount)
         val contentType: MediaType? = (this.mResponse.header("content-type")
             ?: "text/plain; charset=\"utf-8\"").toMediaTypeOrNull()
         val responseBody: ResponseBody =
-            mBuffer.asResponseBody(contentType)
+            buffer.asResponseBody(contentType)
         val newRequest = originalRequest.newBuilder().url(info.url).build()
         this.mResponse = this.mResponse.newBuilder().body(responseBody).request(newRequest).build()
-        mResponseCondition.open()
+
         eventListener?.callEnd(mCall)
         if (responseCallback != null) {
             try {
@@ -134,28 +119,34 @@ class CronetRequestCallback @JvmOverloads internal constructor(
         }
     }
 
+
     //UrlResponseInfo可能为null
     override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
         DebugLog.i(javaClass.name, error.message.toString())
-        val msg = error.localizedMessage
-        val e = IOException(msg?.substring(msg.indexOf("net::")), error)
-        mException = e
-        mResponseCondition.open()
-
-        this.eventListener?.callFailed(mCall, e)
-        responseCallback?.onFailure(mCall, e)
+        mException = IOException(error.message, error)
+        this.eventListener?.callFailed(mCall, error)
+        responseCallback?.onFailure(mCall, error)
     }
 
-    override fun onCanceled(request: UrlRequest, info: UrlResponseInfo?) {
-        mResponseCondition.open()
+    override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
+        super.onCanceled(request, info)
         this.eventListener?.callEnd(mCall)
+        mException = IOException("Cronet Request Canceled")
+    }
 
 
+    init {
+        mResponse = Response.Builder()
+            .sentRequestAtMillis(System.currentTimeMillis())
+            .request(originalRequest)
+            .protocol(Protocol.HTTP_1_0)
+            .code(0)
+            .message("")
+            .build()
     }
 
     companion object {
-        private const val MAX_FOLLOW_COUNT = 20
-
+        const val MAX_FOLLOW_COUNT = 20
         private fun protocolFromNegotiatedProtocol(responseInfo: UrlResponseInfo): Protocol {
             val negotiatedProtocol = responseInfo.negotiatedProtocol.lowercase(Locale.getDefault())
             return when {
@@ -217,16 +208,4 @@ class CronetRequestCallback @JvmOverloads internal constructor(
                 .build()
         }
     }
-
-    init {
-        this.mResponse = Response.Builder()
-            .sentRequestAtMillis(System.currentTimeMillis())
-            .request(originalRequest)
-            .protocol(Protocol.HTTP_1_0)
-            .code(0)
-            .message("")
-            .build()
-        this.responseCallback = responseCallback
-        this.eventListener = eventListener
-    }
 }

+ 8 - 3
app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt

@@ -1,5 +1,6 @@
 package io.legado.app.help.http.cronet
 
+import android.os.Build
 import io.legado.app.help.http.CookieStore
 import io.legado.app.utils.printOnDebug
 import okhttp3.*
@@ -42,10 +43,14 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
     }
 
     private fun proceedWithCronet(request: Request, call: Call): Response? {
-        val callback = CronetRequestCallback(request, call)
-        buildRequest(request, callback)?.let {
+        val callBack = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            NewCallBack(request, call)
+        } else {
+            OldCallback(request, call)
+        }
+        buildRequest(request, callBack)?.let {
             it.start()
-            return callback.waitForDone(it)
+            return callBack.waitForDone(it)
         }
         return null
     }

+ 1 - 0
app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt

@@ -34,6 +34,7 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
     private var cpuAbi: String? = null
     private var md5: String
     var download = false
+    @Volatile
     private var cacheInstall = false
 
     init {

+ 66 - 0
app/src/main/java/io/legado/app/help/http/cronet/NewCallBack.kt

@@ -0,0 +1,66 @@
+package io.legado.app.help.http.cronet
+
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import okhttp3.Call
+import okhttp3.Request
+import okhttp3.Response
+import org.chromium.net.CronetException
+import org.chromium.net.UrlRequest
+import org.chromium.net.UrlResponseInfo
+import java.io.IOException
+import java.nio.ByteBuffer
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+
+@RequiresApi(api = Build.VERSION_CODES.N)
+class NewCallBack(originalRequest: Request, mCall: Call) : AbsCallBack(originalRequest, mCall) {
+    private val responseFuture = CompletableFuture<Response>()
+
+
+    @Throws(IOException::class)
+    override fun waitForDone(urlRequest: UrlRequest): Response {
+        return responseFuture.get(mCall.timeout().timeoutNanos(), TimeUnit.NANOSECONDS)
+    }
+
+    override fun onRedirectReceived(
+        request: UrlRequest,
+        info: UrlResponseInfo,
+        newLocationUrl: String
+    ) {
+        super.onRedirectReceived(request, info, newLocationUrl)
+        if (mException != null) {
+            responseFuture.completeExceptionally(mException)
+        }
+    }
+
+
+    override fun onReadCompleted(
+        request: UrlRequest,
+        info: UrlResponseInfo,
+        byteBuffer: ByteBuffer
+    ) {
+        super.onReadCompleted(request, info, byteBuffer)
+        if (mException != null) {
+            responseFuture.completeExceptionally(mException)
+        }
+    }
+
+    override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
+        super.onSucceeded(request, info)
+        responseFuture.complete(mResponse)
+    }
+
+    override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
+        super.onFailed(request, info, error)
+        responseFuture.completeExceptionally(mException)
+    }
+
+    override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
+        super.onCanceled(request, info)
+        responseFuture.completeExceptionally(mException)
+    }
+
+
+}

+ 66 - 0
app/src/main/java/io/legado/app/help/http/cronet/OldCallback.kt

@@ -0,0 +1,66 @@
+package io.legado.app.help.http.cronet
+
+import android.os.ConditionVariable
+import okhttp3.Call
+import okhttp3.Request
+import okhttp3.Response
+import org.chromium.net.CronetException
+import org.chromium.net.UrlRequest
+import org.chromium.net.UrlResponseInfo
+import java.io.IOException
+import java.nio.ByteBuffer
+
+class OldCallback(originalRequest: Request, mCall: Call) : AbsCallBack(originalRequest, mCall) {
+
+    private val mResponseCondition = ConditionVariable()
+
+    @Throws(IOException::class)
+    override fun waitForDone(urlRequest: UrlRequest): Response {
+        //获取okhttp call的完整请求的超时时间
+        val timeOutMs: Long = mCall.timeout().timeoutNanos() / 1000000
+        if (timeOutMs > 0) {
+            mResponseCondition.block(timeOutMs)
+        } else {
+            mResponseCondition.block()
+        }
+        //ConditionVariable 正常open或者超时open后,检查urlRequest是否完成
+        if (!urlRequest.isDone) {
+            urlRequest.cancel()
+            mException = IOException("Cronet timeout after wait " + timeOutMs + "ms")
+        }
+
+        if (mException != null) {
+            throw mException as IOException
+        }
+        return this.mResponse
+    }
+
+
+    override fun onReadCompleted(
+        request: UrlRequest,
+        info: UrlResponseInfo,
+        byteBuffer: ByteBuffer
+    ) {
+        super.onReadCompleted(request, info, byteBuffer)
+        if (mException != null) {
+            mResponseCondition.open()
+        }
+    }
+
+    override fun onSucceeded(request: UrlRequest, info: UrlResponseInfo) {
+        super.onSucceeded(request, info)
+        mResponseCondition.open()
+    }
+
+    override fun onFailed(request: UrlRequest, info: UrlResponseInfo?, error: CronetException) {
+        super.onFailed(request, info, error)
+        mResponseCondition.open()
+    }
+
+
+    override fun onCanceled(request: UrlRequest?, info: UrlResponseInfo?) {
+        super.onCanceled(request, info)
+        mResponseCondition.open()
+    }
+
+}

+ 1 - 1
build.gradle

@@ -16,7 +16,7 @@ buildscript {
         classpath 'com.android.tools.build:gradle:7.1.2'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.4'
-        classpath 'de.undercouch:gradle-download-task:4.1.2'
+        classpath 'de.undercouch:gradle-download-task:5.0.1'
         classpath 'com.google.gms:google-services:4.3.10'
     }
 }