service的基本用法+远程服务

本文深入讲解Android中的服务机制,包括进程回收优先级、服务的生命周期、如何开启和关闭服务、绑定服务的过程及注意事项等,同时介绍了本地服务与远程服务的区别及其应用场景。

 

一、进程回收的优先级

android操作系统会尽可能长期的保留应用程序的进程,系统根据进程的优先级回收进程,释放内存。

优先级顺序:

前台进程,Foreground process,能看到应用程序的界面,并且可以操作这个应用程序。

可视进程Visible process能看到应用程序,但是不能操作

服务进程Service process应用程序带一个后台运行的服务,服务没有停止。

后台进程Background process,应用程序被最小化,但是没有退出。

空进程Empty process ,应用程序没有任何活动的组件了。

二、服务 service:

长期后台运行,一般没有应用程序界面

子线程也是没有界面,长期在后台运行,为什么不用子线程而用服务?

退出应用程序之后子线程没有界面长期在后台运行,这时的应用程序是一个空进程,优先级最低,如果进程被系统回收,开启的所有的线程都不在了。

服务进程的优先级比较高,不容易被系统回收,即使因为内存不足而被系统回收了,在内存充足的时候服务还是可以自动被恢复的。

 

可以把服务理解成一个没有界面的Activity,service的父类是Activity父类(Context)的父类。

电话窃听器:

 

创建服务窃听电话的服务PhoneService继承Service,实现窃听电话的逻辑:

大致分为以下几步:

1.开启服务开始窃听

2.设置窃听器:

     <1准备录音

     <2开始录音

     <3停止录音,释放录音资源

3.取消窃听停止服务

