一、服務
1、服務
服務是Android實作程式背景運作的方案,其不依賴于任何使用者界面
2、Android多線程
(1)線程的啟動方法一:
A:
class MyThread extends Thread{
public void run(){
// 這裡輸入處理事件的邏輯
··············
}
}
//啟動該線程
new MyThread().start();
(2)線程啟動的方法二:
B:
class MyThread implements Runnable{
public void run(){
// 這裡輸入處理事件的邏輯
··············
}
}
//啟動線程
MyThread myThread = new MyThread();
new Thread(myThread) .start();
(3)線程啟動的方法三(常用):
C:
new Thread(new Runnable(){
public void run(){
// // 這裡輸入處理事件的邏輯
··············
}).start;
}
注意:UI元素必須在主線程中進行,不能在子線程中進行。
這裡我們建立一個工程為AndroidThreadTest的工程來學習:
具體思想為建立一個按鈕和文本顯示内容,在點選事件中建立子線程,用該線程實作文本内容的變化,具體代碼如下:
activity_main.xml:
<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">
<Button
android:id="@+id/chang_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="改變文本内容"/>
<TextView
android:id="@+id/text"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:text="Hello World!"
/>
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button changText = (Button)findViewById(R.id.chang_text);
text = (TextView)findViewById(R.id.text);
changText.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.chang_text:
//開始建立子線程
new Thread(new Runnable() {
@Override
//執行線程操作
public void run() {
Log.d("MainActivity","子線程建立成功,正在運作,下一步,執行UI操作");
text.setText("修改界面成功,歡迎看見你");
}
}).start();
}
}
}
然後運作,點選按鈕後發現程式崩潰了。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL4MzN1ATOycDM4ITMwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
檢視錯誤資訊可知,我們在子線程中進行UI操作這是不行的,UI操作應該切換至主線程中去完成。
那麼,我們可以采用異步消息處理機制去解決在子線程中的UI操作。
異步通信機制通過在主線程中運作的handleMessage()方法進行處理子線程的想要完成的UI操作,我們通過消息(data資料)來通知主線程的進行UI顯示。
在AndroidThreadTest的基礎上進行修改。
在MainActivity中,
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView text;
//定義整形變量,用于更新TextView這個動作
public static final int UPDATE_TEXT = 1;
//定義一個Handler對象
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) { //重寫父類的handleMessage()方法
/*如果接受字段等于UPDATE_TEXT,将修改UI内容*/
switch (msg.what) {
case UPDATE_TEXT:
Log.d("MainActivity","修改内容...");
text.setText("修改界面成功");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button changText = (Button)findViewById(R.id.chang_text);
text = (TextView)findViewById(R.id.text);
changText.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.chang_text:
//開始建立子線程
new Thread(new Runnable() {
@Override
//執行線程操作
public void run() {
Log.d("MainActivity","子線程建立成功");
//建立一個Message對象,
Message message = new Message();
//将對象的what字段設定為UPDATE_TEXT
message.what = UPDATE_TEXT;
//将資訊發送出去,就會調用handler方法去修改文本内容。
handler.sendMessage(message);
/*Log.d("MainActivity","子線程建立成功,正在運作,下一步,執行UI操作");
text.setText("修改界面成功,歡迎看見你");*/
}
}).start();
break;
default:
break;
}
}
}
3:異步消息處理機制。
其處理過程主要有四部分組成:(1)Message、(2)Handler、(3)MessageQueue、(4)Looper。
下面我們就來具體介紹:
(1)Message
是線上程間傳遞的消息,其内部攜帶少量資訊,也可采 用 arg1和arg2攜帶整形資料,obj攜帶Object對象。
(2)Handler
用于發送和處理消息,發送消息一般采用Handler的 sendMessage()方法,而消息會最終到達 handleMessage()方法中。
(3)MessageQueue
為消息隊列,用于存放所有通過Handler發送的消息,每個線程隻有一個MessageQueue對象。
(4)Looper
為每個線程中的,隊列MessageQueue管理者。調用Looper的loop()方法後,就會進入一個無限循環,每發現MessageQueue隊列中存在消息,就會将其取出,并傳遞到handleMessage()方法中
4、AsyncTask
是一個需要建立子類去繼承的抽象類,繼承時需指定三個泛行參數,分别為(1)params:AsyncTask執行時需要傳入的參數,可在背景任務中使用。(2)Progress:進度機關,顯示任務進度。(3)Result:對任務結果進行傳回
如:
class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
···
}
而在該類中我們需要重寫AsyncTask的方法。常用的有四個:
(1)onPreExecute()
背景任務執行前調用,進行界面的初始化操作,比如顯示進度對話框
(2)onInBackground(Params…)
該方法在子線程中完成,用于處理所有的耗時任務,任務完成可使用return語句将執行結果傳回,如果像将耗時任務進度回報給主線程,可調用publishProgress(Params…)方法來完成
(3)onProgressUpdate(Progress…)
當背景任務調用publishProgress()方法後,該方法就會被調用,,該方法中攜帶的參數是背景任務中傳遞出來的,該方法可以對UI進行操作,利用參數中的數值對目前界面元素進行更新。
(4)onPostExecute(Result)
背景任務執行完畢并通過returen傳回後,該方法就會被調用,可利用傳回的資料進行UI操作,比如提醒任務執行的結果以及關閉進度條對話框,
啟動該AsyncTask異步機制的方法為,new +子類+().execute();
二、服務的基本用法
建立服務的方法:
new --------Service ------Service
重寫父類的三個方法:
(1)onCreate():服務建立時調用
(2)onStartCommand():服務啟動時調用
(3)onDestroy():服務銷毀時調用
服務為四大組建需要在AndroidMainifest.xml中注冊。
我們建立一個ServiceTest項目,在java包中建立一個服務為MyService,在activty_main.xml中定義兩個按鈕,用于啟動和停止服務:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/start_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:hint="啟動服務"
/>
<Button
android:id="@+id/stop_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="停止服務"
android:textSize="20dp"/>
</LinearLayout>
在主函數中處理兩個按鈕點選事件,通過Context類中的startService()和stopService()對服務進行啟動和停止,如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = findViewById(R.id.start_service);
Button stopService = findViewById(R.id.stop_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_service:
Intent startIntent = new Intent(this,MyService.class);
startService(startIntent); //啟動服務
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this,MyService.class);
stopService(stopIntent); //停止服務
break;
default:
break;
}
}
}
我們可以在MyService的任意位置調用stopSelf()讓服務停下來。
那麼怎麼讓服務去做什麼呢?完成什麼樣的任務?
我們需要onBinder()方法内建構我們的任務對象。例如,我們需要讓背景去下載下傳一個檔案,我們需要建構一個Binder對象來下載下傳,修改MyService中的内容:
·······································
private DownloadBinder mbinder = new DownloadBinder();
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mbinder;
}
class DownloadBinder extends Binder {
public void startDownload(){
Log.d("TAG","開始下載下傳中...");
}
public void stopDownload(){
Log.d("TAG","停止下載下傳...");
}
public int getProgress(){
Log.d("TAG"," 得到下載下傳進度...");
return 0;
}
}
··································
當活動與服務綁定後,調用服務裡的Binder提供的方法,在MainActivity中實作:
······························
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder)service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = findViewById(R.id.start_service);
Button stopService = findViewById(R.id.stop_service);
Button unbindService = findViewById(R.id.unbind_service);
Button onbindService = findViewById(R.id.onbind_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
onbindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start_service:
Intent startIntent = new Intent(this,MyService.class);
startService(startIntent); //啟動服務
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this,MyService.class);
stopService(stopIntent); //停止服務
break;
case R.id.onbind_service:
Intent bindIntent = new Intent(this,MyService.class);
bindService(bindIntent,connection,BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
unbindService(connection);
break;
default:
break;
}
}
·················
在oncreate()方法中添加一個背景通知,将MyService程式設計一個前台服務。如下所示:
//服務建立時調用
public void onCreate(){
super.onCreate();
Log.d(TAG,"服務建立中...");
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("This is content title")
.setContentText("This is content")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setContentIntent(pi)
.build();
startForeground(1,notification);
}
對于耗時操作中需要用到開啟子線程和結束服務的方法,android提供了一個異步會自動停止的服務的類IntentService類。這裡我們建立一個MyIntentService類:
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService"); //調用父類的有參構造函數
}
@Override
protected void onHandleIntent(Intent intent) {
//列印目前線程的id
Log.d("MyIntentService","Thread id is " +Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService","銷毀執行中");
}
}
MainActivity中添加以下邏輯:
case R.id.start_intent_service:
Log.d("MainActivity","Thread id is "+Thread.currentThread().getId());
Intent intent = new Intent(this,MyIntentService.class);
startService(intent);
break;
運作後發現:
程式運作完成後,自動銷毀。