睿诚科技协会

Android 4.0访问网络如何实现?

在 Android 4.0 时代,有两个主流的网络访问方式:

Android 4.0访问网络如何实现?-图1
(图片来源网络,侵删)
  1. HttpURLConnection:这是 Google 推荐的标准方式,功能强大,相对简单。
  2. Apache HttpClient:在早期版本中非常流行,但在 Android 6.0 (API 23) 中已被彻底移除。

重要提示:虽然 Android 4.0 仍然支持 HttpClient,但为了代码的长期可维护性和未来的兼容性,强烈建议你优先使用 HttpURLConnection

下面我将围绕 HttpURLConnection 展开讲解,因为它更符合现代 Android 开发的最佳实践。


核心步骤

在 Android 中进行网络操作,通常遵循以下步骤:

  1. 添加网络权限:在 AndroidManifest.xml 中声明应用需要访问网络的权限。
  2. 选择执行线程网络操作绝对不能在主线程(UI线程)中执行,否则会抛出 NetworkOnMainThreadException 异常并可能导致应用无响应,必须在后台线程(如 AsyncTask, Thread, Thread + Handler, ExecutorService 或现代的 Coroutine/RxJava)中执行。
  3. 创建和配置 HttpURLConnection:指定 URL,设置请求方法(GET/POST等),添加请求头等。
  4. 发送请求并获取响应码:对于 GET 请求,此步骤会直接获取服务器响应。
  5. 读取服务器返回的数据流:将输入流转换为字符串(如 JSON 或 XML)。
  6. 解析数据并更新 UI:将解析后的数据通过 HandlerrunOnUiThread 等方式传递回主线程,以更新 UI。

详细代码示例 (使用 HttpURLConnection 和 AsyncTask)

这是一个完整的、在 Android 4.0+ 环境下工作的示例,使用 AsyncTask 在后台线程执行网络请求,并在主线程更新 UI。

Android 4.0访问网络如何实现?-图2
(图片来源网络,侵删)

添加网络权限

打开你的 app/src/main/AndroidManifest.xml 文件,在 <manifest> 标签内添加 <uses-permission>

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.networkdemo">
    <!-- 1. 添加网络权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        ... >
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

创建布局文件

res/layout/activity_main.xml 中添加一个用于显示结果的 TextView

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <TextView
        android:id="@+id/tv_result"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="点击按钮获取网络数据"
        android:textSize="18sp" />
    <Button
        android:id="@+id/btn_fetch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv_result"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="20dp"
        android:text="获取数据" />
</RelativeLayout>

编写网络请求逻辑 (AsyncTask)

在 Activity 中,我们将创建一个继承自 AsyncTask 的内部类来处理网络请求。

为什么用 AsyncTask? 在 Android 4.0 时代,AsyncTask 是处理后台任务并更新 UI 的标准方式,它封装了线程和 Handler 的复杂性,非常适合这种“一次性”的网络请求。

注意AsyncTask 在 Android 3.0 (Honeycomb) 后的默认行为是串行执行,所有任务会按顺序排队执行,对于简单的网络请求,这通常不是问题。

Android 4.0访问网络如何实现?-图3
(图片来源网络,侵删)
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class MainActivity extends Activity {
    private TextView tvResult;
    private Button btnFetch;
    // 一个用于测试的公开API
    private static final String API_URL = "https://jsonplaceholder.typicode.com/todos/1";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvResult = findViewById(R.id.tv_result);
        btnFetch = findViewById(R.id.btn_fetch);
        btnFetch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 当点击按钮时,启动AsyncTask
                new NetworkTask().execute(API_URL);
            }
        });
    }
    /**
     * 定义一个AsyncTask来执行网络请求
     * Params: 传入的参数类型,这里是String类型的URL
     * Progress: 后台任务进度的类型,这里不需要
     * Result: 后台任务返回结果的类型,这里是String类型(JSON数据)
     */
    private class NetworkTask extends AsyncTask<String, Void, String> {
        // 4.0+ 可以显示一个ProgressDialog来提示用户等待
        // ProgressDialog progressDialog;
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            // 在任务开始前执行,通常在UI线程上显示一个加载对话框
            // progressDialog = ProgressDialog.show(MainActivity.this, "请稍候", "正在加载数据...");
            tvResult.setText("正在加载...");
        }
        @Override
        protected String doInBackground(String... urls) {
            // 3. 在后台线程中执行网络请求
            if (urls == null || urls.length == 0) {
                return null;
            }
            String urlString = urls[0];
            HttpURLConnection urlConnection = null;
            BufferedReader reader = null;
            StringBuilder response = new StringBuilder();
            try {
                URL url = new URL(urlString);
                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET"); // 设置请求方法为GET
                urlConnection.setConnectTimeout(15000); // 设置连接超时时间(毫秒)
                urlConnection.setReadTimeout(15000);  // 设置读取超时时间(毫秒)
                urlConnection.connect(); // 发起请求
                // 4. 获取响应码
                int responseCode = urlConnection.getResponseCode();
                if (responseCode == HttpURLConnection.HTTP_OK) { // 200 OK
                    // 5. 读取输入流
                    InputStream inputStream = urlConnection.getInputStream();
                    if (inputStream == null) {
                        return null;
                    }
                    reader = new BufferedReader(new InputStreamReader(inputStream));
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line).append("\n");
                    }
                } else {
                    // 服务器返回错误
                    return "Error: Server returned HTTP " + responseCode;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return "Error: " + e.getMessage();
            } finally {
                // 6. 关闭连接和流
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return response.toString();
        }
        @Override
        protected void onPostExecute(String result) {
            // 7. 任务完成后,在UI线程上更新UI
            super.onPostExecute(result);
            // if (progressDialog != null && progressDialog.isShowing()) {
            //     progressDialog.dismiss();
            // }
            if (result != null) {
                tvResult.setText(result);
            } else {
                tvResult.setText("获取数据失败");
                Toast.makeText(MainActivity.this, "网络请求失败", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

关键点解析

  1. AsyncTask 的三个泛型参数

    • Params: doInBackground 方法的输入参数类型,我们传入一个 String (URL)。
    • Progress: onProgressUpdate 方法的参数类型,我们不需要进度更新,所以用 Void
    • Result: doInBackground 的返回值和 onPostExecute 的输入参数类型,我们返回一个 String (JSON 数据)。
  2. doInBackground(String... urls):

    • 这是核心的网络执行方法,运行在后台线程池中。
    • 使用 try-catch-finally 块来处理异常
分享:
扫描分享到社交APP
上一篇
下一篇