安卓关于使用Jetpack中组件ViewModel+Livedata以及网络库Retrofit并解析复杂JSON数据以及使用Apifox对接接口的使用笔记(使用Kotlin语言+个人理解)

目录

ViewModel

基本用法:

ViewModel+Livedata

基本用法

Retrofit

基本用法

解析JSON数据

使用Retrofit+Apifox对接后台接口

query

body

Retrofit接口中的方法代码


ViewModel

ViewModel其实就是一个类,只不过它主要用于存放与界面相关的数据和数据处理,原则上所有与界面相关的数据都应该存放在View Model中而不是activity中,这样可以减少activity中的逻辑,并且每个activity都应该建立一个对应的ViewModel,因为ViewModel的生命周期比activity的生命周期要长,所以对于数据的处理部分推荐都放在View Model中,比如创建一个方法在需要的地方调用一下就行

基本用法:

在 app/build.gradle文件中添加如下依赖:
dependencies {
 ...
 implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
}

创建一个类,命名推荐使用对应界面的名字+viewmodel,并让它继承ViewModel

class MainViewModel : ViewModel() {
    val count = 0
}

在activity中使用它

class MainActivity : AppCompatActivity() {

     lateinit var viewModel: MainViewModel

     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_main)

         将界面与viewmodel绑定
         viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
            
         //使用ViewModel中的count变量
          infoText.text = viewModel.count.toString()
    }
}

到这里应该可以获取到View Model中的数据了,但如果想向view Model中传递数据可以定义一个形参,通过ViewModelProvider.Factory,这里不在赘述,详情可以去看第一行代码第三版,如果想要优雅的传递数据,继续向下

ViewModel+Livedata

你在敲代码时是不是常常遇到异步操作时需要更新数据的问题,这时一般会通过接口写一个回调方法,但是如果数据一多,这样的方法就太麻烦了,怎么办?答案是使用Livedata,如果你学习过观察者模式,你应该知道它的描述是当一个对象的状态发生改变时,所有依赖与它的对象都会得到消息并自动更新,怎么在安卓实现呢?使用Livedata,LiveData可以包含任何类型的数据,并在数据发生变化的时候通知给观察者。也就是说,如果我们将View Model中的数据使用LiveData来包装,然后在Activity中去观察它,就可以 主动将数据变化通知给Activity

基本用法

首先改造ViewModel

class MainViewModel : ViewModel() {
    val count = MutableLiveData<Int>()
}

将count变量修改成了一个MutableLiveData对象,并指定它的泛型为Int,表 示它包含的是整型数据。MutableLiveData是一种可变的LiveData,它的用法很简单,主要 有3种读写数据的方法,分别是getValue()、setValue()和postValue()方法。 getValue()方法用于获取LiveData中包含的数据;setValue()方法用于给LiveData设置数 据,但是只能在主线程中调用;postValue()方法用于在非主线程中给LiveData设置数据

在activity中创建ViewModel中viewModel.count的observe()方法来观察数据的变化,无论在哪里改变view Model中的变量的value都会被它的observe()方法检测到并立即执行observe方法,然后你就可以在里面编写具体的处理逻辑

class MainActivity : AppCompatActivity() {
     ...
     override fun onCreate(savedInstanceState: Bundle?){

        viewModel.count.observe(this, Observer { count ->
             infoText.text = count.toString()
         })
    }
}

Retrofit

Retrofit的用法就是,首先我们配置好一个根路径,然后在指定服务器接口地址时只需要使用相对路径即可,这样就不用每次都指定完整的URL地址了。 另外,Retrofit允许我们对服务器接口进行归类,将功能同属一类的服务器接口定义到同一个接 口文件当中,从而让代码结构变得更加合理。我们也完全不用关心网络通信的细节,只需要在接口文件中声明一系列方法和返回值, 然后通过注解的方式指定该方法对应哪个服务器接口,以及需要提供哪些参数。当我们在程序 中调用该方法时,Retrofit会自动向对应的服务器接口发起请求,并将响应的数据解析成返回值 声明的类型。这就使得我们可以用更加面向对象的思维来进行网络操作。

基本用法

编辑app/build.gradle文件,在 dependencies闭包中添加如下内容

dependencies {
     ...
     implementation 'com.squareup.retrofit2:retrofit:2.6.1'
     implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
     //解析后端服务器发送下来的json格式数据
     implementation 'com.google.code.gson:gson:2.8.5'
}

新建AppService接口,代码如下所示(注:下面返回和解析的是JSON格式的数据,如果你不是可以自行搜索)

interface AppService {

     @Headers("Content-Type: application/json")
     @GET("get_data.json")
     fun getAppData(): Call<ResponseBody>
}

上述代码中有两点需要我们注意。第一就是在getAppData()方法上面添加的注解,这里使用 了一个@GET注解,表示当调用getAppData()方法时Retrofit会发起一条GET请求,请求的地址就是我们在@GET注解中传入的具体参数,如果发起的是POST请求使用@POST注解,参数一样

第二就是getAppData()方法的返回值必须声明成Retrofit中内置的Call类型,并通过泛型来 指定服务器响应的数据应该转换成什么对象。@Headers()用来指定内容类型为json

由于在接口中我们会创建很多的方法去获取后台的不同数据,所以一般推荐创建一个接口的实现类,并在这个实现类中创建方法去实现接口中定义的方法

class AppServiceImpl(){

    fun getdata(){
        
    }
}

创建Retrofit对象并创建AppService中方法的回调方法,这个方法会在retrofit发起请求后,收到后台返回的数据时调用

class AppServiceImpl(){

    fun getdata(){
        val retrofit = Retrofit.Builder()
         .baseUrl("http://10.0.2.2/") //URL中的端口号那一部分
         .addConverterFactory(GsonConverterFactory.create())
         .build()
        val appService = retrofit.create(AppService::class.java)
        appService.getAppData().enqueue(object : Callback<ResponseBody>> {

        override fun onResponse(call: Call<ResponseBody>,response:
            Response<ResponseBody>){

                
            }
        override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
                Log.d("queryAirConList", "激活回调:onFailure")
            }
        }
    }
}

使用Retrofit.Builder来构建 一个Retrofit对象,其中baseUrl()方法用于指定所有Retrofit请求的根路径, addConverterFactory()方法用于指定Retrofit在解析数据时所使用的转换库,这里指定成 GsonConverterFactory。注意这两个方法都是必须调用的。 有了Retrofit对象之后,我们就可以调用它的create()方法,并传入具体Service接口所对应 的Class类型,创建一个该接口的动态代理对象。如果你并不熟悉什么是动态代理也没有关 系,你只需要知道有了动态代理对象之后,我们就可以随意调用接口中定义的所有方法,而 Retrofit会自动执行具体的处理就可以了

需要注意retrofit中.baseUrl中传入的URL+接口方法注解中传入的URL应该等于你要访问的URL的全路径,注意baseURL尾或注解URL首保证有一个应该有"/"符号

在onResponse方法中我们可以用response方法的isSuccessful来判断回调是否成功

if (response.isSuccessful) {
    Log.d("queryAirConList", "激活回调:onResponse==成功")
}

解析JSON数据

如果JSON数据时这样:

{                        
    "httpStatus":200,
    "code":"000000",
    "message":"success",
    "description":"成功",
    "data":{
        "pageSize":10,
        "pageNo":1,
        "rowCount":1,
        "pageCount":1,
        "entityList":[
            Object{...}
        ]
    }
}

如果是 { } 以大括号包住的可以先转化为一个map,在onResponse方法中先用isSuccessful判断回调是否成功,成功后就可以开始解析了

override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
    if (response.isSuccessful) {
        val responsedata = response.body()?.string()

        val dataMap = Gson().fromJson(responsedata, Map::class.java)
        //这一步就已经将最外层的数据转为map格式
                    
        val code = dataMap["code"] as String //这样就可以获得json中的code的值
                    
        //但是你如果想获得data中的数据,你就会发现data中的数据又是一个{}包裹的json
        //但你直接这样读取时val data = dataMap["data"] 会发现不报错但是这个map中的                        
        //数据读取不出来,因为Kotlin自己转化的map类型不对,没关系强转一下
                    
        val data = dataMap["data"]as LinkedTreeMap<String,Any> 
        //这里的Any表示任意类型,因为我们可以发现在data中的entityList中数据是[{...}]
        //这表示它是一个jsonList,但是其余的值都是String,所以使用Any

        val pageSize = data["pageSize"] as String //这样就可以获取json中的pageSize的值
    }

}   
                    

展开entityList后的json,注意在未展开的json中一个object{...}表示一个列表项,演示的只有一个

 "pageSize":10,
        "pageNo":1,
        "rowCount":1,
        "pageCount":1,
        "entityList":[
            {
                "deleteFlag":"0",
                "deleteTime":null,
                "rowState":"",
                "id":"10086",
                "device":Object{...},
                "orgCode":"master",
            }
        ]

继续解析

    //前面说了data中entityList是一个list,同样的道理继续强转一下就行
    val entityList = data["entityList"] as ArrayList<LinkedTreeMap<String, Any>>

    //同样的道理Any还是因为在entityList 中的device又是一个JSON,如果没有继续嵌套使用String就行
     
    //然后遍历entityList 获取每个entity

    if (entityList.size!=0) { //该空间有entity
        for (entity in entityList) {
            val id = entity["id"] as String  //这时就获取到了entityList 中一个子项的id值
            //同样的道理只要一层层解析就可以获取到所有的数据了
        }
    }

使用Retrofit+Apifox对接后台接口

query

body

 然后再Apifox中传入参数就可以点击发送来测试接口了

Retrofit接口中的方法代码

@Headers("Content-Type: application/json")
    @POST("/airconditioner/airconditionerDevice/queryAirconditionerDeviceList")
   //Apifox中需要传入的参数,一个query参数使用@Query注解,一个Body参数使用@Body注解
     fun queryAirConList(@Query("access_token") access_token: String,@Body                 
     params:HashMap<String,String>):Call<ResponseBody>

需要调用的实际URL为:

http://172.17.36.201:8012/airconditioner/airconditionerDevice/queryAirconditionerDeviceList?access_token=b59b5bed-b46e-4508-9ce9-90bb00f72c5b

 注意在url中用?连接了access_token,在实际需求中如果在这里连接了两个参数,那么接口方法中分别用@Query注解传入两个参数

比如:

fun getCurtainDetail(@Query("id") id :String,@Query("access_token") access_token: String) :Call<ResponseBody>

然后在该方法的实现位置传入值

fun queryAirConList(){
        val params = HashMap<String, String>()
        //这里就是你要传入body的map
        params["spaceId"]="64197bc85caef34d5e8aa1fe" //空间id   64197bc85caef34d5e8aa1fe

        val retrofit = Retrofit.Builder().baseUrl("http://172.17.36.201:8012") 
            .addConverterFactory(GsonConverterFactory.create()).build()
        val service = retrofit.create(BoardRoomControlService::class.java)
        
        //Apifox上显示要传入的两个参数
        service.queryAirConList(access_token, params).enqueue(object:Callback<ResponseBody>        
        {
           override fun onResponse(call:Call<ResponseBody>,response:Response<ResponseBody>) 
           {
            。。。
            }
         }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小趴菜8227

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值