睿诚科技协会

Android网络应用开发如何入门?

目录

  1. 核心概念:网络请求与响应
  2. Android网络开发演进史
    • 传统方式:HttpURLConnection
    • 推荐方式:OkHttp
    • 数据解析:Gson / Moshi
    • 协程 + Retrofit:现代标准方案
  3. 现代网络请求最佳实践:Retrofit + OkHttp + Coroutines + Gson
    • 为什么是这套组合?
    • 完整实现步骤
    • 代码示例
  4. 处理网络状态
    • 检查网络连接
    • 处理加载状态
    • 优雅地处理错误
  5. 高级主题
    • 数据缓存
    • 文件上传与下载
    • OAuth 2.0 认证
    • WebSocket
  6. 安全与性能
    • HTTPS
    • 防止内存泄漏
    • 网络超时与重试
  7. 学习资源推荐

核心概念:网络请求与响应

所有网络通信都遵循客户端-服务器模型:

Android网络应用开发如何入门?-图1
(图片来源网络,侵删)
  • 客户端:你的Android App。
  • 服务器:提供数据服务的远程计算机(如你的API服务器)。

一个典型的网络请求流程如下:

  1. 客户端发起请求:App创建一个请求,包含:
    • URL:服务器的地址(https://api.example.com/users)。
    • HTTP方法GET(获取数据)、POST(提交数据)、PUT(更新数据)、DELETE(删除数据)等。
    • 请求头:附加信息,如Content-Type(数据类型)、Authorization(认证信息)。
    • 请求体:对于POST/PUT请求,要发送的数据(通常是JSON格式)。
  2. 服务器处理请求:服务器接收请求,执行相应的业务逻辑(如查询数据库)。
  3. 服务器返回响应:服务器将处理结果返回给客户端,包含:
    • 响应码:如200 OK(成功)、404 Not Found(资源不存在)、500 Internal Server Error(服务器错误)。
    • 响应头:附加信息。
    • 响应体:客户端需要的数据(通常是JSON格式)。
  4. 客户端处理响应:App解析响应体,更新UI或执行后续操作。

Android网络开发演进史

Android的网络库在不断进化,了解历史有助于理解为什么现在的方案是最佳选择。

传统方式:HttpURLConnection / HttpClient (已废弃)

  • 优点:Android SDK内置,无需额外依赖。
  • 缺点
    • API繁琐,使用不便。
    • 不支持现代HTTP特性,如HTTP/2。
    • 处理异步请求需要手动开线程和解析,容易出错。
    • HttpClient在API 22已被彻底废弃。

推荐方式:OkHttp

  • 是什么:一个现代、高效、功能丰富的HTTP客户端。
  • 优点
    • 自动管理连接池:复用TCP连接,极大提升性能。
    • 支持HTTP/2:允许在单个连接上并行请求,速度快。
    • 支持GZIP:自动压缩请求/响应体,减少流量。
    • 强大的拦截器机制:可以统一处理日志、缓存、认证等。
    • 简化了异步请求的处理。

数据解析:Gson / Moshi

  • 是什么:JSON解析库,服务器返回的数据通常是JSON字符串,我们需要将其转换成Kotlin/Java对象(反序列化),或将对象转换成JSON字符串发送给服务器(序列化)。
  • Gson:Google出品,稳定可靠,使用广泛。
  • Moshi:Square出品(与Retrofit同一家),对Kotlin支持更好(默认支持KClass),性能略优。

协程 + Retrofit:现代标准方案

  • 是什么
    • Retrofit:一个类型安全的HTTP客户端,它不是直接发送网络请求,而是将Java/Kotlin接口注解(如@GET, @POST)转换成OkHttp请求。
    • Kotlin Coroutines:提供了一种更简洁、更安全的方式来处理异步代码,它避免了回调地狱,代码看起来像同步代码一样线性。
  • 为什么是这套组合?
    • 类型安全:Retrofit将API端点定义成接口,编译器会帮你检查URL、参数等是否正确。
    • 代码简洁:配合协程,异步代码变得极其易读和易于维护。
    • 功能强大:Retrofit内置了Gson/Moshi适配器、RxJava适配器等,生态系统非常完善。
    • 社区标准:目前业界开发Android网络应用的首选和标准方案。

现代网络请求最佳实践:Retrofit + OkHttp + Coroutines + Gson

下面我们来实现一个从GitHub获取用户信息的完整例子。

第一步:添加依赖

app/build.gradle.kts (或 build.gradle) 文件中添加:

Android网络应用开发如何入门?-图2
(图片来源网络,侵删)
// OkHttp 和 Retrofit
implementation("com.squareup.okhttp3:okhttp:4.12.0") // 使用最新版本
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0") // Gson转换器
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0") // OkHttp日志拦截器
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")

第二步:添加网络权限

app/src/main/AndroidManifest.xml 中:

<uses-permission android:name="android.permission.INTERNET" />

第三步:创建数据类

根据GitHub API返回的JSON结构创建Kotlin数据类。

import com.google.gson.annotations.SerializedName
data class User(
    val login: String,
    val id: Int,
    val avatar_url: String,
    val name: String?,
    val bio: String?
)
// Response wrapper (if your API has one)
data class UserResponse(
    val items: List<User>
)

第四步:创建Retrofit API接口

定义你的API方法。

import retrofit2.http.GET
import retrofit2.http.Path
interface GitHubApiService {
    // GET https://api.github.com/users/{username}
    @GET("users/{username}")
    suspend fun getUser(@Path("username") username: String): User // 使用 suspend 关键字
    // GET https://api.github.com/search/users?q=tetris
    @GET("search/users")
    suspend fun searchUsers(@Query("q") query: String): UserResponse // suspend 关键字
}
  • suspend 关键字告诉协程这个函数是挂起函数,可以在协程中安全地调用。

第五步:创建Retrofit实例

import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitClient {
    private const val BASE_URL = "https://api.github.com/"
    val instance: GitHubApiService by lazy {
        // 创建日志拦截器
        val loggingInterceptor = HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
        // 创建OkHttpClient并添加拦截器
        val okHttpClient = OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .build()
        // 创建Retrofit实例
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(okHttpClient)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(GitHubApiService::class.java)
    }
}

第六步:在ViewModel或Activity/Fragment中调用

这是在 ViewModel 中使用协程进行网络请求的标准做法。

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
class MyViewModel : ViewModel() {
    // 使用 MutableLiveData 来持有UI状态
    private val _user = MutableLiveData<User>()
    val user: LiveData<User> = _user
    private val _error = MutableLiveData<String>()
    val error: LiveData<String> = _error
    private val _isLoading = MutableLiveData<Boolean>()
    val isLoading: LiveData<Boolean> = _isLoading
    fun fetchUser(username: String) {
        viewModelScope.launch { // 在协程作用域内启动
            try {
                _isLoading.value = true
                val fetchedUser = RetrofitClient.instance.getUser(username)
                _user.value = fetchedUser
            } catch (e: Exception) {
                _error.value = "Failed to fetch user: ${e.message}"
            } finally {
                _isLoading.value = false
            }
        }
    }
}

第七步:在Activity/Fragment中观察LiveData并更新UI

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import coil.load // 使用 Coil 库加载图片
class MyActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my)
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
        val button: Button = findViewById(R.id.button_fetch)
        val progressBar: ProgressBar = findViewById(R.id.progress_bar)
        val textViewName: TextView = findViewById(R.id.text_view_name)
        val imageViewAvatar: ImageView = findViewById(R.id.image_view_avatar)
        viewModel.isLoading.observe(this) { isLoading ->
            progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
        }
        viewModel.user.observe(this) { user ->
            textViewName.text = user.name ?: user.login
            imageViewAvatar.load(user.avatar_url) {
                crossfade(true)
                placeholder(R.drawable.ic_placeholder) // 占位图
                error(R.drawable.ic_error) // 错误图
            }
        }
        viewModel.error.observe(this) { errorMessage ->
            Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show()
        }
        button.setOnClickListener {
            viewModel.fetchUser("google") // 获取 "google" 用户的资料
        }
    }
}

