- 本篇參考資料《第一行代碼 第三版》 2020.4月出版
- 本篇文章只是本人看書的理解和整理的筆記,更完整的內(nèi)容還在書上
- 尊重原作者 請購買正版圖書
一開始就知道有四大組件,結(jié)果學(xué)習(xí)Android開發(fā)這么久,一直還只用到了Activity,這里需要進(jìn)行惡補(bǔ),從Service開始
四大組件之一 Service
Service是Android中實現(xiàn)程序后臺運行的解決方案,它非常適合執(zhí)行那些不需要和用戶交互而且還要求長期運行的任務(wù)。Service的運行不依賴于任何用戶界面,即使程序被切換到后臺,或者用戶打開了另外一個應(yīng)用程序,Service仍然能夠保持正常運行。
另外,不要被Service的后臺概念所迷惑,實際上Service并不會自動開啟線程,所有的代碼都是默認(rèn)運行在主線程當(dāng)中的。也就是說,我們需要在Service的內(nèi)部手動創(chuàng)建子線程,并在這里執(zhí)行具體的任務(wù),否則就有可能出現(xiàn)主線程被阻塞的情況。
一. Android多線程編程
當(dāng)我們需要執(zhí)行一些耗時操作,比如文件的讀寫,網(wǎng)絡(luò)請求等等,為了避免主線程被阻塞影響用戶使用,一般會放到子線程中執(zhí)行,
這里推薦學(xué)習(xí)框架Rxjava http://www.itdecent.cn/p/b002d8ea2648
郭神在書上為了方便理解只講了最簡單的多線程實現(xiàn)方式,以下為書中內(nèi)容筆記
1.1 線程的基本用法
比如我們想打印1~10 每次打印間隔1秒
方式一:使用繼承Thread方式:
fun main(){
val myThread=MyThread()
myThread.start()
}
class MyThread:Thread(){
override fun run() {
super.run()
for(i in 0..10){
println(i)
sleep(1000)
}
}
}
方式二:使用實現(xiàn)接口Runnable方式:
fun main() {
val myThread= MyThread()
Thread(myThread).start()
}
class MyThread : Runnable {
override fun run() {
for (i in 0..10) {
println(i)
sleep(1000)
}
}
}
可以看到這里是Thread的構(gòu)造函數(shù)接收了一個Runnable參數(shù),創(chuàng)建了一個Thread對象,再調(diào)用start方法,就開始了子線程
方式三:使用Lambda表達(dá)式 (不需要再專門定義一個類)
fun main() {
Thread{
for (i in 0..10) {
println(i)
sleep(1000)
}
}.start()
}
以上三種再java中也是同樣的用法,但是接下來,Kotlin為我們提供了一種更簡單的開啟子線程的方式
方式四:使用thread函數(shù)
fun main() {
thread{
for (i in 0..10) {
println(i)
sleep(1000)
}
}
}
可以看到連start也不需要調(diào)用了,thread函數(shù)內(nèi)部全部幫我們處理好了
1.2 嘗試子線程中更新UI
Android的UI是線程不安全的,也就是說,如果想要更新應(yīng)用程序里的UI元素,必須在主線程中進(jìn)行,否則就會出現(xiàn)異常。對于這種情況,Android提供了一套異步消息處理機(jī)制,完美地解決了在子線程中進(jìn)行UI操作的問題。
接下來我們試試在子線程中更新UI,點擊按鈕,啟動一個子線程,在子線程中設(shè)置TextView的內(nèi)容:
布局:
<Button
android:id="@+id/changeTextBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change Text"
/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="25sp"
android:text="Hello World!"
/>
Activity內(nèi)
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
changeTextBtn.setOnClickListener {
thread {
textView.text = "Nice to meet you !"
}
}
}
}
注意!?。哼@里沒有使用findViewById(),這是Kotlin一個非常優(yōu)秀的設(shè)計,可以直接
import kotlinx.android.synthetic.main.activity_main.*
導(dǎo)入這個界面對應(yīng)的布局layout文件,View的id就可以作為對象直接操作
當(dāng)然你非要用findViewById()也可也
private lateinit var button:Button
...
button = findViewById(R.id.button)
使用 Kotlin Android Extensions 直接生成對應(yīng)的 View 作為屬性。不需要 findViewById,不需要定義變量,直接使用。使用時需要注意訪問的 View 屬于哪個 Layout,因為智能提示的候選項會提供所有布局中的 View 供你選擇,然后幫你 import 對應(yīng)包以便你訪問這個 View;假如 import 的多個同一層級的 layout 中具有相同的 id,則這個 id 對應(yīng)的 View 將無法訪問。
接下來我們運行這個app,并點擊按鈕,可以看到程序崩潰了,出現(xiàn)了一條報錯信息:
Only the original thread that created a view hierarchy can touch its views.
這就證明Android不可以在子線程中操作UI,那么對于這種情況我們,Android提供了一套異步消息處理機(jī)制,完美的解決了在子線程中修改UI的問題
1.3 Android 異步消息處理機(jī)制
使用handler接收異步處理的信息,可以在handler中修改ui,將上邊的代碼改成如下:
class MainActivity : AppCompatActivity() {
//msg識別碼
val upDataText=10011
val handler=object:Handler(){
//接收信息
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
//判斷信息識別碼 根據(jù)不同的識別碼進(jìn)行不同動作
when(msg.what){
//修改ui
upDataText-> {
//讀取參數(shù)對象 并使用as關(guān)鍵字轉(zhuǎn)強(qiáng)制換為Student1類型
//msg.obj可能為null 注意使用?進(jìn)行非空判斷
var result:Student1?=msg.obj as? Student1
//在ui中顯示
//如果是空的 則顯示null
textView.text=result?.sex?:"null"
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
changeTextBtn.setOnClickListener {
thread {
val msg=Message()
//信息識別碼
msg.what=upDataText
//msg傳遞一個參數(shù) 對象
msg.obj=Student1("girl")
//發(fā)出信息
handler.sendMessage(msg)
}
}
}
}
在以上的代碼中,我們將異步線程處理結(jié)果,封裝成一個message傳遞到handler。message主要屬性第一個是msg.what是msg的識別碼,用戶handler判斷不同信息并采取不同動作。msg.obj可以傳遞一些對象參數(shù),msg.arg1可以傳遞一些整型參數(shù)。在handler中,使用as關(guān)鍵字將msg.obj強(qiáng)制轉(zhuǎn)換為具體的類型,這樣就可以進(jìn)行操作了。(注意使用?非空判斷)
1.4 使用AsyncTask來進(jìn)行異步操作
http://www.itdecent.cn/p/9724355bf01b