复习笔记
1. 对变量延迟初始化
延迟初始化使用的是lateinit关键字,它可以告诉Kotlin编译器,我会在晚些时候对这个变量进行初始化,这样就不用在一开始的时候将它赋值为null。
当你对一个全局变量使用了lateinit关键字时,请一定要确保它在被任何地方调用之前已经完成了初始化工作,否则Kotlin将无法确保程序的安全性。
private lateinit var adapter: TestAdapter
另外,我们还可以通过代码来判断一个全局变量是否已经完成了初始化,这样在某些时候能够有效地避免重复对某一个变量进行初始化操作。
private lateinit var adapter: TestAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recycler)
...
if (!::adapter.isInitialized){
adapter = TestAdapter(data)
}
...
}
具体的语法就是这样,::adapter.isInitialized可用于判断adapt变量是否已经初始化。虽然语法看上去有点奇怪,但这是固定的写法。
2. by lazy
- 延迟属性(lazy properties) 是 Kotlin 标准库中的标准委托之一,可以通过 by lazy 来实现
- 其中,lazy() 是一个函数,可以接受一个 Lambda 表达式作为参数,第一次调用时会执行 Lambda 表达式,后续调用会直接返回之前的结果
val str: String by lazy{ println("test") "test2" // 最后一行为返回值 } fun main(args: Array<String>) { println(str) println("-----------") println(str) } // 执行结果 test test2 ----------- test2
把想要延迟执行的代码放到by lazy代码块中,这样代码块中的代码在一开始的时候就不会执行,只有当uriMatcher变量首次被调用的时候,代码块中的代码才会执行。
代码块中的代码只会执行一次。
3. 运行时权限
Android将常用的权限大致归成了两类,一类是普通权限,一类是危险权限。
这样我们就将拨打电话的功能成功实现了,并且在低于Android6.0系统的手机上都是可以正常运行的。但是如果我们在Android6.0或者更高版本系统的手机上运行,点击“Make Call”按钮就没有任何效果了,会报错。因为Android6.0及以上系统在使用危险权限时必须进行运行时权限处理。
4. Service
它非常适合执行那些不需要和用户交互而且还要求长期运行的任务。Service的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,Service仍然能够保持正常运行。
不过需要注意的是,Service并不是运行在一个独立的进程当中的,而是依赖于创建Service时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行。
另外,也不要被Service的后台概念所迷惑,实际上Service并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,需要再Service的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况。
5. Android多线程编程之线程的基本用法
①定义一个线程的方式,一是新建一个类继承自Thread,然后重写父类的run()方法。
②当然,使用继承的方式耦合性有点高,更多地会选择使用实现Runnable接口的方式来定义一个线程。
③也可以使用Lambda的方式,这种写法更为常见。
Thread{
}.start()
以上几种线程的使用方式不陌生,因为在Java中创建和启动线程也是使用同样的方式。
④而Kotlin还提供了一种更加简单的开启线程的方式,连start()方法都不用调用,thread函数在内部帮我们全部都处理好了:
thread{ // 小写 // 编写具体的逻辑 }
6. Android多线程编程与Java多线程编程不同的地方之在子线程中更新UI
这样就掌握了Android异步消息处理的基本用法(handler+message),使用这种机制就可以出色地解决在子线程中更新UI的问题。
每个线程中只会有一个MessageQueue对象。
每个线程中只会有一个Looper对象。
7. Service的基本用法
在Activity中指挥Service去干什么,Service就去干什么。--借助onBind()方法。
任何一个Service在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity绑定,还可以和任何一个其他的Activity进行绑定,而且在绑定完成后,它们都可以获取相同的DownloadBinder实例。
虽然每调用一次startService()方法,onStartCommand()就会执行一次,但实际上每个Service只会存在一个实例。所以不管你调用了多少次startServcie()方法,只需调用一次stopService()和stopSelf()方法,Service就会停止。
11. 回调机制
P451. OKHttp在enqueue()方法的内部已经帮我们开好子线程了,然后会在子线程中执行http请求,并将最终的请求结果回调到okhttp3.Callback当中。
P445. 服务器响应的数据会回调到enqueue()方法中传入的Callback实现里面。
11.4 使用GSON解析JSON数组数据
如果需要解析的是一段JSON数组,会稍微麻烦一点。
需要借助TypeToken将期望解析成的数据类型传入fromJson()方法中,如下所示:
val typeOf = object : TypeToken<List<Person>>() {}.type
val people = gson.fromJson<List<Person>>(jsonData, typeOf)
11.6 使用Retrofit处理复杂接口地址类型
1. @Path注解和@query注解
2. ResponseBody是啥
interface ExampleService { @DELETE("data/{id}") fun deleteData(@Path("id") id: String): Call<ResponseBody> }
在返回值声明的时候,我们将Call的泛型指定成了ResponseBody,这是什么意思呢?
由于POST、PUT、PATCH、DELETE这几种请求类型与GET请求不同,它们更多是用于操作服务器上的数据,而不是获取服务器上的数据,所以通常它们对于服务器响应的数据并不关心。这个时候就可以使用ResponseBody,表示Retrofit能够接受任意类型的响应数据,并且不会对响应数据进行解析。
3. @Body注解 P458
向服务器提交数据,使用POST请求,需要将数据放到HTTP请求的body部分,这个功能在Retrofit中可以借助@Body注解来完成:
interface ExampleService { @POST("data/create") fun createData(@Body data: Data): Call<ResponseBody> }
当Retrofit发出POST请求时,就会自动将Data对象中的数据转换成JSON格式的文本,并放到HTTP请求的body部分,服务器在收到请求之后只需要从body中将这部分数据解析出来即可。
4. @Headers注解和@Header注解
有些服务器接口还可能会要求我们在HTTP请求的header中指定参数。这些header参数其实就是一个个的键值对,我们可以在Retrofit中直接使用@Headers注解来对它们进行声明。
interface ExampleService { @Headers("User-Agent: okhttp", "Cache-Control: max-age=0") @GET("get_data.json") fun getData(): Call<Data> }
但是这种写法只能进行静态header声明,如果想要动态指定header的值,则需要使用@Header注解:
1 interface ExampleService { 2 @GET("get_data.json") 3 fun getData(@Header("User-Agent") userAgent: String, @Header("Cache-Control") cacheControl: String): Call<Data> 4 }
5. Service接口的动态代理对象是什么 P459
不熟悉动态代理也没关系,只需要知道有了动态代理对象之后,就可以随意调用接口中定义的所有方法,而Retrofit会自动执行具体的处理就可以了。
6. Retrofit构建器的最佳写法
object ServiceCreator { private const val BASE_URL = "http://10.0.2.2/" private val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)
// 或者更加简洁,不带参数的create()方法
// inline fun <reified T> create(): T = create(T::class.java) }