处理网络状态

检查网络连接

可以使用 ConnectivityManager 来检查网络是否可用。

fun isNetworkAvailable(context: Context): Boolean {
    val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val activeNetwork = connectivityManager.activeNetwork ?: return false
        val capabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
        return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
               capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
               capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)
    } else {
        @Suppress("DEPRECATION")
        val networkInfo = connectivityManager.activeNetworkInfo ?: return false
        @Suppress("DEPRECATION")
        return networkInfo.isConnected
    }
}

在发起网络请求前,最好先检查一下,并提示用户。

处理加载状态

如上面的例子所示,使用一个 isLoadingLiveData 来控制进度条或加载动画的显示/隐藏。

优雅地处理错误

使用 try-catch 块捕获网络请求可能抛出的异常(如 IOException, HttpException),并通过一个 errorLiveData 将错误信息传递给UI层,显示给用户。


高级主题

  • 数据缓存:OkHttp内置了强大的缓存机制,只需在构建OkHttpClient时配置一个Cache目录即可,你也可以使用Retrofit的@CacheControl注解来控制缓存策略。
  • 文件上传与下载
    • 下载:Retrofit的@Streaming注解可以处理大文件下载,防止内存溢出。
    • 上传:使用@Multipart@Part注解,并传递RequestBodyMultipartBody.Part
  • OAuth 2.0 认证:对于需要用户登录的API,通常使用OAuth 2.0,可以使用Interceptor在每个请求中添加Authorization头。
  • WebSocket:OkHttp也提供了对WebSocket的支持,适合需要实时、双向通信的场景,如聊天室、实时通知等。

安全与性能

  • HTTPS必须使用HTTPS! 它可以加密数据,防止中间人攻击,Retrofit和OkHttp默认支持HTTPS。
  • 防止内存泄漏:在ViewModel中使用viewModelScope.launch可以确保当ViewModel被销毁时,所有正在运行的协程都会自动取消,从而避免内存泄漏。
  • 网络超时与重试:可以在OkHttpClient中配置connectTimeout, readTimeout, writeTimeout,使用Interceptor可以实现自动重试逻辑。

学习资源推荐

  • 官方文档
  • 优秀教程
    • ProAndroidDev:Medium上的一个高质量博客,有很多关于Android架构和现代开发的深入文章。
    • Android Developer Fundamentals (Kotlin):Google官方的免费课程,其中网络部分讲得很好。
    • B站/YouTube:搜索 "Android Retrofit", "Kotlin Coroutines" 等关键词,有很多视频教程。
  • 代码示例

希望这份详细的指南能帮助你顺利入门并精通Android网络应用开发!

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