在Android应用开发过程中,从网络下载XML数据并完成解析是常见需求,而给用户展示清晰的加载进度条能够提升交互体验,避免用户因等待产生困惑。实现这个功能需要同时处理网络下载、XML解析、进度更新三个核心环节,并且要保证三者之间的状态同步。
核心实现思路
整个功能的流程可以分为三个步骤:首先通过异步任务发起XML文件下载,在下载过程中实时获取已下载的字节数,计算下载进度并回调更新UI;下载完成后立即启动XML解析任务,解析过程中可以根据已解析的节点数量或者已读取的字节数计算解析进度,同样同步更新进度条;最后解析完成后将XML数据传递给上层业务使用,同时隐藏进度条。
下载阶段进度更新实现
我们可以使用AsyncTask配合HttpURLConnection实现下载功能,在下载循环中不断计算进度并发布到UI线程。以下是下载任务的示例代码:
import android.os.AsyncTask;
import android.widget.ProgressBar;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class XmlDownloadTask extends AsyncTask<String, Integer, byte[]> {
private ProgressBar progressBar;
private DownloadCallback callback;
public XmlDownloadTask(ProgressBar progressBar, DownloadCallback callback) {
this.progressBar = progressBar;
this.callback = callback;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// 初始化进度条
progressBar.setMax(100);
progressBar.setProgress(0);
}
@Override
protected byte[] doInBackground(String... params) {
String xmlUrl = params[0];
HttpURLConnection connection = null;
InputStream inputStream = null;
try {
URL url = new URL(xmlUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(15000);
connection.setReadTimeout(15000);
// 获取总文件大小
int totalSize = connection.getContentLength();
inputStream = connection.getInputStream();
byte[] buffer = new byte[1024];
int len;
int downloadedSize = 0;
// 用于存储下载的字节数据
java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
downloadedSize += len;
// 计算下载进度并发布
if (totalSize > 0) {
int progress = (int) ((downloadedSize * 100L) / totalSize);
publishProgress(progress);
}
}
return outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (connection != null) {
connection.disconnect();
}
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// 更新下载进度
progressBar.setProgress(values[0]);
}
@Override
protected void onPostExecute(byte[] result) {
super.onPostExecute(result);
if (callback != null) {
callback.onDownloadComplete(result);
}
}
public interface DownloadCallback {
void onDownloadComplete(byte[] xmlData);
}
}
XML解析阶段进度更新实现
下载完成后,我们可以使用XmlPullParser解析XML数据,通过统计已解析的节点数量来计算解析进度。以下是解析任务的示例代码:
import android.os.AsyncTask;
import android.widget.ProgressBar;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
public class XmlParseTask extends AsyncTask<byte[], Integer, List<String>> {
private ProgressBar progressBar;
private ParseCallback callback;
// 假设XML总节点数已知,实际可以通过预解析获取
private int totalNodeCount = 50;
public XmlParseTask(ProgressBar progressBar, ParseCallback callback) {
this.progressBar = progressBar;
this.callback = callback;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// 重置进度条为解析阶段
progressBar.setMax(100);
progressBar.setProgress(0);
}
@Override
protected List<String> doInBackground(byte[]... params) {
byte[] xmlData = params[0];
List<String> resultList = new ArrayList<>();
try {
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser parser = factory.newPullParser();
ByteArrayInputStream inputStream = new ByteArrayInputStream(xmlData);
parser.setInput(inputStream, "UTF-8");
int eventType = parser.getEventType();
int parsedNodeCount = 0;
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
String tagName = parser.getName();
if ("item".equals(tagName)) {
// 解析item节点内容
String itemValue = parser.nextText();
resultList.add(itemValue);
parsedNodeCount++;
// 计算解析进度并发布
int progress = (int) ((parsedNodeCount * 100L) / totalNodeCount);
publishProgress(progress);
}
}
eventType = parser.next();
}
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return resultList;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// 更新解析进度
progressBar.setProgress(values[0]);
}
@Override
protected void onPostExecute(List<String> result) {
super.onPostExecute(result);
if (callback != null) {
callback.onParseComplete(result);
}
}
public interface ParseCallback {
void onParseComplete(List<String> dataList);
}
}
完整调用示例
在Activity中组合下载和解析任务,实现完整的进度展示逻辑:
import android.os.Bundle;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
private TextView resultTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progress_bar);
resultTv = findViewById(R.id.result_tv);
// 发起下载解析流程
startDownloadAndParse("https://ipipp.com/test.xml");
}
private void startDownloadAndParse(String xmlUrl) {
XmlDownloadTask downloadTask = new XmlDownloadTask(progressBar, new XmlDownloadTask.DownloadCallback() {
@Override
public void onDownloadComplete(byte[] xmlData) {
if (xmlData != null) {
// 下载完成,启动解析任务
XmlParseTask parseTask = new XmlParseTask(progressBar, new XmlParseTask.ParseCallback() {
@Override
public void onParseComplete(List<String> dataList) {
// 解析完成,展示结果
StringBuilder sb = new StringBuilder();
for (String item : dataList) {
sb.append(item).append("n");
}
resultTv.setText(sb.toString());
progressBar.setProgress(100);
}
});
parseTask.execute(xmlData);
} else {
resultTv.setText("下载失败");
}
}
});
downloadTask.execute(xmlUrl);
}
}
注意事项
- 网络请求和XML解析都属于耗时操作,必须在子线程执行,避免阻塞UI线程导致应用无响应。
- 进度更新操作需要在UI线程执行,
AsyncTask的onProgressUpdate和onPostExecute方法已经运行在UI线程,无需额外处理线程切换。 - 如果XML文件大小未知,下载阶段可以只展示 indeterminate 形式的进度条,等获取到总大小后再切换为精确进度模式。
- 解析阶段的进度计算如果无法提前获取总节点数,可以通过已读取的字节数占下载总字节数的比例来估算进度。
AndroidXML解析进度条DownloadManager修改时间:2026-06-22 09:07:07