Merge pull request #1276 from ag2s20150909/master

使用Gradle下载Cronet,自动计算md5
pull/1279/head^2
kunfei 3 years ago committed by GitHub
commit 1518aa3fc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/.gitignore
  2. 4
      app/build.gradle
  3. BIN
      app/cronetlib/src/cronet_api-src.jar
  4. BIN
      app/cronetlib/src/cronet_impl_common_java-src.jar
  5. BIN
      app/cronetlib/src/cronet_impl_native_java-src.jar
  6. BIN
      app/cronetlib/src/cronet_impl_platform_java-src.jar
  7. 110
      app/download.gradle
  8. 1
      app/src/main/assets/cronet.json
  9. 17
      app/src/main/java/io/legado/app/help/http/cronet/CronetHelper.kt
  10. 3
      app/src/main/java/io/legado/app/help/http/cronet/CronetInterceptor.kt
  11. 89
      app/src/main/java/io/legado/app/help/http/cronet/CronetLoader.kt
  12. 1
      build.gradle
  13. 2
      gradle.properties

1
app/.gitignore vendored

@ -1 +1,2 @@
/build /build
/so

@ -3,6 +3,7 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-parcelize' apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'de.timfreiheit.resourceplaceholders' apply plugin: 'de.timfreiheit.resourceplaceholders'
apply from:'download.gradle'
static def releaseTime() { static def releaseTime() {
return new Date().format("yy.MMddHH", TimeZone.getTimeZone("GMT+8")) return new Date().format("yy.MMddHH", TimeZone.getTimeZone("GMT+8"))
@ -55,14 +56,17 @@ android {
} }
buildTypes { buildTypes {
release { release {
buildConfigField "String", "Cronet_Version", "\"$CronetVersion\""
if (project.hasProperty("RELEASE_STORE_FILE")) { if (project.hasProperty("RELEASE_STORE_FILE")) {
signingConfig signingConfigs.myConfig signingConfig signingConfigs.myConfig
} }
applicationIdSuffix '.release' applicationIdSuffix '.release'
minifyEnabled false minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
debug { debug {
buildConfigField "String", "Cronet_Version", "\"$CronetVersion\""
if (project.hasProperty("RELEASE_STORE_FILE")) { if (project.hasProperty("RELEASE_STORE_FILE")) {
signingConfig signingConfigs.myConfig signingConfig signingConfigs.myConfig
} }

@ -0,0 +1,110 @@
import java.security.MessageDigest
apply plugin: 'de.undercouch.download'
def BASE_PATH = "https://storage.googleapis.com/chromium-cronet/android/" + CronetVersion + "/Release/cronet/"
def assetsDir = projectDir.toString() + "/src/main/assets"
def libPath = projectDir.toString() + "/cronetlib"
def soPath = projectDir.toString() + "/so"
/**
* MD5
* @param file
* @return
*/
static def generateMD5(final file) {
MessageDigest digest = MessageDigest.getInstance("MD5")
file.withInputStream() { is ->
byte[] buffer = new byte[1024]
int numRead = 0
while ((numRead = is.read(buffer)) > 0) {
digest.update(buffer, 0, numRead)
}
}
return String.format("%032x", new BigInteger(1, digest.digest())).toLowerCase()
}
/**
* Cronet相关的jar
*/
task downloadJar(type: Download) {
src([
BASE_PATH + "cronet_api.jar",
BASE_PATH + "cronet_impl_common_java.jar",
BASE_PATH + "cronet_impl_native_java.jar",
BASE_PATH + "cronet_impl_platform_java.jar",
])
dest libPath
overwrite true
onlyIfModified true
}
/**
* Cronet的arm64-v8a so
*/
task downloadARM64(type: Download) {
src BASE_PATH + "libs/arm64-v8a/libcronet." + CronetVersion + ".so"
dest soPath + "/arm64-v8a.so"
overwrite true
onlyIfModified true
}
/**
* Cronet的armeabi-v7a so
*/
task downloadARMv7(type: Download) {
src BASE_PATH + "libs/armeabi-v7a/libcronet." + CronetVersion + ".so"
dest soPath + "/armeabi-v7a.so"
overwrite true
onlyIfModified true
}
/**
* Cronet的x86_64 so
*/
task downloadX86_64(type: Download) {
src BASE_PATH + "libs/x86_64/libcronet." + CronetVersion + ".so"
dest soPath + "/x86_64.so"
overwrite true
onlyIfModified true
}
/**
* Cronet的x86 so
*/
task downloadX86(type: Download) {
src BASE_PATH + "libs/x86/libcronet." + CronetVersion + ".so"
dest soPath + "/x86.so"
overwrite true
onlyIfModified true
}
/**
* Cronet版本时执行这个task
* gradle.properties
* gradlew app:downloadCronet
*/
task downloadCronet() {
dependsOn downloadJar, downloadARM64, downloadARMv7, downloadX86_64, downloadX86
doLast {
StringBuilder sb = new StringBuilder("{")
def files = new File(soPath).listFiles()
for (File file : files) {
println file.name.replace(".so", "")
sb.append("\"").append(file.name.replace(".so", "")).append("\":\"").append(generateMD5(file)).append("\",")
}
sb.append("\"version\":\"").append(CronetVersion).append("\"}")
println sb.toString()
println assetsDir
def f1 = new File(assetsDir + "/cronet.json")
if (!f1.exists()) {
f1.parentFile.mkdirs()
f1.createNewFile()
}
f1.text = sb.toString()
}
}

@ -0,0 +1 @@
{"arm64-v8a":"1f4e088f6e00175e12ee153e4004d283","armeabi-v7a":"a6726219c9a6217b95763baa3d61eb18","x86":"73b6a220fe16e0cdeebf1094980825c2","x86_64":"12fd5bb0a12664294b64fffccd326347","version":"92.0.4515.159"}

@ -1,6 +1,7 @@
package io.legado.app.help.http.cronet package io.legado.app.help.http.cronet
import android.util.Log import android.util.Log
import com.google.android.gms.net.CronetProviderInstaller
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import okhttp3.Headers import okhttp3.Headers
import okhttp3.MediaType import okhttp3.MediaType
@ -18,22 +19,24 @@ import java.util.concurrent.Executors
val executor: Executor by lazy { Executors.newCachedThreadPool() } val executor: Executor by lazy { Executors.newCachedThreadPool() }
val cronetEngine: ExperimentalCronetEngine by lazy { val cronetEngine: ExperimentalCronetEngine by lazy {
CronetLoader.preDownload() if (AppConfig.isGooglePlay) {
CronetProviderInstaller.installProvider(appCtx)
} else {
CronetLoader.preDownload()
}
val builder = ExperimentalCronetEngine.Builder(appCtx).apply { val builder = ExperimentalCronetEngine.Builder(appCtx).apply {
if (!AppConfig.isGooglePlay) { if (!AppConfig.isGooglePlay&&CronetLoader.install()) {
setLibraryLoader(CronetLoader)//设置自定义so库加载 setLibraryLoader(CronetLoader)//设置自定义so库加载
} }
setStoragePath(appCtx.externalCacheDir?.absolutePath)//设置缓存路径 setStoragePath(appCtx.externalCacheDir?.absolutePath)//设置缓存路径
enableHttpCache(HTTP_CACHE_DISK, (1024 * 1024 * 50))//设置缓存模式 enableHttpCache(HTTP_CACHE_DISK, (1024 * 1024 * 50).toLong())//设置缓存模式
enableQuic(true)//设置支持http/3 enableQuic(true)//设置支持http/3
enableHttp2(true) //设置支持http/2 enableHttp2(true) //设置支持http/2
enablePublicKeyPinningBypassForLocalTrustAnchors(true) enablePublicKeyPinningBypassForLocalTrustAnchors(true)
//enableNetworkQualityEstimator(true)
//Brotli压缩 enableBrotli(true)//Brotli压缩
enableBrotli(true)
//setExperimentalOptions("{\"quic_version\": \"h3-29\"}")
} }
val engine = builder.build() val engine = builder.build()
Log.d("Cronet", "Cronet Version:" + engine.versionString) Log.d("Cronet", "Cronet Version:" + engine.versionString)

@ -15,6 +15,7 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
} else try { } else try {
//移除Keep-Alive,手动设置会导致400 BadRequest //移除Keep-Alive,手动设置会导致400 BadRequest
builder.removeHeader("Keep-Alive") builder.removeHeader("Keep-Alive")
builder.removeHeader("Accept-Encoding")
val cookieStr = getCookie(original.url) val cookieStr = getCookie(original.url)
//设置Cookie //设置Cookie
if (cookieStr.length > 3) { if (cookieStr.length > 3) {
@ -44,7 +45,7 @@ class CronetInterceptor(private val cookieJar: CookieJar?) : Interceptor {
val callback = CronetUrlRequestCallback(request, call) val callback = CronetUrlRequestCallback(request, call)
val urlRequest = buildRequest(request, callback) val urlRequest = buildRequest(request, callback)
urlRequest.start() urlRequest.start()
return callback.waitForDone(urlRequest) return callback.waitForDone(urlRequest)
} }
private fun getCookie(url: HttpUrl): String { private fun getCookie(url: HttpUrl): String {

@ -6,11 +6,12 @@ import android.content.pm.ApplicationInfo
import android.os.Build import android.os.Build
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import com.google.android.gms.net.CronetProviderInstaller
import io.legado.app.BuildConfig
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.utils.getPrefString
import io.legado.app.utils.putPrefString
import org.chromium.net.CronetEngine import org.chromium.net.CronetEngine
import org.json.JSONObject
import splitties.init.appCtx import splitties.init.appCtx
import java.io.* import java.io.*
import java.math.BigInteger import java.math.BigInteger
@ -19,28 +20,25 @@ import java.net.URL
import java.security.MessageDigest import java.security.MessageDigest
import java.util.* import java.util.*
object CronetLoader : CronetEngine.Builder.LibraryLoader() { object CronetLoader : CronetEngine.Builder.LibraryLoader() {
//https://storage.googleapis.com/chromium-cronet/android/92.0.4515.127/Release/cronet/libs/arm64-v8a/libcronet.92.0.4515.127.so //https://storage.googleapis.com/chromium-cronet/android/92.0.4515.127/Release/cronet/libs/arm64-v8a/libcronet.92.0.4515.159.so
//https://cdn.jsdelivr.net/gh/ag2s20150909/cronet-repo@92.0.4515.127/cronet/92.0.4515.127/arm64-v8a/libcronet.92.0.4515.127.so.js //https://cdn.jsdelivr.net/gh/ag2s20150909/cronet-repo@92.0.4515.127/cronet/92.0.4515.127/arm64-v8a/libcronet.92.0.4515.159.so.js
private const val TAG = "CronetLoader" private const val TAG = "CronetLoader"
private const val soVersion = "92.0.4515.159" private const val soVersion = BuildConfig.Cronet_Version
private const val soName = "libcronet.$soVersion.so" private const val soName = "libcronet.$soVersion.so"
private val soUrl: String private val soUrl: String
private val md5Url: String
private val soFile: File private val soFile: File
private val downloadFile: File private val downloadFile: File
private var cpuAbi: String? = null private var cpuAbi: String? = null
private var md5: String? = appCtx.getPrefString("soMd5") private var md5: String
private val version: String? = appCtx.getPrefString("soVersion", soVersion)
var download = false var download = false
init { init {
soUrl = ("https://storage.googleapis.com/chromium-cronet/android/" soUrl = ("https://storage.googleapis.com/chromium-cronet/android/"
+ soVersion + "/Release/cronet/libs/" + soVersion + "/Release/cronet/libs/"
+ getCpuAbi(appCtx) + "/" + soName) + getCpuAbi(appCtx) + "/" + soName)
md5Url = ("https://cdn.jsdelivr.net/gh/ag2s20150909/cronet-repo@" + md5 = getMd5(appCtx)
soVersion + "/cronet/" + soVersion + "/"
+ getCpuAbi(appCtx) + "/" + soName + ".js")
val dir = appCtx.getDir("cronet", Context.MODE_PRIVATE) val dir = appCtx.getDir("cronet", Context.MODE_PRIVATE)
soFile = File(dir.toString() + "/" + getCpuAbi(appCtx), soName) soFile = File(dir.toString() + "/" + getCpuAbi(appCtx), soName)
downloadFile = File(appCtx.cacheDir.toString() + "/so_download", soName) downloadFile = File(appCtx.cacheDir.toString() + "/so_download", soName)
@ -54,13 +52,20 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
if (AppConfig.isGooglePlay) { if (AppConfig.isGooglePlay) {
return true return true
} }
if (md5.length != 32 || !soFile.exists() || md5 != getFileMD5(soFile)) {
return false
}
return soFile.exists() return soFile.exists()
} }
fun preDownload() { fun preDownload() {
if (AppConfig.isGooglePlay) return if (AppConfig.isGooglePlay) {
CronetProviderInstaller.installProvider(appCtx)
return
}
Coroutine.async { Coroutine.async {
md5 = getUrlMd5(md5Url) //md5 = getUrlMd5(md5Url)
if (soFile.exists() && md5 == getFileMD5(soFile)) { if (soFile.exists() && md5 == getFileMD5(soFile)) {
Log.e(TAG, "So 库已存在") Log.e(TAG, "So 库已存在")
} else { } else {
@ -70,6 +75,27 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
} }
} }
private fun getMd5(context: Context): String {
val stringBuilder = StringBuilder()
return try {
//获取assets资源管理器
val assetManager = context.assets
//通过管理器打开文件并读取
val bf = BufferedReader(
InputStreamReader(
assetManager.open("cronet.json")
)
)
var line: String?
while (bf.readLine().also { line = it } != null) {
stringBuilder.append(line)
}
JSONObject(stringBuilder.toString()).optString(getCpuAbi(context), "")
} catch (e: java.lang.Exception) {
return ""
}
}
@SuppressLint("UnsafeDynamicallyLoadedCode") @SuppressLint("UnsafeDynamicallyLoadedCode")
override fun loadLibrary(libName: String) { override fun loadLibrary(libName: String) {
Log.e(TAG, "libName:$libName") Log.e(TAG, "libName:$libName")
@ -89,9 +115,9 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
//如果找不到,则从远程下载 //如果找不到,则从远程下载
//删除历史文件 //删除历史文件
deleteHistoryFile(Objects.requireNonNull(soFile.parentFile), soFile) deleteHistoryFile(Objects.requireNonNull(soFile.parentFile), soFile)
md5 = getUrlMd5(md5Url) //md5 = getUrlMd5(md5Url)
Log.i(TAG, "soMD5:$md5") Log.i(TAG, "soMD5:$md5")
if (md5 == null || md5!!.length != 32 || soUrl.isEmpty()) { if (md5.length != 32 || soUrl.isEmpty()) {
//如果md5或下载的url为空,则调用系统行为进行加载 //如果md5或下载的url为空,则调用系统行为进行加载
System.loadLibrary(libName) System.loadLibrary(libName)
return return
@ -144,39 +170,6 @@ object CronetLoader : CronetEngine.Builder.LibraryLoader() {
return cpuAbi return cpuAbi
} }
@Suppress("SameParameterValue")
private fun getUrlMd5(url: String): String? {
//这样在下载成功后,遇到无网条件下,只要版本未发生变化也能获取md5
if (md5 != null && md5!!.length == 32 && version == soVersion) {
appCtx.putPrefString("soMd5", md5)
appCtx.putPrefString("soVersion", soVersion)
return md5
}
val inputStream: InputStream
val outputStream: OutputStream
return try {
outputStream = ByteArrayOutputStream()
val connection = URL(url).openConnection() as HttpURLConnection
inputStream = connection.inputStream
val buffer = ByteArray(1024)
var read: Int
while (inputStream.read(buffer).also { read = it } != -1) {
outputStream.write(buffer, 0, read)
outputStream.flush()
}
val tmd5 = outputStream.toString()
//成功获取到md5后保存md5和版本
if (tmd5.length == 32) {
appCtx.putPrefString("soMd5", tmd5)
appCtx.putPrefString("soVersion", soVersion)
}
return tmd5
} catch (e: IOException) {
null
}
}
/** /**
* 删除历史文件 * 删除历史文件

@ -13,6 +13,7 @@ buildscript {
classpath 'com.android.tools.build:gradle:7.0.1' classpath 'com.android.tools.build:gradle:7.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.4' classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.4'
classpath 'de.undercouch:gradle-download-task:4.1.2'
} }
} }

@ -21,3 +21,5 @@ android.enableJetifier=true
kotlin.code.style=official kotlin.code.style=official
android.enableResourceOptimizations=true android.enableResourceOptimizations=true
CronetVersion=92.0.4515.159

Loading…
Cancel
Save