凌峰创科服务平台

Android如何解析服务器返回的XML数据?

  1. 传统方式:使用 HttpURLConnection (适用于较旧的 API 或不想引入第三方库的场景)
  2. 现代方式:使用第三方库 Retrofit + OkHttp + Gson (目前业界最主流、最推荐的方式)

概念:Android 网络请求的基本流程

  1. 发起请求: 客户端(你的 App)向服务器指定的 URL 发送一个网络请求。
  2. 服务器响应: 服务器处理请求,并返回一个响应,其中包含状态码(如 200 表示成功)和响应体(即 XML 数据)。
  3. 解析响应: 客户端接收到 XML 数据后,需要将其解析成 Android 可以方便操作的 Java/Kotlin 对象。
  4. 更新 UI: 将解析后的数据展示在界面上。

使用 HttpURLConnection (原生 API)

这是 Android SDK 自带的网络请求方式,无需添加任何依赖。

Android如何解析服务器返回的XML数据?-图1
(图片来源网络,侵删)

步骤 1: 添加网络权限

app/src/main/AndroidManifest.xml 文件中,必须声明网络权限。

<manifest ...>
    <!-- 允许应用访问网络 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <application ...>
        ...
    </application>
</manifest>

步骤 2: 在后台线程执行网络请求

非常重要! Android 4.0 (API level 11) 以后,网络操作不允许在主线程(UI 线程)中进行,否则会抛出 NetworkOnMainThreadException 异常,我们可以使用 AsyncTaskThread + Handler 或现代的 Coroutines/RxJava 来在后台执行任务。

这里我们使用 AsyncTask 作为示例(虽然它已不推荐用于新项目,但非常适合演示基本原理)。

步骤 3: 编写代码获取和解析 XML

假设服务器返回一个简单的 XML 文件,内容如下:

Android如何解析服务器返回的XML数据?-图2
(图片来源网络,侵删)
<users>
    <user>
        <id>1</id>
        <name>张三</name>
        <email>zhangsan@example.com</email>
    </user>
    <user>
        <id>2</id>
        <name>李四</name>
        <email>lisi@example.com</email>
    </user>
</users>

完整代码示例:

import android.os.AsyncTask
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
class MainActivity : AppCompatActivity() {
    private lateinit var tvResult: TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tvResult = findViewById(R.id.tvResult)
        val btnFetch: Button = findViewById(R.id.btnFetch)
        btnFetch.setOnClickListener {
            // 执行异步任务
            FetchXmlTask().execute("https://your-server.com/path/to/users.xml")
        }
    }
    // AsyncTask 的三个泛型参数分别是:
    // Params: execute() 方法传入的参数类型 (String)
    // Progress: 后台任务执行进度的类型 (这里不需要)
    // Result: 后台任务返回的结果类型 (String)
    private inner class FetchXmlTask : AsyncTask<String, Void, String>() {
        override fun doInBackground(vararg params: String): String? {
            val urlString = params[0]
            var connection: HttpURLConnection? = null
            var inputStream: InputStream? = null
            return try {
                val url = URL(urlString)
                connection = url.openConnection() as HttpURLConnection
                connection.connect()
                val responseCode = connection.responseCode
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    inputStream = connection.inputStream
                    parseXml(inputStream)
                } else {
                    "Error: HTTP Response Code $responseCode"
                }
            } catch (e: Exception) {
                e.printStackTrace()
                "Error: ${e.message}"
            } finally {
                inputStream?.close()
                connection?.disconnect()
            }
        }
        override fun onPostExecute(result: String?) {
            // 在主线程更新 UI
            tvResult.text = result
        }
        // 解析 XML 的函数
        private fun parseXml(inputStream: InputStream): String {
            val factory = DocumentBuilderFactory.newInstance()
            val builder = factory.newDocumentBuilder()
            val document: Document = builder.parse(inputStream)
            // 获取根节点
            val documentElement: Element = document.documentElement
            val nodeList: NodeList = documentElement.getElementsByTagName("user")
            val sb = StringBuilder()
            for (i in 0 until nodeList.length) {
                val node: Node = nodeList.item(i)
                if (node.nodeType == Node.ELEMENT_NODE) {
                    val userElement = node as Element
                    val id = userElement.getElementsByTagName("id").item(0).textContent
                    val name = userElement.getElementsByTagName("name").0).textContent
                    val email = userElement.getElementsByTagName("email").item(0).textContent
                    sb.append("用户ID: $id\n")
                    sb.append("姓名: $name\n")
                    sb.append("邮箱: $email\n")
                    sb.append("---------------------\n")
                }
            }
            return sb.toString()
        }
    }
}

使用 Retrofit (现代、推荐的方式)

Retrofit 是一个由 Square 公司开发的类型安全的 HTTP 客户端,它能极大地简化网络请求,特别是配合 OkHttpGson (或其他转换器) 使用时。

步骤 1: 添加依赖

app/build.gradle.kts (或 build.gradle) 文件中添加以下依赖:

// build.gradle.kts (Kotlin DSL)
dependencies {
    // Retrofit for networking
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    // Converter for parsing XML
    implementation("com.squareup.retrofit2:converter-simplexml:2.9.0")
    // OkHttp (Retrofit uses it internally)
    implementation("com.squareup.okhttp3:okhttp:4.11.0")
    // For running coroutines on Android main thread
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
}
// build.gradle (Groovy DSL)
dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-simplexml:2.9.0'
    implementation 'com.squareup.okhttp3:okhttp:4.11.0'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
}

