下载okhttp,并导入自己工程
okhttp 简介
OkHttp 是一套处理 HTTP 网络请求的依赖库,由 Square 公司设计研发并开源,目前可以在 Java 和 Kotlin 中使用。对于 Android App 来说,OkHttp 现在几乎已经占据了所有的网络请求操作,RetroFit + OkHttp 实现网络请求似乎成了一种标配。因此它也是每一个 Android 开发工程师的必备技能,了解其内部实现原理可以更好地进行功能扩展、封装以及优化。
okhttp 导入自己的工程
平常我们使用okhttp 是直接使用grable 依赖的方式倒入工程
implementation("com.squareup.okhttp3:okhttp:4.10.0")
之前查看源码,也是直接通过工程点击查看,发现有些不方便,比如,我想在源码中添加些代码 发现,代码都是只读的如:
那么我们怎么能添加我们自己的代码呢?这就需要以工程依赖的方式倒入okhttp的源码
step1: 下载okhttp 源码
源码地址:https://github.com/square/okhttp
可以直接下载zip包,或者通过git clone 方式
step2: 通过android studio 打开工程
1、android studio 版本尽量使用最新的
2、注意自己使用的jdk 版本,需要和源码中的是一样的,否则会报错
导入后效果:
step4: 创建android Application 工程
创建工程的过程后,编译,可能会报错:
The 'java' plugin has been applied, but it is not compatible with the Android plugins.
需要在:
okhttp/build.gradle.kts 下添加 :
if (project.name == "myokhttpapplication") return@subprojects
//myokhttpapplication 为你的功能
完成代码:
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import com.vanniktech.maven.publish.SonatypeHost
import groovy.util.Node
import groovy.util.NodeList
import java.net.URL
import kotlinx.validation.ApiValidationExtension
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import ru.vyarus.gradle.plugin.animalsniffer.AnimalSnifferExtension
buildscript {
dependencies {
classpath(libs.gradlePlugin.dokka)
classpath(libs.gradlePlugin.kotlin)
classpath(libs.gradlePlugin.androidJunit5)
classpath(libs.gradlePlugin.android)
classpath(libs.gradlePlugin.graal)
classpath(libs.gradlePlugin.bnd)
classpath(libs.gradlePlugin.shadow)
classpath(libs.gradlePlugin.animalsniffer)
classpath(libs.gradlePlugin.errorprone)
classpath(libs.gradlePlugin.spotless)
classpath(libs.gradlePlugin.mavenPublish)
classpath(libs.gradlePlugin.binaryCompatibilityValidator)
}
repositories {
mavenCentral()
gradlePluginPortal()
google()
}
}
allprojects {
group = "com.squareup.okhttp3"
version = "5.0.0-SNAPSHOT"
repositories {
mavenCentral()
google()
}
tasks.create("downloadDependencies") {
description = "Download all dependencies to the Gradle cache"
doLast {
for (configuration in configurations) {
if (configuration.isCanBeResolved) {
configuration.files
}
}
}
}
normalization {
runtimeClasspath {
metaInf {
ignoreAttribute("Bnd-LastModified")
}
}
}
}
/** Configure building for Java+Kotlin projects. */
subprojects {
val project = this@subprojects
if (project.name == "okhttp-bom") return@subprojects
if (project.name == "okhttp-android") return@subprojects
// 添加工程
if (project.name == "myokhttpapplication") return@subprojects
if (project.name == "android-test") return@subprojects
if (project.name == "regression-test") return@subprojects
apply(plugin = "checkstyle")
apply(plugin = "ru.vyarus.animalsniffer")
apply(plugin = "biz.aQute.bnd.builder")
tasks.withType<JavaCompile> {
options.encoding = Charsets.UTF_8.toString()
}
configure<JavaPluginExtension> {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
}
}
tasks.withType<Checkstyle>().configureEach {
exclude("**/CipherSuite.java")
}
val checkstyleConfig: Configuration by configurations.creating
dependencies {
checkstyleConfig(rootProject.libs.checkStyle) {
isTransitive = false
}
}
configure<CheckstyleExtension> {
config = resources.text.fromArchiveEntry(checkstyleConfig, "google_checks.xml")
toolVersion = rootProject.libs.versions.checkStyle.get()
sourceSets = listOf(project.sourceSets["main"])
}
// Animal Sniffer confirms we generally don't use APIs not on Java 8.
configure<AnimalSnifferExtension> {
annotation = "okhttp3.internal.SuppressSignatureCheck"
sourceSets = listOf(project.sourceSets["main"])
}
val signature: Configuration by configurations.getting
dependencies {
signature(rootProject.libs.signature.android.apilevel21)
signature(rootProject.libs.codehaus.signature.java18)
}
tasks.withType<KotlinCompile> {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
freeCompilerArgs = listOf(
"-Xjvm-default=all",
)
}
}
val platform = System.getProperty("okhttp.platform", "jdk9")
val testJavaVersion = System.getProperty("test.java.version", "11").toInt()
val testRuntimeOnly: Configuration by configurations.getting
dependencies {
testRuntimeOnly(rootProject.libs.junit.jupiter.engine)
testRuntimeOnly(rootProject.libs.junit.vintage.engine)
}
tasks.withType<Test> {
useJUnitPlatform()
jvmArgs = jvmArgs!! + listOf(
"-Dokhttp.platform=$platform",
"-XX:+HeapDumpOnOutOfMemoryError"
)
val javaToolchains = project.extensions.getByType<JavaToolchainService>()
javaLauncher.set(javaToolchains.launcherFor {
languageVersion.set(JavaLanguageVersion.of(testJavaVersion))
})
maxParallelForks = Runtime.getRuntime().availableProcessors() * 2
testLogging {
exceptionFormat = TestExceptionFormat.FULL
}
systemProperty("okhttp.platform", platform)
systemProperty("junit.jupiter.extensions.autodetection.enabled", "true")
}
if (platform == "jdk8alpn") {
// Add alpn-boot on Java 8 so we can use HTTP/2 without a stable API.
val alpnBootVersion = alpnBootVersion()
if (alpnBootVersion != null) {
val alpnBootJar = configurations.detachedConfiguration(
dependencies.create("org.mortbay.jetty.alpn:alpn-boot:$alpnBootVersion")
).singleFile
tasks.withType<Test> {
jvmArgs = jvmArgs!! + listOf("-Xbootclasspath/p:${alpnBootJar}")
}
}
} else if (platform == "conscrypt") {
dependencies {
testRuntimeOnly(rootProject.libs.conscrypt.openjdk)
}
} else if (platform == "openjsse") {
dependencies {
testRuntimeOnly(rootProject.libs.openjsse)
}
}
tasks.withType<JavaCompile> {
sourceCompatibility = JavaVersion.VERSION_1_8.toString()
targetCompatibility = JavaVersion.VERSION_1_8.toString()
}
}
/** Configure publishing and signing for published Java and JavaPlatform subprojects. */
subprojects {
tasks.withType<DokkaTask>().configureEach {
dokkaSourceSets.configureEach {
reportUndocumented.set(false)
skipDeprecated.set(true)
jdkVersion.set(8)
perPackageOption {
matchingRegex.set("okhttp3\\.internal.*")
suppress.set(true)
}
perPackageOption {
matchingRegex.set("mockwebserver3\\.internal.*")
suppress.set(true)
}
if (project.file("Module.md").exists()) {
includes.from(project.file("Module.md"))
}
externalDocumentationLink {
url.set(URL("https://square.github.io/okio/2.x/okio/"))
packageListUrl.set(URL("https://square.github.io/okio/2.x/okio/package-list"))
}
}
if (name == "dokkaGfm") {
outputDirectory.set(file("${rootDir}/docs/4.x"))
}
}
plugins.withId("com.vanniktech.maven.publish.base") {
val publishingExtension = extensions.getByType(PublishingExtension::class.java)
configure<MavenPublishBaseExtension> {
publishToMavenCentral(SonatypeHost.S01)
signAllPublications()
pom {
name.set(project.name)
description.set("Square’s meticulous HTTP client for Java and Kotlin.")
url.set("https://square.github.io/okhttp/")
licenses {
license {
name.set("The Apache Software License, Version 2.0")
url.set("https://www.apache.org/licenses/LICENSE-2.0.txt")
distribution.set("repo")
}
}
scm {
connection.set("scm:git:https://github.com/square/okhttp.git")
developerConnection.set("scm:git:ssh://git@github.com/square/okhttp.git")
url.set("https://github.com/square/okhttp")
}
developers {
developer {
name.set("Square, Inc.")
}
}
}
// Configure the kotlinMultiplatform artifact to depend on the JVM artifact in pom.xml only.
// This hack allows Maven users to continue using our original OkHttp artifact names (like
// com.squareup.okhttp3:okhttp:5.x.y) even though we changed that artifact from JVM-only
// to Kotlin Multiplatform. Note that module.json doesn't need this hack.
val mavenPublications = publishingExtension.publications.withType<MavenPublication>()
mavenPublications.configureEach {
if (name != "jvm") return@configureEach
val jvmPublication = this
val kmpPublication = mavenPublications.getByName("kotlinMultiplatform")
kmpPublication.pom.withXml {
val root = asNode()
val dependencies = (root["dependencies"] as NodeList).firstOrNull() as Node?
?: root.appendNode("dependencies")
for (child in dependencies.children().toList()) {
dependencies.remove(child as Node)
}
dependencies.appendNode("dependency").apply {
appendNode("groupId", jvmPublication.groupId)
appendNode("artifactId", jvmPublication.artifactId)
appendNode("version", jvmPublication.version)
appendNode("scope", "compile")
}
}
}
}
}
plugins.withId("binary-compatibility-validator") {
configure<ApiValidationExtension> {
ignoredPackages += "okhttp3.logging.internal"
ignoredPackages += "mockwebserver3.internal"
ignoredPackages += "okhttp3.internal"
ignoredPackages += "mockwebserver3.junit5.internal"
ignoredPackages += "okhttp3.brotli.internal"
ignoredPackages += "okhttp3.sse.internal"
ignoredPackages += "okhttp3.tls.internal"
}
}
}
tasks.wrapper {
distributionType = Wrapper.DistributionType.ALL
}
step5: 以工程依赖的方式引入okhttp
完整代码:
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
namespace = "com.example.myokhttpapplication"
compileSdk = 32
defaultConfig {
applicationId = "com.example.myokhttpapplication"
minSdk = 21
targetSdk = 32
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation("androidx.core:core-ktx:1.7.0")
implementation("androidx.appcompat:appcompat:1.4.1")
implementation("com.google.android.material:material:1.5.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.3")
// 添加工程依赖
implementation(project(mapOf("path" to ":okhttp-android")))
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
}
step6: 添加测试代码
package com.example.myokhttpapplication
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import java.nio.charset.Charset
import java.nio.charset.UnsupportedCharsetException
import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okio.Buffer
import okio.BufferedSource
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<View>(R.id.send_request).setOnClickListener{
Thread(Runnable {
var client = OkHttpClient()
val request: Request = Request.Builder()
.url("https://api.github.com/gists/c2a7c39532239ff261be")
.build()
val response: Response = client.newCall(request).execute()
if (!response.isSuccessful) {
Log.i("zhao","请求失败")
}
var bodyStrRespon = ""
val source: BufferedSource = response.body.source()
source.request(Long.MAX_VALUE)
val buffer: Buffer = source.buffer
var charset: Charset = Charset.forName("UTF-8")
val contentType: MediaType? = response.body.contentType()
if (contentType != null) {
try {
charset = contentType.charset(Charset.forName("UTF-8"))!!
} catch (e: UnsupportedCharsetException) {
e.printStackTrace()
}
}
bodyStrRespon = buffer.clone().readString(charset)
bodyStrRespon = if (bodyStrRespon === "") "无响应Body" else bodyStrRespon
Log.i("zhao","请求结果"+bodyStrRespon)
}).start()
}
}
}
哈哈哈,到这里就完结了
参考资料: