服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开另一个应用,服务仍然能保持独立正常运行。
服务依赖于创建服务时所在的应用程序进程,当此应用程序进程被杀死,所依赖于该进程的服务也会停止。
服务并不会自动开启线程,所有的代码都是默认运行在主线程中。
需要在服务内部手动创建子线程,执行具体任务,否则容易造成线程阻塞。
服务的基本用法
Context提供API
startService(Intent intent); 开启服务
stopService(Intent intent); 暂停服务
1.自定义服务
public class MyService extends Service {
private static final String TAG = "MyService";
int i = 0;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
Log.i(TAG, String.valueOf(i++));
}
}, 1000, 2000);
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
2.mainfests中声明服务
<application>
<service android:name=".service.MyService"/>
</application>
3.在Activity中开启服务
public class MainActivity extends AppCompatActivity {
private Button mBtnSitchService;
private Context mContext;
private boolean switchFlag;
private Intent mServiceIntent;
private void initEvent() {
mBtnSitchService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (switchFlag) {
stopService();
} else {
startService();
}
switchFlag = !switchFlag;
}
});
}
private void startService() {
//开启服务
mServiceIntent = new Intent(this, MyService.class);
startService(mServiceIntent);
}
private void stopService() {
//暂停服务
stopService(mServiceIntent);
}
}
IntentService
因为服务所有代码默认还是运行在主线程,每次做耗时操作都要开启线程,否则会出现ANR,太过繁琐。
Android提供了线程服务,直接运行在子线程中,并且每次运行完会自动关闭服务。
public class MyService extends IntentService {
private static final String TAG = "MyService";
public MyService() {
super("MyIntentService");//调用父类有参的构造函数
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.i(TAG, "Thread id is" + Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
服务的生命周期
每个服务都只会存在一个实例。
不管调用多少次startService(),只需要调用一次stopService()或stopSelf(),服务就会停下来。
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
public void onCreate() {
super.onCreate();
log("服务第一次创建时调用");
}
@Override
public void onDestroy() {
super.onDestroy();
log("服务停止或解绑时调用");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
log("服务创建后,每次启动连接时调用");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
log("服务绑定时调用");
return null;
}
@Override
public boolean onUnbind(Intent intent) {
log("服务解绑时调用");
return super.onUnbind(intent);
}
private void log(String msg) {
Log.i(TAG, msg);
}
}
两种服务启动方式
1.Intent service = new Intent(this, MyService.class);
startService(service);
stopService(service);
启动时Service调用的API
onCreate()
onStartCommand(Intent intent, int flags, int startId)
停止时调用的API
onDestroy()
2.Intent service = new Intent(this, MyService.class);
bindService(service, conn, BIND_AUTO_CREATE)
unbindService(conn);
绑定时调用Service调用的API
onCreate()
onBind(Intent intent)
解绑时调用的API
onUnbind(Intent intent)
onDestroy()
两种启动的区别
startService()
服务启动后,会在后台一直运行,退出程序不会被销毁,但是不能交互数据。
bindService()
能够交互数据,但是,当启动的组件(Activity)被销毁时,Service也会跟着销毁。
注意:一定要再onDestroy()里面调用unbindService(conn)解绑服务。
混合启动
在开发应用时一般两种服务混合启动
先start再Bind,同一个绑定服务不能多次解绑,否则会报错。
private void startService() {
mServiceIntent = new Intent(this, MyService.class);
//开启服务
startService(mServiceIntent);
bindService(mServiceIntent, mConn, Service.BIND_AUTO_CREATE);
}
private void stopService() {
//暂停服务
unbindService(mConn);
stopService(mServiceIntent);
}
混合启动解绑
需要先调用stopService(),再调用unbindService(),onDestroy()才会执行。
Service通讯
注意:
任何一个服务在整个应用程序范围内都是通用的,可以和任何一个活动绑定。
绑定后,可以获取相同的Binder实例。
定义接口,用来构建Service和Activity的通信桥梁
public interface IMyBindDemo {
void call();
}
自定义服务
public class MyService extends Service {
private static final String TAG = "MyService";
class MyBind extends Binder implements IMyBindDemo {
@Override
public void call() {
Log.i(TAG, "Service Demo");
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//绑定服务时,绑定接口
return new MyBind();
}
}
开启服务,获取服务内的数据
public class MainActivity extends AppCompatActivity {
private Button mBtnSwitchService,mBtnGetServiceData;
private Context mContext;
private boolean switchFlag;
private Intent mServiceIntent;
private IMyBindDemo mBind;
//服务连接
private ServiceConnection mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName,
IBinder iBinder) {
//iBinder 与 onBind的返回值绑定
mBind = (IMyBindDemo) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initEvent();
}
private void initEvent() {
mBtnSwitchService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (switchFlag) {
stopService();
} else {
startService();
}
switchFlag = !switchFlag;
}
});
mBtnGetServiceData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//获取通信
mBind.call();
}
});
}
private void initView() {
mContext = this;
mBtnSwitchService = findViewById(R.id.btn_switch);
mBtnGetServiceData = findViewById(R.id.btn_get_service_data);
}
private void startService() {
mServiceIntent = new Intent(this, MyService.class);
//开启服务
startService(mServiceIntent);
bindService(mServiceIntent, mConn, Service.BIND_AUTO_CREATE);
}
private void stopService() {
//暂停服务
unbindService(mConn);
stopService(mServiceIntent);
}
}
AIDL(Android interface definition Language)
AIDL即Android接口定义语言,是Android提供的一种用于进程间通信 (IPC)的机制。用来定义不同进程间,进行相互通信的接口。
AIDL支持的数据类型
1. Java 的基本数据类型
2. List 和 Map
元素必须是 AIDL 支持的数据类型
Server 端具体的类里则必须是 ArrayList 或者 HashMap
3. 其他 AIDL 生成的接口
4. 实现 Parcelable 的实体
AIDL的基本实现
举个例子:客户端向服务端发送验证支付请求
服务端
1. 自定义服务接口
参数最好封装成 Parcelable 的实体,为了演示就不封装了。
public interface IAlipayBinder {
/**
* @param account 账号
* @param pwd 密码
* @param payPwd 支付密码
* @param money 支付数额
* @param currTime 当前时间戳
*/
int callDealAlipay(String account, String pwd,
String payPwd, double money, long currTime);
}
2. 在工程的 main下新建 aidl 文件夹。
右键新建aidl接口文件,注意包名要和java内的一致。
新建完成后修改aidl文件内的方法,和定义的接口方法保持一致。
此时需要重新编译一下工程,否则找不到aidl的接口。
3.1 自定义服务
public class AlipayService extends Service {
private static final String TAG = "AlipayService";
//这里不需要修饰符
class MyBinder extends IAlipayBinder.Stub {
@Override
public int callDealAlipay(String account, String pwd,
String payPwd, double money,
long currTime) throws RemoteException {
return dealAlipay(account, pwd, payPwd, money, currTime);
}
}
@Override
public void onCreate() {
Log.i(TAG, "service create: OK");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
public int dealAlipay(String account, String pwd,
String payPwd, double money,
long currTime) {
if (!account.equals("admin") || !pwd.equals("123")) {
return 0;
}
if (!payPwd.equals("123456")) {
return 1;
}
if (money > 1000) {
return 2;
}
return 3;
}
}
3.2 在mainfests中声明,注意要加入action,客户端启动时用得到。
<application>
<service android:name=".service.AlipayService">
<intent-filter>
<action android:name="com.w.review.service.ALIPAY" />
</intent-filter>
</service>
</application>
3.3 启动服务
private void startService() {
Intent intent = new Intent(this, AlipayService.class);
startService(intent);
}
1. 客户端进程
同样新建一个aidl文件夹,将服务端的aidl文件复制过来。
注意保持包名和服务端的包名一致。
2.使用bindService()进行通讯
public class MainActivity extends AppCompatActivity {
private Button mBtnStart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initEvent();
}
private void initEvent() {
mBtnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startAlipay();
}
});
}
private void initView() {
mBtnStart = findViewById(R.id.btn_start);
}
private void startAlipay() {
Intent intent = new Intent();
intent.setAction("com.w.review.service.ALIPAY");
注意,Android5.0后,要想隐式启动Service,需要加包名
intent.setPackage("com.w.review");
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName,
IBinder iBinder) {
IAlipayBinder binder = IAlipayBinder.Stub.asInterface(iBinder);
try {
int state = binder.callDealAlipay(
"admin",
"123",
"123456",
100,
100L);
switch (state) {
case 0:
showToast("账户或密码错误");
break;
case 1:
showToast("支付密码错误");
break;
case 2:
showToast("余额不足");
break;
case 3:
showToast("支付成功");
break;
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
}, Service.BIND_AUTO_CREATE);
}
private void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}