《Android编程权威指南》之HTTP与后台任务(一)

19 篇文章 0 订阅
13 篇文章 1 订阅

《Android编程权威指南》第 24 章啦,本章又有个新应用啦,叫 PhotoGallery,用来获取 Flickr 网站的最新公共图片「不限版权的图片」。本章将学习 Retrofit 网络请求库,Json 数据,Gson 解析 Json 等等。

一、创建 PhotoGallery 应用

按照惯例,创建应用,先写下 xml 文件,这里又是用 activity 嵌 fragment 的方式。 main_activity.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/flayout_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" /> 

fragment 中放入列表: fragment_photo_gallery.xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/recyclerview_photo"
    android:layout_width="match_parent"
    android:layout_height="match_parent" /> 

MainActivity.kt:

class MainActivity : AppCompatActivity() {

    private lateinit var mBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mBinding.root)

        val isFragmentContainerEmpty = savedInstanceState == null
        if (isFragmentContainerEmpty){
            supportFragmentManager
                .beginTransaction()
                .add(R.id.flayout_container, PhotoGalleryFragment.newInstance())
                .commit()
        }
    }
}
上面采用检查 savedInstanceState 的方式判断当前 Activity 是不是重建或者第一次创建,再添加 fragment。 

PhotoGalleryFragment.kt:

class PhotoGalleryFragment : Fragment() {

    private lateinit var photoRecyclerView: RecyclerView

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_photo_gallery, container, false)
        photoRecyclerView = view.findViewById(R.id.recyclerview_photo)
        photoRecyclerView.layoutManager = GridLayoutManager(context, 3)
        return super.onCreateView(inflater, container, savedInstanceState)
    }

    companion object {
        fun newInstance() = PhotoGalleryFragment()
    }

} 

目前运行起来还是个空页面,因为没有给 RecyclerView 绑定数据。

二、Retrofit 网络连接基本

Retrofit 「square.github.io/retrofit/」是 Square 公司创建和维护的一个开源库。但本质上,它的 HTTP 客户端封装使用的是 OkHttp 「square.github.io/okhttp/」 库。

Retrofit 可创建 HTTP 网关类。给 Retrofit 一个带注解方法的接口,它会做接口实现。Retrofit 的接口实现能发起 HTTP 请求,收到 HTTP 响应数据后会解析为一个 OkHttp.ResponseBody。然而,OkHttp.ResponseBody 无法直接使用:你要将其转换为自己应用需要的数据类型。为解决这个问题,可以注册一个响应数据转换器。随后,在准备网络请求需要的数据以及从网络响应解析数据时,Retrofit 就可以用这个转换器进行各种数据类型的相互转换了。

先在 build.gradle 文件添加 Retrofit 依赖:

 implementation 'com.squareup.retrofit2:retrofit:2.9.0' 
  • 定义 Retrofit API 接口

新建个包放接口 api,新建一个接口文件,FlickrApi.kt:

import retrofit2.Call
import retrofit2.http.GET

interface FlickrApi {
    @GET("/")
    fun fetchContents(): Call<String>
} 

这里接口中的每一个函数都对应着一个特定的 HTTP 请求,必须使用 HTTP 请求方法注解。

常见的 HTTP 请求类型有 @GET、@POST、@PUT、@DELETE 和 @HEAD。

@GET("/") 注解的作用是把 fetchContents() 函数返回的 Call 配置成一个 GET 请求。字符串"/“表示一个相对路径 URL —— 针对 Flickr API 端点基 URL 来说的相对路径。大多数 HTTP 请求方法注解包括相对路径。这里,”/" 相对路径是指请求会发往我们稍后提供的基 URL。

所有 Retrofit 网络请求默认都会返回一个 retrofit2.Call 对象(一个可执行的网络请求)。执行 Call 网络请求就会返回一个相应的 HTTP 网络响应。(也可以配置 Retrofit 返回 RxJava Observable「目前主流方式」)

