Android Studio:四大元件之Service
一.服務基本上分為兩種形式:
啟動
當應用元件(如 Activity)通過呼叫 startService() 啟動服務時,服務即處於“啟動”狀態。一旦啟動,服務即可在後臺無限期執行,即使啟動服務的元件已被銷燬也不受影響。 已啟動的服務通常是執行單一操作,而且不會將結果返回給呼叫方。例如,它可能通過網路下載或上傳檔案。 操作完成後,服務會自行停止執行。
繫結
當應用元件通過呼叫 bindService() 繫結到服務時,服務即處於“繫結”狀態。繫結服務提供了一個客戶端-伺服器介面,允許元件與服務進行互動、傳送請求、獲取結果,甚至是利用程序間通訊 (IPC) 跨程序執行這些操作。 僅當與另一個應用元件繫結時,繫結服務才會執行。 多個元件可以同時繫結到該服務,但全部取消繫結後,該服務即會被銷燬。
二.啟動Service
1.新建名為Service的Project
java檔案(MainActivity.java(主Activity),HelloIntentService.java,HelloService.java)
xml檔案(activity_main.xml)
2.Strings.xml
<resources>
<string name="app_name">Service</string>
<string name="button1_name">StartService</string >
<string name="button2_name">StopService</string>
</resources>
3.activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width ="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/button1_name"/>
<Button
android:id="@+id/button2"
android:text="@string/button2_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
4.MainActivity.java
此處使用OnClickListener介面實現Button監聽事件
package com.example.administrator.service;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1=findViewById(R.id.button1);
Button button2=findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent intent=new Intent(this,HelloIntentService.class);
switch (v.getId()){
case R.id.button1:startService(intent);break;
case R.id.button2:stopService(intent);break;}
}
}
5.啟動Service
5.1從傳統上講,可以擴充套件兩個類來建立啟動服務:
Service
這是適用於所有服務的基類。擴充套件此類時,必須建立一個用於執行所有服務工作的新執行緒,因為預設情況下,服務將使用應用的主執行緒,這會降低應用正在執行的所有 Activity 的效能。
IntentService
這是 Service 的子類,它使用工作執行緒逐一處理所有啟動請求。如果您不要求服務同時處理多個請求,這是最好的選擇。 您只需實現 onHandleIntent() 方法即可,該方法會接收每個啟動請求的 Intent,使您能夠執行後臺工作。
5.2擴充套件 IntentService 類來啟動服務
(特點:通過工作佇列處理啟動請求)
IntentService執行以下操作:
1.建立預設的工作執行緒,用於在應用的主執行緒外執行傳遞給 onStartCommand() 的所有 Intent。
2.建立工作佇列,用於將 Intent 逐一傳遞給 onHandleIntent() 實現,這樣您就永遠不必擔心多執行緒問題。
3.在處理完所有啟動請求後停止服務,因此您永遠不必呼叫 stopSelf()。
4.提供 onBind() 的預設實現(返回 null)。(因此此處無需有public IBinder onBind(Intent intent) {return null; }
)
5.提供 onStartCommand() 的預設實現,可將 Intent 依次傳送到工作佇列和 onHandleIntent() 實現。(若有需要可重寫其他回撥方法(如 onCreate()、onStartCommand() 或 onDestroy()),請確保呼叫超類實現,以便 IntentService 能夠妥善處理工作執行緒的生命週期。)
綜上所述,只需實現 onHandleIntent() 來完成客戶端提供的工作即可。(不過,您還需要為服務提供小型建構函式。)
HelloIntentService.java
package com.example.administrator.service;
import android.app.IntentService;
import android.content.Intent;
import android.widget.Toast;
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("HelloIntentService");
}
/**
* The IntentService calls this method from the default worker thread with
* the intent that started the service. When this method returns, IntentService
* stops the service, as appropriate.
*/
@Override
protected void onHandleIntent( Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
System.out.println("Thread are sleeping");
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
//onStartCommand() 必須返回預設實現(即,如何將 Intent 傳遞給 onHandleIntent()):
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
@Override
public void onDestroy() {
System.out.println("Service onDestroy");
super.onDestroy();
}
}
5.3擴充套件 Service 類來啟動服務
(特點:服務執行多執行緒處理每個 Intent)
5.3.1在Strings.xml新增
<string name="button3_name">StartService1</string>
<string name="button4_name">StopService1</string>
5.3.2在activity_main.xml中新增
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button3"
android:text="@string/button3_name"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button4"
android:text="@string/button4_name"/>
5.3.3MainActivity.java
package com.example.administrator.service;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1=findViewById(R.id.button1);
Button button2=findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
//新增部分
Button button3=findViewById(R.id.button3);
Button button4=findViewById(R.id.button4);
button3.setOnClickListener(new buttonclick(this));
button4.setOnClickListener(new buttonclick(this));
}
@Override
public void onClick(View v) {
Intent intent=new Intent(this,HelloIntentService.class);
switch (v.getId()){
case R.id.button1:startService(intent);break;
case R.id.button2:stopService(intent);break; }
}
//新增:此處為外部類(獨立類)實現Button監聽事件
public class buttonclick implements View.OnClickListener{
private Context context;
public buttonclick(Context ct){this.context=ct;}
Intent intent=new Intent(MainActivity.this,HelloService.class);
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.button3:startService(intent);break;
case R.id.button4:stopService(intent);break;}
}
}
}
5.3.4HelloService.java
package com.example.administrator.service;
import android.app.Service;
import android.content.Intent;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.widget.Toast;
public class HelloService extends Service {
private ServiceHandler mServiceHandler;
private Looper mServiceLooper;
// Handler that receives messages from the thread
private final class ServiceHandler extends android.os.Handler {
public ServiceHandler(Looper looper) {
super(looper); }
@Override
public void handleMessage(Message msg){
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try { Thread.sleep(5000); }
catch (InterruptedException e){
// Restore interrupt status.
Thread.currentThread().interrupt();}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
/**Start up the thread running the service. Note that we create a
separate thread because the service normally runs in the process's
main thread, which we don't want to block. We also make it
background priority so CPU-intensive work will not disrupt our UI.*/
HandlerThread thread=new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper=thread.getLooper();
mServiceHandler=new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg=mServiceHandler.obtainMessage();
msg.arg1=startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
// We don't provide binding, so return null
@Override
public IBinder onBind(Intent intent){
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
super.onDestroy(); }
}
與使用 IntentService 相比,這需要執行更多工作。
但是,因為是由自己處理對 onStartCommand() 的每個呼叫,因此可以同時執行多個請求。此示例並未這樣做,此示例此時只有一個請求,但如果希望同時執行多個請求,則可為每個請求建立一個新執行緒,然後立即執行這些執行緒(而不是等待上一個請求完成)。
5.3.5onStartCommand() 方法返回值
請注意,onStartCommand() 方法必須返回整型數。整型數是一個值,用於描述系統應該如何在服務終止的情況下繼續執行服務(如上所述,IntentService 的預設實現將為您處理這種情況,不過您可以對其進行修改)。從 onStartCommand() 返回的值必須是以下常量之一:
START_NOT_STICKY
如果系統在 onStartCommand() 返回後終止服務,則除非有掛起 Intent 要傳遞,否則系統不會重建服務。這是最安全的選項,可以避免在不必要時以及應用能夠輕鬆重啟所有未完成的作業時執行服務。
START_STICKY
如果系統在 onStartCommand() 返回後終止服務,則會重建服務並呼叫 onStartCommand(),但不會重新傳遞最後一個 Intent。相反,除非有掛起 Intent 要啟動服務(在這種情況下,將傳遞這些 Intent ),否則系統會通過空 Intent 呼叫 onStartCommand()。這適用於不執行命令、但無限期執行並等待作業的媒體播放器(或類似服務)。
START_REDELIVER_INTENT
如果系統在 onStartCommand() 返回後終止服務,則會重建服務,並通過傳遞給服務的最後一個 Intent 呼叫 onStartCommand()。任何掛起 Intent 均依次傳遞。這適用於主動執行應該立即恢復的作業(例如下載檔案)的服務。
6.執行
點選StartService/StartService1按鈕,睡眠5秒後會自動停止
若點選StartService/StartService1按鈕,又馬上點選StopService/StopService1按鈕,也會直接停止執行。