《第一行代碼》第十章 后臺默默的勞動者 探究Service

  • 本篇參考資料《第一行代碼 第三版》 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

二. Service的使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容