android 学习笔记9-服务 启动停止 调用服务方法 远程服务 AIDL进程通信
来源:互联网 发布:淘宝考试答案50题2016 编辑:程序博客网 时间:2024/05/22 01:39
1、服务的概念
就是默默运行在后台的组件,可以理解为是没有前台的activity,适合用来运行不需要前台界面的代码,比如说下载文件
服务可以被手动关闭,不会重启;但是如果被自动关闭,内存充足就会重启
startService启动服务的生命周期
onCreate-onStartCommand-onDestroy
重复的调用startService会导致onStartCommand被重复调用,但是不会一直create
注意:在内存充足的情况下,你退出应用程序,你的下载在子线程中还在继续执行,但是在内存不足的情况下就很容易进程被杀死,下载也就停止了,所以我们要在服务中运行
2、5个进程优先级
1. 前台进程:拥有一个正在与用户交互的activity(onResume方法被调用)的进程
2. 可见进程:拥有一个非前台,但是对用户可见的activity(onPause方法被调用)的进程
3. 服务进程:拥有一个通过startService方法启动的服务的进程
4. 后台进程:拥有一个后台activity(onStop方法被调用)的进程(比如你按home键退出到主页了)
5. 空进程:没有拥有任何活动的应用组件的进程,也就是没有任何服务和activity在运行。(比如你按返回键退出了,但是进程不会被销毁,便于下一次启动更快,但是非常容易被杀死)
3、服务的启动与停止:系统杀死服务进程会自动重启,但是用户手动停止就不能启动了。
Service和activity很类似,也是个上下文,也需要在清单文件中配置,也可以显示或隐式启动。
但是服务在内存中只有一个实例,不像activity那样可以启动多个实例。
a,定一个服务 MyService.java 继承Service。
b,配置下清单文件:
因为我们显示启动,不用配置intent-filter和action
<service android:name="com.example.runservice.MyService"></service>
代码演示:
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void start(View v){//绑定一个按钮事件,在xml文件中定义
//启动服务
Intent intent = new Intent(this, MyService.class);
startService(intent);
}
public void stop(View v){
//停止服务
Intent intent = new Intent(this, MyService.class);
stopService(intent);
}
}
MyService.java
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {//暂时先不用,这个是绑定服务会调用
System.out.println("bind");
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {//启动服务系统调用
System.out.println("start");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {//创建服务系统调用
System.out.println("create");
super.onCreate();
}
@Override
public void onDestroy() {//停止服务系统调用
System.out.println("destroy");
super.onDestroy();
}
}
4、服务的案例-电话侦听
电话状态:
空闲 TelephonyManager.CALL_STATE_IDLE
响铃 TelephonyManager.CALL_STATE_OFFHOOK
接听 TelephonyManager.CALL_STATE_RINGING
获取电话管理器,设置侦听
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
tm.listen(new MyPhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE);
代码演示:
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View v){//点击按钮启动服务
Intent intent = new Intent(this, RecorderService.class);
startService(intent);
}
}
RecorderService.java代码:
public class RecorderService extends Service {
private MediaRecorder recorder;//定义一个录音机
@Override
public IBinder onBind(Intent intent) {//暂时不用
return null;
}
@Override
public void onCreate() {//在创建服务的时候就开始监听电话
super.onCreate();
//获取电话管理器
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
//监听电话状态
tm.listen(new MyListener(), PhoneStateListener.LISTEN_CALL_STATE);
}
class MyListener extends PhoneStateListener{
//电话状态改变时回调,我们只要判断回调函数中的电话状态,就可以进行相应的逻辑操作
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
//判断当前是什么状态
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
if(recorder != null){
System.out.println("停止录音");
//停止录音
recorder.stop();
//释放录音机占用的资源
recorder.release();
recorder = null;
}
break;
case TelephonyManager.CALL_STATE_RINGING:
if(recorder == null){
recorder = new MediaRecorder();
//设置音频来源
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置输出格式
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setOutputFile("sdcard/voice.3gp");
//设置音频编码
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
System.out.println("准备好");
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
if(recorder != null){
System.out.println("开始录音");
recorder.start();
}
break;
}
}
}
}
5、服务两种启动方式:
startService
开始服务,会使进程变成为服务进程
启动服务的activity和服务不再有任何关系
bindService
绑定服务不会使进程变成服务进程(那么为什么还要绑定服务呢?绑定服务可以让activity与sercice通信,可以调用服务里面的方法,我们可以混合启动服务就达到了可以在后台运行又可以通信的目的)
绑定服务,是activity与服务建立连接,如果activity销毁了,服务也会被解绑并销毁,但是如果服务被销毁,activity不会被销毁
绑定服务和解绑服务的生命周期方法:onCreate->onBind->onUnbind->onDestroy
代码演示下两种启动:
清单文件就不显示了,和之前的一样。
MainActivity.java:
public class MainActivity extends Activity {
private MyConn conn;//自定义的一个ServiceConnection对象,绑定服务的时候需要传的一个参数
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
conn = new MyConn();
}
public void start(View v){//界面按钮的绑定事件,在xml文件定义了
Intent intent = new Intent(this, MyService.class);
startService(intent);//显示启动
}
public void stop(View v){//界面按钮的绑定事件,在xml文件定义了
Intent intent = new Intent(this, MyService.class);
stopService(intent);//显示启动
}
public void bind(View v){//界面按钮的绑定事件,在xml文件定义了
Intent intent = new Intent(this, MyService.class);
//绑定服务
//需要传一个ServiceConnection对象,因为ServiceConnection是一个接口,所以我们实现它,conn也可以使用匿名内部类。
//但是我们解绑还要用,所以就不用,
//BIND_AUTO_CREATE表示如果服务不存在,就创建服务
bindService(intent, conn, BIND_AUTO_CREATE);
}
public void unbind(View v){//界面按钮的绑定事件,在xml文件定义了
unbindService(conn);//解绑服务,记住解绑的一定要是conn这个对象,不然会报错的
}
//创建一个类,实现ServiceConnection接口
class MyConn implements ServiceConnection{
//到服务的连接被建立了,系统回调此方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
//到服务的连接中断了,系统回调此方法
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
MyService.java//定义一个服务,重写下里面的一些回调方法
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
System.out.println("绑定");//绑定服务的时候调用
return null;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("解绑");
return super.onUnbind(intent);
}
@Override
public void onCreate() {
System.out.println("创建");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("开始");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
System.out.println("摧毁");
super.onDestroy();
}
}
特别注意:
如果一个服务没有绑定,你去解绑的话,程序会崩溃的,报一个非法参数异常IllegalArgumentException
6、调用服务中的方法-通过Binder实现
服务中有定义一个方法,前提是不是静态的,你在MainActivity中怎么调用,发现根本调不了,因为startService和bindService都没有返回值的。
我们只能通过bindService,回调的onBind方法,里面会返回一个IBinder对象(类似一个中间人对象),可以通过Binder来调用,这就是bindService存在的意义。
我们activity和service之间不能直接通信,需要有个中间人,IBinder就类似中间人
代码演示:
MainActivity.java
public class MainActivity extends Activity {
MyBinder = mb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//服务是需要通过intent来实现的。
Intent intent = new Intent(this, LeaderService.class);
bindService(intent, new ServiceConnection() {//这个ServiceConnection参数是通过匿名内部类,因为我们后面不用解绑,所以没关系
//连接因为异常而终止才会调用,正常的解绑是不会调用的
@Override
public void onServiceDisconnected(ComponentName name) {
}
//onBind有返回值此方法才会调用,并不是说连接建立了就调用
//service:这个对象就是onBind返回的中间人对象
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mb = (MyBinder)service;//强转下,因为service是onBind返回的中间人对象
}
}, BIND_AUTO_CREATE);
}
public void click(View v){//点击按钮调用
mb.qianXian();//这样就可以通过中间人MyBinder调用到服务中的serviceMethod方法了。
}
}
LeaderService.java//定义的一个服务
public class LeaderService extends Service {
@Override
public IBinder onBind(Intent intent) {
// 返回中间人对象
return new MyBinder();//我们发现IBinder是一个接口,肯定google给我们提供了它的实现类,ctrl+T发现有个Binder实现了这个接口
}
class MyBinder extends Binder{//我们定义一个中间类,继承下Binder类就行,Binder实现了IBinder这个接口
//中间人
public void qianXian(){
//调用服务中的方法(领导中方法)
serviceMethod();//java内部类可以直接访问外部类里面的方法
}
}
public void serviceMethod(){//这个是服务里面的一个方法,就是我们activity想调用的方法
System.out.println("成功调用服务中的方法");
}
}
7、服务中中间人方法抽取到接口,MyBinder类里面的方法
为什么要抽到接口中呢?现在MyBinder里面只有一个serviceMethod方法,如果还有一些其它的方法,那么这个时候我们的activity中也可以调用了。
为了不让activity调用,就把方法都抽到接口中(也可以私有方法,但是我们这里不这样)
a,定义一个接口:
PublicBusiness.java
public interface PublicBusiness {
void qianXian();//把对公业务抽取到这里
}
b,MyBinder类就实现接口
class MyBinder extends Binder implements PublicBusiness{
//中间人
public void qianXian(){
serviceMethod();
}
public void takeSoap(){
System.out.println("不想activity调用的方法);
}
}
c,在 MainActivity中就不强转为 MyBinder了,而是转为 PublicBusiness
PublicBusiness pb;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
pb = (PublicBusiness)service;//强转为接口(转为MyBinder,PublicBusiness都可以),那么就只能调用接口中实现的方法了,不能调用takeSoap了
}
public void click(View v){//点击按钮调用
pb.qianXian();
}
8、混合启动服务,实现音乐播放
代码演示:
a,MainActivity.java
public class MainActivity extends Activity {
ControllerInterface ci;//服务中抽取的接口类
@Override
protected void onCreate(Bundle savedInstanceState) {//应用启动的时候就启动服务
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MusicService.class);
//把进程变成服务进程
startService(intent);
//绑定服务获取中间人对象
bindService(intent, new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ci = (ControllerInterface) service;
}
}, BIND_AUTO_CREATE);
}
public void play(View v){//点击按钮播放
ci.play();
}
public void pause(View v){//点击按钮停止播放
ci.pause();
}
}
b,MusicService.java//定义一个音乐播放服务
public class MusicService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new MusicController();//返回一个IBinder
}
class MusicController extends Binder implements ControllerInterface{
@Override
public void play() {
MusicService.this.play();//this.play() 和play()都是调用自己会进入死循环,调用外部类的方法,也就是服务中的的播放方法
}
@Override
public void pause() {
MusicService.this.pause();
}
}
public void play(){
System.out.println("播放音乐");
}
public void pause(){
System.out.println("暂停音乐");
}
}
c,ControllerInterface.java//抽调为接口
public interface ControllerInterface {
void play();
void pause();
}
这样播放的框架就OK了
温馨提示:混合启动就可以调用服务里面的播放方法,也是一个服务进程了,不容易被系统杀死
9、服务的分类
本地服务:服务和启动它的组件在同一个进程
远程服务:服务和启动它的组件不在同一个进程
远程服务只能隐式启动,类似隐式启动Activity,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点
启动远程服务首先需要那个服务所在的进程已经启动,不然启动不了。
配置远程服务需要在清单文件中配置 action
例如:
a,配置下第1个工程清单文件
<service android:name="com.example.remoteservice.RemoteService">
<intent-filter >
<action android:name="a.b.c"/>
</intent-filter>
</service>
b,定义服务,和之前的代码一样的。
c,在另外一个工程(第2个工程)里面启动这个工程的服务
public void start(View v){
Intent intent = new Intent();
intent.setAction("a.b.c");
startService(intent);
}
public void stop(View v){
Intent intent = new Intent();
intent.setAction("a.b.c");
stopService(intent);
}
这样就OK了
但是有个问题就是,我们怎么调用远程服务里面的方法呢?比如说上面音乐播放的play方法和7中的serviceMethod方法?
public void play(){
System.out.println("播放音乐");
}
10、AIDL 进程间通信
Android interface definition language 安卓接口定义语言,google专门给android写的
应用场景:远程服务中的中间人对象,其他应用是拿不到的,那么在通过绑定服务获取中间人对象时,就无法强制转换,
使用aidl,就可以在其他应用中拿到中间人类所实现的接口
比如:我们在自己的进程中是不能强转service,这个时候就需要用到AIDL 拿到别人进程的实现的接口
bindService(intent, new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ci = (ControllerInterface) service;//在自己的进程中是不能强转service,因为ControllerInterface这个接口不是在自己进程中
}
}, BIND_AUTO_CREATE);
远程服务修改的具体的步骤:
修改远程服务的后缀名称
1. 把接口文件的后缀名改成aidl,比如ControllerInterface.java修改为ControllerInterface.aidl
2. aidl文件中所有东西都是public的,不需要也不能自己定义访问修饰符,如果自己定义了就会报一个语法错误
注意:如果定义了一个aidl就会在gen目录下面自动生成一个java文件,例如ControllerInterface.java
3. 中间人对象继承Stub,这个对象已经继承了Binder并实现了PublicBusiness接口,就类似一个新的中间人,就和7里面的MyBinder功能类似。
Stub定义可以在gen文件夹中的ControllerInterface.java查看
4.中间人对象就不要像下面这样写:
class MyBinder extends Binder implements PublicBusiness{
//中间人
public void qianXian(){
serviceMethod();
}
}
要写成:
class MyBinder extends Stub{
//中间人
public void qianXian(){
serviceMethod();
}
}
我们自己的应用修改具体步骤:(我们调用远程服务)
1. 把远程服务工程里面的aidl文件复制到自己项目,然后aidl所在的包名必须一致(我们先建一个和它一样的包名,然后在把aidl复制过来)
2. 把我们项目获取到的中间人对象使用Stub.asInterface强转
例如:
ControllerInterface ci;//这个是在MainActivity中定义的
//摘取其中一段关键修改代码
bindService(intent, new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ci = Stub.asInterface(service);//在自己的进程中是不能强转service,因为ControllerInterface这个接口不是在自己进程中,使用aidl就行了
}
}, BIND_AUTO_CREATE);
就是默默运行在后台的组件,可以理解为是没有前台的activity,适合用来运行不需要前台界面的代码,比如说下载文件
服务可以被手动关闭,不会重启;但是如果被自动关闭,内存充足就会重启
startService启动服务的生命周期
onCreate-onStartCommand-onDestroy
重复的调用startService会导致onStartCommand被重复调用,但是不会一直create
注意:在内存充足的情况下,你退出应用程序,你的下载在子线程中还在继续执行,但是在内存不足的情况下就很容易进程被杀死,下载也就停止了,所以我们要在服务中运行
2、5个进程优先级
1. 前台进程:拥有一个正在与用户交互的activity(onResume方法被调用)的进程
2. 可见进程:拥有一个非前台,但是对用户可见的activity(onPause方法被调用)的进程
3. 服务进程:拥有一个通过startService方法启动的服务的进程
4. 后台进程:拥有一个后台activity(onStop方法被调用)的进程(比如你按home键退出到主页了)
5. 空进程:没有拥有任何活动的应用组件的进程,也就是没有任何服务和activity在运行。(比如你按返回键退出了,但是进程不会被销毁,便于下一次启动更快,但是非常容易被杀死)
3、服务的启动与停止:系统杀死服务进程会自动重启,但是用户手动停止就不能启动了。
Service和activity很类似,也是个上下文,也需要在清单文件中配置,也可以显示或隐式启动。
但是服务在内存中只有一个实例,不像activity那样可以启动多个实例。
a,定一个服务 MyService.java 继承Service。
b,配置下清单文件:
因为我们显示启动,不用配置intent-filter和action
<service android:name="com.example.runservice.MyService"></service>
代码演示:
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void start(View v){//绑定一个按钮事件,在xml文件中定义
//启动服务
Intent intent = new Intent(this, MyService.class);
startService(intent);
}
public void stop(View v){
//停止服务
Intent intent = new Intent(this, MyService.class);
stopService(intent);
}
}
MyService.java
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {//暂时先不用,这个是绑定服务会调用
System.out.println("bind");
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {//启动服务系统调用
System.out.println("start");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {//创建服务系统调用
System.out.println("create");
super.onCreate();
}
@Override
public void onDestroy() {//停止服务系统调用
System.out.println("destroy");
super.onDestroy();
}
}
4、服务的案例-电话侦听
电话状态:
空闲 TelephonyManager.CALL_STATE_IDLE
响铃 TelephonyManager.CALL_STATE_OFFHOOK
接听 TelephonyManager.CALL_STATE_RINGING
获取电话管理器,设置侦听
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
tm.listen(new MyPhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE);
代码演示:
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View v){//点击按钮启动服务
Intent intent = new Intent(this, RecorderService.class);
startService(intent);
}
}
RecorderService.java代码:
public class RecorderService extends Service {
private MediaRecorder recorder;//定义一个录音机
@Override
public IBinder onBind(Intent intent) {//暂时不用
return null;
}
@Override
public void onCreate() {//在创建服务的时候就开始监听电话
super.onCreate();
//获取电话管理器
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
//监听电话状态
tm.listen(new MyListener(), PhoneStateListener.LISTEN_CALL_STATE);
}
class MyListener extends PhoneStateListener{
//电话状态改变时回调,我们只要判断回调函数中的电话状态,就可以进行相应的逻辑操作
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
//判断当前是什么状态
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
if(recorder != null){
System.out.println("停止录音");
//停止录音
recorder.stop();
//释放录音机占用的资源
recorder.release();
recorder = null;
}
break;
case TelephonyManager.CALL_STATE_RINGING:
if(recorder == null){
recorder = new MediaRecorder();
//设置音频来源
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置输出格式
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setOutputFile("sdcard/voice.3gp");
//设置音频编码
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
System.out.println("准备好");
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
if(recorder != null){
System.out.println("开始录音");
recorder.start();
}
break;
}
}
}
}
5、服务两种启动方式:
startService
开始服务,会使进程变成为服务进程
启动服务的activity和服务不再有任何关系
bindService
绑定服务不会使进程变成服务进程(那么为什么还要绑定服务呢?绑定服务可以让activity与sercice通信,可以调用服务里面的方法,我们可以混合启动服务就达到了可以在后台运行又可以通信的目的)
绑定服务,是activity与服务建立连接,如果activity销毁了,服务也会被解绑并销毁,但是如果服务被销毁,activity不会被销毁
绑定服务和解绑服务的生命周期方法:onCreate->onBind->onUnbind->onDestroy
代码演示下两种启动:
清单文件就不显示了,和之前的一样。
MainActivity.java:
public class MainActivity extends Activity {
private MyConn conn;//自定义的一个ServiceConnection对象,绑定服务的时候需要传的一个参数
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
conn = new MyConn();
}
public void start(View v){//界面按钮的绑定事件,在xml文件定义了
Intent intent = new Intent(this, MyService.class);
startService(intent);//显示启动
}
public void stop(View v){//界面按钮的绑定事件,在xml文件定义了
Intent intent = new Intent(this, MyService.class);
stopService(intent);//显示启动
}
public void bind(View v){//界面按钮的绑定事件,在xml文件定义了
Intent intent = new Intent(this, MyService.class);
//绑定服务
//需要传一个ServiceConnection对象,因为ServiceConnection是一个接口,所以我们实现它,conn也可以使用匿名内部类。
//但是我们解绑还要用,所以就不用,
//BIND_AUTO_CREATE表示如果服务不存在,就创建服务
bindService(intent, conn, BIND_AUTO_CREATE);
}
public void unbind(View v){//界面按钮的绑定事件,在xml文件定义了
unbindService(conn);//解绑服务,记住解绑的一定要是conn这个对象,不然会报错的
}
//创建一个类,实现ServiceConnection接口
class MyConn implements ServiceConnection{
//到服务的连接被建立了,系统回调此方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
//到服务的连接中断了,系统回调此方法
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
MyService.java//定义一个服务,重写下里面的一些回调方法
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
System.out.println("绑定");//绑定服务的时候调用
return null;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("解绑");
return super.onUnbind(intent);
}
@Override
public void onCreate() {
System.out.println("创建");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("开始");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
System.out.println("摧毁");
super.onDestroy();
}
}
特别注意:
如果一个服务没有绑定,你去解绑的话,程序会崩溃的,报一个非法参数异常IllegalArgumentException
6、调用服务中的方法-通过Binder实现
服务中有定义一个方法,前提是不是静态的,你在MainActivity中怎么调用,发现根本调不了,因为startService和bindService都没有返回值的。
我们只能通过bindService,回调的onBind方法,里面会返回一个IBinder对象(类似一个中间人对象),可以通过Binder来调用,这就是bindService存在的意义。
我们activity和service之间不能直接通信,需要有个中间人,IBinder就类似中间人
代码演示:
MainActivity.java
public class MainActivity extends Activity {
MyBinder = mb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//服务是需要通过intent来实现的。
Intent intent = new Intent(this, LeaderService.class);
bindService(intent, new ServiceConnection() {//这个ServiceConnection参数是通过匿名内部类,因为我们后面不用解绑,所以没关系
//连接因为异常而终止才会调用,正常的解绑是不会调用的
@Override
public void onServiceDisconnected(ComponentName name) {
}
//onBind有返回值此方法才会调用,并不是说连接建立了就调用
//service:这个对象就是onBind返回的中间人对象
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mb = (MyBinder)service;//强转下,因为service是onBind返回的中间人对象
}
}, BIND_AUTO_CREATE);
}
public void click(View v){//点击按钮调用
mb.qianXian();//这样就可以通过中间人MyBinder调用到服务中的serviceMethod方法了。
}
}
LeaderService.java//定义的一个服务
public class LeaderService extends Service {
@Override
public IBinder onBind(Intent intent) {
// 返回中间人对象
return new MyBinder();//我们发现IBinder是一个接口,肯定google给我们提供了它的实现类,ctrl+T发现有个Binder实现了这个接口
}
class MyBinder extends Binder{//我们定义一个中间类,继承下Binder类就行,Binder实现了IBinder这个接口
//中间人
public void qianXian(){
//调用服务中的方法(领导中方法)
serviceMethod();//java内部类可以直接访问外部类里面的方法
}
}
public void serviceMethod(){//这个是服务里面的一个方法,就是我们activity想调用的方法
System.out.println("成功调用服务中的方法");
}
}
7、服务中中间人方法抽取到接口,MyBinder类里面的方法
为什么要抽到接口中呢?现在MyBinder里面只有一个serviceMethod方法,如果还有一些其它的方法,那么这个时候我们的activity中也可以调用了。
为了不让activity调用,就把方法都抽到接口中(也可以私有方法,但是我们这里不这样)
a,定义一个接口:
PublicBusiness.java
public interface PublicBusiness {
void qianXian();//把对公业务抽取到这里
}
b,MyBinder类就实现接口
class MyBinder extends Binder implements PublicBusiness{
//中间人
public void qianXian(){
serviceMethod();
}
public void takeSoap(){
System.out.println("不想activity调用的方法);
}
}
c,在 MainActivity中就不强转为 MyBinder了,而是转为 PublicBusiness
PublicBusiness pb;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
pb = (PublicBusiness)service;//强转为接口(转为MyBinder,PublicBusiness都可以),那么就只能调用接口中实现的方法了,不能调用takeSoap了
}
public void click(View v){//点击按钮调用
pb.qianXian();
}
8、混合启动服务,实现音乐播放
代码演示:
a,MainActivity.java
public class MainActivity extends Activity {
ControllerInterface ci;//服务中抽取的接口类
@Override
protected void onCreate(Bundle savedInstanceState) {//应用启动的时候就启动服务
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MusicService.class);
//把进程变成服务进程
startService(intent);
//绑定服务获取中间人对象
bindService(intent, new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ci = (ControllerInterface) service;
}
}, BIND_AUTO_CREATE);
}
public void play(View v){//点击按钮播放
ci.play();
}
public void pause(View v){//点击按钮停止播放
ci.pause();
}
}
b,MusicService.java//定义一个音乐播放服务
public class MusicService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new MusicController();//返回一个IBinder
}
class MusicController extends Binder implements ControllerInterface{
@Override
public void play() {
MusicService.this.play();//this.play() 和play()都是调用自己会进入死循环,调用外部类的方法,也就是服务中的的播放方法
}
@Override
public void pause() {
MusicService.this.pause();
}
}
public void play(){
System.out.println("播放音乐");
}
public void pause(){
System.out.println("暂停音乐");
}
}
c,ControllerInterface.java//抽调为接口
public interface ControllerInterface {
void play();
void pause();
}
这样播放的框架就OK了
温馨提示:混合启动就可以调用服务里面的播放方法,也是一个服务进程了,不容易被系统杀死
9、服务的分类
本地服务:服务和启动它的组件在同一个进程
远程服务:服务和启动它的组件不在同一个进程
远程服务只能隐式启动,类似隐式启动Activity,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点
启动远程服务首先需要那个服务所在的进程已经启动,不然启动不了。
配置远程服务需要在清单文件中配置 action
例如:
a,配置下第1个工程清单文件
<service android:name="com.example.remoteservice.RemoteService">
<intent-filter >
<action android:name="a.b.c"/>
</intent-filter>
</service>
b,定义服务,和之前的代码一样的。
c,在另外一个工程(第2个工程)里面启动这个工程的服务
public void start(View v){
Intent intent = new Intent();
intent.setAction("a.b.c");
startService(intent);
}
public void stop(View v){
Intent intent = new Intent();
intent.setAction("a.b.c");
stopService(intent);
}
这样就OK了
但是有个问题就是,我们怎么调用远程服务里面的方法呢?比如说上面音乐播放的play方法和7中的serviceMethod方法?
public void play(){
System.out.println("播放音乐");
}
10、AIDL 进程间通信
Android interface definition language 安卓接口定义语言,google专门给android写的
应用场景:远程服务中的中间人对象,其他应用是拿不到的,那么在通过绑定服务获取中间人对象时,就无法强制转换,
使用aidl,就可以在其他应用中拿到中间人类所实现的接口
比如:我们在自己的进程中是不能强转service,这个时候就需要用到AIDL 拿到别人进程的实现的接口
bindService(intent, new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ci = (ControllerInterface) service;//在自己的进程中是不能强转service,因为ControllerInterface这个接口不是在自己进程中
}
}, BIND_AUTO_CREATE);
远程服务修改的具体的步骤:
修改远程服务的后缀名称
1. 把接口文件的后缀名改成aidl,比如ControllerInterface.java修改为ControllerInterface.aidl
2. aidl文件中所有东西都是public的,不需要也不能自己定义访问修饰符,如果自己定义了就会报一个语法错误
注意:如果定义了一个aidl就会在gen目录下面自动生成一个java文件,例如ControllerInterface.java
3. 中间人对象继承Stub,这个对象已经继承了Binder并实现了PublicBusiness接口,就类似一个新的中间人,就和7里面的MyBinder功能类似。
Stub定义可以在gen文件夹中的ControllerInterface.java查看
4.中间人对象就不要像下面这样写:
class MyBinder extends Binder implements PublicBusiness{
//中间人
public void qianXian(){
serviceMethod();
}
}
要写成:
class MyBinder extends Stub{
//中间人
public void qianXian(){
serviceMethod();
}
}
我们自己的应用修改具体步骤:(我们调用远程服务)
1. 把远程服务工程里面的aidl文件复制到自己项目,然后aidl所在的包名必须一致(我们先建一个和它一样的包名,然后在把aidl复制过来)
2. 把我们项目获取到的中间人对象使用Stub.asInterface强转
例如:
ControllerInterface ci;//这个是在MainActivity中定义的
//摘取其中一段关键修改代码
bindService(intent, new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ci = Stub.asInterface(service);//在自己的进程中是不能强转service,因为ControllerInterface这个接口不是在自己进程中,使用aidl就行了
}
}, BIND_AUTO_CREATE);
0 0
- android 学习笔记9-服务 启动停止 调用服务方法 远程服务 AIDL进程通信
- Android笔记 使用AIDL和远程服务实现进程通信
- android,aidl,绑定远程服务,调用远程服务的方法
- Android使用AIDL和远程服务实现进程通信
- Android使用AIDL和远程服务实现进程通信
- android进程间通信(远程服务aidl,服务端)
- android进程间通信(远程服务aidl,客户端)
- Android学习笔记_23_服务Service之AIDL和远程服务实现进程通信以及进程间传递自定义类型参数
- aidl远程服务调用
- Android远程服务一:android AIDL远程服务调用
- Android通过AIDL与远程服务通信
- Android AIDL服务学习笔记
- Android AIDL服务学习笔记
- Android中使用AIDL调用远程服务
- android-------采用AIDL调用远程服务
- android aidl调用进程间服务的方法
- 采用AIDL调用远程服务
- Android -- service的开启方式, start开启和绑定开启服务,调用服务的的方法, aidl调用远程服务
- vue项目实践(vuex + vue-router + vue-resource)
- spring中 context:property-placeholder 导入多个独立的配置文件
- 使用 Vue.js 制作一个简单的调查问卷平台
- 一个简单的 vue.js 实践教程
- ParameterizedType获取java泛型参数类型
- android 学习笔记9-服务 启动停止 调用服务方法 远程服务 AIDL进程通信
- 程序员面试金典: 9.9 递归和动态规划 9.5求字符串的全排列
- 黑马Java测试题错题归纳
- 【Unity】LineRenderer
- AlphaGo 超快棋遍虐人类高手(职业棋手讲解及大量网友评论)
- Caused by: java.lang.NumberFormatException: For input string: "${jdbc.initialSize}"
- 新的开始
- 不要让CPU空转
- iTerm 2 && Oh My Zsh【DIY教程——亲身体验过程】