public class PhoneService extends Service {
//电话管理器,是一个系统服务,长期运行在后台没有界面
private TelephonyManager tm;
private MyListener listener;
//录音器
private MediaRecorder recorder;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
        //一、创建服务开始监听
tm= (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
listener=new MyListener();
tm.listen(listener,PhoneStateListener.LISTEN_CALL_STATE);
super.onCreate();
}
private class MyListener extends PhoneStateListener{
//二、设置窃听器
@Override
//当呼叫状态发生变化时,调用的方法
public void onCallStateChanged(int state, String incomingNumber) {
//电话状态:铃响,接听电话,空闲
switch (state) {
case TelephonyManager.CALL_STATE_IDLE://空闲状态
//2.3停止录音
if (recorder!=null) {
recorder.stop();
//释放资源
recorder.release();
recorder=null;
}
break;
case TelephonyManager.CALL_STATE_RINGING://铃响状态
           //2.1实现准备录音方法
PrepareRecorder(incomingNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK://电话接通
//2.2开始录音7.
recorder.start();
break;
}
super.onCallStateChanged(state, incomingNumber);
}
     /**
      * 2.1准备录音
      * @param incomingNumber 来电号码
      */
private void PrepareRecorder(String incomingNumber) {
// TODO Auto-generated method stub
try {
//1.创建一个录音机的实例
recorder=new MediaRecorder();
//2.设置录制的数据源,VOICE_CALL可以录制双方声音,但是只有国产手机支持,因为涉及隐私问题,MIC只支持录制麦克风的声音 recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//3.设置输入文件的格式	recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//4.设置保存的文件名称	recorder.setOutputFile("/sdcard/"+incomingNumber+".3gp");	    //5.设置编码模式 recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
   //6.准备录音
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onDestroy() {
//三、取消监听停止服务
tm.listen(listener, PhoneStateListener.LISTEN_NONE);
super.onDestroy();
}

在清单文件中配置服务

<service android:name="com.example.TeleListener.PhoneService">
        </service>

在清单文件中添加权限:

//监听电话状态的权限

android.permission.READ_PHONE_STATE

//写sd卡的权限

android.permission.WRITE_EXTERNAL_STORAGE

//读音频的权限

android.permission.RECORD_AUDIO

应用程序没有界面没有Activity,利用开机启动,直接开启服务。

所以把开机启动项目拿来把广播接收者代码改为:

public void onReceive(Context context, Intent intent) {
Intent i=new Intent(context,PhoneService.class);
context.startService(i);
}

还可以通过其他的广播接收者启动这个服务。

三、

服务里能不能进行耗时的操作?

服务是运行在主线程的ui线程,同样不能进行耗时操作。

如果要在服务里执行耗时的操作,需要在服务里面开启子线程执行耗时的操作。

服务定义的长期活动是指服务不容易被系统回收,就算回收了,也会在内存充足的时候再开启。

四、

利用startService(intent);stopService(intent);开启服务与关闭服务:

服务的生命周期:

onCreate();创建服务

onStartComment();开启服务

onDestory();销毁服务

通过startService(intent)开启服务,多次执行这个方法onCreate方法只能被执行一次,而onStartComment()方法会被执行多次。并且当应用程序退出的时候,也不会去执行onDestory(),方法。

通过stopService(intent)关闭服务,多次执行这个方法onDestory方法也只能被执行一次。

通过startService(intent)开启服务框架会传一个上下文给服务,而直接去new 服务的话将会出现上下文为空现象。所以自己不可以new服务调用服务的方法,服务只能由框架创建。

五、

创建服务时必须实现onBind()方法,这个方法是在服务被绑定的时候调用的方法。

bindService绑定服务的步骤大致可以分为以下几步:

1.创建service类继承Service,在清单文件中配置<service>节点.

2.创建实现IBinder接口的内部类,通常继承Binder实现,在内部类里创建新的方法调用服务内部的方法。

public class MyBinder extends Binder{//服务的内部人员可以调用服务的方法
public void callMethodService() {
 ServiceMethod();
}  
}
public void ServiceMethod () {
System.out.println("服务的内部方法");
}

3.重写服务里的onBind方法,返回一个实现了IBinder接口的内部类对象(相当于服务的内部人员)。

public IBinder onBind(Intent intent) {
return new MyBinder();
}

接下来都是在Activity代码里写

4.写打开服务器的意图

Intent intent=new Intent(this,BindService.class);

并且定义一个全局的MyBinder mybinder;

5.写实现了ServiceConnection接口的内部类,实现ServiceConnection没有完成的方法。

6.在onServiceConnected方法里获取服务的内部人员

private class  Myconnection implements ServiceConnection{
//当服务连接成功时调用的方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通过IBinder service传递数据
mybinder=(MyBinder) service;//获取到服务的内部人员
}
//当服务连接失败时调用的方法
@Override
public void onServiceDisconnected(ComponentName name) {
}
}

7.通过bindService绑定服务:

//BIND_AUTO_CREATE=1,如果绑定服务的时候不存在,会自动创建

bindService(intent, new Myconnection(), BIND_AUTO_CREATE);

8.可以利用服务器内部人员的实例去调用自己的方法,间接的调用服务内部的方法;

mybinder.callMethodService();

利用代理模式屏蔽内部的相关细节,只通过代理来传递部分数据。这里的代理就是服务器的内部人员。

9.用接口隐藏服务内部不想暴露的方法

定义一个接口IService,里面定义几个方法,在服务的内部人员的类里实现这个接口里没有实现的方法,通过实现这些方法去间接调用服务内部的可以暴露的方法。这时可以把服务的内部人员定义成private,在其他的类里不能直接实例化这个服务内部人员,但是可以通过接口调用,服务内部的方法。

在原来的Activity代码里:

1.声明接口,Iservice mybinder;

2.在onServiceConnected方法里获取服务的内部人员已经实现的接口,mybinder=(Iservice)service;

六、

绑定服务的细节:

1.如果onbind方法返回值是null,onServiceConnected方法就不会被调用。

2.绑定的服务在系统设置界面,正在运行的条目里看不到。

3.如果调用者Activity退出了,绑定服务就自动跟着退出。所以要在Activity的onDestroy方法执行之前执行服务的unbindService

4.(ServiceConnection)解绑服务,这里的ServiceConnection必须和bindService用的ServiceConnection是同一个。如果连续绑定多次导致第一次绑定的ServiceConnection丢失,解绑的时候将会因为找不到第一次定义的ServiceConnection而产生异常

5.服务只能被解除绑定一次,多次解绑服务会抛出异常。

七、混合方式开启服务,服务的生命周期

startService------stopService

onCreate()--->onstartCommand()--->onstartCommand()--->onDestroy()

bindService------unbindService

onCreate()--->onbind()--->onUnbind()--->onDestroy()

bindService开启的服务将不会调用onstartcommand()方法。

为什么需要采用混合的方式开启服务?

startService服务长期后台运行,但是不可以调用服务里面的方法,不能和服务之间传递数据。

bindService可以调用服务的方法能和服务之间传递数据,但是不能长期后台运行。

混合方式开启服务可以保证服务长期后台运行,又可以调用服务里面的方法,也能和服务之间传递数据。

通过startService开启服务之后,再用bindService绑定服务,这时再用stopService停止服务就不能停止服务了,bindService必须用unbindService解除绑定之后才能销毁服务。

用startService开启服务之后,再用bindService绑定服务,并且没有用stopService停止服务过,再用unbindService解除绑定服务,这时只是解除了绑定服务并没有销毁服务。

推荐的步骤:

1.用startService开启服务----保证服务长期后台运行

2.再用bindService绑定服务----保证可以调用服务内部的方法,与服务进行数据交换

3.和已经绑定的服务完成数据交换。

4.再用unbindService解除绑定服务----服务没有停止还是可以长期后台运行。

(如果重写的unbind()方法的返回值是true,这时候再绑定服务会调用rebind()服务。)

5.如果要停止服务就调用stopService。

在服务解除绑定之后还可以间接调用服务内部的方法,因为它的引用还在Activity里被使用还没有被垃圾回收站回收掉。

八、

开一个应用就打开一个进程,进程之间彼此独立,内存空间彼此独立。

ipc(inter process communication进程间通信)可以实现不同进程之间通信。

1.把要通信的数据写在sd卡---安全性,效率比较低,不推荐

2.信号量,消息邮箱,消息队列,操作系统提供一块公共的内存空间-binder底层是内存驱动。不同的进程都可以在公共内存空间的相应区域读写数据,相对安全。

本地服务和远程服务:

本地服务:服务的代码在自己应用程序内部。

远程服务:服务的代码在另外一个应用程序的里面。

远程服务是被别的应用程序开启的所以服务代码里配置清单文件的时候要配置隐式意图,

绑定远程服务时,和本地服务相比,Activity和服务之间进行通信的接口拿不到,这时就需要用到:

aidl(android interface defination language安卓接口定义语言)

它的特点是满足两个进程之间,接口数据的交换(ipc--inter process communication进程间通信)

1.把服务的应用里原来定义的IService接口,后缀名java改为aidl,再把IService接口里面所有的修饰符public去掉,因为它本来默认的就已经是public的了。

2.在工程目录gen目录下会自动编译生成IService.java的接口文件

3.把服务里面原来定义的extends Binder implement IService的服务内部人员的类改为extends ISercice.Stub

4.在调用远程服务的应用程序里面新建一个包,包名和远程服务的包名一样,再把原来的接口文件即aidl文件复制到这个包下

5.在onServiceConnected代码里获取接口,iService=IService.Stub.asInterface(service);

远程服务的应用场景:

1.超级大公司,写出来的逻辑供别的程序员使用。

2.手机企业,手机定制厂商,提供一些方便的逻辑供程序员使用。

3.系统源码内置很多服务

查看系统源码:

有的时候可能直接用ctrl一步步点下去最终看到的是一个抽象方法,这时要想看到它的内部实现就在方法后面打断点,再查看方法抽象之前调用这个方法的对象具体在哪个包里,再去查看这个包的源码。

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值