雖然是Android四大組件之一,但從開(kāi)發(fā)Android以來(lái)基本上沒(méi)怎么用過(guò),真是慚愧,今天有時(shí)間來(lái)倒騰倒騰Service,作為一個(gè)假Android,還是總結(jié)下好 ??,省得后面需要了又到處去找。
一說(shuō)起Service,印象里只有在需要處理耗時(shí)的后臺(tái)任務(wù)時(shí)才會(huì)想起它,實(shí)際開(kāi)發(fā)中應(yīng)該也是這種情況居多吧。好了,廢話不多說(shuō)。下面開(kāi)始整理:
按照大的分類來(lái)說(shuō)Service可以分為兩種:Start Service 和 Bound Service,他們都要繼承自Service基類。
1. Start Service
1.先上代碼,首先,自定義Service:
public class MyService extends Service {
private static final String TAG = "MyService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.w(TAG, "in onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w(TAG, "in onStartCommand");
Log.w(TAG, "MyService: " + this);
String name = intent.getStringExtra("name");
Log.w(TAG, "name: " + name);
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.w(TAG, "in onDestroy");
}
}
自定義Service只需要實(shí)現(xiàn)基類中的onBind(...)方法就可以了,這個(gè)方法的返回值只對(duì)Bound Service有用,這里直接返回nll就可以了。
后面的onCreate(),onStartCommand(...),onDestroy(...)都是生命周期方法,一個(gè)service start的時(shí)候會(huì)先調(diào)用onCreate,再調(diào)用onStartCommand。
注意:同一個(gè)Service,只能用startService(...)啟動(dòng)一次,后面再啟動(dòng)也不會(huì)執(zhí)行onCreate()方法了,直接執(zhí)行onStartCommand(...),同理,一個(gè)service不管被啟動(dòng)多少次,只需要調(diào)用一次stopService()(或者service自己調(diào)用stopSelf())即可終止service。
然后看下onStartCommand(...)這個(gè)方法
onStartCommand(Intent intent, int flags, int startId)
intent:包含外部Client啟動(dòng)service時(shí)傳遞的一些參數(shù)。
flags: 默認(rèn)是0,對(duì)應(yīng)的常量是START_STICKY_COMPATIBILITY
startId: 當(dāng)多次start該Service的時(shí)候,startId會(huì)遞增。
返回值:
START_NOT_STICKY: 當(dāng)service因系統(tǒng)內(nèi)存不足被kill后,即時(shí)未來(lái)系統(tǒng)內(nèi)存足夠了也不會(huì)主動(dòng)去重新創(chuàng)建該service,除非程序中再次調(diào)用startService()啟動(dòng)該service.
START_STICKY: 和START_NOT_STICKY不同,被kill后,未來(lái)系統(tǒng)內(nèi)存足夠后會(huì)自動(dòng)重建該service,重建后會(huì)調(diào)用onStartCommand(),但其中的intent參數(shù)會(huì)是null,pendingintent除外。
START_REDELIVER_INTENT: 和START_STICKY唯一的不同是,重建后onstartCommand()方法中的intent為最后一次startService()中的intent.
最常用的就這三種,其他的類型需要的時(shí)候再去翻一下即可。
最后需要注意的是,如果用戶強(qiáng)制kill了service,onDestroy()是不會(huì)執(zhí)行的。
2. AndroidManifest.xml中注冊(cè)
必須在AndroidManifest.xml中進(jìn)行注冊(cè),如果不注冊(cè)的話也不會(huì)崩潰異常,所以一定要注意:
<service
android:name=".MyService"
android:enabled="true"
android:exported="false"
android:icon="@drawable/ic_launcher_background"
android:isolatedProcess="true"
android:label="@string/app_name"
android:permission="@string/app_name"
android:process=":Myservice"/>
如果service要運(yùn)行在單獨(dú)的進(jìn)程中,需要在process中指明此進(jìn)程名稱
當(dāng)此service需要對(duì)其他App開(kāi)放時(shí),exported設(shè)為true即可。
3. 啟動(dòng)service
Intent serviceIntent = new Intent(MainActivity.this, MyService.class);
startService(serviceIntent);
調(diào)用startService(...)即可啟動(dòng)service。這里,當(dāng)Client可通過(guò)intent傳遞參數(shù)給service,當(dāng)service需要傳遞參數(shù)給Client的時(shí)候,可以使用廣播,Client只要注冊(cè)該廣播就可以了。
以上就是Start Service的簡(jiǎn)單用法了。
2. Bound Service
Bound Service 與 Start Service不同的是它的生命周期依賴于Client,當(dāng)Client銷(xiāo)毀的時(shí)候,它也就不存在了。另外,Bound Service可以通過(guò)Binder對(duì)象來(lái)和Client進(jìn)行通信。
Bound Service的使用過(guò)程是這樣的:
1. 自定義Service繼承自Service,并實(shí)現(xiàn)onBind()方法,返回具體的Binder對(duì)象
2.Client通過(guò)ServiceConnection接口自定義一個(gè)ServiceConnection,實(shí)現(xiàn)onServerConnected(),并獲取Service端Binder實(shí)例,通過(guò)binder實(shí)例進(jìn)行service端其他公共方法的調(diào)用。
3.Client端通過(guò)bindService(...)方法將Service綁定到該Client上
4.Client在恰當(dāng)?shù)臅r(shí)候(比如在onDestory的時(shí)候),通過(guò)unBindService()解綁service.
另外,在Bound Service的具體使用過(guò)程中,根據(jù)onBind中返回的Binder對(duì)象的定義方式不同,又具體分為三種方式,每種方式又不同的特點(diǎn),適應(yīng)不同的場(chǎng)景。
2.1 繼承Binder類
這是Bound Service最常用也是最簡(jiǎn)單的一種
局限:
Client和Service必須同屬于一個(gè)進(jìn)程,不能實(shí)現(xiàn)進(jìn)程間通信(IPC)。否則會(huì)出現(xiàn)類似于 "android.os.BinderProxy cannot be cast to xxxx." 的錯(cuò)誤
自定義Service:
public class BoundService1 extends Service {
private static final String TAG = "BoundService1";
/////// 自定義binder
private MyBinder mBinder = new MyBinder();
public class MyBinder extends Binder {
BoundService1 getService() {
return BoundService1.this;
}
}
/////// 自定義binder
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.w(TAG, "in onBind");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.w(TAG, "in onUnbind" );
return super.onUnbind(intent);
}
@Override
public void onCreate() {
super.onCreate();
Log.w(TAG, "in onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.w(TAG, "in onDestroy");
}
}
AndroidManifest.xml中注冊(cè):
<service android:name=".BoundService1"/>
Client中使用:
public class BActivity extends AppCompatActivity {
private static final String TAG = "BActivity";
private Button bindServiceBtn;
private Button unBindServiceBtn;
private Button startIntentService;
private Intent serviceIntent;
private ServiceConnection sc = new MyServiceConnection();
private MyBinder mBinder;
private BoundService1 mBindService;
private boolean mBound;
//////////////自定義ServiceConnection
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.w(TAG, "in MyServiceConnection onServiceConnected");
mBinder = (MyBinder) binder;
mBindService = mBinder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.w(TAG, "in MyServiceConnection onServiceDisconnected");
mBound = false;
}
}
//////////////自定義ServiceConnection
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
bindServiceBtn = findViewById(R.id.bind_service_btn);
unBindServiceBtn = findViewById(R.id.unBind_service_btn);
startIntentService = findViewById(R.id.start_intent_service_btn);
bindServiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(BActivity.this, BoundService1.class);
bindService(intent, sc, Context.BIND_AUTO_CREATE);
}
});
unBindServiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
excuteUnBindService();
}
});
startIntentService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(BActivity.this, MyIntentService.class);
startService(intent);
}
});
}
private void excuteUnBindService() {
if (mBound) {
unbindService(sc);
mBound = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.w(TAG, "in onDestroy");
excuteUnBindService();
}
}
同樣這里即使多次點(diǎn)擊綁定,也是會(huì)綁定一次,BoundService1中的生命周期方法也只會(huì)執(zhí)行一次綁定。
2.2 使用Messenger
通過(guò)Messenger方式返回Binder對(duì)象可以不用考慮Client-Service是否屬于同一個(gè)進(jìn)程的問(wèn)題,并且,可以實(shí)現(xiàn)Client-Service之間的雙向通信。
局限:
不支持嚴(yán)格意義上的多線程并發(fā)處理,實(shí)際上是以隊(duì)列去處理
自定義Service:
public class BoundService2 extends Service {
private static final String TAG = "BoundService2";
public static final int MSG_FROM_CLIENT_TO_SERVICE = 1;
public static final int MSG_FROM_SERVICE_TO_CLIENT = 2;
private Messenger mClientMessenger;
private Messenger mServiceMessenger = new Messenger(new ServiceHandler());
class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.w(TAG, "thread name: " + Thread.currentThread().getName());
switch (msg.what) {
case MSG_FROM_CLIENT_TO_SERVICE:
Log.w(TAG, "receive msg from client");
mClientMessenger = msg.replyTo;
// service發(fā)送消息給client
Message toClientMsg = Message.obtain(null, MSG_FROM_SERVICE_TO_CLIENT);
try {
Log.w(TAG, "service begin send msg to client");
mClientMessenger.send(toClientMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.w(TAG, "in onBind");
return mServiceMessenger.getBinder();
}
@Override
public boolean onUnbind(Intent intent) {
Log.w(TAG, "in onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.w(TAG, "in onDestroy");
super.onDestroy();
}
}
AndroidManifest.xml中注冊(cè):
<service android:name=".BoundService2"/>
Client中的使用:
public class CActivity extends AppCompatActivity {
private static final String TAG = "CActivity";
private Button bindServiceBtn;
private Button unBindServiceBtn;
private Button sendMsgToService;
private ServiceConnection sc = new MyServiceConnection();
private boolean mBound;
private Messenger mServiceMessenger;
private Handler mClientHandler = new MyClientHandler();
private Messenger mClientMessenger = new Messenger(mClientHandler);
/////////// Message handler && ServiceConnection
private class MyClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == BoundService2.MSG_FROM_SERVICE_TO_CLIENT) {
Log.w(TAG, "receive msg from service.");
}
}
}
private class MyServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.w(TAG, "in MyServiceConnection onServiceConnected.");
mServiceMessenger = new Messenger(binder);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.w(TAG, "in MyServiceConnection onServiceDisconnected.");
mBound = false;
}
}
/////////// Message handler && ServiceConnection
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_c);
bindServiceBtn = findViewById(R.id.bind_service_btn);
unBindServiceBtn = findViewById(R.id.unBind_service_btn);
sendMsgToService = findViewById(R.id.send_msg_to_service);
bindServiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(CActivity.this, BoundService2.class);
bindService(intent, sc, Context.BIND_AUTO_CREATE);
}
});
unBindServiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
excuteUnBindService();
}
});
sendMsgToService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sayHello();
}
});
}
public void sayHello() {
if (!mBound) {
return;
}
Message msg = Message.obtain(null, BoundService2.MSG_FROM_CLIENT_TO_SERVICE,0,0);
msg.replyTo = mClientMessenger;
try {
mServiceMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void excuteUnBindService() {
if (mBound) {
unbindService(sc);
mBound = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.w(TAG, "in onDestroy");
excuteUnBindService();
}
}
這里在ServiceConnection中的onServiceConnected(...)方法中根據(jù)service端傳來(lái)的binder參數(shù)實(shí)例化一個(gè)Messenger對(duì)象,就可以使用它發(fā)送消息給Service了,
另外,在給service發(fā)送消息的時(shí)候,在消息中夾帶一個(gè)Client端的Messenger實(shí)例:
Message msg = Message.obtain(null, BoundService2.MSG_FROM_CLIENT_TO_SERVICE,0,0);
msg.replyTo = mClientMessenger;
在service端獲取到該Messenger實(shí)例,就可以在service端發(fā)送消息回Client端了。
3. AIDL (Android Interface Definition Language)
一般情況下,Messenger這種方式都是可以滿足需求的,當(dāng)然,通過(guò)自定義AIDL方式相對(duì)更加靈活。這種方式需要自己在項(xiàng)目中自定義xxx.aidl文件,然后系統(tǒng)會(huì)自動(dòng)在gen目錄下生成相應(yīng)的接口類文件,接下來(lái)整個(gè)的流程與Messenger方式差別不大。
首先在服務(wù)端的src目錄下按照下圖中的樣子創(chuàng)建

在aidl.aidl下新建一個(gè)MyAIDLService.aidl文件,文件內(nèi)容如下:
// MyAIDLService.aidl
package aidl.aidl;
// Declare any non-default types here with import statements
interface MyAIDLService {
// 這個(gè)AIDL service 對(duì)外的接口只有一個(gè),就是獲取字符串
String getString();
}
注意這里的package后面的名稱,與后面客戶端的名稱要一致,不然會(huì)出錯(cuò)。這個(gè)文件中我們只定義了一個(gè)對(duì)外的接口String getString()。這時(shí)候Build一下,編譯器會(huì)自動(dòng)在Build->generated->source->aidl->debug->aidl->aidl下生成對(duì)應(yīng)的接口文件
接下來(lái),在服務(wù)端創(chuàng)建一個(gè)MyService:
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
class MyBinder extends MyAIDLService.Stub {
@Override
public String getString() throws RemoteException {
return "我是從服務(wù)器返回的";
}
}
}
這里只需要注意,onBind(...)中返回一個(gè)MyAIDLService.Stub的實(shí)例。
然后到AndroidManifest.xml中注冊(cè)一下就可以了:
<service android:name=".MyService">
<intent-filter>
<action android:name="com.server.aidl.service.MyService"/>
</intent-filter>
</service>
下面,來(lái)實(shí)現(xiàn)客戶端的調(diào)用:
首先,將服務(wù)端的MyAIDLService.aidl文件拷貝過(guò)來(lái),并要保持包名和服務(wù)端一致,如下圖所示:

接下來(lái),直接調(diào)用就可以了:
public class MainActivity extends AppCompatActivity {
private Button bindServiceBtn, unBindServiceBtn;
private TextView tvData;
private MyAIDLService mMyAIDLService;
private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
// 這里獲取Service的方式有點(diǎn)兒區(qū)別
mMyAIDLService = MyAIDLService.Stub.asInterface(binder);
try {
String str = mMyAIDLService.getString();
tvData.setText(str);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMyAIDLService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindServiceBtn = findViewById(R.id.bind_service);
unBindServiceBtn = findViewById(R.id.unBind_service);
tvData = findViewById(R.id.tv_from_server);
bindServiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("com.server.aidl.service.MyService");
// 從Android 5.0開(kāi)始,隱式Intent綁定服務(wù)的方式已經(jīng)不能使用了,這里需要設(shè)置Service所在服務(wù)端的包名
intent.setPackage("com.server.aidl.service.serviceaidlserver");
bindService(intent, sc, BIND_AUTO_CREATE);
}
});
unBindServiceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(sc);
}
});
}
}
這樣,AIDL的方式也介紹完了。
最后, Bound Service還有很重要的一點(diǎn)和Start Service不同,就是解綁的時(shí)候一定要確認(rèn)已經(jīng)綁定的才能解綁,否則會(huì)崩潰,而Start Service不需要。
另外,四大組件中BroadcastReceiver不能作為Bound Service的Client,因?yàn)樗纳芷诤芏?,而B(niǎo)ound Service的生命周期和Client的生命周期是綁定的。因此,在Android中,不允許BroadcastReceiver去bindService(...),當(dāng)有此類需求時(shí),可以用start service去代替。
需要補(bǔ)充的一點(diǎn)是:所有的Service都是運(yùn)行在其所在進(jìn)程的主線程上,如果Service和Client同屬一個(gè)進(jìn)程,則它就會(huì)運(yùn)行在主線程,也就是UI線程上,此時(shí)要注意不要在Service中執(zhí)行耗時(shí)任務(wù),如果要執(zhí)行耗時(shí)任務(wù),就要在Service中新建線程,避免阻塞UI導(dǎo)致ANR;Service一旦創(chuàng)建,需要停止時(shí)都需要顯示調(diào)用相應(yīng)的方法(started service調(diào)用stopService()或Service自身調(diào)用stopSelf(),bound service需要unBindService()),否則started service將一直處于運(yùn)行狀態(tài),對(duì)于bound service,則只有等到Client生命周期結(jié)束后才會(huì)銷(xiāo)毀。
補(bǔ)充:
介紹一下IntentService
IntentService是系統(tǒng)提供給我們的一個(gè)繼承自Service的特殊類,它的特殊性在于:
- 默認(rèn)實(shí)現(xiàn)了onBind()方法,直接返回null
- 定義了抽象方法onHandleIntent(), 用戶需要實(shí)現(xiàn)此方法,該方法用來(lái)處理耗時(shí)任務(wù),并已經(jīng)在新的線程中,用戶無(wú)需再新建線程。
- 當(dāng)耗時(shí)任務(wù)結(jié)束(onHandleIntent()方法執(zhí)行完畢),此IntentService自動(dòng)結(jié)束,無(wú)需人為調(diào)用方法使其結(jié)束
- IntentService處于任務(wù)時(shí),也是按照隊(duì)列的方式一個(gè)一個(gè)去處理,而非真正意義上的多線程并發(fā)方式.
簡(jiǎn)單定義:
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
super(TAG);
}
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.w(TAG, "in onHandleIntent");
Log.w(TAG, "thread name: " + Thread.currentThread().getName());
}
}
注冊(cè):
<service android:name=".MyIntentService"/>
使用:
Intent intent = new Intent(BActivity.this, MyIntentService.class);
startService(intent);