当前位置: 代码迷 >> 综合 >> 【Android-Kotlin】网络架构整合笔记(HttpURLConnection,Okhttp,Volley,Retrofit)
  详细解决方案

【Android-Kotlin】网络架构整合笔记(HttpURLConnection,Okhttp,Volley,Retrofit)

热度:44   发布时间:2023-12-15 11:17:57.0

目录

  • 网络架构测试
  • 声明. 联网权限,非危险权限,xml声明即可
  • 一. HttpURLConnection
  • 二. okhttp
      • 1. post方法
      • 2. Get方法[多一个请求体]
  • 2.5 封装Okhttp实现自身接口回调,不然得自己写一个接口,也不难。但okhttp自带了
  • 三. [Volley架构](https://blog.csdn.net/u012602304/article/details/79170137)
      • 3.5.[Glide组件](https://github.com/bumptech/glide)
  • 四. [Retrofit2转载出处](https://blog.csdn.net/m0_37796683/article/details/90702095)及基础定义
      • 1. 定义
      • 2. 导包
      • 3. 其使用了注解与自定义拼接字
      • 4 . 优秀的数据解析
      • 5. 同步与异步发送网络请求
      • 6. 实现回调接口
  • 五 实例一:Retrofit推荐写法【来自第一行代码】
      • 1. 由于通过Gson解析需要一个实体类
      • 2. 新建AppService接口
  • 六 实例二:Retrofit演示
      • 1. 效果图
      • 2. Demo代码块
      • 补充资料:

网络架构测试

声明. 联网权限,非危险权限,xml声明即可

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

一. HttpURLConnection

  1. 最简单的写法
    private suspend fun getImageFromNet()= withContext(Dispatchers.IO){
    //1. 定义一个Urlval url = URL("https://picjumbo.com/wp-content/uploads/white-swans-near-charles-bridge-in-prague-2210x1473.jpg")//2. 调用openConnection方法即可val connection=url.openConnection() as HttpURLConnection//3. 获取数据流即可val inputStream=connection.inputStreamBitmapFactory.decodeStream(inputStream)}
  1. 现在网络请求都不放在主线程,具体的去 协程那边看
  2. 还可以通过connection的setConnectionTimerout和ReadTimerout来进行具体细节设定等等。

二. okhttp

  1. 注意8.0以后。http需要另外配置文件
  2. 线程问题, 在4.0之后在主线程里面执行Http请求都会报这个错,大概是怕Http请求时间太长造成程序假死的情况吧。

1. post方法

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)send_request.setOnClickListener {
    //1.需要一个clientsendRequestWithOkHttp();}}fun sendRequestWithOkHttp() {
    Thread {
    try {
    //1.建立okhttpclientval client = OkHttpClient()//2.发起请求,需要创建一个request对象val request: Request = Request.Builder().url("http://www.baidu.com").build()//3.调用OkHttpClient的newCall()方法 创建一个Call对象, 并调用它的execute()方法//来发送请求并获取服务器返回的数据val response = client.newCall(request).execute()val responseData = response.body().string()println("1111111"+responseData)response_text.text=responseData} catch (e: Exception) {
    e.printStackTrace()}}.start()}
}

2. Get方法[多一个请求体]

  1. 由於在子綫程建立的okhttp請求,这边使用runOnUiThread在主线程更新。共两种方式
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)send_requestbyget.setOnClickListener {
    //1.需要一个clientsendRequestWithOkHttpByGet();}send_requestbypost.setOnClickListener {
    sendRequestWithOkHttpByPost();}}fun sendRequestWithOkHttpByGet() {
    Thread {
    try {
    //1.建立okhttpclientval client = OkHttpClient()//2.发起请求,需要创建一个request对象val request: Request = Request.Builder().url("http://www.baidu.com").build()//3.调用OkHttpClient的newCall()方法 创建一个Call对象, 并调用它的execute()方法//来发送请求并获取服务器返回的数据val response = client.newCall(request).execute()val responseData = response.body().string()showResponse(responseData)} catch (e: Exception) {
    e.printStackTrace()}}.start()}fun sendRequestWithOkHttpByPost() {
    Thread {
    try {
    //1.建立okhttpclientval client = OkHttpClient()//2.发起请求,需要创建一个request对象//2.5请求体val requestBody: RequestBody = FormBody.Builder().add("username", "admin").add("password", "123456").build()val request: Request = Request.Builder().url("https://www.jd.com").post(requestBody).build()//3.调用OkHttpClient的newCall()方法 创建一个Call对象, 并调用它的execute()方法//来发送请求并获取服务器返回的数据val response = client.newCall(request).execute()val responseData = response.body().string()showResponse(responseData)} catch (e: Exception) {
    e.printStackTrace()}}.start()}private fun showResponse(response: String) {
    runOnUiThread {
     // 在这里进行UI操作, 将结果显示到界面上response_text.setText(response)}}}

在这里插入图片描述

在这里插入图片描述

2.5 封装Okhttp实现自身接口回调,不然得自己写一个接口,也不难。但okhttp自带了

//普通类的静态方法
public class HttpUtil {
    public static void sendOkHttpRequest(String address, okhttp3.Callback callback) {
    OkHttpClient client = new OkHttpClient();Request request = new Request.Builder().url(address).build();client.newCall(request).enqueue(callback);}
}//调用时
HttpUtil.sendOkHttpRequest("http://www.baidu.com", new okhttp3.Callback() {
    @Overridepublic void onResponse(Call call, Response response) throws IOException {
    
// 得到服务器返回的具体内容String responseData = response.body().string();} @Overridepublic void onFailure(Call call, IOException e) {
    
// 在这里对异常情况进行处理}
});

三. Volley架构

  1. 总体操作
    1)创建一个队列,可以封装为单例Singleton(队列较耗资源)
    2)创建请求,将其加入队列,volley通过参数回调执行返回结果即可

implementation ‘com.android.volley:volley:1.1.1’

  1. 样例测试
    1)设置布局textView在设置scrollview滚动条布局中,保证放得下
    2)新建请求选择StringRequest方法,两个构造函数,其中的一个四/三参数的可以指定请求(Request分两类为StringRequest和GsonRequest)
    a. 请求方式,获取或上传(三参数不设置这次)
    b. 请求网站
    c. 成功回调
    d. 错误回调,这两个回调简写就是类lambda表达式,如果用Kotlin写,第一个参数不能指定,找不到Method (暂时原因不明)

  2. 测试结果,注意Volley传送门里作者写的,添加网络请求权限,这个不属于危险权限之一在这里插入图片描述
    在这里插入图片描述

  3. 修改获取网上图片(Volley图片加载)
    1)改写第二步的请求,使用ImageLoader,第一个参数是请求队列(暂时用不到,第二个建立个cache缓存,内部方法有get和put方法(如果本地有了就拿缓存,具体不处理也能达到一点效果)
    2)该对象设置了请求队列,使用get方法传入url和监听对象,并且直接内部成功/失败回调,setImageBitmap代码中设定图片

      ImageLoader imageLoader=new ImageLoader(mQueue, new ImageLoader.ImageCache() {
    //建立缓存 设置最大缓存量private LruCache<String,Bitmap> cache=new LruCache<>(50);@Overridepublic Bitmap getBitmap(String url) {
    return cache.get(url);}@Overridepublic void putBitmap(String url, Bitmap bitmap) {
    cache.put(url,bitmap);}});imageLoader.get(url, new ImageLoader.ImageListener() {
    @Overridepublic void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
    imageView.setImageBitmap(response.getBitmap());}@Overridepublic void onErrorResponse(VolleyError error) {
    }});