Call 的泛型参数是什么类型,Retrofit 在反序列化 HTTP 响应数据后就会生成同样的数据类型。Retrofit 默认会把 HTTP 响应数据反序列化为一个 OkHttp.ResponseBody 对象。指定 Call 就是告诉 Retrofit ,我们需要的是 String 对象,而不是 OkHttp.ResponseBody 对象。

  • 构建 Retrofit 对象并创建 API 实例

Retrofit 实例负责实现和创建 API 接口实例。为基于定义的 API 接口生成网络请求。现在开始构建 Retrofit 实例。

 val retrofit = Retrofit.Builder()
            .baseUrl("https://www.flickr.com/")
            .addConverterFactory(ScalarsConverterFactory.create())
            .build()
        
        val flickrApi = retrofit.create(FlickrApi::class.java) 

Retrofit.Builder() 是一个流接口,用来配置并构建 Retrofit 实例。 baseUrl(…) 提供要访问的基 URL 端点。 Retrofit.Builder() 进行参数设定后调用 build() 函数会返回一个配置好的 Retrofit实例。

再添加个依赖包,做数据类型转换。

implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' 

利用 addConverterFactory(…) 函数添加特定的数据类型转换器实例。在返回 Call 结果之前,Retrofit对象就会使用这个字符串数据转换器把 ResponseBody 对象转换为 String 对象。当然,Square 还为 Retrofit 提供了其他一些开源数据类型转换器。

  • 执行网络请求

创建 Call 请求:

 val flickrHomePageRequest : Call<String> = flickrApi.fetchContents() 

注意,调用 FlickrApi 的 fetchContents() 并不是执行网络请求,而是返回一个代表网络请求的 Call 对象。

然后,在 onCreate(savedInstanceState: Bundle?) 里调用 enqueue(…) 去执行代表网络请求的 Call 对象。

 flickrHomePageRequest.enqueue(object : Callback<String> {
            override fun onResponse(call: Call<String>, response: Response<String>) {
                Log.d(TAG, "Response received : ${response.body()}")
            }

            override fun onFailure(call: Call<String>, t: Throwable) {
                Log.e(TAG, "Failed to fetch photos", t)
            }
        }) 

Retrofit 天生就遵循两个最重要的Android多线程规则。

(1) 仅在后台线程上执行耗时任务。

(2) 仅在主线程上做 UI 更新操作。

Call.enqueue(…) 函数执行代表网络请求的 Call 对象。最关键的是,它是在后台线程上执行网络请求的。这一切都由 Retrofit 管理和调度的。

传递给 onResponse() 和 onFailure() 函数的 Call 对象就是最初发起网络请求的 Call 对象。

  • 获取网络使用权限

在 AndroidManifest.xml 中添加网络权限:

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

运行可以看到打印日志「注意此 api 需要翻墙访问,so,可以自行找个其他国内公开的 api 进行访问」

Log

  • 使用仓库模式联网

这里把 Retrofit 配置代码和 API 联网代码抽出来,移到一个新类中。

private const val TAG = "FlickrFetchr"

class FlickrFetchr {

    private val flickrApi :FlickrApi

    init {
        val retrofit = Retrofit.Builder()
            .baseUrl("https://www.flickr.com/")
            .addConverterFactory(ScalarsConverterFactory.create())
            .build()
        flickrApi = retrofit.create(FlickrApi::class.java)
    }

    fun fetchContents():LiveData<String>{

        val responseLiveData : MutableLiveData<String> = MutableLiveData()

        val flickrHomePageRequest: Call<String> = flickrApi.fetchContents()

        flickrHomePageRequest.enqueue(object : Callback<String> {
            override fun onResponse(call: Call<String>, response: Response<String>) {
                Log.d(TAG, "Response received : ${response.body()}")
                responseLiveData.value = response.body()
            }

            override fun onFailure(call: Call<String>, t: Throwable) {
                Log.e(TAG, "Failed to fetch photos", t)
            }
        })
        return responseLiveData
    }
} 

注意,fetchContents() 函数返回的是个无法修改的 LiveData。可修改的 LiveData 对象尽量不要对外暴露,以防被其他外部代码篡改。LiveData 里的数据流动应保持一个方向。

