目录

android-框架网络访问Okhttp

android 框架—网络访问Okhttp

下面给出一份 最新可直接拷贝运行 的 Android OkHttp 基本使用速查表,
覆盖“依赖 → 权限 → 常用 GET/POST/上传 → 同步/异步 → 线程模型 → 踩坑”6 大环节,

依赖 & 权限(一次到位)

依赖


implementation 'com.squareup.okhttp3:okhttp:4.12.0'

权限


<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- Android 9+ 允许 http 明文(开发阶段)-->
<application android:usesCleartextTraffic="true" ... >

全局单例工具类(线程安全)


public final class HttpUtil {
    private static final OkHttpClient CLIENT = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .writeTimeout(20, TimeUnit.SECONDS)
            .addInterceptor(chain -> {                       // 极简日志拦截器
                Request req = chain.request();
                Log.d("OKHTTP", req.method() + " " + req.url());
                return chain.proceed(req);
            })
            .build();

    public static OkHttpClient client() { return CLIENT; }
}

GET 请求(异步)


public static void getAsync(String url, Callback cb) {
    Request req = new Request.Builder().url(url).get().build();
    HttpUtil.client().newCall(req).enqueue(cb);
}

/* 使用 */
getAsync("https://api.github.com/users/google", new Callback() {
    @Override public void onFailure(@NonNull Call call, @NonNull IOException e) {
        Log.e("OKHTTP", "get error", e);
    }
    @Override public void onResponse(@NonNull Call call, @NonNull Response resp) throws IOException {
        if (resp.isSuccessful()) {
            String body = resp.body().string();
            Log.i("OKHTTP", "get body=" + body);
        }
    }
});

POST 表单(application/x-www-form-urlencoded)


public static void postFormAsync(String url, Map<String,String> params, Callback cb) {
    FormBody.Builder fb = new FormBody.Builder();
    for (Map.Entry<String,String> e : params.entrySet()) fb.add(e.getKey(), e.getValue());
    Request req = new Request.Builder()
            .url(url)
            .post(fb.build())
            .build();
    HttpUtil.client().newCall(req).enqueue(cb);
}

POST JSON(application/json)


public static void postJsonAsync(String url, String json, Callback cb) {
    RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
    Request req = new Request.Builder()
            .url(url)
            .post(body)
            .build();
    HttpUtil.client().newCall(req).enqueue(cb);
}

文件上传(Multipart,带进度)


public static void uploadAsync(String url, File file, Callback cb) {
    RequestBody fileBody = RequestBody.create(file, MediaType.parse("application/octet-stream"));
    MultipartBody body = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("file", file.getName(), fileBody)
            .addFormDataPart("key", "extra")   // 可继续加字段
            .build();
    Request req = new Request.Builder().url(url).post(body).build();
    HttpUtil.client().newCall(req).enqueue(cb);
}

下载文件(带进度,Kotlin 版更简洁,这里给 Java)


public static void downloadAsync(String url, File saveFile, ProgressListener listener, Callback cb) {
    Request req = new Request.Builder().url(url).build();
    OkHttpClient downClient = HttpUtil.client().newBuilder()
            .addNetworkInterceptor(chain -> {
                Response original = chain.proceed(chain.request());
                return original.newBuilder()
                        .body(new ProgressResponseBody(original.body(), listener))
                        .build();
            }).build();
    downClient.newCall(req).enqueue(cb);
}

/* 进度回调 */
public interface ProgressListener {
    void update(long bytesRead, long contentLength, boolean done);
}

/* 包装 ResponseBody 计算进度 */
private static class ProgressResponseBody extends ResponseBody {
    private final ResponseBody delegate;
    private final ProgressListener listener;
    ProgressResponseBody(ResponseBody d, ProgressListener l) { delegate = d; listener = l; }
    @Override public long contentLength() { return delegate.contentLength(); }
    @Override public MediaType contentType() { return delegate.contentType(); }
    @Override public BufferedSource source() {
        return Okio.buffer(new ForwardingSource(delegate.source()) {
            long total = 0L;
            @Override public long read(Buffer sink, long byteCount) throws IOException {
                long bytes = super.read(sink, byteCount);
                total += (bytes != -1 ? bytes : 0);
                listener.update(total, contentLength(), bytes == -1);
                return bytes;
            }
        });
    }
}

同步调用(不推荐主线程,仅给单元测试)


public static String getSync(String url) throws IOException {
    Request req = new Request.Builder().url(url).get().build();
    try (Response resp = HttpUtil.client().newCall(req).execute()) {
        if (!resp.isSuccessful()) throw new IOException("code=" + resp.code());
        return resp.body().string();
    }
}

注意事项(面试常问)

  • 所有 onResponse 回调不在主线程,若更新 UI 记得 runOnUiThread() / Handler / LiveData.postValue()
  • ResponseBody.string() 只能读一次,再读需要 cache() 或自己缓存。
  • Android 9+ 默认禁止 HTTP 明文,需在 AndroidManifest.xml
    android:usesCleartextTraffic="true" 或配置 networkSecurityConfig
  • 上传/下载大文件用 @Streaming + MultipartBody 边读边写,防止 OOM。

“先 client 后 request,enqueue 异步 execute 同步;
FormBody 表单,RequestBody JSON,Multipart 传文件;
回调非 UI 线程,刷新界面要切换!”