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

网络架构测试

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

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

一. HttpURLConnection

  1. 最简单的写法
    private suspend fun getImageFromNet()= withContext(Dispatchers.IO){
        //1. 定义一个Url
        val 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.inputStream
        BitmapFactory.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.需要一个client
            sendRequestWithOkHttp();

        }

    }

    fun sendRequestWithOkHttp() {
        Thread {
            try {
                //1.建立okhttpclient
                val 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.需要一个client
            sendRequestWithOkHttpByGet();
        }
        send_requestbypost.setOnClickListener {
            sendRequestWithOkHttpByPost();
        }

    }

    fun sendRequestWithOkHttpByGet() {
        Thread {
            try {
                //1.建立okhttpclient
                val 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.建立okhttpclient
                val 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() {
    @Override
    public void onResponse(Call call, Response response) throws IOException {
// 得到服务器返回的具体内容
        String responseData = response.body().string();} @
    Override
    public 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);
            @Override
            public Bitmap getBitmap(String url) {
                return cache.get(url);
            }

            @Override
            public void putBitmap(String url, Bitmap bitmap) {
                cache.put(url,bitmap);
            }
        });
        imageLoader.get(url, new ImageLoader.ImageListener() {
            @Override
            public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
                imageView.setImageBitmap(response.getBitmap());
            }

            @Override
            public 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>() {
                 @Override
                 public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
                     return false;
                 }

                 @Override
                 public 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 = 0
    var message: String? = null
    var data: T? = null
        private set

    fun setData(data: T) {
        this.data = data
    }
}
  1. 建立泛型对应之一的Info类,这个是要根据APi来创建的
class Info {
    var name: String? = null
    var url: String? = null
    var 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? = null
    var 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: Unit
        private get() {//这种东西访问不到外面的实例的


            //步骤6:对发送请求进行封装,传入接口参数
            val jsonDataCall = api?.getJsonData("新歌榜", "json")

            //步骤7:发送网络请求  同步执行  要自己写接口比较麻烦
            //Response<Data<Info>> execute = jsonDataCall.execute();

            //步骤7:发送网络请求(异步) enqueue???、timer用的是schedule
            /*
            //异步请求
             dataCall.enqueue(new Callback<Data<Info>>() {
            //请求成功回调
                @Override
                public void onResponse(Call<Data<Info>> call, Response<Data<Info>> response) {
                }
            //请求失败回调
                @Override
                public 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=json
            Log.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() ?: return
                    val info: Info = body.data ?: return
                    tv!!.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() ?: return
                    tv.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 链接中 井号#、问号?、连接符& 分别有什么作用?
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值