在这里插入图片描述
ps:改为Volley自带的NetworkImageview控件,不用监听的话也可以这样写
在这里插入图片描述

3.5.Glide组件

非网络架构,但真的很好用,就放同级了

implementation ‘com.github.bumptech.glide:glide:4.11.0’
annotationProcessor ‘com.github.bumptech.glide:compiler:4.11.0’

  1. 在原第二步基础上改下,就能达到同样效果,这岂不是美滋滋,并且智能缓存,还有更多个性化的操作
 Glide.with(this).load(url).placeholder(R.drawable.ic_launcher_background)//占位符.listener(new RequestListener<Drawable>() {
    @Overridepublic boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
    return false;}@Overridepublic boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
    return false;}}).into(imageView);

四. Retrofit2转载出处及基础定义

1. 定义

  1. OkHttp侧重的是底层通信的实现,而Retrofit侧重的是上层接口的封装。
  2. 使用okhttp一般自己也还要再封装一层,见FarawayPlayer。Retrofit就是Square公司在OkHttp的基础上进一步开发出来的应用层网络通信库,使得我们可以用更加面向对象的思维进行网络操作。
  3. 样例见6.2 PS:Retrofit里可以.client()封一个okhttp,然后里面指定三个时间
    3.1 1 分钟搞懂Retrofit 里的各种timeout分别都代表什么,这里指断断续续能连上的状态,亲测断网直接就有失败回调了。

2. 导包

implementation 'com.squareup.retrofit2:retrofit:2.5.0'

3. 其使用了注解与自定义拼接字

  1. 请求方法注解如get,post等等
  2. 请求头注解
  3. 请求参数注解,如Body,post,uri
  4. 请求和响应格式(标记)注解(两部分)
    1)第一部分,创建Retrofit时的.baseUrl设置
    //构建Retrofit实例Retrofit retrofit = new Retrofit.Builder()//设置网络请求BaseUrl地址.baseUrl("https://api.uomg.com/")//设置数据解析器.addConverterFactory(GsonConverterFactory.create()).build();

2)第二部分 API方法中获取到注解方式

 @GET("api/rand.music")

3)上面地址拼接后的完整地址为:https://api.uomg.com/api/rand.music?sort=&format=,然后通过addConverterFactory设置数据解释器,这里添加的是Gson解释器
添加依赖:

implementation 'com.squareup.retrofit2:converter-gson:2.0.2'

4 . 优秀的数据解析

在这里插入图片描述

5. 同步与异步发送网络请求

在这里插入图片描述

6. 实现回调接口

在这里插入图片描述

五 实例一:Retrofit推荐写法【来自第一行代码】

1. 由于通过Gson解析需要一个实体类

class App(val id: String, val name: String, val version: String)

2. 新建AppService接口

  1. 这里使用了一个@GET注解,表示当调用getAppData()方法时Retrofit会发起一条GET请求,请求的地址就是我们在@GET注解中传入的具体参数。

  2. 另外,getAppData()方法的返回值必须声明成Retrofit中内置的Call类型,并通过泛型来指定服务器响应的数据应该转换成什么对象。

interface AppService {
    @GET("get_data.json")fun getAppData(): Call<List<App>>
}
  1. 发送请求并执行回调
//1. 新建一个retrofit对象(可封装为静态synchronized单例对象 //使用@Synchronized注解)val retrofit = Retrofit.Builder().baseUrl("http://10.0.2.2/").addConverterFactory(GsonConverterFactory.create()).build()//2.建立一个appService对象为retrofit通过create方法传递接口类val appService = retrofit.create(AppService::class.java)//3. 通过create对象便可执行对应方法并回调 appService.getAppData().enqueue(object : Callback<List<App>> {
    override fun onResponse(call: Call<List<App>>,response: Response<List<App>>) {
    val list = response.body()if (list != null) {
    for (app in list) {
    Log.d("MainActivity", "id is ${
      app.id}")Log.d("MainActivity", "name is ${
      app.name}")Log.d("MainActivity", "version is ${
      app.version}")}}}override fun onFailure(call: Call<List<App>>, t: Throwable) {
    t.printStackTrace()}})

六 实例二:Retrofit演示

1. 效果图

在这里插入图片描述

2. Demo代码块

  1. 建立一个Data数据类专门用于处理回调,其中data为泛型
class Data<T> {
    var code = 0var message: String? = nullvar data: T? = nullprivate setfun setData(data: T) {
    this.data = data}
}
  1. 建立泛型对应之一的Info类,这个是要根据APi来创建的
class Info {
    var name: String? = nullvar url: String? = nullvar picurl: String? = null}
  1. 创建API类,存放第二部分的副链接
interface Api {
    //get请求@GET("api/rand.music")fun getJsonData(@Query("sort") sort: String?,@Query("format") format: String?): Call<Data<Info?>?>?//post请求 // 请求格式注解,请求实体是一个From表单,每个键值对需要使用@Field注解@FormUrlEncoded@POST("api/comments.163")fun postDataCall(@Field("format") format: String?): Call<Any?>?}
  1. Main调用
    1)get这边指定了回调类为Data的泛型Info
    2)post这边直接接收了any。。。比较神奇
