利用 Java 原生序列化機制,配合 HttpURLConnection,實現 Android 客戶端與 Servlet 服務端的對象級雙向通訊。適合內部系統、簡單 RPC 场景。
serialVersionUID)java.io.SerializableContent-Type: application/x-java-serialized-objectpackage com.example.data; // 雙端包名必須一致!
public class dataPost implements Serializable {
private static final long serialVersionUID = 1L; // 版本鎖定,防止反序列化失敗
private String name;
private String password;
public dataPost(String name, String password) {
this.name = name;
this.password = password;
}
// Getter/Setter 省略
}
public String sendDataByPost(String urlStr) {
String result = "";
try {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 基礎配置
conn.setConnectTimeout(2000);
conn.setReadTimeout(2000);
conn.setDoOutput(true); // 允許寫出
conn.setDoInput(true); // 允許讀入
conn.setUseCaches(false);
conn.setRequestMethod("POST");
// 關鍵:聲明發送序列化對象
conn.setRequestProperty("Content-Type", "application/x-java-serialized-object");
// 構建對象輸出流
ObjectOutputStream objOut = new ObjectOutputStream(conn.getOutputStream());
// 發送對象
dataPost data = new dataPost("Tom", null);
objOut.writeObject(data);
objOut.flush();
objOut.close();
// 讀取服務端返回對象
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
ObjectInputStream objIn = new ObjectInputStream(conn.getInputStream());
dataPost response = (dataPost) objIn.readObject();
result = response.getPassword(); // 獲取服務端設置的密碼
objIn.close();
} else {
Log.w("HTTP", "連接失敗: " + conn.getResponseCode());
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
@WebServlet("/dataExchange")
public class DataExchangeServlet extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 1. 反序列化請求對象
ObjectInputStream objIn = new ObjectInputStream(req.getInputStream());
dataPost data = (dataPost) objIn.readObject();
objIn.close();
// 2. 業務處理
data.setPassword("9964646"); // 模擬業務邏輯
// 3. 序列化返回對象
resp.setContentType("application/x-java-serialized-object");
ObjectOutputStream objOut = new ObjectOutputStream(resp.getOutputStream());
objOut.writeObject(data);
objOut.flush();
objOut.close();
}
}
| 坑點 | 現象 | 解決 |
|---|---|---|
| URL 中文亂碼 | 請求參數含中文導致 Server 解析異常 | URI uri = new URI(url, "utf-8"); url = uri.toString(); 進行編碼 |
| 主線程網絡請求崩潰 | NetworkOnMainThreadException | **必須**在子線程/AsyncTask/RxJava/協程中執行網絡請求 |
| 包名不一致 | ClassNotFoundException: com.example.data.dataPost | 雙端 **完全相同包名**、類名、字段順序,建議提取為公共 Module/JAR |
| serialVersionUID 不匹配 | InvalidClassException: local class incompatible | 雙端**顯式聲明相同 serialVersionUID**,勿依賴編譯器自動生成 |
new Thread(() -> {
String result = sendDataByPost("https://10.0.2.2:8080/server/dataExchange");
runOnUiThread(() -> {
if (!result.isEmpty()) {
Log.i("HTTP", "服務端返回: " + result);
// 更新 UI
}
});
}).start();
| 優點 | 缺點 |
|---|---|
| 零依賴,JDK 原生 | Java 序列化二進制格式不跨語言 |
| 對象圖完整傳遞 (含引用關係) | 序列化體積大、效能低、不安全 (反序列化漏洞) |
| 代碼簡單,適合快速原型 | 版本演進極難兼容 (增刪字段易破壞) |
理解原生序列化通訊原理,有助於理解上層框架 (Retrofit/gRPC) 的底層行為,但**生產系統請優先選擇成熟的序列化協議與通訊框架**。