首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android HTTPS 上傳 XML 並解析回應實現版本更新檢查完整流程

     編輯:程式設計綜合問答

架構圖

App (Client) → HTTPS POST (XML Body) → Server → HTTPS Response (XML) → Parse → Version Compare → UI Prompt

核心組件

1. 網絡層:OkHttp 4.x + XmlPullParser (Android 內建)

// build.gradle
implementation "com.squareup.okhttp3:okhttp:4.12.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"

2. 統一網絡客戶端 (單例)

public class ApiClient {
    private static final OkHttpClient CLIENT = new OkHttpClient.Builder()
        .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
        .connectTimeout(10, TimeUnit.SECONDS)
        .readTimeout(30, TimeUnit.SECONDS)
        .writeTimeout(30, TimeUnit.SECONDS)
        .hostnameVerifier((hostname, session) -> true) // 僅測試環境,生產需嚴格驗證
        .build();

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

3. 版本更新請求/回應 Bean

@Data
public class UpdateRequest {
    private String appId = "com.example.app";
    private String currentVersion = BuildConfig.VERSION_NAME;
    private int currentVersionCode = BuildConfig.VERSION_CODE;
    private String deviceId; // 可選
    private String os = "Android";
}

@Data
public class UpdateResponse {
    private int code; // 200 成功
    private String message;
    private UpdateInfo data;
}

@Data
public class UpdateInfo {
    private String versionName;     // "2.1.0"
    private int versionCode;        // 20100
    private String downloadUrl;     // "https://cdn.example.com/app-v2.1.0.apk"
    private String releaseNotes;    // "修復崩潰、新增功能..."
    private boolean forceUpdate;    // true: 必須更新
}

4. XML 序列化/反序列化 (Simple-XML 或手工)

**輕量選擇:手工拼接/解析** (無反射、包體積小)

public class XmlUtil {
    // Request → XML String
    public static String toXml(UpdateRequest req) {
        return "" +
            "" + escape(req.appId) + "" +
            "" + escape(req.currentVersion) + "" +
            "" + req.currentVersionCode + "" +
            "" + escape(req.deviceId) + "" +
            "" + escape(req.os) + "" +
            "";
    }

    // Response XML → UpdateResponse
    public static UpdateResponse parse(String xml) throws XmlPullParserException, IOException {
        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(new StringReader(xml));
        UpdateResponse resp = new UpdateResponse();
        UpdateInfo info = null;
        int eventType = parser.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            String tag = parser.getName();
            switch (eventType) {
                case XmlPullParser.START_TAG:
                    if ("data".equals(tag)) info = new UpdateInfo();
                    break;
                case XmlPullParser.TEXT:
                    String text = parser.getText();
                    if (info != null) {
                        switch (tag) {
                            case "versionName": info.versionName = text; break;
                            case "versionCode": info.versionCode = Integer.parseInt(text); break;
                            case "downloadUrl": info.downloadUrl = text; break;
                            case "releaseNotes": info.releaseNotes = text; break;
                            case "forceUpdate": info.forceUpdate = Boolean.parseBoolean(text); break;
                        }
                    } else {
                        switch (tag) {
                            case "code": resp.code = Integer.parseInt(text); break;
                            case "message": resp.message = text; break;
                        }
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if ("data".equals(tag)) resp.data = info;
                    break;
            }
            eventType = parser.next();
        }
        return resp;
    }

    private static String escape(String s) {
        return s == null ? "" : s.replace("&", "&").replace("<", "<").replace(">", ">")
            .replace("\"", """).replace("'", "'");
    }
}

5. 版本檢查協程 (Kotlin) / 線程 (Java)

// Kotlin 協程版
suspend fun checkUpdate(context: Context): UpdateInfo? = withContext(Dispatchers.IO) {
    val req = UpdateRequest().apply { deviceId = getDeviceId(context) }
    val xmlBody = XmlUtil.toXml(req)
    val request = Request.Builder()
        .url("https://api.example.com/update/check")
        .post(xmlBody.toRequestBody("application/xml; charset=utf-8".toMediaType()))
        .build()
    ApiClient.get().newCall(request).execute().use { response ->
        if (!response.isSuccessful) return@withContext null
        val xml = response.body?.string() ?: return@withContext null
        val parsed = XmlUtil.parse(xml)
        if (parsed.code == 200 && parsed.data != null && parsed.data.versionCode > BuildConfig.VERSION_CODE) {
            parsed.data
        } else null
    }
}

// UI 層調用
lifecycleScope.launch {
    val info = checkUpdate(this@MainActivity)
    if (info != null) showUpdateDialog(info)
}

6. 彈窗與靜默下載安裝

private fun showUpdateDialog(info: UpdateInfo) {
    AlertDialog.Builder(this)
        .setTitle("發現新版本 v${info.versionName}")
        .setMessage(info.releaseNotes)
        .setCancelable(!info.forceUpdate)
        .setPositiveButton("立即更新") { _, _ -> startDownload(info.downloadUrl) }
        .setNegativeButton("稍後") { _, _ -> if (info.forceUpdate) finish() }
        .show()
}

private fun startDownload(url: String) {
    val request = DownloadManager.Request(Uri.parse(url))
        .setTitle("App 更新")
        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
        .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "app-update.apk")
    val dm = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
    val downloadId = dm.enqueue(request)
    // 可註冊 BroadcastReceiver 監聽 DOWNLOAD_COMPLETE 自動安裝
}

安全與合規要點

  • 證書釘裹:`CertificatePinner` 綁定服務器證書指紋,防中間人攻擊
  • 簽名一致性:下載 APK 驗證簽名與當前應用一致,防劫持替換
  • 最小權限:`android.permission.INTERNET`、`FOREGROUND_SERVICE` (Android 12+ 下載)、`REQUEST_INSTALL_PACKAGES` (安裝未知來源)
  • 網絡安全配置:`res/xml/network_security_config.xml` 明文禁用、指定信任錨點

總結

**OkHttp + XmlPullParser + 版本比較邏輯 + DownloadManager** 構建輕量、安全、用戶體驗良好的 Android 版本更新檢查機制。核心在於**嚴格的 XML 解析容錯**與**證書釘裹/簽名驗證**保障安全。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved