服务简介
Service在继承关系上是Activity的大爷,也就是说Activity多继承了一个类,封装了界面相关,等等其他操作。
服务没有onPause、onStop、onResume、onRestart方法,因为service没有界面,长期运行在后台。
startService方式启动服务
Start方式启动服务声明周期:
服务被创建时依次调用onCreate、onStartCommand;
同一个服务只能被创建一次(onCreate),再次创建就只会执行onStartCommand;
一个服务只能被停止一次;
生命周期的方法:
onCreate:服务被创建的时候调用这个方法;
onStartCommand :开启服务
onDestroy:销毁服务
Start方式服务的创建步骤
1. 自定义服务类继承Service,并实现相关函数
2. 在配置文件中声明服务
3. 启动服务(startService,后面讲解bindService)
bindService方式启动服务
bind方式开启服务的生命周期
bindService绑定服务、unBindService解除绑定的服务;
服务是在被绑定的时候被创建,调用oncreate、onbind方法;
服务只能被绑定一次;
服务只能被解除绑定一次,解除绑定的时候调用onUnbind、onDestrory方法,如果多次解除绑定会抛出异常;
推荐的方式:
startService:开启并创建一个服务,服务长期运行在后台;
bindService:绑定服务,可以调用服务里面的方法;
unBindService:解除服务,停止服务里面的方法;
stopService:停止服务,销毁服务对象;
为什么要引入bindservice的API
1.服务启动后有时候会需要调用服务里面的方法,这就需要bindService来绑定服务
2.为了调用服务中的业务逻辑方法。
绑定服务调用服务方法的过程
通过bindservice方式实现调用服务里面业务逻辑方法:
步骤:
1、在服务类中创建一个中间人MyBinder类,继承了Binder,Binder实现了IBinder接口:
public class MyBinder extends Binder{ }
2、在服务类里面创建了一个MyBinder的成员变量:
private MyBinder myBinder;
3、在MyBinder类中写一个方法用于调用服务的业务逻辑方法:
public class MyBinder extends Binder{ //使用中间人调用服务里的方法 public void callMethodInService(){ methodInService(); } }
4、在activity中bindService时,定义了ServiceConnection,用它来监听绑定完成时的函数回调,在这个连接中实现了两个函数:
private class MyConn implements ServiceConnection { /** * 服务连接成功时调用这个方法 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { //得到服务绑定成功后返回的中间人MyBinder对象 myBinder = (MyBinder) service; } /** * 服务断开成功时调用这个方法 */ @Override public void onServiceDisconnected(ComponentName name) { System.out.println("-------onServiceDisconnected-------"); } }
5、通过在activity中通过中间人条用服务的业务逻辑方法:
myBinder.callMethodInService();
绑定服务抽取接口
接口(interface): 对外开放暴露的功能,但是不会暴露功能实现的细节;
让中间人实现服务接口的目的:只对外暴露接口里面业务逻辑方法,隐藏中间人里面的其他方法;
我们上面定义的MyBinder类太开放,如果别人获取了我们的MyBinder类的对象,就可以查看MyBinder类中的所有方法,
这是我们不允许的,我们只想别人看到我们允许别人调用的方法,而不想别人看到MyBinder类中的其他方法。要想实现这个
要求,我们让MyBinder再实现一个接口,接口中只有我们想让别人看到的方法,然后把接口给别人。
步骤:
1、创建一个服务的接口类,里面包含需要对外暴露的业务逻辑方法:
public interface IService { public void callMethodInService(); }
2、让服务中的中间人实现了服务的接口类:
private class MyBinder extends Binder implements IService{ //(实现服务接口中的方法)使用中间人调用服务里的方法 public void callMethodInService(){ methodInService(); } }
3、在activity中声明接口的成员变量:
private IService myBinder;
4、强制转换成服务的接口类型
private class MyConn implements ServiceConnection { /** * 服务连接成功时调用这个方法 */ @Override public void onServiceConnected(ComponentName name, IBinder service) { //强制转换成服务的接口类型 myBinder = (IService) service; }
5、在activity中通过接口的成员变量调用服务的业务逻辑方法:
public void call(View view){ myBinder.callMethodInService(); }
绑定服务的应用场景
1、需要在后台运行一定的业务逻辑,而且需要与服务器端交互数据,都是写在服务里面的。
2、天气预报、股票行情软件;
尤其是音乐播放器的播放暂停等,我们希望退出Activity后音乐可以继续播放,这就用到了Service,它可以在后台运行,我们又想在Activity中控制
音乐的播放暂停等功能,这就用到了BindService来调用服务里面的方法。
服务案例
电话窃听器(start方式启动服务)
步骤:
1、在工程中添加一个服务Service,重新onCreate方法;
2、在清单文件中配置服务;
3、在activity中开启服务;
4、在onCreate方法中使用TelephonyManager监听电话的状态;
5、在清单配置文件中添加权限
示例代码:
1、在工程中添加一个服务Service,重新onCreate方法:
public class DHQTService extends Service { /** * 当服务被创建的时候调用这个方法 */ @Override public void onCreate() { System.out.println("=========onCreate========="); super.onCreate(); TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); tm.listen(new MyListener(), PhoneStateListener.LISTEN_CALL_STATE); } }
2、在清单文件中配置服务:
<service android:name="com.itheima.dhqtq.DHQTService"></service>
3、在activity中开启服务:
service = new Intent(this,DHQTService.class); //开启服务 startService(service);
4、在onCreate方法中使用TelephonyManager监听电话的状态:
/** * 当服务被创建的时候调用这个方法 */ @Override public void onCreate() { System.out.println("=========onCreate========="); super.onCreate(); <span style="white-space:pre"> </span>//通过获取系统服务得到TelephonyManager,用它来监听电话状态 TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); <span style="white-space:pre"> </span>//使用TelephonyManager监听电话状态,并设置监听器 tm.listen(new MyListener(), PhoneStateListener.LISTEN_CALL_STATE); } /** *自定义一个电话状态监听器,监听电话 */ private class mylistener extends phonestatelistener { <span style="white-space:pre"> </span>//使用MediaRecorder类录音 private MediaRecorder r; <span style="white-space:pre"> </span>//电话状态改变会调用此函数 @Override public void onCallStateChanged(int state, String incomingNumber) { try { // super.onCallStateChanged(state, incomingNumber); System.out.println("====state===============" + state); switch (state) { case TelephonyManager.CALL_STATE_IDLE:// 闲置状态 System.out.println("关闭录音机,上传音频文件.................."); if(r != null){ r.stop();//录制完成 r.release();//释放资源 r = null; //上传文件 } break; case TelephonyManager.CALL_STATE_RINGING:// 来电话状态 System.out.println("准备好录音机,准备录音.................."); <span style="white-space:pre"> </span>//创建一个MediaRecorder对象 r = new MediaRecorder(); <span style="white-space:pre"> </span>//设置捕获设备为麦克 r.setAudioSource(MediaRecorder.AudioSource.MIC); r.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//设置输出格式为3gp //设置输出目录 //r.setOutputFile("/mnt/sdcard/info.3gp"); r.setOutputFile(Environment.getExternalStorageDirectory()+"/info.3gp"); r.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//设置音频编码格式 r.prepare(); //准备录制 break; case TelephonyManager.CALL_STATE_OFFHOOK:// 接听状态 System.out.println("开始录音.................."); r.start();//开始录制 break; } } catch (Exception e) { e.printStackTrace(); } } }
5、在清单配置文件中添加权限:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
利用服务注册广播接收者
操作频繁的广播事件,如果只是在清单配置文件配置,是不生效的。需要使用代码注册才能生效;比如 屏幕解锁开锁,
电池电量低了,只能通过代码的方式进行注册才会生效。但是如果在Activity中动态注册广播,在Activity销毁时如果没有解除注册广播,Activity会提示
一个错误(Activity漏气(直译)了),所以在Activity销毁时需要解除注册广播,但解除我们就无法收到广播了,
如果你想一直接收操作频繁的广播事件,那就在服务中动态注册广播,而不去解除注册广播。
步骤:
动态注册广播接收者
// 1、得到广播接收者的对象 ScreenBroadCastReceiver screenReceiver = new ScreenBroadCastReceiver(); // 2、创建一个intentFilter对象 IntentFilter filter = new IntentFilter(); // 3、注册接收的事件类型 filter.addAction("android.intent.action.SCREEN_ON"); filter.addAction("android.intent.action.SCREEN_OFF"); // 4、注册广播接收者 this.registerReceiver(screenReceiver, filter);
远程服务aidl的写法
service的2个概念
本地服务 -- 运行在当前自己应用的service
远程服务 --- 运行在其他应用进程里的service
调用远程服务
aidl--- Android interface definition language 出现目的是为了解决进程间通信
IPC---inner process communication
aidl调用远程服务的步骤
//首先开启远程服务支持
1.把src目录中的接口定义文件Iservice.java的后缀名改为aidl,要把public关键字去掉,保存后自动在gen目录下生成iservice.java文件
iservice.java中自动生成了Stub类
2.定义的中间人对象MyBinder 继承Stub
private class Mybinder extends Stub{.....}
//调用远程服务,把远程服务中的aidl文件拷贝过来
3. 要保证2个应用是统一aidl文件 aidl所在保证包名一样
4. 在获取中间人对象的时候直接用Stub类获取即可
iservice = Stub.asInterface(service);
远程服务的应用场景
1. 支付宝
2. 捕鱼达人 花钱买超级大炮 欢乐斗地主,