步骤 2: 添加网络权限

同方式一,在 AndroidManifest.xml 中添加 INTERNET 权限。

Android如何解析服务器返回的XML数据?-图3
(图片来源网络,侵删)

步骤 3: 创建数据模型类

你需要创建一个 Kotlin 数据类,其结构与服务器返回的 XML 相对应。SimpleXMLConverter 会自动进行映射。

import org.simpleframework.xml.Element
import org.simpleframework.xml.ElementList
import org.simpleframework.xml.Root
// Root 注解对应 XML 的根节点 <users>
@Root(name = "users", strict = false) // strict = false 允许有未知标签
data class UserResponse(
    // ElementList 注解对应 <users> 标签下的所有 <user> 标签
    // entry = "user" 指定列表中每个元素的标签名
    @field:ElementList(entry = "user", inline = true)
    var users: List<User>? = null
)
// User 类对应 <user> 标签
data class User(
    @field:Element(name = "id") // name 指定对应的 XML 标签名
    var id: Int = 0,
    @field:Element(name = "name")
    var name: String = "",
    @field:Element(name = "email")
    var email: String = ""
)

步骤 4: 创建 Retrofit API 接口

定义一个接口来描述你的 API 请求。

import retrofit2.Call
import retrofit2.http.GET
interface ApiService {
    // @GET 注解表示这是一个 GET 请求
    // "users.xml" 是 API 的相对路径
    @GET("users.xml")
    fun getUsers(): Call<UserResponse>
}

步骤 5: 执行请求并处理结果

在 Activity 或 ViewModel 中使用 Retrofit 发起请求,这里我们使用 lifecycleScopecoroutines 来处理异步,这是现代 Android 开发的最佳实践。

import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.simpleframework.xml.convert.Registry
import org.simpleframework.xml.convert.RegistryStrategy
import org.simpleframework.xml.core.Persister
import retrofit2.Retrofit
import retrofit2.converter.simplexml.SimpleXmlConverterFactory
class RetrofitActivity : AppCompatActivity() {
    private lateinit var tvResult: TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main) // 假设你有一个布局文件
        tvResult = findViewById(R.id.tvResult)
        val btnFetch: Button = findViewById(R.id.btnFetch)
        btnFetch.setOnClickListener {
            fetchUsersWithRetrofit()
        }
    }
    private fun fetchUsersWithRetrofit() {
        // lifecycleScope.launch 会在 Activity/Fragment 销毁时自动取消协程
        lifecycleScope.launch {
            // 在 IO 线程执行网络请求
            val result = withContext(Dispatchers.IO) {
                try {
                    // 1. 创建 Retrofit 实例
                    val retrofit = Retrofit.Builder()
                        .baseUrl("https://your-server.com/path/to/") // 必须以 / 
                        .addConverterFactory(createSimpleXmlConverterFactory())
                        .build()
                    // 2. 创建 API 接口的实例
                    val apiService = retrofit.create(ApiService::class.java)
                    // 3. 发起同步请求 (在 withContext 内部是安全的)
                    val response = apiService.getUsers().execute()
                    // 4. 检查响应是否成功
                    if (response.isSuccessful) {
                        response.body()?.users?.joinToString("\n") { user ->
                            "ID: ${user.id}, Name: ${user.name}, Email: ${user.email}"
                        } ?: "Empty response"
                    } else {
                        "Error: ${response.code()} ${response.message()}"
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                    "Error: ${e.message}"
                }
            }
            // 5. 在主线程更新 UI
            tvResult.text = result
        }
    }
    // 为了解决 SimpleXML 的命名空间问题,有时需要自定义 Persister
    private fun createSimpleXmlConverterFactory(): SimpleXmlConverterFactory {
        val registry = Registry()
        registry.bind(User::class.java, UserConverter())
        // 可以继续绑定其他需要的类
        val strategy = RegistryStrategy(registry)
        val persister = Persister(strategy)
        return SimpleXmlConverterFactory.create(persister)
    }
}
// 如果你的 XML 有命名空间或者需要更复杂的解析,可以创建自定义 Converter
// 对于简单示例,可以省略这个类
class UserConverter : Converter<User> {
    // ... 实现转换逻辑 ...
}

总结与对比

特性 HttpURLConnection Retrofit
依赖 无需添加,Android 原生 需要添加 retrofit, okhttp, converter 等库
易用性 较低,需要手动处理连接、流、解析等 极高,注解驱动,代码简洁
类型安全 弱,手动解析容易出错 ,通过接口和数据模型保证类型安全
异步处理 需要自己实现(如 AsyncTask, Handler 优秀,天然支持 Call/CallbackCoroutinesRxJava
功能扩展 有限 强大,支持拦截器、适配器、多种数据格式转换(JSON, XML, Protobuf等)
适用场景 学习网络原理、轻量级、无依赖需求 几乎所有现代 Android App,特别是复杂项目

强烈建议:在新的 Android 项目中,优先选择 Retrofit 的方式,它能让你更专注于业务逻辑,而不是繁琐的网络操作细节,对于初学者,理解 HttpURLConnection 的工作原理也是有价值的。

分享:
扫描分享到社交APP
上一篇
下一篇