- 本篇參考資料《第一行代碼 第三版》 2020.4月出版
- 本篇文章只是本人看書的理解和整理的筆記灰瞻,更完整的內(nèi)容還在書上
- 尊重原作者 請購買正版圖書
一開始就知道有四大組件龙优,結(jié)果學(xué)習(xí)Android開發(fā)這么久愕贡,一直還只用到了Activity济舆,這里需要進行惡補抵怎,從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.reibang.com/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表達式 (不需要再專門定義一個類)
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元素溺欧,必須在主線程中進行喊熟,否則就會出現(xiàn)異常。對于這種情況姐刁,Android提供了一套異步消息處理機制芥牌,完美地解決了在子線程中進行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 !"
}
}
}
}
注意0匕小F怼:這里沒有使用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提供了一套異步消息處理機制,完美的解決了在子線程中修改UI的問題
1.3 Android 異步消息處理機制
使用handler接收異步處理的信息磁携,可以在handler中修改ui,將上邊的代碼改成如下:
class MainActivity : AppCompatActivity() {
//msg識別碼
val upDataText=10011
val handler=object:Handler(){
//接收信息
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
//判斷信息識別碼 根據(jù)不同的識別碼進行不同動作
when(msg.what){
//修改ui
upDataText-> {
//讀取參數(shù)對象 并使用as關(guān)鍵字轉(zhuǎn)強制換為Student1類型
//msg.obj可能為null 注意使用良风?進行非空判斷
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強制轉(zhuǎn)換為具體的類型钞艇,這樣就可以進行操作了啄寡。(注意使用?非空判斷)
1.4 使用AsyncTask來進行異步操作
http://www.reibang.com/p/9724355bf01b