Service也是一個單獨的Android組件葵腹。通常用于為其他組件提供后臺服務或者監(jiān)控其他組件的運行狀態(tài)歉摧。
Activity與Service的對比
相似點
- 都是單獨的Android組件
- 都擁有獨立的生命周期
- 都是Context的派生類岂傲,所以可以調(diào)用Context類定義的如getResources()痰洒、getContentResolver()等方法
- 都擁有自己生命周期回調(diào)方法
不同點
- Activity運行于前臺有圖形用戶界面麦向,負責與用戶交互瘟裸;Service通常位于后臺運行,不需要與用戶交互诵竭,也沒有圖形用戶界面话告。
應用場景
如果某個程序組件需要在運行時向用戶呈現(xiàn)界面,或者程序需要與用戶交互卵慰,就需要用Activity沙郭,否則就應該考慮使用Service了。
創(chuàng)建Service
- 定義一個繼承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子類必須實現(xiàn)的方法病线。該方法返回一個IBinder對象,應用程序可通過IBinder對象與Service組件通信
* @param intent
* @return
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("===================onBind FirstService=================");
return null;
}
/**
* 當Service上綁定的所有客戶端都斷開連接時會回調(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),
* 當客戶端調(diào)用startService(Intent)方法啟動Service時都會回調(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元素說明該Service可被哪些Intent啟動 -->
<intent-filter>
<!-- 為intent-filter配置action -->
<action android:name="com.rave.simpledemo.service.FIRST_SERVICE" />
</intent-filter>
</service>
啟動和停止Service
啟動Service
① 通過Context的startService()方法啟動Service送挑,訪問者與Service之間沒有關(guān)聯(lián),Service和訪問者之間也無法進行通信暖眼、數(shù)據(jù)交換惕耕。即使訪問者退出,Service依然運行:
Intent intentService = new Intent(this, FirstService.class);
intentService.setAction("com.rave.simpledemo.service.FIRST_SERVICE");
//啟動Service
startService(intentService);
//停止Service
//stopService(intentService);
特點:每當Service被創(chuàng)建時會回調(diào)onCreate方法罢荡,每次Service被啟動時都會回調(diào)onStartCommand方法赡突。多次啟動一個已有的Service組件將不會再回調(diào)onCreate方法对扶,但每次啟動時都會回調(diào)onStartCommand方法。
② 通過Context的bindService()方法啟動Service惭缰,訪問者與Service綁定在一起浪南。訪問者與Service之間可以進行方法調(diào)用或數(shù)據(jù)交換。訪問者一旦退出漱受,Service也就終止:
boolean bindService(Intent service, ServiceConnection conn,int flags);
unbindService(ServiceConnection conn)
①service:該參數(shù)通過Intent指定要啟動的Service络凿;
②conn:該參數(shù)是一個ServiceConnection對象,該對象用于監(jiān)聽訪問者與Service之間的連接情況昂羡。當訪問者與Service之間連接成功時將回調(diào)ServiceConnection對象的onServiceConnected(ComponentName name絮记,IBinder service)方法;當訪問者與Service之間斷開連接時將回調(diào)ServiceConnection對象的onServiceDisconnected(ComponentName name)方法虐先;
③flags:指定綁定時是否自動創(chuàng)建Service(如果Service還未創(chuàng)建)怨愤。該參數(shù)可指定為0(不自動創(chuàng)建)或BIND_AUTO_CREATE(自動創(chuàng)建)。
- 如何與被綁定的Service進行本地通信(遠程的蛹批、跨進程的通信會采用AIDL撰洗,下文會單獨介紹)
ServiceConnection對象的onServiceConnected方法中有一個IBinder對象更卒,該對象可實現(xiàn)與被綁定Service之間的通信呀袱。在開發(fā)Service類時,該Service類必須提供一個IBinder onBind(Intent intent)方法帜消,在綁定本地Service的情況下猪勇,onBind(Intent intent)方法所返回的IBinder對象將會傳給ServiceConnection對象里onServiceConnected(ComponentName name, IBinder service)方法的service參數(shù)设褐,訪問者就可通過該IBinder對象與Service進行通信。在實際開發(fā)中通常會采用繼承Binder(IBinder的實現(xiàn)類)的方式實現(xiàn)自己的IBinder對象泣刹。
具體步驟:
①在Service里面創(chuàng)建一個IBinder對象(通過繼承Binder類來實現(xiàn)自定義的Binder)助析;
②在onBind(Intent intent)方法里面返回這個Binder對象;
③客戶端在綁定Service時所需的ServiceConnection對象的onServiceConnected方法里面獲取到onBind方法返回的Binder對象项玛;
④客戶端通過持有的Binder對象來訪問Service貌笨。
對于Service的onBind()方法所返回的IBinder對象來說,它可被當成該Service組件所返回的回調(diào)對象襟沮,Service允許客戶端通過該IBinder對象來訪問Service內(nèi)部的數(shù)據(jù),這樣即可實現(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對象
private MyBinder binder = new MyBinder();
// 通過繼承Binder來實現(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;
//啟動Service時返回的IBinder對象
BindService.MyBinder binder;
//定義一個ServiceConnection對象
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對象)
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的生命周期
上文的啟動Service的兩個示例中开伏,我們都有對Service的生命周期方法進行觀測。我們發(fā)現(xiàn)根據(jù)啟動方式的不同Service回調(diào)的生命周期方法也不同遭商。
總結(jié)一下Service的生命周期方法回調(diào):
startService()和bindService()是兩個獨立的操作固灵,我們可以只啟動不綁定;也可以通過bindService()方法的第三個參數(shù)劫流,在綁定時啟動巫玻;還可以先啟動后綁定丛忆;
①被啟動的服務的生命周期:如果一個Service被某個Activity調(diào)用Context.startService方法啟動(Service也可以啟動服務),那么不管是否有Activity使用bindService綁定或者unbindService解除綁定該Service仍秤,該Service都在后臺運行熄诡。如果一個Service被startService方法多次啟動,那么onCreate方法只會調(diào)用一次诗力,但是onStartCommand將會每次都被回調(diào)凰浮,并且系統(tǒng)只會創(chuàng)建Service的一個實例(因此你只需要調(diào)用一次stopService來停止)。該Service將會一直在后臺運行苇本,而不管對應程序的Activity是否在運行袜茧,直到被調(diào)用stopService,或自身的stopSelf方法瓣窄。當然如果系統(tǒng)資源不足笛厦,Android系統(tǒng)也可能結(jié)束服務。
②被綁定的服務的生命周期:如果一個Service被某個Activity調(diào)用Context.bindService方法綁定啟動俺夕,不管調(diào)用bindService調(diào)用幾次裳凸,onCreate方法都只會調(diào)用一次,同時onStartCommand方法始終不會調(diào)用啥么。當鏈接建立之后登舞,Service將會一直運行,除非調(diào)用Context.unbindService斷開連接或者之前調(diào)用bindService的Context不存在了(如Activity被finish的時候)悬荣,系統(tǒng)將會自動停止Service菠秒,對應onDestroy將被調(diào)用。
③被啟動又被綁定的服務的生命周期:如果一個Service先被啟動氯迂,后又被綁定践叠,則該Service將會一直在后臺運行。并且不管如何調(diào)用嚼蚀,onCreate始終只會調(diào)用一次禁灼,對應startService調(diào)用多少次,Service的onStartCommand就會調(diào)用多少次轿曙。調(diào)用unbindService將不會停止Service弄捕,而必須調(diào)用stopService或Service自身的stopSelf來停止服務(在沒有解綁的前提下使用stopService是無法停止服務的)。
④當服務被停止時的清除工作:當一個Service被終止(1导帝、調(diào)用stopService守谓;2、調(diào)用stopSelf您单;3斋荞、不再有綁定的連接(通過bindService啟動))時,onDestroy方法將會被回調(diào)虐秦,在這里應當做一些清除工作(如停止Service中創(chuàng)建并運行的線程平酿、注冊的偵聽器凤优、接收器等)。
使用Service時的注意事項
①在調(diào)用bindService綁定到Service的時候蜈彼,就應當保證在某處調(diào)用unbindService解除綁定(盡管Activity被finish的時候會自動解除綁定筑辨,并且會自動停止Service);
②使用startService啟動服務之后柳刮,一定要使用stopService停止服務挖垛,不管你是否使用bindService;
③同時使用startService和bindService時要注意:Service的終止秉颗,需要unbindService與stopService同時調(diào)用痢毒,才能終止Service。關(guān)于調(diào)用順序蚕甥,如果先調(diào)用unbindService此時服務不會自動終止哪替,再調(diào)用stopService服務才會停止;如果先調(diào)用stopService此時服務也不會終止菇怀,需要再調(diào)用unbindService(或者調(diào)用bindService的Context不存在了凭舶,如Activity被finish的時候)服務才會自動停止。
④當旋轉(zhuǎn)手機屏幕的時候爱沟,如果發(fā)生了Activity的重建(請參考Android四大組件(一)Activity)帅霜,旋轉(zhuǎn)之前的使用bindService建立的連接便會斷開(Context不存在了)。
⑤在sdk 2.0之前的版本呼伸,使用的onStart方法被onStartCommand方法替換了身冀,但是onStart方法仍然有效。
跨進程調(diào)用Service(AIDL服務)
理解什么是AIDL(Android Interface Definition Language,即Android接口定義語言)
Android系統(tǒng)中括享,應用程序都運行在自己的進程中搂根,進程之間一般無法直接進行數(shù)據(jù)交換。在Java技術(shù)中铃辖,RMI可實現(xiàn)跨進程調(diào)用剩愧;在Android的中,采用了相似的方式來實現(xiàn)跨進程調(diào)用Service娇斩,這就是AIDL仁卷。與RMI相似,也是先定義一個遠程調(diào)用接口犬第,然后為該接口提供一個實現(xiàn)類五督;與RMI不同的是,客戶端訪問Service時瓶殃,Android并不是直接返回Service對象給客戶端。這點在綁定本地Service時已經(jīng)看到副签,Service只是將一個回調(diào)對象(IBinder對象)通過onBind()方法返回給客戶端遥椿。因此Android的AIDL遠程接口的實現(xiàn)類就是IBinder實現(xiàn)類基矮。與綁定本地Service不同的是,本地Service的onBind()方法會直接把IBinder對象本身傳給客戶端的ServiceConnection的onServiceConnected方法的第二個參數(shù)冠场。遠程Service的onBind方法只是將IBinder對象的代理傳給客戶端的ServiceConnection的onServiceConnected方法的第二個參數(shù)家浇。客戶端獲取了遠程Service的IBinder對象的代理后碴裙,就可通過該IBinder對象去回調(diào)遠程Service的屬性或方法了钢悲。
AIDL的操作步驟
Service端
1、新建AIDL文件
2舔株、按需求自己定義接口
3莺琳、重新build工程,AS會在build目錄中生成aidl對應的java實現(xiàn)
4载慈、編寫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不一樣惭等,沒有直接繼承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對象
* 在綁定本地Service的情況下办铡,該catBinder對象會直接傳給客戶端的
* ServiceConnection對象的onServiceConnected方法的第二個參數(shù)辞做;
* 在綁定遠程Service的情況下,只將catBinder對象的代理傳給客戶端的
* ServiceConnection對象的onServiceConnected方法的第二個參數(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秤茅、當然不要忘記在manifest.xml里注冊該Service
<service android:name=".aidldemo.AidlUserService">
<intent-filter>
<action android:name="com.rave.simpledemo.service.AIDL_USER_SERVICE" />
</intent-filter>
</service>
客戶端訪問AIDL服務
1、將Service端的AIDL接口文件復制到客戶端應用中童叠,要連文件路徑一起復制
2框喳、同Service端一樣,重新build工程拯钻,AS會調(diào)用ADT在build目錄中生成aidl對應的java實現(xiàn)
3帖努、創(chuàng)建ServiceConnection對象,并以ServiceConnection對象作為參數(shù)粪般,調(diào)用bindService()方法綁定遠程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不同拼余,綁定遠程Service的ServiceConnection不能直接獲取Service的onBind方法所返回的對象;
* 它只能返回onBind()方法所返回的對象的代理亩歹,因此需要在這里做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以后需要用顯示意圖來啟動Service,否則會報異常
// java.lang.IllegalArgumentException: Service Intent must be explicit
// 這里由于是跨進程所以用Component來解決這個問題,試過用setPackage的方式小作,無法解決這個問題
intent.setComponent(new ComponentName("com.rave.simpledemo", "com.rave.simpledemo.aidldemo.AidlUserService"));
//這種方式很多博客上說可以亭姥,經(jīng)測試這種方式可以startService成功的啟動遠程service,但是在啟動之后再綁定會出現(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);
}
}
實踐中遇到的問題
1达罗、Android 5.0以后系統(tǒng)禁止使用隱式意圖來啟動Service,所以在跨進程特別是跨APP的綁定Service的時候,可以采用如下方式:
Intent intent = new Intent();
intent.setAction("com.rave.simpledemo.service.AIDL_USER_SERVICE");
// Android 5.0以后需要用顯示意圖來啟動Service粮揉,否則會報異常
// java.lang.IllegalArgumentException: Service Intent must be explicit
// 這里由于是跨進程所以用Component來解決這個問題,試過用setPackage的方式巡李,無法解決這個問題
intent.setComponent(new ComponentName("com.rave.simpledemo", "com.rave.simpledemo.aidldemo.AidlUserService"));
//這種方式很多博客上說可以,經(jīng)測試這種方式可以startService成功的啟動遠程service扶认,但是在啟動之后再綁定會出現(xiàn)綁定失敗的情況侨拦。
//intent.setPackage("com.rave.simpledemo");
2、無法通過bindService設置BIND_AUTO_CREATE來創(chuàng)建服務辐宾,只能先startService啟動狱从,再調(diào)用bindService綁定:
// 在使用AIDL的時候,必須要先用startService來啟動服務叠纹,再用bindService來綁定服務季研,否則會綁定失敗。
startService(intent);
boolean flag = bindService(intent, conn, Service.BIND_AUTO_CREATE);
3吊洼、在Service的onDestroy里面一定要清理資源训貌,避免內(nèi)存泄漏。已經(jīng)客戶端調(diào)用bindService之后要手動解綁冒窍。
傳遞復雜數(shù)據(jù)的AIDL服務
上面我們所展示的都是通過AIDL傳輸基本數(shù)據(jù)類型递沪。如果是自定義類的話,Android要求調(diào)用遠程Service的參數(shù)和返回值都必須實現(xiàn)Parcelable接口综液。具體步驟如下:
服務端
1款慨、使用AIDL代碼來定義這些自定義類型(包括參數(shù)類型和返回值類型):
2、定義實現(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) {
//把對象所包含的數(shù)據(jù)寫到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接口中定義方法時檩奠,需要指定形參的傳遞模式。對于java語言來說附帽,一般都是采用傳入?yún)?shù)的方式埠戳,因此上面指定為in模式。具體的傳參方式有以下幾種:
① in:參數(shù)由客戶端設置蕉扮,或者理解為客戶端傳入?yún)?shù)值整胃;
② out:參數(shù)由服務器設置,或者理解成由服務端返回值喳钟;
③ inout:客戶端服務端都可以設置屁使,或者理解成可以雙向通信。任何自定義類型都需要在這里顯示使用import引入(即便該類的AIDL文件和通信接口是在同一個包下)奔则,否則會報錯:
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里注冊該Service
客戶端
1、跟傳輸基本類型一樣易茬,將Service端的AIDL接口文件復制到客戶端應用中(包括定義接口的aidl的文件IPet.aidl酬蹋、定義的自定義類型的AIDL文件(包括參數(shù)類型和返回值類型)、定義的自定義類型的Java類文件(包括參數(shù)類型和返回值類型))。同樣要保證文件路徑和類的包名和跟Service端一致除嘹。
2写半、后面的啟動服務和綁定服務的操作跟傳輸基本數(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)服務
1尉咕、 電話管理器(TelephonyManager):
//getSystemService(String name):根據(jù)服務名稱來獲取系統(tǒng)服務。
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
- 獲取網(wǎng)絡狀態(tài)
- 獲取SIM卡信息
- 監(jiān)聽通話狀態(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對聲音進行增大還是減械ノ摺;flags調(diào)整聲音時的標志犁柜。
- 設置麥克風靜音:setMicrophoneMute(boolean on)洲鸠。
- 設置聲音模式:setMode(int mode),可設置的值有NORMAL馋缅、RINGTONE和IN_CALL扒腕。
- 設置手機的電話鈴聲模式:setRingerMode(int ringerMode),可設置鈴聲模式萤悴、靜音模式瘾腰、振動模式。
- 設置打卡擴音器:setSpeakerphoneOn(boolean on)覆履。
- 將制定類型的聲音調(diào)整為靜音:setStreamMute(int streamType, boolean state)蹋盆,streamType聲音類型。
- 設置手機指定類型的音量值:setStreamVolume(int streamType, int index, int flags)硝全,streamType聲音類型栖雾;index具體的音量值;flags調(diào)整聲音時的標志伟众。
4析藕、振動器(Vibrator):
Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
- 控制手機振動:vibrate(long milliseconds)、vibrate(long[] pattern, int repeat)振動赂鲤;cancel()關(guān)閉振動噪径。
5、手機鬧鐘服務(AlarmManager):
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
- 開發(fā)鬧鐘應用
- 全局定時器
特殊的Service——IntentService
通過上面的內(nèi)容数初,我們已經(jīng)發(fā)現(xiàn)了找爱。服務的代碼默認運行在主線程里的。如果要在服務里面執(zhí)行耗時操作的代碼泡孩,就需要開啟一個主線程去處理這些代碼车摄。比如在啟動和停止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();
}
我們知道服務一旦啟動就會一直運行下去,必須手動調(diào)用stopService()或者stopSelf()方法才能讓服務停止下來。所以在上例中我們需要在run()方法的最后吮播,調(diào)用stopSelf來結(jié)束服務变屁。谷歌給我們提供IntentService,就是為了方便開發(fā)者處理在Service中開啟子線程的情況意狠。它幫我們開發(fā)者封裝了開啟子線程粟关、線程間通信(采用Handler)、以及自動銷毀服務等操作环戈。
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");
}
/**
* 必須實現(xiàn)的抽象方法,我們的業(yè)務邏輯就是在這個方法里面去實現(xiàn)的
* 方法在子線程運行院塞,我們不用去關(guān)心ANR的問題
* 在OnCreate方法里面創(chuà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;
}
}
/**
* 為了驗證onHandleIntent執(zhí)行后县遣,服務會不會自動銷毀,我們在這里重寫onDestroy方法
* 如果會自動銷毀汹族,那么在"IntetnService Running"出現(xiàn)后萧求,應該會出現(xiàn)"IntetnService Stop"
*/
@Override
public void onDestroy() {
System.out.println("==================BindIntentService onDestroy:" + count + "====================");
//經(jīng)測試,如果onHandleIntent里面的代碼邏輯沒有走完鞠抑,在服務外部調(diào)用stopService來停止服務饭聚,并不會立即結(jié)束子線程
quit = true;
super.onDestroy();
}
}
2、在Manifest里面注冊IntentService搁拙,注冊方式跟Service一樣
3秒梳、啟動IntentService
我們推薦使用startService(intent)的方式來啟動服務。
注意!!!也可以使用bindService的方式來啟動服務箕速。但是在使用bindService的啟動的時候酪碘,即使onHandleIntent里面的邏輯執(zhí)行完畢,也不會自動銷毀服務盐茎。原因應該是Service還是被綁定狀態(tài)兴垦,調(diào)用stopSelf無法停止。所以如果使用bindService啟動服務將會失去IntentService的一大特點字柠,使用時請謹慎.
4探越、銷毀IntentService
IntentService的一大特點就是onHandleIntent里面的代碼邏輯執(zhí)行完之后,自動銷毀Service窑业。所以我們可以不用專門做停止IntentService的操作钦幔。
注意!!!我們也可以在客戶端手動調(diào)用stopService來銷毀服務,但是用這種方式不會停止IntentService里面啟動的子線程常柄。如果要采用這種方式銷毀服務鲤氢,一定要注意子線程無法停止搀擂,從而導致內(nèi)存泄漏的問題。