然后修改 PhotoGalleryFragment 中的 onCreate() 方法。

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val flickrLiveData: LiveData<String> = FlickrFetchr().fetchContents()
        flickrLiveData.observe(this,
            Observer { responseString ->
                Log.d(TAG, "Response received:$responseString")
            })
    } 

这里借鉴了 Google 应用架构指导推崇的仓库模式。FlickrFetchr 充当基本仓库的角色。这种仓库类封装了从一个或多个数据源获取数据的逻辑。不管是本地数据库,还是远程服务器,它都知道该如何获取或保存各种数据。UI 代码不关心数据的获取和保存(仓库类自己的内部实现),需要数据时,找仓库类就行了。

运行程序,可以看到日志打印跟上述一样,是 Flickr 主页内容。

其他

这里内容有点多哇,继续分篇,学习需要循序渐进。💪🏻

PhotoGallery 项目 Demo 地址:

github.com/visiongem/A…


🌈关注我吖~❤️

公众号:妮K妮K妮

github.com/visiongem/A…](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fvisiongem%2FAndroidGuideApp%2Ftree%2Fmaster%2FPhotoGallery “https://github.com/visiongem/AndroidGuideApp/tree/master/PhotoGallery”)


🌈关注我吖~❤️

公众号:妮K妮K妮

最后

有小伙伴私信问Compose的问题,好不好用啊,现在要不要学啊?

其实答案很简单,自从谷歌2019年公布了声明式UI框架Jetpack Compose后,两年多的时间,各种大力宣传,和大量资源的倾斜,API功能都趋于稳定了。

至于好不好用,各种用过的同行都是持肯定态度的。优势大概就是这四点:

强大的工具和直观的Kotlin API
简化并加速了Android上的UI开发
可以帮助开发者用更少更直观的代码创建View
有更强大的功能,以及还能提高开发速度

这么大的优势,毋庸置疑,肯定是要学的嘛,而且越快掌握越好。别等刀架到脖子上了,才去练金钟罩。

至于怎么快速上手,可以给大家免费分享一份**《Jetpack Compose 完全开发手册》**,手把手教大家从入门到精通。

第一章 初识 Jetpack Compose

  • 为什么我们需要一个新的UI 工具?

  • Jetpack Compose的着重点

    加速开发
    强大的UI工具
    直观的Kotlin API

图片

  • API 设计

图片

  • Compose API 的原则
    一切都是函数
    顶层函数(Top-level function)
    组合优于继承
    信任单一来源

图片

  • 深入了解Compose
    Core
    Foundation
    Material

图片

  • 插槽API

第二章 Jetpack Compose构建Android UI

  • Android Jetpack Compose 最全上手指南
    Jetpack Compose 环境准备和Hello World
    布局
    使用Material design 设计
    Compose 布局实时预览
    ……

图片

  • 深入详解 Jetpack Compose | 优化 UI 构建
    Compose 所解决的问题
    Composable 函数剖析
    声明式 UI
    组合 vs 继承
    封装
    重组
    ……

图片

  • 深入详解 Jetpack Compose | 实现原理
    @Composable 注解意味着什么?
    执行模式
    Positional Memoization (位置记忆化)
    存储参数
    重组
    ……

图片

第三章 Jetpack Compose 项目实战演练(附Demo)

  • Jetpack Compose应用1
    开始前的准备
    创建DEMO
    遇到的问题

图片

  • Jetpack Compose应用2
  • Jetpack Compose应用做一个倒计时器
    数据结构
    倒计时功能
    状态模式
    Compose 布局
    绘制时钟

图片

  • 用Jetpack Compose写一个玩安卓App
    准备工作
    引入依赖
    新建 Activity
    创建 Compose
    PlayTheme
    画页面
    底部导航栏
    管理状态
    添加页面

图片

  • 用Compose Android 写一个天气应用
    开篇
    画页面
    画背景
    画内容
    ……

图片

  • 用Compose快速打造一个“电影App”
    成品
    实现方案
    实战
    不足
    ……

图片

由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
有需要的话可以点下面二维码免费领取↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值