class MainActivity : AppCompatActivity() {
    companion object {
    private val TAG = MainActivity::class.java.simpleName}private var mRetrofit: Retrofit? = nullvar api:Api ?=null/*** @param savedInstanceState*/override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)步骤4:构建Retrofit实例//设置网络请求BaseUrl地址 Retrofit把网络请求的URL 分成了两部分设置 第一部分:在创建Retrofit实例时通过.baseUrl()设置,//第二部分:在网络请求接口的注解设置,就是在上面的APi接口中用GET注解的字符串: @GET("api/rand.music") 及两个query//进行拼接:https://api.uomg.com/api/rand.music?sort=&format=//然后通过addConverterFactory设置数据解释器,这里添加的是Gson解释器。这是为了使来自接口的json结果会自动解析成定义好的字段和类型都相符的json对象接受类mRetrofit = Retrofit.Builder().baseUrl("https://api.uomg.com/").addConverterFactory(GsonConverterFactory.create()).build()// 步骤5:创建网络请求接口API对象实例api = mRetrofit!!.create(Api::class.java)btn1.setOnClickListener{
    jsonData}btn2.setOnClickListener{
    postJsonData()}}//步骤8:请求处理,输出结果// 步骤5:创建网络请求接口对象实例//步骤6:对发送请求进行封装,传入接口参数//步骤7:发送网络请求(异步)/*** 示例,get加载Json数据*/private val jsonData: Unitprivate get() {
    //这种东西访问不到外面的实例的//步骤6:对发送请求进行封装,传入接口参数val jsonDataCall = api?.getJsonData("新歌榜", "json")//步骤7:发送网络请求 同步执行 要自己写接口比较麻烦//Response<Data<Info>> execute = jsonDataCall.execute();//步骤7:发送网络请求(异步) enqueue???、timer用的是schedule/*//异步请求dataCall.enqueue(new Callback<Data<Info>>() {//请求成功回调@Overridepublic void onResponse(Call<Data<Info>> call, Response<Data<Info>> response) {}//请求失败回调@Overridepublic void onFailure(Call<Data<Info>> call, Throwable t) {}});* *///对异步返回的数据进行处理,网络加载完成。get的请求地址为:https://api.uomg.com/api/rand.music?sort=%E6%96%B0%E6%AD%8C%E6%A6%9C&format=jsonLog.e(TAG, "get == url:" + jsonDataCall!!.request().url())jsonDataCall.enqueue(object : Callback<Data<Info?>?> {
    @SuppressLint("SetTextI18n")override fun onResponse(call: Call<Data<Info?>?>, response: Response<Data<Info?>?>) {
    //步骤8:请求处理,输出结果Toast.makeText(this@MainActivity, "get回调成功:异步执行", Toast.LENGTH_SHORT).show()val body = response.body() ?: returnval info: Info = body.data ?: returntv!!.text = """返回的数据:${
      info.name} ${
      info.picurl}""".trimIndent()}override fun onFailure(call: Call<Data<Info?>?>, t: Throwable) {
    Log.e(TAG, "get回调失败:" + t.message + "," + t.toString())Toast.makeText(this@MainActivity, "get回调失败", Toast.LENGTH_SHORT).show()}})}/*** 示例,Post加载Json数据*/private fun postJsonData() {
    //步骤6:对发送请求进行封装:传入参数val call = api?.postDataCall("JSON")//调用api实施的post方法if (call != null) {
    call.enqueue(object : Callback<Any?> {
    @SuppressLint("SetTextI18n")override fun onResponse(call: Call<Any?>, response: Response<Any?>) {
    //步骤8:请求处理,输出结果val body = response.body() ?: returntv.setText("""返回的数据: ${
      response.body().toString()}""".trimIndent())Toast.makeText(this@MainActivity, "post回调成功:异步执行", Toast.LENGTH_SHORT).show()}override fun onFailure(call: Call<Any?>, t: Throwable) {
    Log.e(TAG, "post回调失败:" + t.message + "," + t.toString())Toast.makeText(this@MainActivity, "post回调失败", Toast.LENGTH_SHORT).show()}})}}

补充资料:

  1. URL 链接中 井号#、问号?、连接符& 分别有什么作用?
  相关解决方案