系列导航
概述
网络编程是现代Android应用的核心功能之一。本指南全面介绍Android平台的网络编程技术,包括原生API、流行框架、WebView使用、数据解析等内容,帮助开发者构建高效、安全的网络应用。
学习目标
- 掌握Android网络权限和安全配置
- 学习使用HttpURLConnection和OkHttp
- 精通Retrofit框架的使用
- 理解WebView的高级用法
- 实现文件的上传和下载
- 掌握JSON和XML数据解析
- 优化网络请求性能
网络权限配置
添加网络权限
<!-- AndroidManifest.xml -->
<manifest ...>
<!-- 基本网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 网络状态权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- WiFi状态权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
...>
</application>
</manifest>
网络安全配置
<!-- res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- 默认配置 -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<!-- 开发环境配置 -->
<debug-overrides>
<trust-anchors>
<certificates src="user" />
</trust-anchors>
</debug-overrides>
<!-- 特定域名配置 -->
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">api.example.com</domain>
<domain includeSubdomains="true">10.0.2.2</domain> <!-- 模拟器本地主机 -->
</domain-config>
</network-security-config>
HttpURLConnection
基本GET请求
public class HttpHelper {
public static String doGet(String urlString) throws IOException {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
// 设置请求方法
connection.setRequestMethod("GET");
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
// 设置请求头
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("User-Agent", "Android App");
// 获取响应码
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
// 读取响应
InputStream inputStream = connection.getInputStream();
return readStream(inputStream);
} else {
throw new IOException("HTTP error code: " + responseCode);
}
} finally {
connection.disconnect();
}
}
private static String readStream(InputStream is) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line).append('\n');
}
reader.close();
return response.toString();
}
}
POST请求
public static String doPost(String urlString, String jsonBody) throws IOException {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
try {
// 设置POST请求
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
// 设置请求头
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setRequestProperty("Accept", "application/json");
// 写入请求体
OutputStream os = connection.getOutputStream();
os.write(jsonBody.getBytes("UTF-8"));
os.flush();
os.close();
// 获取响应
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream inputStream = connection.getInputStream();
return readStream(inputStream);
} else {
InputStream errorStream = connection.getErrorStream();
String error = readStream(errorStream);
throw new IOException("HTTP error code: " + responseCode + ", " + error);
}
} finally {
connection.disconnect();
}
}
异步请求封装
public class NetworkManager {
private final ExecutorService executorService = Executors.newFixedThreadPool(4);
public interface NetworkCallback {
void onSuccess(String response);
void onError(Exception e);
}
public void getAsync(final String url, final NetworkCallback callback) {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
String response = HttpHelper.doGet(url);
runOnUiThread(() -> callback.onSuccess(response));
} catch (Exception e) {
runOnUiThread(() -> callback.onError(e));
}
}
});
}
public void postAsync(final String url, final String body, final NetworkCallback callback) {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
String response = HttpHelper.doPost(url, body);
runOnUiThread(() -> callback.onSuccess(response));
} catch (Exception e) {
runOnUiThread(() -> callback.onError(e));
}
}
});
}
private void runOnUiThread(Runnable runnable) {
new Handler(Looper.getMainLooper()).post(runnable);
}
public void shutdown() {
executorService.shutdown();
}
}
OkHttp使用
添加依赖
dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
}
基本使用
public class OkHttpManager {
private static OkHttpManager instance;
private final OkHttpClient client;
private OkHttpManager() {
// 创建日志拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// 创建OkHttpClient
client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.addInterceptor(new HeaderInterceptor())
.cache(new Cache(getCacheDir(), 10 * 1024 * 1024)) // 10MB缓存
.build();
}
public static OkHttpManager getInstance() {
if (instance == null) {
synchronized (OkHttpManager.class) {
if (instance == null) {
instance = new OkHttpManager();
}
}
}
return instance;
}
// GET请求
public void get(String url, Callback callback) {
Request request = new Request.Builder()
.url(url)
.get()
.build();
client.newCall(request).enqueue(callback);
}
// POST请求 - JSON
public void postJson(String url, String json, Callback callback) {
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
client.newCall(request).enqueue(callback);
}
// POST请求 - 表单
public void postForm(String url, Map<String, String> params, Callback callback) {
FormBody.Builder builder = new FormBody.Builder();
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.add(entry.getKey(), entry.getValue());
}
Request request = new Request.Builder()
.url(url)
.post(builder.build())
.build();
client.newCall(request).enqueue(callback);
}
// 文件上传
public void uploadFile(String url, File file, Callback callback) {
MediaType mediaType = MediaType.parse("application/octet-stream");
MultipartBody body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(),
RequestBody.create(file, mediaType))
.build();
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
client.newCall(request).enqueue(callback);
}
// 自定义拦截器
private static class HeaderInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("Authorization", "Bearer " + getToken())
.header("Platform", "Android")
.header("Version", BuildConfig.VERSION_NAME)
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
private String getToken() {
// 获取token逻辑
return "your_auth_token";
}
}
}
使用示例
public class NetworkActivity extends AppCompatActivity {
private void fetchData() {
OkHttpManager.getInstance().get("https://api.example.com/data",
new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
runOnUiThread(() -> {
Toast.makeText(NetworkActivity.this,
"网络请求失败: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
});
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response)
throws IOException {
if (response.isSuccessful()) {
String json = response.body().string();
runOnUiThread(() -> {
// 更新UI
updateUI(json);
});
} else {
runOnUiThread(() -> {
Toast.makeText(NetworkActivity.this,
"请求失败: " + response.code(),
Toast.LENGTH_SHORT).show();
});
}
}
});
}
}
Retrofit框架
添加依赖
dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
implementation 'com.google.code.gson:gson:2.10.1'
}
定义API接口
public interface ApiService {
// GET请求
@GET("users/{id}")
Call<User> getUser(@Path("id") int userId);
// GET请求带查询参数
@GET("users")
Call<List<User>> getUsers(@Query("page") int page,
@Query("size") int size);
// POST请求
@POST("users")
Call<User> createUser(@Body User user);
// PUT请求
@PUT("users/{id}")
Call<User> updateUser(@Path("id") int userId, @Body User user);
// DELETE请求
@DELETE("users/{id}")
Call<Void> deleteUser(@Path("id") int userId);
// 文件上传
@Multipart
@POST("upload")
Call<UploadResponse> uploadFile(@Part MultipartBody.Part file,
@Part("description") RequestBody description);
// 文件下载
@Streaming
@GET
Call<ResponseBody> downloadFile(@Url String fileUrl);
// 使用RxJava
@GET("users")
Observable<List<User>> getUsersRx(@Query("page") int page);
}
创建Retrofit实例
public class RetrofitClient {
private static RetrofitClient instance;
private final Retrofit retrofit;
private final ApiService apiService;
private RetrofitClient() {
// 创建OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY))
.addInterceptor(new AuthInterceptor())
.build();
// 创建Retrofit
retrofit = new Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.build();
apiService = retrofit.create(ApiService.class);
}
public static RetrofitClient getInstance() {
if (instance == null) {
synchronized (RetrofitClient.class) {
if (instance == null) {
instance = new RetrofitClient();
}
}
}
return instance;
}
public ApiService getApiService() {
return apiService;
}
// 认证拦截器
private static class AuthInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder builder = original.newBuilder()
.header("Authorization", "Bearer " + getToken());
Request request = builder.build();
return chain.proceed(request);
}
private String getToken() {
// 从SharedPreferences或其他地方获取token
return "your_auth_token";
}
}
}
数据模型
public class User {
@SerializedName("id")
private int id;
@SerializedName("name")
private String name;
@SerializedName("email")
private String email;
@SerializedName("avatar_url")
private String avatarUrl;
@SerializedName("created_at")
private Date createdAt;
// Getters and setters
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getAvatarUrl() { return avatarUrl; }
public void setAvatarUrl(String avatarUrl) { this.avatarUrl = avatarUrl; }
public Date getCreatedAt() { return createdAt; }
public void setCreatedAt(Date createdAt) { this.createdAt = createdAt; }
}
使用Retrofit
public class UserViewModel extends AndroidViewModel {
private final MutableLiveData<List<User>> usersLiveData = new MutableLiveData<>();
private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
private final ApiService apiService;
public UserViewModel(@NonNull Application application) {
super(application);
apiService = RetrofitClient.getInstance().getApiService();
}
public LiveData<List<User>> getUsers() {
return usersLiveData;
}
public LiveData<String> getError() {
return errorLiveData;
}
public void loadUsers(int page) {
apiService.getUsers(page, 20).enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.isSuccessful() && response.body() != null) {
usersLiveData.postValue(response.body());
} else {
errorLiveData.postValue("加载失败: " + response.code());
}
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
errorLiveData.postValue("网络错误: " + t.getMessage());
}
});
}
public void createUser(User user) {
apiService.createUser(user).enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
// 用户创建成功
loadUsers(1); // 重新加载列表
} else {
errorLiveData.postValue("创建失败: " + response.code());
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
errorLiveData.postValue("网络错误: " + t.getMessage());
}
});
}
}
WebView详解
基本配置
public class WebViewActivity extends AppCompatActivity {
private WebView webView;
private ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
webView = findViewById(R.id.webView);
progressBar = findViewById(R.id.progressBar);
initWebView();
webView.loadUrl("https://www.example.com");
}
@SuppressLint("SetJavaScriptEnabled")
private void initWebView() {
WebSettings settings = webView.getSettings();
// 基本设置
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setDatabaseEnabled(true);
settings.setAppCacheEnabled(true);
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
// 支持缩放
settings.setSupportZoom(true);
settings.setBuiltInZoomControls(true);
settings.setDisplayZoomControls(false);
// 自适应屏幕
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
// 文件访问
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
// 混合内容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
// 设置WebViewClient
webView.setWebViewClient(new MyWebViewClient());
// 设置WebChromeClient
webView.setWebChromeClient(new MyWebChromeClient());
// 添加JavaScript接口
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
}
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
String url = request.getUrl().toString();
// 处理特殊协议
if (url.startsWith("tel:")) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
startActivity(intent);
return true;
} else if (url.startsWith("mailto:")) {
Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(url));
startActivity(intent);
return true;
}
// 在WebView中加载URL
view.loadUrl(url);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressBar.setVisibility(View.GONE);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request,
WebResourceError error) {
super.onReceivedError(view, request, error);
// 加载错误页面
view.loadUrl("file:///android_asset/error.html");
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler,
SslError error) {
// 处理SSL错误
AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this);
builder.setMessage("SSL证书错误,是否继续?");
builder.setPositiveButton("继续", (dialog, which) -> handler.proceed());
builder.setNegativeButton("取消", (dialog, which) -> handler.cancel());
builder.create().show();
}
}
private class MyWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
progressBar.setProgress(newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
setTitle(title);
}
@Override
public boolean onJsAlert(WebView view, String url, String message,
JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this);
builder.setMessage(message)
.setPositiveButton("确定", (dialog, which) -> result.confirm())
.setCancelable(false)
.create()
.show();
return true;
}
@Override
public boolean onShowFileChooser(WebView webView,
ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
// 处理文件选择
openFileChooser(filePathCallback);
return true;
}
}
@Override
public void onBackPressed() {
if (webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
@Override
protected void onDestroy() {
if (webView != null) {
webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
webView.clearHistory();
((ViewGroup) webView.getParent()).removeView(webView);
webView.destroy();
webView = null;
}
super.onDestroy();
}
}
JavaScript交互
public class WebAppInterface {
private Context context;
public WebAppInterface(Context context) {
this.context = context;
}
@JavascriptInterface
public void showToast(String message) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public String getDeviceInfo() {
JSONObject json = new JSONObject();
try {
json.put("model", Build.MODEL);
json.put("brand", Build.BRAND);
json.put("version", Build.VERSION.RELEASE);
json.put("sdk", Build.VERSION.SDK_INT);
} catch (JSONException e) {
e.printStackTrace();
}
return json.toString();
}
@JavascriptInterface
public void saveData(String key, String value) {
SharedPreferences prefs = context.getSharedPreferences("WebData",
Context.MODE_PRIVATE);
prefs.edit().putString(key, value).apply();
}
@JavascriptInterface
public String getData(String key) {
SharedPreferences prefs = context.getSharedPreferences("WebData",
Context.MODE_PRIVATE);
return prefs.getString(key, "");
}
}
HTML中调用Android方法
<!DOCTYPE html>
<html>
<head>
<title>WebView Demo</title>
<script type="text/javascript">
function showAndroidToast(message) {
Android.showToast(message);
}
function getDeviceInfo() {
var info = Android.getDeviceInfo();
var device = JSON.parse(info);
document.getElementById('deviceInfo').innerHTML =
'Model: ' + device.model + '<br>' +
'Brand: ' + device.brand + '<br>' +
'Version: ' + device.version + '<br>' +
'SDK: ' + device.sdk;
}
function saveToAndroid() {
var key = document.getElementById('key').value;
var value = document.getElementById('value').value;
Android.saveData(key, value);
showAndroidToast('Data saved!');
}
function loadFromAndroid() {
var key = document.getElementById('key').value;
var value = Android.getData(key);
document.getElementById('value').value = value;
}
</script>
</head>
<body>
<h1>WebView JavaScript Interface Demo</h1>
<button onclick="showAndroidToast('Hello from WebView!')">Show Toast</button>
<button onclick="getDeviceInfo()">Get Device Info</button>
<div id="deviceInfo"></div>
<input type="text" id="key" placeholder="Key">
<input type="text" id="value" placeholder="Value">
<button onclick="saveToAndroid()">Save</button>
<button onclick="loadFromAndroid()">Load</button>
</body>
</html>
文件上传下载
文件下载
public class DownloadManager {
private static final int BUFFER_SIZE = 4096;
public interface DownloadListener {
void onProgress(int progress);
void onComplete(File file);
void onError(Exception e);
}
public void downloadFile(String fileUrl, String savePath,
DownloadListener listener) {
new Thread(() -> {
try {
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
// 获取文件大小
int fileLength = connection.getContentLength();
// 创建输入输出流
InputStream input = new BufferedInputStream(connection.getInputStream());
File file = new File(savePath);
OutputStream output = new FileOutputStream(file);
byte[] buffer = new byte[BUFFER_SIZE];
int count;
long total = 0;
while ((count = input.read(buffer)) != -1) {
total += count;
// 计算进度
if (fileLength > 0) {
int progress = (int) (total * 100 / fileLength);
runOnUiThread(() -> listener.onProgress(progress));
}
output.write(buffer, 0, count);
}
output.flush();
output.close();
input.close();
runOnUiThread(() -> listener.onComplete(file));
} catch (Exception e) {
runOnUiThread(() -> listener.onError(e));
}
}).start();
}
// 使用OkHttp下载
public void downloadWithOkHttp(String url, String destPath,
DownloadListener listener) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
listener.onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
listener.onError(new IOException("Unexpected code " + response));
return;
}
ResponseBody body = response.body();
long contentLength = body.contentLength();
InputStream input = body.byteStream();
File file = new File(destPath);
OutputStream output = new FileOutputStream(file);
byte[] buffer = new byte[BUFFER_SIZE];
long total = 0;
int count;
while ((count = input.read(buffer)) != -1) {
total += count;
int progress = (int) (total * 100 / contentLength);
runOnUiThread(() -> listener.onProgress(progress));
output.write(buffer, 0, count);
}
output.flush();
output.close();
input.close();
runOnUiThread(() -> listener.onComplete(file));
}
});
}
private void runOnUiThread(Runnable runnable) {
new Handler(Looper.getMainLooper()).post(runnable);
}
}
文件上传
public class UploadManager {
public interface UploadListener {
void onProgress(int progress);
void onSuccess(String response);
void onError(Exception e);
}
// 使用HttpURLConnection上传
public void uploadFile(String uploadUrl, File file,
Map<String, String> params,
UploadListener listener) {
String boundary = "----" + System.currentTimeMillis();
new Thread(() -> {
try {
URL url = new URL(uploadUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求属性
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + boundary);
DataOutputStream dos = new DataOutputStream(connection.getOutputStream());
// 添加参数
for (Map.Entry<String, String> entry : params.entrySet()) {
dos.writeBytes("--" + boundary + "\r\n");
dos.writeBytes("Content-Disposition: form-data; name=\"" +
entry.getKey() + "\"\r\n\r\n");
dos.writeBytes(entry.getValue() + "\r\n");
}
// 添加文件
dos.writeBytes("--" + boundary + "\r\n");
dos.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" +
file.getName() + "\"\r\n");
dos.writeBytes("Content-Type: application/octet-stream\r\n\r\n");
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[4096];
int bytesRead;
long totalBytes = file.length();
long uploadedBytes = 0;
while ((bytesRead = fis.read(buffer)) != -1) {
dos.write(buffer, 0, bytesRead);
uploadedBytes += bytesRead;
int progress = (int) (uploadedBytes * 100 / totalBytes);
runOnUiThread(() -> listener.onProgress(progress));
}
dos.writeBytes("\r\n");
dos.writeBytes("--" + boundary + "--\r\n");
fis.close();
dos.flush();
dos.close();
// 获取响应
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader br = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
response.append(line);
}
br.close();
runOnUiThread(() -> listener.onSuccess(response.toString()));
} else {
throw new IOException("Server returned: " + responseCode);
}
} catch (Exception e) {
runOnUiThread(() -> listener.onError(e));
}
}).start();
}
// 使用Retrofit上传
public void uploadWithRetrofit(File file, String description) {
RequestBody requestFile = RequestBody.create(
MediaType.parse("multipart/form-data"), file);
MultipartBody.Part body = MultipartBody.Part.createFormData(
"file", file.getName(), requestFile);
RequestBody descBody = RequestBody.create(
MediaType.parse("text/plain"), description);
ApiService service = RetrofitClient.getInstance().getApiService();
service.uploadFile(body, descBody).enqueue(new Callback<UploadResponse>() {
@Override
public void onResponse(Call<UploadResponse> call,
Response<UploadResponse> response) {
if (response.isSuccessful()) {
// 上传成功
}
}
@Override
public void onFailure(Call<UploadResponse> call, Throwable t) {
// 上传失败
}
});
}
}
网络状态监测
public class NetworkStateManager {
private Context context;
private ConnectivityManager connectivityManager;
private NetworkCallback networkCallback;
public interface NetworkStateListener {
void onNetworkAvailable();
void onNetworkLost();
void onNetworkTypeChanged(NetworkType type);
}
public enum NetworkType {
WIFI, MOBILE, ETHERNET, NONE
}
public NetworkStateManager(Context context) {
this.context = context;
this.connectivityManager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
public boolean isNetworkAvailable() {
NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnected();
}
public NetworkType getCurrentNetworkType() {
NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
if (activeNetwork == null || !activeNetwork.isConnected()) {
return NetworkType.NONE;
}
switch (activeNetwork.getType()) {
case ConnectivityManager.TYPE_WIFI:
return NetworkType.WIFI;
case ConnectivityManager.TYPE_MOBILE:
return NetworkType.MOBILE;
case ConnectivityManager.TYPE_ETHERNET:
return NetworkType.ETHERNET;
default:
return NetworkType.NONE;
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void registerNetworkCallback(NetworkStateListener listener) {
networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
listener.onNetworkAvailable();
}
@Override
public void onLost(Network network) {
listener.onNetworkLost();
}
@Override
public void onCapabilitiesChanged(Network network,
NetworkCapabilities capabilities) {
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
listener.onNetworkTypeChanged(NetworkType.WIFI);
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
listener.onNetworkTypeChanged(NetworkType.MOBILE);
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
listener.onNetworkTypeChanged(NetworkType.ETHERNET);
}
}
};
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
connectivityManager.registerNetworkCallback(request, networkCallback);
}
public void unregisterNetworkCallback() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && networkCallback != null) {
connectivityManager.unregisterNetworkCallback(networkCallback);
}
}
}
数据解析
JSON解析
public class JsonParser {
// 使用Gson解析
public static <T> T parseJson(String json, Class<T> clazz) {
Gson gson = new Gson();
return gson.fromJson(json, clazz);
}
public static <T> List<T> parseJsonList(String json, Type typeOfT) {
Gson gson = new Gson();
return gson.fromJson(json, typeOfT);
}
// 手动解析JSON
public static User parseUser(String json) throws JSONException {
JSONObject jsonObject = new JSONObject(json);
User user = new User();
user.setId(jsonObject.getInt("id"));
user.setName(jsonObject.getString("name"));
user.setEmail(jsonObject.optString("email", ""));
// 解析嵌套对象
if (jsonObject.has("address")) {
JSONObject addressJson = jsonObject.getJSONObject("address");
Address address = new Address();
address.setStreet(addressJson.getString("street"));
address.setCity(addressJson.getString("city"));
user.setAddress(address);
}
// 解析数组
if (jsonObject.has("tags")) {
JSONArray tagsArray = jsonObject.getJSONArray("tags");
List<String> tags = new ArrayList<>();
for (int i = 0; i < tagsArray.length(); i++) {
tags.add(tagsArray.getString(i));
}
user.setTags(tags);
}
return user;
}
// 生成JSON
public static String toJson(Object object) {
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
return gson.toJson(object);
}
}
XML解析
public class XmlParser {
// 使用Pull解析器
public static List<Item> parseXml(InputStream inputStream)
throws XmlPullParserException, IOException {
List<Item> items = new ArrayList<>();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(inputStream, "UTF-8");
int eventType = parser.getEventType();
Item currentItem = null;
while (eventType != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
switch (eventType) {
case XmlPullParser.START_TAG:
if ("item".equals(tagName)) {
currentItem = new Item();
} else if (currentItem != null) {
if ("title".equals(tagName)) {
currentItem.setTitle(parser.nextText());
} else if ("description".equals(tagName)) {
currentItem.setDescription(parser.nextText());
} else if ("link".equals(tagName)) {
currentItem.setLink(parser.nextText());
}
}
break;
case XmlPullParser.END_TAG:
if ("item".equals(tagName) && currentItem != null) {
items.add(currentItem);
currentItem = null;
}
break;
}
eventType = parser.next();
}
return items;
}
// 使用DOM解析
public static Document parseWithDom(InputStream inputStream)
throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(inputStream);
}
}
网络安全
HTTPS配置
public class HttpsUtils {
// 信任所有证书(仅用于测试)
public static void trustAllCertificates() {
try {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
} catch (Exception e) {
e.printStackTrace();
}
}
// 使用自定义证书
public static SSLSocketFactory getSSLSocketFactory(Context context, int[] certificates) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
for (int i = 0; i < certificates.length; i++) {
InputStream certificate = context.getResources().openRawResource(certificates[i]);
keyStore.setCertificateEntry(String.valueOf(i),
certificateFactory.generateCertificate(certificate));
if (certificate != null) {
certificate.close();
}
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
数据加密
public class EncryptionUtils {
private static final String AES_MODE = "AES/CBC/PKCS7Padding";
private static final String CHARSET = "UTF-8";
// AES加密
public static String encrypt(String data, String key, String iv) {
try {
Cipher cipher = Cipher.getInstance(AES_MODE);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(CHARSET), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(CHARSET));
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(data.getBytes(CHARSET));
return Base64.encodeToString(encrypted, Base64.NO_WRAP);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// AES解密
public static String decrypt(String data, String key, String iv) {
try {
byte[] encrypted = Base64.decode(data, Base64.NO_WRAP);
Cipher cipher = Cipher.getInstance(AES_MODE);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(CHARSET), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes(CHARSET));
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted, CHARSET);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// MD5加密
public static String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
String hex = Integer.toHexString(0xFF & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
性能优化
1. 使用缓存
public class CacheManager {
private static final long CACHE_SIZE = 10 * 1024 * 1024; // 10MB
private static final String CACHE_DIR = "http_cache";
public static Cache createCache(Context context) {
File cacheDir = new File(context.getCacheDir(), CACHE_DIR);
return new Cache(cacheDir, CACHE_SIZE);
}
public static OkHttpClient createCachedClient(Context context) {
return new OkHttpClient.Builder()
.cache(createCache(context))
.addNetworkInterceptor(new CacheInterceptor())
.build();
}
private static class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// 无网络时使用缓存
if (!isNetworkAvailable()) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (isNetworkAvailable()) {
// 有网络时缓存60秒
response = response.newBuilder()
.header("Cache-Control", "public, max-age=60")
.removeHeader("Pragma")
.build();
} else {
// 无网络时缓存4周
response = response.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=2419200")
.removeHeader("Pragma")
.build();
}
return response;
}
private boolean isNetworkAvailable() {
// 检查网络状态
return true;
}
}
}
2. 请求合并和批处理
public class BatchRequestManager {
private final List<Request> pendingRequests = new ArrayList<>();
private final Handler handler = new Handler();
private final Runnable batchRunnable = this::executeBatch;
private static final long BATCH_DELAY = 100; // 100ms
public void addRequest(Request request) {
synchronized (pendingRequests) {
pendingRequests.add(request);
}
// 延迟执行批处理
handler.removeCallbacks(batchRunnable);
handler.postDelayed(batchRunnable, BATCH_DELAY);
}
private void executeBatch() {
List<Request> requests;
synchronized (pendingRequests) {
requests = new ArrayList<>(pendingRequests);
pendingRequests.clear();
}
// 执行批量请求
for (Request request : requests) {
// 执行请求
}
}
}
3. 图片加载优化
public class ImageLoader {
private final LruCache<String, Bitmap> memoryCache;
private final DiskLruCache diskCache;
public ImageLoader(Context context) {
// 内存缓存
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
memoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
// 磁盘缓存
File cacheDir = new File(context.getCacheDir(), "images");
diskCache = DiskLruCache.open(cacheDir, 1, 1, 50 * 1024 * 1024);
}
public void loadImage(String url, ImageView imageView) {
// 先从内存缓存获取
Bitmap bitmap = memoryCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
// 从磁盘缓存获取
bitmap = getBitmapFromDisk(url);
if (bitmap != null) {
memoryCache.put(url, bitmap);
imageView.setImageBitmap(bitmap);
return;
}
// 从网络加载
downloadImage(url, imageView);
}
private void downloadImage(String url, ImageView imageView) {
new AsyncTask<Void, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(Void... params) {
try {
URL imageUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream input = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(input);
// 保存到缓存
memoryCache.put(url, bitmap);
saveBitmapToDisk(url, bitmap);
return bitmap;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
}.execute();
}
}
最佳实践
1. 使用依赖注入
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
}
2. 错误处理
public class ErrorHandler {
public static void handleError(Context context, Throwable error) {
String message;
if (error instanceof IOException) {
message = "网络连接失败,请检查网络设置";
} else if (error instanceof HttpException) {
HttpException httpException = (HttpException) error;
switch (httpException.code()) {
case 401:
message = "认证失败,请重新登录";
break;
case 404:
message = "请求的资源不存在";
break;
case 500:
message = "服务器错误,请稍后重试";
break;
default:
message = "请求失败: " + httpException.code();
}
} else if (error instanceof JsonSyntaxException) {
message = "数据解析失败";
} else {
message = "未知错误: " + error.getMessage();
}
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}
3. 请求重试
public class RetryInterceptor implements Interceptor {
private static final int MAX_RETRY = 3;
private static final long RETRY_DELAY = 1000;
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = null;
IOException lastException = null;
for (int i = 0; i < MAX_RETRY; i++) {
try {
response = chain.proceed(request);
if (response.isSuccessful()) {
return response;
}
// 服务器错误不重试
if (response.code() >= 500) {
return response;
}
} catch (IOException e) {
lastException = e;
if (i < MAX_RETRY - 1) {
try {
Thread.sleep(RETRY_DELAY * (i + 1));
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
}
if (lastException != null) {
throw lastException;
}
return response;
}
}
调试技巧
1. 使用Charles或Fiddler抓包
// 配置代理
public static OkHttpClient createDebugClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
// 设置代理
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("192.168.1.100", 8888));
builder.proxy(proxy);
// 信任所有证书(仅调试使用)
builder.hostnameVerifier((hostname, session) -> true);
builder.sslSocketFactory(createInsecureSocketFactory(),
createInsecureTrustManager());
}
return builder.build();
}
2. 网络日志
public class NetworkLogger {
private static final String TAG = "Network";
public static void logRequest(Request request) {
Log.d(TAG, "━━━━━━ REQUEST ━━━━━━");
Log.d(TAG, "URL: " + request.url());
Log.d(TAG, "Method: " + request.method());
Headers headers = request.headers();
for (int i = 0; i < headers.size(); i++) {
Log.d(TAG, headers.name(i) + ": " + headers.value(i));
}
if (request.body() != null) {
try {
Buffer buffer = new Buffer();
request.body().writeTo(buffer);
Log.d(TAG, "Body: " + buffer.readUtf8());
} catch (IOException e) {
Log.d(TAG, "Body: [Error reading body]");
}
}
}
public static void logResponse(Response response) {
Log.d(TAG, "━━━━━━ RESPONSE ━━━━━━");
Log.d(TAG, "Code: " + response.code());
Log.d(TAG, "Message: " + response.message());
Headers headers = response.headers();
for (int i = 0; i < headers.size(); i++) {
Log.d(TAG, headers.name(i) + ": " + headers.value(i));
}
}
}
常见问题
1. NetworkOnMainThreadException
问题:在主线程执行网络请求
解决方案:
// 使用AsyncTask
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... params) {
// 网络请求
return HttpHelper.doGet(url);
}
@Override
protected void onPostExecute(String result) {
// 更新UI
}
}.execute();
// 或使用协程(Kotlin)
lifecycleScope.launch(Dispatchers.IO) {
val result = api.getData()
withContext(Dispatchers.Main) {
// 更新UI
}
}
2. SSL证书问题
问题:HTTPS证书验证失败
解决方案:
// 1. 使用正确的证书
// 2. 添加自定义证书到raw目录
// 3. 配置network_security_config.xml
3. 大文件传输
问题:上传/下载大文件时内存溢出
解决方案:
// 使用流式传输
@Streaming
@GET
Call<ResponseBody> downloadFile(@Url String url);
// 分块上传
public void uploadLargeFile(File file) {
long fileSize = file.length();
long chunkSize = 1024 * 1024; // 1MB
long chunks = (fileSize + chunkSize - 1) / chunkSize;
for (int i = 0; i < chunks; i++) {
// 上传每个块
}
}
相关文章
- Android Service服务组件
- Android数据存储
- Android多线程编程
- Android权限管理
- Android架构组件
总结
Android网络编程是构建现代应用的基础。掌握网络编程需要:
- 选择合适的网络库:根据项目需求选择HttpURLConnection、OkHttp或Retrofit
- 处理网络权限:正确配置网络权限和安全策略
- 数据解析:熟练使用JSON和XML解析
- 性能优化:使用缓存、批处理、图片优化等技术
- 错误处理:优雅地处理各种网络错误
- 安全考虑:使用HTTPS、数据加密保护用户隐私
通过合理使用这些技术,可以构建高效、安全、用户体验良好的网络应用。