目錄
3.3 startService 與 bindService 的區別
1. Service介紹
Android 四大組件之Service(中譯:服務)學習。
1.1 Service是什麼,有什麼作用
Service(中譯:服務)是一個組件,用於在後臺執行長時間運行的操作,如播放音樂、處理網絡事務、交互內容提供者等。Service沒有任何UI界面,所以不需要與用戶交互,而且即使應用程序被破壞,它也能正常工作。
2. Service生命週期
Service有兩種形式,分別是Start(啓動)和Bound(綁定):
Start(啓動):當app組件(如Activity)通過調用startService()來啓動服務,服務一旦啓動,就可以在後臺無限期運行,即使啓動它的組件(如Activity)被銷燬。可以通過調用stopService()方法來停止服務,而且服務本身可以通過調用stopSelf()方法來自行停止。
Bounded(綁定):當app組件通過調用bindService()方法綁定到服務時,服務將被綁定。綁定服務提供了一個client-server interface(客戶機-服務器接口),該接口允許組件與服務交互、發送請求、獲取結果。在所有客戶端解除服務綁定之前,無法停止服務。
public class ExampleService extends Service {
int startMode; //指示在服務被終止時如何行爲
IBinder binder; //用於綁定客戶的接口
boolean allowRebind; //指示是否應該使用onRebind()
/**
* 在最初創建服務時(在調用onStartCommand()或onBind()之前),系統將調用此方法以執行一次性設置過程。
* 如果服務已經在運行,則不會調用此方法。
*/
@Override
public void onCreate() {
//正在創建服務
}
/**
* 當另一個組件(例如活動)請求啓動服務時,系統通過調用startService()來調用此方法。
* 執行此方法時,服務將啓動並可以無限期在後臺運行。
* 如果執行此操作,則 必須 通過調用stopSelf()或stopService()在服務完成後停止該服務。如果只想提供綁定,則不需要實現此方法。
* @param intent
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//調用startService(),服務正在啓動
return mStartMode;
}
/**
* 在call bindService()方法時調用。在此方法的實現中,必須提供一個接口,客戶端可以通過返回IBinder使用該接口與服務進行通信。
* 當你實現綁定時,必須始終實現此方法。但是,如果不允許綁定,則應返回null。
* @param intent
* @return
*/
@Override
public IBinder onBind(Intent intent) {
//客戶端使用bindService()來綁定到服務
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
//所有客戶端都使用unbindService()來解除綁定
return mAllowRebind;
}
@Override
public void onRebind(Intent intent) {
//重新綁定,指調用onUnbind()之後,客戶端使用bindService()重新綁定到服務
}
/**
* 當服務不再使用並被銷燬時,系統將調用此方法,所以這是服務收到的最後一個呼叫。
* 您的服務應實現此目的,以清理所有資源,例如線程,註冊的偵聽器或接收器。
*/
@Override
public void onDestroy() {
//服務不再使用,正在被銷燬
}
}
3. 自定義Service例子
3.1 觀察Service生命週期
startService 的週期爲:開啓 -> onCreate -> onStartCommand ,銷燬 -> onDestry
簡單的創建一個Service,然後啓動它-然後銷燬它,利用Toast顯示狀態,效果如下所示:
1. 創建MyService服務,添加Toast用於顯示Service狀態
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "Service onCreate", Toast.LENGTH_SHORT).show();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service onDestroy", Toast.LENGTH_SHORT).show();
}
}
2. 在AndroidManifest簽單文件中定義聲明我們剛剛創建的MyService。(當然這一步可以讓Android Studio來幫我們完成)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.servicetest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
//聲明我們的服務
<service
android:name=".MyService"
android:enabled="true"
android:exported="true">
</service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
關於 Service 在清單文件中的一些參數說明:
1. android: name:對應 Service 類名,必須填寫。
2. android: enabled:是否可以被系統實例化,默認爲 true。
3.android: exported:代表是否能被其他應用所調用,如果將其設置爲 false,則確保服務僅適用於您的應用。其默認值取決於定義的 Service 是否設置了 intent-filter,如果有 intent-filter,默認值爲 true,否則爲 false。爲 false 的情況下,即使有 intent-filter 匹配,也無法打開,即無法被其他應用隱式調用。
3. 在我們的app組件-MainActivity.java文件中進行 Service 的操作,(啓動服務,停止服務)
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startBtn = findViewById(R.id.start_service_btn);
startBtn.setOnClickListener(this);
Button stopBtn = findViewById(R.id.stop_service_btn);
stopBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service_btn:
//啓動服務
Intent intent = new Intent(this, MyService.class);
startService(intent);
break;
case R.id.stop_service_btn:
//停止服務
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
default:
break;
}
}
}
3.2 綁定Service例子
相對比與Start Service,bound service會相對複雜些,見下例所示:
具體代碼如下:
public class MyBoundService extends Service {
private final IBinder mBinder = new MyBinder();
private final Random mGenerator = new Random();
public MyBoundService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class MyBinder extends Binder {
MyBoundService getService() {
//返回MyBoundService的實例
return MyBoundService.this;
}
}
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
public class BoundServiceTestActivity extends AppCompatActivity {
private MyBoundService boundService;
private boolean isBoundService = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bound_service_test);
Button getRandomBtn = findViewById(R.id.get_number_btn);
getRandomBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isBoundService) {
Toast.makeText(BoundServiceTestActivity.this,
"random Number is: " + boundService.getRandomNumber(),
Toast.LENGTH_SHORT)
.show();
}
}
});
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, MyBoundService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if (isBoundService) {
unbindService(serviceConnection);
isBoundService = false;
}
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyBoundService.MyBinder binder = (MyBoundService.MyBinder) service;
boundService = binder.getService();
isBoundService = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBoundService = false;
}
};
}
3.3 startService 與 bindService 的區別
從二者的生命週期上看:
startService 開啓的 Service 的生命週期不與開啓它的 Activity 的生命週期相關聯,即使開啓它的 Activity 銷燬了,該 Service 還會一直運行,直到調用 stopService() 或 Service 自身調用 stopSelf(),該 Service 纔會被銷燬。
bindService 開啓的 Service 的生命週期與開啓它的 Activity 的生命週期相關聯,如果開啓它的 Activity 被銷燬了,那該 Service 也就會被銷燬。
4. IntentService
4.1 IntentService是什麼
IntentService是Service的子類,繼承於Service類,用於處理需要異步請求的任務。用戶通過調用Context.StartService(Intent)發送請求,服務根據需要啓動,使用工作線程依次處理每個Intent,並在處理完所有工作後自身停止服務。
使用時,擴展IntentService並實現onHandleIntent(android.content.Intent)。IntentService接收Intent,啓動工作線程,並在適當時機停止服務。
所有的請求都在同一個工作線程上處理,一次處理一個請求,所以處理完所以的請求可能會花費很長的時間,但由於IntentService是另外創建的線程來工作,所以保證不會阻止App的主線程。
4.2 IntentService與Service的區別
從何時使用,觸發方法,運行環境,何時停止四個方面分析。
1、何時使用:
Service用於沒有UI工作的任務,但不能執行長任務(長時間的任務),如果需要Service來執行長時間的任務,則必須手動開啓一個線程來執行該Service。
IntentService可用於執行不與主線程溝通的長任務。
2、觸發方法:
Service通過調用 startService() 方法來觸發。
而IntentService通過Intent來觸發,開啓一個新的工作線程,並在線程上調用 onHandleIntent() 方法。
3、運行環境:
Service 在App主線程上運行,沒有與用戶交互,即在後臺運行,如果執行長時間的請求任務會阻止主線程工作。
IntentService在自己單獨開啓的工作線程上運行,即使執行長時間的請求任務也不會阻止主線程工作。
4、何時停止
如果執行了Service,我們是有責任在其請求任務完成後關閉服務,通過調用 stopSelf() 或 stopService()來結束服務。
IntentService會在執行完所有的請求任務後自行關閉服務,所以我們不必額外調用 stopSelf() 去關閉它。
4.3 IntentService例子Demo
通過Android Studio來創建我們的MyIntentService。
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public static final String DOWNLOAD_ACTION = "DOWNLOAD_ACTION";
public static final String READ_ACTION = "READ_ACTION";
public static final String TEST_AUTHOR = "TEST_AUTHOR";
public MyIntentService() {
super("MyIntentService");
}
/**
* 進行一些耗時操作
* @param intent 通過startService(Intent intent)方法傳入
*/
@Override
protected void onHandleIntent(Intent intent) {
Log.e(TAG, "onHandleIntent: ");
if (intent != null) {
final String action = intent.getAction();
String author = intent.getExtras().getString(TEST_AUTHOR);
//模擬下載動作
if (DOWNLOAD_ACTION.equals(action)) {
for (int i = 0; i < 5; i++) {
try {
//線程等待1s,模擬耗時操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, author + " " + action + " " + i);
}
}
//模擬讀操作
if (READ_ACTION.equals(action)) {
for (int i = 0; i < 5; i++) {
try {
//線程等待2s,模擬耗時操作
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, author + " " + action + " " + i);
}
}
}
}
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onCreate: ");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Log.e(TAG, "onStart: ");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy: ");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
}
}
如果你是手動創建IntentService的,得在AndroidManifest.xml文件中註冊該IntentService,如果通過Android Studio創建則免去這一步了。
<service
android:name=".intentservice.MyIntentService"
android:exported="false">
</service>
在主線程中通過調用 startService(Intent) 方法,發送請求給 Service。
public class IntentServiceActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service);
//模擬 Jere 做下載動作
Intent intent = new Intent(this, MyIntentService.class);
intent.setAction(MyIntentService.DOWNLOAD_ACTION);
intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere");
startService(intent);
//模擬 James 做讀取動作
Intent jamesIntent = new Intent(this, MyIntentService.class);
jamesIntent.setAction(MyIntentService.READ_ACTION);
jamesIntent.putExtra(MyIntentService.TEST_AUTHOR, "James");
startService(jamesIntent);
}
}
打印 Log 信息如下所示:
IntentService 的執行順序是: onCreate() -> onStartCommand() -> onStart() -> onHandleIntent() -> ··· 執行完所有請求 ··· -> onDestroy()。
END~