Service也是一個(gè)單獨(dú)的Android組件。通常用于為其他組件提供后臺(tái)服務(wù)或者監(jiān)控其他組件的運(yùn)行狀態(tài)。
Activity與Service的對(duì)比
相似點(diǎn)
- 都是單獨(dú)的Android組件
- 都擁有獨(dú)立的生命周期
- 都是Context的派生類,所以可以調(diào)用Context類定義的如getResources()、getContentResolver()等方法
- 都擁有自己生命周期回調(diào)方法
不同點(diǎn)
- Activity運(yùn)行于前臺(tái)有圖形用戶界面,負(fù)責(zé)與用戶交互;Service通常位于后臺(tái)運(yùn)行,不需要與用戶交互,也沒(méi)有圖形用戶界面。
應(yīng)用場(chǎng)景
如果某個(gè)程序組件需要在運(yùn)行時(shí)向用戶呈現(xiàn)界面,或者程序需要與用戶交互,就需要用Activity,否則就應(yīng)該考慮使用Service了。
創(chuàng)建Service
- 定義一個(gè)繼承Service的子類;
package com.rave.simpledemo.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
/**
* Created by Administrator on 2019/1/21.
*/
public class FirstService extends Service {
/**
* Service子類必須實(shí)現(xiàn)的方法。該方法返回一個(gè)IBinder對(duì)象,應(yīng)用程序可通過(guò)IBinder對(duì)象與Service組件通信
* @param intent
* @return
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("===================onBind FirstService=================");
return null;
}
/**
* 當(dāng)Service上綁定的所有客戶端都斷開(kāi)連接時(shí)會(huì)回調(diào)該方法
* @param intent
* @return
*/
@Override
public boolean onUnbind(Intent intent) {
System.out.println("===================onUnbind FirstService=================");
return super.onUnbind(intent);
}
/**
* Service第一次被創(chuàng)建后回調(diào)該方法
*/
@Override
public void onCreate() {
super.onCreate();
System.out.println("===================onCreate FirstService=================");
}
/**
* Service被關(guān)閉之前回調(diào)該方法
*/
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("===================onDestroy FirstService=================");
}
/**
* 該方法的早期版本是onStart(Intent intent, int startId),
* 當(dāng)客戶端調(diào)用startService(Intent)方法啟動(dòng)Service時(shí)都會(huì)回調(diào)該方法
* @param intent
* @param flags
* @param startId
* @return
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("===================onStartCommand FirstService=================");
return super.onStartCommand(intent, flags, startId);
}
}
- 在Manifest.xml文件中配置該Service。
<service android:name="com.rave.simpledemo.service.FirstService">
<!-- 配置intent-filter元素說(shuō)明該Service可被哪些Intent啟動(dòng) -->
<intent-filter>
<!-- 為intent-filter配置action -->
<action android:name="com.rave.simpledemo.service.FIRST_SERVICE" />
</intent-filter>
</service>
啟動(dòng)和停止Service
啟動(dòng)Service
① 通過(guò)Context的startService()方法啟動(dòng)Service,訪問(wèn)者與Service之間沒(méi)有關(guān)聯(lián),Service和訪問(wèn)者之間也無(wú)法進(jìn)行通信、數(shù)據(jù)交換。即使訪問(wèn)者退出,Service依然運(yùn)行:
Intent intentService = new Intent(this, FirstService.class);
intentService.setAction("com.rave.simpledemo.service.FIRST_SERVICE");
//啟動(dòng)Service
startService(intentService);
//停止Service
//stopService(intentService);
特點(diǎn):每當(dāng)Service被創(chuàng)建時(shí)會(huì)回調(diào)onCreate方法,每次Service被啟動(dòng)時(shí)都會(huì)回調(diào)onStartCommand方法。多次啟動(dòng)一個(gè)已有的Service組件將不會(huì)再回調(diào)onCreate方法,但每次啟動(dòng)時(shí)都會(huì)回調(diào)onStartCommand方法。
② 通過(guò)Context的bindService()方法啟動(dòng)Service,訪問(wèn)者與Service綁定在一起。訪問(wèn)者與Service之間可以進(jìn)行方法調(diào)用或數(shù)據(jù)交換。訪問(wèn)者一旦退出,Service也就終止:
boolean bindService(Intent service, ServiceConnection conn,int flags);
unbindService(ServiceConnection conn)
①service:該參數(shù)通過(guò)Intent指定要啟動(dòng)的Service;
②conn:該參數(shù)是一個(gè)ServiceConnection對(duì)象,該對(duì)象用于監(jiān)聽(tīng)訪問(wèn)者與Service之間的連接情況。當(dāng)訪問(wèn)者與Service之間連接成功時(shí)將回調(diào)ServiceConnection對(duì)象的onServiceConnected(ComponentName name,IBinder service)方法;當(dāng)訪問(wèn)者與Service之間斷開(kāi)連接時(shí)將回調(diào)ServiceConnection對(duì)象的onServiceDisconnected(ComponentName name)方法;
③flags:指定綁定時(shí)是否自動(dòng)創(chuàng)建Service(如果Service還未創(chuàng)建)。該參數(shù)可指定為0(不自動(dòng)創(chuàng)建)或BIND_AUTO_CREATE(自動(dòng)創(chuàng)建)。
- 如何與被綁定的Service進(jìn)行本地通信(遠(yuǎn)程的、跨進(jìn)程的通信會(huì)采用AIDL,下文會(huì)單獨(dú)介紹)
ServiceConnection對(duì)象的onServiceConnected方法中有一個(gè)IBinder對(duì)象,該對(duì)象可實(shí)現(xiàn)與被綁定Service之間的通信。在開(kāi)發(fā)Service類時(shí),該Service類必須提供一個(gè)IBinder onBind(Intent intent)方法,在綁定本地Service的情況下,onBind(Intent intent)方法所返回的IBinder對(duì)象將會(huì)傳給ServiceConnection對(duì)象里onServiceConnected(ComponentName name, IBinder service)方法的service參數(shù),訪問(wèn)者就可通過(guò)該IBinder對(duì)象與Service進(jìn)行通信。在實(shí)際開(kāi)發(fā)中通常會(huì)采用繼承Binder(IBinder的實(shí)現(xiàn)類)的方式實(shí)現(xiàn)自己的IBinder對(duì)象。
具體步驟:
①在Service里面創(chuàng)建一個(gè)IBinder對(duì)象(通過(guò)繼承Binder類來(lái)實(shí)現(xiàn)自定義的Binder);
②在onBind(Intent intent)方法里面返回這個(gè)Binder對(duì)象;
③客戶端在綁定Service時(shí)所需的ServiceConnection對(duì)象的onServiceConnected方法里面獲取到onBind方法返回的Binder對(duì)象;
④客戶端通過(guò)持有的Binder對(duì)象來(lái)訪問(wèn)Service。
對(duì)于Service的onBind()方法所返回的IBinder對(duì)象來(lái)說(shuō),它可被當(dāng)成該Service組件所返回的回調(diào)對(duì)象,Service允許客戶端通過(guò)該IBinder對(duì)象來(lái)訪問(wèn)Service內(nèi)部的數(shù)據(jù),這樣即可實(shí)現(xiàn)客戶端與Service之間的通信。
package com.rave.simpledemo.service;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
/**
* Created by Administrator on 2019/1/22.
*/
public class BindService extends Service {
private boolean quit = false;
private int count = 0;
//定義onBinder方法要返回的Binder對(duì)象
private MyBinder binder = new MyBinder();
// 通過(guò)繼承Binder來(lái)實(shí)現(xiàn)IBinder類
public class MyBinder extends Binder {
public int getCount() {
return count;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("==================BindService onBind====================");
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("==================BindService onUnbind====================");
return true;
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("==================BindService onCreate====================");
new Thread(new Runnable() {
@Override
public void run() {
while (!quit) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
stopSelf();
}
}).start();
}
@Override
public void onDestroy() {
super.onDestroy();
this.quit = true;
System.out.println("==================BindService onDestroy====================");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("===================BindService onStartCommand=================");
return super.onStartCommand(intent, flags, startId);
}
}
package com.rave.simpledemo.service;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.rave.simpledemo.R;
public class BindServiceActivity extends AppCompatActivity {
private Button btnStartService;
private Button btnStopService;
private Button btnGetStatus;
//啟動(dòng)Service時(shí)返回的IBinder對(duì)象
BindService.MyBinder binder;
//定義一個(gè)ServiceConnection對(duì)象
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("===================BindService onServiceConnected=================");
binder = (BindService.MyBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("===================BindService onServiceDisconnected=================");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bind_service);
btnStartService = (Button) findViewById(R.id.btn_startservice);
btnStopService = (Button) findViewById(R.id.btn_stopservice);
btnGetStatus = (Button) findViewById(R.id.btn_getstatus);
final Intent intent = new Intent(BindServiceActivity.this, BindService.class);
intent.setAction("com.rave.simpledemo.service.BIND_SERVICE");
btnStartService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//綁定指定的Service
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
btnStopService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//解除綁定Service(這里的參數(shù)ServiceConnection對(duì)象)
unbindService(conn);
}
});
btnGetStatus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int count = binder.getCount();
Toast.makeText(BindServiceActivity.this, count + "", Toast.LENGTH_LONG).show();
}
});
}
}
Service的生命周期
上文的啟動(dòng)Service的兩個(gè)示例中,我們都有對(duì)Service的生命周期方法進(jìn)行觀測(cè)。我們發(fā)現(xiàn)根據(jù)啟動(dòng)方式的不同Service回調(diào)的生命周期方法也不同。
總結(jié)一下Service的生命周期方法回調(diào):
startService()和bindService()是兩個(gè)獨(dú)立的操作,我們可以只啟動(dòng)不綁定;也可以通過(guò)bindService()方法的第三個(gè)參數(shù),在綁定時(shí)啟動(dòng);還可以先啟動(dòng)后綁定;
①被啟動(dòng)的服務(wù)的生命周期:如果一個(gè)Service被某個(gè)Activity調(diào)用Context.startService方法啟動(dòng)(Service也可以啟動(dòng)服務(wù)),那么不管是否有Activity使用bindService綁定或者unbindService解除綁定該Service,該Service都在后臺(tái)運(yùn)行。如果一個(gè)Service被startService方法多次啟動(dòng),那么onCreate方法只會(huì)調(diào)用一次,但是onStartCommand將會(huì)每次都被回調(diào),并且系統(tǒng)只會(huì)創(chuàng)建Service的一個(gè)實(shí)例(因此你只需要調(diào)用一次stopService來(lái)停止)。該Service將會(huì)一直在后臺(tái)運(yùn)行,而不管對(duì)應(yīng)程序的Activity是否在運(yùn)行,直到被調(diào)用stopService,或自身的stopSelf方法。當(dāng)然如果系統(tǒng)資源不足,Android系統(tǒng)也可能結(jié)束服務(wù)。
②被綁定的服務(wù)的生命周期:如果一個(gè)Service被某個(gè)Activity調(diào)用Context.bindService方法綁定啟動(dòng),不管調(diào)用bindService調(diào)用幾次,onCreate方法都只會(huì)調(diào)用一次,同時(shí)onStartCommand方法始終不會(huì)調(diào)用。當(dāng)鏈接建立之后,Service將會(huì)一直運(yùn)行,除非調(diào)用Context.unbindService斷開(kāi)連接或者之前調(diào)用bindService的Context不存在了(如Activity被finish的時(shí)候),系統(tǒng)將會(huì)自動(dòng)停止Service,對(duì)應(yīng)onDestroy將被調(diào)用。
③被啟動(dòng)又被綁定的服務(wù)的生命周期:如果一個(gè)Service先被啟動(dòng),后又被綁定,則該Service將會(huì)一直在后臺(tái)運(yùn)行。并且不管如何調(diào)用,onCreate始終只會(huì)調(diào)用一次,對(duì)應(yīng)startService調(diào)用多少次,Service的onStartCommand就會(huì)調(diào)用多少次。調(diào)用unbindService將不會(huì)停止Service,而必須調(diào)用stopService或Service自身的stopSelf來(lái)停止服務(wù)(在沒(méi)有解綁的前提下使用stopService是無(wú)法停止服務(wù)的)。
④當(dāng)服務(wù)被停止時(shí)的清除工作:當(dāng)一個(gè)Service被終止(1、調(diào)用stopService;2、調(diào)用stopSelf;3、不再有綁定的連接(通過(guò)bindService啟動(dòng)))時(shí),onDestroy方法將會(huì)被回調(diào),在這里應(yīng)當(dāng)做一些清除工作(如停止Service中創(chuàng)建并運(yùn)行的線程、注冊(cè)的偵聽(tīng)器、接收器等)。
使用Service時(shí)的注意事項(xiàng)
①在調(diào)用bindService綁定到Service的時(shí)候,就應(yīng)當(dāng)保證在某處調(diào)用unbindService解除綁定(盡管Activity被finish的時(shí)候會(huì)自動(dòng)解除綁定,并且會(huì)自動(dòng)停止Service);
②使用startService啟動(dòng)服務(wù)之后,一定要使用stopService停止服務(wù),不管你是否使用bindService;
③同時(shí)使用startService和bindService時(shí)要注意:Service的終止,需要unbindService與stopService同時(shí)調(diào)用,才能終止Service。關(guān)于調(diào)用順序,如果先調(diào)用unbindService此時(shí)服務(wù)不會(huì)自動(dòng)終止,再調(diào)用stopService服務(wù)才會(huì)停止;如果先調(diào)用stopService此時(shí)服務(wù)也不會(huì)終止,需要再調(diào)用unbindService(或者調(diào)用bindService的Context不存在了,如Activity被finish的時(shí)候)服務(wù)才會(huì)自動(dòng)停止。
④當(dāng)旋轉(zhuǎn)手機(jī)屏幕的時(shí)候,如果發(fā)生了Activity的重建(請(qǐng)參考Android四大組件(一)Activity),旋轉(zhuǎn)之前的使用bindService建立的連接便會(huì)斷開(kāi)(Context不存在了)。
⑤在sdk 2.0之前的版本,使用的onStart方法被onStartCommand方法替換了,但是onStart方法仍然有效。
跨進(jìn)程調(diào)用Service(AIDL服務(wù))
理解什么是AIDL(Android Interface Definition Language,即Android接口定義語(yǔ)言)
Android系統(tǒng)中,應(yīng)用程序都運(yùn)行在自己的進(jìn)程中,進(jìn)程之間一般無(wú)法直接進(jìn)行數(shù)據(jù)交換。在Java技術(shù)中,RMI可實(shí)現(xiàn)跨進(jìn)程調(diào)用;在Android的中,采用了相似的方式來(lái)實(shí)現(xiàn)跨進(jìn)程調(diào)用Service,這就是AIDL。與RMI相似,也是先定義一個(gè)遠(yuǎn)程調(diào)用接口,然后為該接口提供一個(gè)實(shí)現(xiàn)類;與RMI不同的是,客戶端訪問(wèn)Service時(shí),Android并不是直接返回Service對(duì)象給客戶端。這點(diǎn)在綁定本地Service時(shí)已經(jīng)看到,Service只是將一個(gè)回調(diào)對(duì)象(IBinder對(duì)象)通過(guò)onBind()方法返回給客戶端。因此Android的AIDL遠(yuǎn)程接口的實(shí)現(xiàn)類就是IBinder實(shí)現(xiàn)類。與綁定本地Service不同的是,本地Service的onBind()方法會(huì)直接把IBinder對(duì)象本身傳給客戶端的ServiceConnection的onServiceConnected方法的第二個(gè)參數(shù)。遠(yuǎn)程Service的onBind方法只是將IBinder對(duì)象的代理傳給客戶端的ServiceConnection的onServiceConnected方法的第二個(gè)參數(shù)。客戶端獲取了遠(yuǎn)程Service的IBinder對(duì)象的代理后,就可通過(guò)該IBinder對(duì)象去回調(diào)遠(yuǎn)程Service的屬性或方法了。
AIDL的操作步驟
Service端
1、新建AIDL文件
2、按需求自己定義接口
3、重新build工程,AS會(huì)在build目錄中生成aidl對(duì)應(yīng)的java實(shí)現(xiàn)
4、編寫(xiě)Service類
package com.rave.simpledemo.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by Administrator on 2019/1/25.
*/
public class AidlUserService extends Service {
private String name = "rave";
private int age = 0;
private int count = 0;
private Timer timer = new Timer();
/**
* 這里跟本地Service不一樣,沒(méi)有直接繼承Binder類,而是繼承了ADT所生成的IUserInfo.Stub
* 但是IUserInfo.Stub也是Binder的子類
*/
private IBinder stub = new IUserInfo.Stub() {
@Override
public String getName() throws RemoteException {
return name + count;
}
@Override
public int getAge() throws RemoteException {
return age + count;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
/**返回catBinder對(duì)象
* 在綁定本地Service的情況下,該catBinder對(duì)象會(huì)直接傳給客戶端的
* ServiceConnection對(duì)象的onServiceConnected方法的第二個(gè)參數(shù);
* 在綁定遠(yuǎn)程Service的情況下,只將catBinder對(duì)象的代理傳給客戶端的
* ServiceConnection對(duì)象的onServiceConnected方法的第二個(gè)參數(shù)。
*/
return stub;
}
@Override
public void onCreate() {
timer.schedule(new TimerTask() {
@Override
public void run() {
count++;
}
}, 0, 1 * 1000);
}
@Override
public void onDestroy() {
/**
* 在onDestroy里面做清理回收工作
*/
timer.cancel();
}
}
5、當(dāng)然不要忘記在manifest.xml里注冊(cè)該Service
<service android:name=".aidldemo.AidlUserService">
<intent-filter>
<action android:name="com.rave.simpledemo.service.AIDL_USER_SERVICE" />
</intent-filter>
</service>
客戶端訪問(wèn)AIDL服務(wù)
1、將Service端的AIDL接口文件復(fù)制到客戶端應(yīng)用中,要連文件路徑一起復(fù)制
2、同Service端一樣,重新build工程,AS會(huì)調(diào)用ADT在build目錄中生成aidl對(duì)應(yīng)的java實(shí)現(xiàn)
3、創(chuàng)建ServiceConnection對(duì)象,并以ServiceConnection對(duì)象作為參數(shù),調(diào)用bindService()方法綁定遠(yuǎn)程Service
package com.richinfo.aidlclient;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.rave.simpledemo.aidldemo.IUserInfo;
public class UserInfoActivity extends AppCompatActivity {
private TextView tvUserName, tvUserAge;
private Button btnBind, btnUnbind, btnGet;
private Intent intent;
private IUserInfo userInfoService;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/**
* 與本地綁定Service不同,綁定遠(yuǎn)程Service的ServiceConnection不能直接獲取Service的onBind方法所返回的對(duì)象;
* 它只能返回onBind()方法所返回的對(duì)象的代理,因此需要在這里做asInterface的處理。
*/
userInfoService = IUserInfo.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_info);
tvUserName = (TextView) findViewById(R.id.tv_username);
tvUserAge = (TextView) findViewById(R.id.tv_userage);
btnBind = (Button) findViewById(R.id.btn_bind);
btnUnbind = (Button) findViewById(R.id.btn_unbind);
btnGet = (Button) findViewById(R.id.btn_get);
intent = new Intent();
// Android 5.0以后需要用顯示意圖來(lái)啟動(dòng)Service,否則會(huì)報(bào)異常
// java.lang.IllegalArgumentException: Service Intent must be explicit
// 這里由于是跨進(jìn)程所以用Component來(lái)解決這個(gè)問(wèn)題,試過(guò)用setPackage的方式,無(wú)法解決這個(gè)問(wèn)題
intent.setComponent(new ComponentName("com.rave.simpledemo", "com.rave.simpledemo.aidldemo.AidlUserService"));
//這種方式很多博客上說(shuō)可以,經(jīng)測(cè)試這種方式可以startService成功的啟動(dòng)遠(yuǎn)程service,但是在啟動(dòng)之后再綁定會(huì)出現(xiàn)綁定失敗的情況。
//intent.setPackage("com.rave.simpledemo");
intent.setAction("com.rave.simpledemo.service.AIDL_USER_SERVICE");
btnBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(intent);
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
btnUnbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(conn);
}
});
btnGet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
tvUserName.setText(userInfoService.getName());
tvUserAge.setText(userInfoService.getAge()+"");
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
//記得解綁
unbindService(conn);
stopService(intent);
}
}
實(shí)踐中遇到的問(wèn)題
1、Android 5.0以后系統(tǒng)禁止使用隱式意圖來(lái)啟動(dòng)Service,所以在跨進(jìn)程特別是跨APP的綁定Service的時(shí)候,可以采用如下方式:
Intent intent = new Intent();
intent.setAction("com.rave.simpledemo.service.AIDL_USER_SERVICE");
// Android 5.0以后需要用顯示意圖來(lái)啟動(dòng)Service,否則會(huì)報(bào)異常
// java.lang.IllegalArgumentException: Service Intent must be explicit
// 這里由于是跨進(jìn)程所以用Component來(lái)解決這個(gè)問(wèn)題,試過(guò)用setPackage的方式,無(wú)法解決這個(gè)問(wèn)題
intent.setComponent(new ComponentName("com.rave.simpledemo", "com.rave.simpledemo.aidldemo.AidlUserService"));
//這種方式很多博客上說(shuō)可以,經(jīng)測(cè)試這種方式可以startService成功的啟動(dòng)遠(yuǎn)程service,但是在啟動(dòng)之后再綁定會(huì)出現(xiàn)綁定失敗的情況。
//intent.setPackage("com.rave.simpledemo");
2、無(wú)法通過(guò)bindService設(shè)置BIND_AUTO_CREATE來(lái)創(chuàng)建服務(wù),只能先startService啟動(dòng),再調(diào)用bindService綁定:
// 在使用AIDL的時(shí)候,必須要先用startService來(lái)啟動(dòng)服務(wù),再用bindService來(lái)綁定服務(wù),否則會(huì)綁定失敗。
startService(intent);
boolean flag = bindService(intent, conn, Service.BIND_AUTO_CREATE);
3、在Service的onDestroy里面一定要清理資源,避免內(nèi)存泄漏。已經(jīng)客戶端調(diào)用bindService之后要手動(dòng)解綁。
傳遞復(fù)雜數(shù)據(jù)的AIDL服務(wù)
上面我們所展示的都是通過(guò)AIDL傳輸基本數(shù)據(jù)類型。如果是自定義類的話,Android要求調(diào)用遠(yuǎn)程Service的參數(shù)和返回值都必須實(shí)現(xiàn)Parcelable接口。具體步驟如下:
服務(wù)端
1、使用AIDL代碼來(lái)定義這些自定義類型(包括參數(shù)類型和返回值類型):
2、定義實(shí)現(xiàn)Parcelable接口的參數(shù)類和返回值類(返回值類一樣這里demo省略代碼)
package com.rave.simpledemo.aidldemo;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by Administrator on 2019/1/28.
*/
public class Person implements Parcelable {
private Integer id;
private String name;
private String pass;
public Person() {
}
public Person(Integer id, String name, String pass) {
super();
this.id = id;
this.name = name;
this.pass = pass;
}
protected Person(Parcel in) {
name = in.readString();
pass = in.readString();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((pass == null) ? 0 : pass.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Person other = (Person) obj;
if (this.name == null) {
if (other.name != null) {
return false;
}
} else if (!this.name.equals(other.name)) {
return false;
}
if (this.pass == null) {
if (other.pass != null) {
return false;
}
} else if (!this.pass.equals(other.pass)) {
return false;
}
return true;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
//把對(duì)象所包含的數(shù)據(jù)寫(xiě)到Parcel中
dest.writeInt(id);
dest.writeString(name);
dest.writeString(pass);
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in.readInt(), in.readString(), in.readString());
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
3、使用AIDL定義通信接口
在AIDL接口中定義方法時(shí),需要指定形參的傳遞模式。對(duì)于java語(yǔ)言來(lái)說(shuō),一般都是采用傳入?yún)?shù)的方式,因此上面指定為in模式。具體的傳參方式有以下幾種:
① in:參數(shù)由客戶端設(shè)置,或者理解為客戶端傳入?yún)?shù)值;
② out:參數(shù)由服務(wù)器設(shè)置,或者理解成由服務(wù)端返回值;
③ inout:客戶端服務(wù)端都可以設(shè)置,或者理解成可以雙向通信。任何自定義類型都需要在這里顯示使用import引入(即便該類的AIDL文件和通信接口是在同一個(gè)包下),否則會(huì)報(bào)錯(cuò):
Error:Execution failed for task ':app:compileDebugAidl'.
com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'G:\Android\sdk1\build-tools\23.0.1\aidl.exe'' finished with non-zero exit value 1
4、再次不要忘記在manifest.xml里注冊(cè)該Service
客戶端
1、跟傳輸基本類型一樣,將Service端的AIDL接口文件復(fù)制到客戶端應(yīng)用中(包括定義接口的aidl的文件IPet.aidl、定義的自定義類型的AIDL文件(包括參數(shù)類型和返回值類型)、定義的自定義類型的Java類文件(包括參數(shù)類型和返回值類型))。同樣要保證文件路徑和類的包名和跟Service端一致。
2、后面的啟動(dòng)服務(wù)和綁定服務(wù)的操作跟傳輸基本數(shù)據(jù)類型一樣。下面給出本例的客戶端代碼:
package com.richinfo.aidlclient;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.rave.simpledemo.aidldemo.IPet;
import com.rave.simpledemo.aidldemo.Person;
import com.rave.simpledemo.aidldemo.Pet;
import java.util.List;
public class ComplexTypeActivity extends AppCompatActivity {
private TextView tvContent;
private Button btnBind;
private Button btnUnbind;
private Button btnGet;
private Intent complexServiceIntent;
private IPet iPetService;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iPetService = IPet.Stub.asInterface(service);
System.out.println("onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_complex_type);
tvContent = (TextView) findViewById(R.id.tv_content);
btnBind = (Button) findViewById(R.id.btn_bind);
btnGet = (Button) findViewById(R.id.btn_get);
btnUnbind = (Button) findViewById(R.id.btn_unbind);
complexServiceIntent = new Intent();
complexServiceIntent.setAction("com.rave.simpledemo.service.COMPLEX_SERVICE");
complexServiceIntent.setComponent(new ComponentName("com.rave.simpledemo", "com.rave.simpledemo.aidldemo.ComplexService"));
btnBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startService(complexServiceIntent);
boolean isSuccess = bindService(complexServiceIntent, conn, Service.BIND_AUTO_CREATE);
System.out.println("bind successed:" + isSuccess);
}
});
btnGet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
List<Pet> petList = iPetService.getPets(new Person(1, "rava", "rave"));
int size = petList.size();
tvContent.setText("pet count:" + size + "");
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
btnUnbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(conn);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
stopService(complexServiceIntent);
}
}
獲取系統(tǒng)服務(wù)
1、 電話管理器(TelephonyManager):
//getSystemService(String name):根據(jù)服務(wù)名稱來(lái)獲取系統(tǒng)服務(wù)。
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
- 獲取網(wǎng)絡(luò)狀態(tài)
- 獲取SIM卡信息
- 監(jiān)聽(tīng)通話狀態(tài)
2、短信管理器(SmsManager):
//獲取SmsManager
SmsManager smsManager = SmsManager.getDefault();
- 短信發(fā)送
3、音頻管理器(AudioManager):
//獲取音頻管理器
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
- 調(diào)節(jié)系統(tǒng)音量:adjustStreamVolume(int streamType, int direction, int flags),streamType聲音類型;direction對(duì)聲音進(jìn)行增大還是減小;flags調(diào)整聲音時(shí)的標(biāo)志。
- 設(shè)置麥克風(fēng)靜音:setMicrophoneMute(boolean on)。
- 設(shè)置聲音模式:setMode(int mode),可設(shè)置的值有NORMAL、RINGTONE和IN_CALL。
- 設(shè)置手機(jī)的電話鈴聲模式:setRingerMode(int ringerMode),可設(shè)置鈴聲模式、靜音模式、振動(dòng)模式。
- 設(shè)置打卡擴(kuò)音器:setSpeakerphoneOn(boolean on)。
- 將制定類型的聲音調(diào)整為靜音:setStreamMute(int streamType, boolean state),streamType聲音類型。
- 設(shè)置手機(jī)指定類型的音量值:setStreamVolume(int streamType, int index, int flags),streamType聲音類型;index具體的音量值;flags調(diào)整聲音時(shí)的標(biāo)志。
4、振動(dòng)器(Vibrator):
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
- 控制手機(jī)振動(dòng):vibrate(long milliseconds)、vibrate(long[] pattern, int repeat)振動(dòng);cancel()關(guān)閉振動(dòng)。
5、手機(jī)鬧鐘服務(wù)(AlarmManager):
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- 開(kāi)發(fā)鬧鐘應(yīng)用
- 全局定時(shí)器
特殊的Service——IntentService
通過(guò)上面的內(nèi)容,我們已經(jīng)發(fā)現(xiàn)了。服務(wù)的代碼默認(rèn)運(yùn)行在主線程里的。如果要在服務(wù)里面執(zhí)行耗時(shí)操作的代碼,就需要開(kāi)啟一個(gè)主線程去處理這些代碼。比如在啟動(dòng)和停止Service的例子中:
@Override
public void onCreate() {
super.onCreate();
System.out.println("==================BindService onCreate====================");
new Thread(new Runnable() {
@Override
public void run() {
while (!quit) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
stopSelf();
}
}).start();
}
我們知道服務(wù)一旦啟動(dòng)就會(huì)一直運(yùn)行下去,必須手動(dòng)調(diào)用stopService()或者stopSelf()方法才能讓服務(wù)停止下來(lái)。所以在上例中我們需要在run()方法的最后,調(diào)用stopSelf來(lái)結(jié)束服務(wù)。谷歌給我們提供IntentService,就是為了方便開(kāi)發(fā)者處理在Service中開(kāi)啟子線程的情況。它幫我們開(kāi)發(fā)者封裝了開(kāi)啟子線程、線程間通信(采用Handler)、以及自動(dòng)銷毀服務(wù)等操作。
IntentService的使用步驟
1、繼承IntentService創(chuàng)建自定義的IntentService類
package com.rave.simpledemo.service;
import android.app.IntentService;
import android.content.Intent;
/**
* Created by Administrator on 2019/1/30.
*/
public class BindIntentService extends IntentService {
private boolean quit = false;
private int count = 0;
public BindIntentService() {
super("BindIntentService");
}
/**
* 必須實(shí)現(xiàn)的抽象方法,我們的業(yè)務(wù)邏輯就是在這個(gè)方法里面去實(shí)現(xiàn)的
* 方法在子線程運(yùn)行,我們不用去關(guān)心ANR的問(wèn)題
* 在OnCreate方法里面創(chuàng)建并啟動(dòng)子線程,
* 在OnStartCommand方法里面,將Intent封裝成Message并傳遞到子線程的handler,然后回調(diào)onHandleIntentonStart
*
* @param intent
*/
@Override
protected void onHandleIntent(Intent intent) {
while (!quit) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
System.out.println("==================Current count:" + count + "====================");
if (count == 10)
quit = true;
}
}
/**
* 為了驗(yàn)證onHandleIntent執(zhí)行后,服務(wù)會(huì)不會(huì)自動(dòng)銷毀,我們?cè)谶@里重寫(xiě)onDestroy方法
* 如果會(huì)自動(dòng)銷毀,那么在"IntetnService Running"出現(xiàn)后,應(yīng)該會(huì)出現(xiàn)"IntetnService Stop"
*/
@Override
public void onDestroy() {
System.out.println("==================BindIntentService onDestroy:" + count + "====================");
//經(jīng)測(cè)試,如果onHandleIntent里面的代碼邏輯沒(méi)有走完,在服務(wù)外部調(diào)用stopService來(lái)停止服務(wù),并不會(huì)立即結(jié)束子線程
quit = true;
super.onDestroy();
}
}
2、在Manifest里面注冊(cè)IntentService,注冊(cè)方式跟Service一樣
3、啟動(dòng)IntentService
我們推薦使用startService(intent)的方式來(lái)啟動(dòng)服務(wù)。
注意!!!也可以使用bindService的方式來(lái)啟動(dòng)服務(wù)。但是在使用bindService的啟動(dòng)的時(shí)候,即使onHandleIntent里面的邏輯執(zhí)行完畢,也不會(huì)自動(dòng)銷毀服務(wù)。原因應(yīng)該是Service還是被綁定狀態(tài),調(diào)用stopSelf無(wú)法停止。所以如果使用bindService啟動(dòng)服務(wù)將會(huì)失去IntentService的一大特點(diǎn),使用時(shí)請(qǐng)謹(jǐn)慎.
4、銷毀IntentService
IntentService的一大特點(diǎn)就是onHandleIntent里面的代碼邏輯執(zhí)行完之后,自動(dòng)銷毀Service。所以我們可以不用專門做停止IntentService的操作。
注意!!!我們也可以在客戶端手動(dòng)調(diào)用stopService來(lái)銷毀服務(wù),但是用這種方式不會(huì)停止IntentService里面啟動(dòng)的子線程。如果要采用這種方式銷毀服務(wù),一定要注意子線程無(wú)法停止,從而導(dǎo)致內(nèi)存泄漏的問(wèn)題。