一、服務的解釋
服務(Service)是Android中實現后臺運行的解決方案,它適合那些去執行不需要和用戶交互而且還要求長期運行的任務。服務的運行不依賴任何的與任何用戶界面,即使程序被切換到后臺,或者用戶打開了另外一個應用程序,服務仍然能夠保持正常運行。
注意:服務并不是運行在一個獨立的進程當中,而是依賴于創建服務時所在的應用程序進程。當某個應用程序被殺死時,所有依賴于該進程的服務也會停止運行。
服務并不會自動開啟線程,所有的代碼都是默認運行在主線程中的。我們在服務的內部手動創建了子線程,并在這里執行具體的任務,否則就有可能出現主線程被阻塞住的情況。
二、Android多線程編程
1、線程的基本用法
這部分與Java相同,線程創建的兩種方式。
2、在子線程中更新UI
與其他許多的GUI庫一樣,Android的UI也是不安全的。也就是說,如果想要更新應用程序的UI元素,則必須在主線程中進行,否則就會出現異常。
實踐:新建AndroidThreadTest項目:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.zhj.androidthreadtest.MainActivity"><Buttonandroid:id="@+id/change_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Change Text"/><TextViewandroid:id="@+id/text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="Hello world"android:textSize="20sp"/>
</RelativeLayout>
一個按鈕,一個TextView,顯示出Hello world,目標效果:當我們點擊按鈕時,會把Hello world 改變成 Nice to meet you。
我們開始寫MainActivity.java
package com.zhj.androidthreadtest;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends AppCompatActivity {private TextView text;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);text =(TextView)findViewById(R.id.text);Button changeText = (Button)findViewById(R.id.change_text);changeText.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {switch(v.getId()){case R.id.change_text:new Thread(new Runnable() {@Overridepublic void run() {text.setText("Nice to meet you");}}).start();break;default:break;}}});}
}
邏輯非常簡單。我們在 按鈕的點擊事件中開啟了一個子線程,然后在子線程中調用TextView的setText()方法將顯示的字符串改成Nice to meet you。我們運行之后,點擊按鈕按鈕發現程序崩潰了。
查看logcat日志:可以看出由于在子線程中更新UI造成的。
由此證實,An'd'roid確實不允許在子線程中進行UI操作的。但是有些時候我們必須在子線程中去執行一些耗時任務,然后根據任務的執行結果來更新相應的UI控件。
對于這種情況,Android 提供了一套異步消息處理機制,完美解決了在子線程中進行UI操作的問題。我們先來了解一下異步消息處理的使用方法,
修改MainActivity的代碼:
package com.zhj.androidthreadtest;import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;public class MainActivity extends AppCompatActivity {public static final int UPDATE_TEXT = 1; //表示更新TextView這個動作private TextView text;private Handler handler = new Handler(){//新增Handler對象,并重寫handleMessage()方法,對具體的Message進行處理,public void handleMessage(Message msg){//如果發現Message的what字段的值等于UPDATA_TEXT,就將TextView顯示的內容改成Nice to meet youswitch (msg.what){case UPDATE_TEXT ://在這里進行UI操作text.setText("Nice to meet you");break;default:break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);text =(TextView)findViewById(R.id.text);Button changeText = (Button)findViewById(R.id.change_text);changeText.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {switch(v.getId()){case R.id.change_text:new Thread(new Runnable() {@Overridepublic void run() {Message message = new Message();//創建一個Message(android.os.Message)對象,并將他的what字段設置為UPDATE_TEXTmessage.what = UPDATE_TEXT;handler.sendMessage(message);//將Message對象發送出去}}).start();break;default:break;}}});}
}
來觀察按鈕的點擊事件中的代碼,我們并沒有在子線程里直接進行UI操作,而是創建了一個Message(android.os.Message)對象,并將它的what字段指定為UPDATE_TEXT,然后調用Handler的sendMessage()方法將這條Message發送出去。Handler會收到這條Message,并在handlerMessage()方法中對它進行處理,注意此時的handlerMessage()方法中的代碼就是在主線程中運行的了,所以我們放心在這里進行UI操作。
運行之后,我們會發現內容被替換成Nice to meet you 。
3、解析異步消息處理機制
Android中的異步消息處理主要分為4個部分:Message、Handler、MessageQueue和Looper。
? ? (1)Message:Message是在線程之間傳遞的消息,它可以在內部攜帶少量的信息,用于在不同線程之間交換數據,可以使用Message的what字段,還可以使用arg1和arg2字段來攜帶一些整型數據,使用obj字段攜帶一個Object對象。
? ? (2)Handler:處理者的意思,主要用于發送和處理消息的,發送消息一般是使用Handler的sendMessage()方法,而發出的消息經過一系列的輾轉處理后,最終會傳遞到Handler的handleMessage()方法中。
????(3)MessageQueue:消息隊列,主要用于存放所有通過Handler發送的消息。這部分消息會一直存放在消息隊列中,等待被處理。每個線程中只有一個MessageQueue對象。
? ? (4)Looper:每個線程中的MessageQueue的管家,調用Looper的loop()方法后,就會進入到一個無限循環當中,然后每當發現MessageQueue中存在一條消息,就會將它取出,并傳遞到Handler的handlerMessage()方法中。每個線程中也只有要給Looper對象。
我們把異步消息處理的整個流程再梳理一遍。首先需要在主線程中創建一個Handler對象,并重寫handlerMessage()方法,這個方法里寫你的邏輯。當主線程中需要進行UI操作時,就創建一個Message對象,并通過Handler把這條消息發送出去。之后這條消息會被添加到MessageQueue的隊列中等待被處理,而Looper則會一直嘗試從MessageQueue中取出待處理消息,最后分發回Handler的handlerMessage()方法中。由于Handler是在主線程中創建的,所以此時handlerMessage()方法中的代碼也會在主線程中運行,于是我們在這里可以進行UI操作了。
一條Message經過這樣一個流程的輾轉調用后,也就從子線程進入到了主線程,從不能更新UI變成了更新UI,整個異步消息處理的核心思想就是如下。
4、使用AsyncTask