Service介绍
- 运行于后台,没有前台界面的组件,用于运行需要在后台运行的代码。
- 在Activity中开启线程下载数据,下载代码是在Activity中的。当点击返回键退出app时,如果内存充足,进程依然存在,那么下载代码会运行下去直到下载完毕;如果内存不足,进程会被杀死,线程自然而然会停止,下载也就中断了,故要将下载代码,播放音视频代码放在服务当中。
进程优先级
空 进 程
- 没有任何活动的应用组件(Activity和Service,Receiver常驻内存,但是被删了等有广播来时,依然会再次启动,provider不常驻内存)
后台进程
- 拥有一个对于用户不可见的Activity(onStop()调用时)
服务进程:拥有一个通过startService启动的服务(下载操作在这里,如果内存不足,会被杀死,如果内存又变得充足,服务会继续启动)
可见进程
- 拥有一个不在前台但是对用于依然可见的Activity(onPause()调用时)
- 拥有一个与可见activity绑定的服务(此服务如果为本地服务,那么(1)肯定满足;如果此服务为远程服务,那么本地绑定远程服务,本地的activity可见,远程服务的activity不可见,按道理来说,远程的服务为后台进程或空进程,其实不是,应该为可见进程,满足(2)的定义)
前台进程
- 拥有一个正在于用户交互的activity(onResume()调用时)
- 拥有一个与正在于用户交互的activity绑定的服务(远程服务为前台进程)
- 拥有一个运行在前台的服务(服务调用了startForeground())
- 拥有一个正在执行其中一个生命周期方法的服务(onCreate()、onStart()、onDestroy(),提高其短暂的优先级用来保证不被杀死)
- 拥有一个正在onReceiver方法的广播接收者(短暂提高保证优先级)
服务的启动与停止
启动服务
public void start(){
Intent intent = new Intent(this,MyService.class);
startService(intent);
}
停止服务
public void stop(){
Intent intent = new Intent(this,MyService.class);
startService(intent);
}~~~
##### 自定义服务
public class MyService extends Service{
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
# startService的生命周期
第一次调用startService:onCreate() - onStartCommand() - onDestory()
多次调用只会多次调用onStartCommand(),onCreate()只会调用一次
多次调用startServie:onCreate() - onStartCommand() - ... - onStartCommand() - onDestory()
public class MyService extends Service{
@Override
public void onCreate() {
super.onCreate();
}
* 此方法被废弃:Activity中onStart()方法表示界面可见,但是没有焦点,所以服务中这个方法不适合
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
* 此方法完全代替onStart(),内部调用onStart()方法
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
*** onStartCommand()使用时,返回的是一个int整型,这个整型有四个返回值:
* START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的
intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一
定会调用onStartCommand(Intent,int,int)方法。如果再次期间没有任何启动命令被传到service,
那么参数Intent将为null.
* START_TO_STICKY:"非粘性的".使用这个返回值时,如果在执行完onStartCommand()后,服务被
异常kill掉,系统不会自动重启该服务.
* START_REDELIVER_INTENT:重传Intent.使用这个返回值时,如果在执行完onStartCommand()后,
服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入.
* STATE_STICKY_COMPATIBILITY:start_sticky的兼容版本,但不保证服务被kill后一定能重启.
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}~~~
实例一:电话录音机
### 添加权限
* 读取通话状态权限
<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" />
public class MyService extends Service {
private MediaRecorder recorder;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
* Actvity和Service都是Context的子类,可直接使用方法
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
* 监听通话状态
* arg0:监听通话的listen,不是接口的原因是方法太多
* arg1:即时不实现方法,但是其他的回调依然存在,所以此方法控制只回调哪些方法
tm.listen(new PhoneStateListener(),PhoneStateListener.LISTEN_CALL_STATE);
}
class PhoneListener 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;
}
}
}
}~~~
# 服务的绑定与解绑
public class TestActivity extends Activity {
private MyBindConnection conn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
conn = new MyBindConnection();
}
public void bind(){
Intent intent = new Intent(this,MyBindService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
public void unBind(){
unbindService(conn);
}
class MyBindConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
* 服务建立连接并且service中onBind()有返回值时此方法才会调用
}
@Override
public void onServiceDisconnected(ComponentName name) {
* 服务的连接被异常中断时调用,unbind()调用时不调用此回调方法,因为是正常中断
}
}
}~~~
绑定服务的生命周期
public class MyBindService extends Service{
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}~~~
# startService和bindService的区别
##### startService
- 通过startService启动的服务,该服务所在的进程会变成服务进程。
- 服务与启动它的Activity不再有任何联系。
##### bindService
- 通过bindService绑定的服务,进程优先级不变。与APP的进程保持一样。
- 绑定的服务与启动它的Activity是同生共死的,Activity销毁了,服务也要销毁,不过服务销毁了,Activity不会销毁。因此,绑定服务是不能做下载操作的。
#####为什么需要bindService?
- 在service中如果有一个方法,那么通过startService则无法调用此方法,因为通过startService启动的服务是没有返回对象的,因而产生了一个bindService。
# 调用bindService中方法的一般逻辑
- 对公的牵线业务
public interface PublicWork {
void qianxian();
}
public class BossService extends Service{
@Override
public IBinder onBind(Intent intent) {
* 返回一个工作者对象
return new Worker();
}
* onBind()需要返回一个IBinder对象,因此Worker类需要实现IBinder接口
* IBinder接口中方法很多,而Binder是IBinder的实现类,因此去继承Binder即可
* 实现对公业务的接口,避免暴露了自己私有的方法
class Worker extends Binder implements PublicWork{
* 对公的业务
@Override
public void qianxian(){
* 中间人的方法去实现Service的方法,然后在Activity中调用中间人的方法
banzheng();
}
* 中间人的私有业务
public void privateWork(){}
}
* Boss服务中有一个办证的方法
public void banzheng(){
System.out.println("办证成功");
}
}
public class TestActivity extends Activity {
* 中间人
private Worker worker;
* 对公业务类
private PublicWork publicWork;
private MyBindConnection conn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
conn = new MyBindConnection();
}
public void bind(){
Intent intent = new Intent(this,BossService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
public void unBind(){
unbindService(conn);
}
class MyBindConnection implements ServiceConnection{
* service就是onBind()返回的中间人
* 服务建立连接并且service中onBind()有返回值时此方法才会调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
* 1.获取中间人的实例
worker = (Worker) service;
* 2.获取中间人的对公业务
publicWork = (PublicWork) service;
}
* 服务的连接被异常中断时调用,unbind()调用时不调用此回调方法,因为是正常中断
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
* 调用中间人的方法来实现服务自己的方法
public void click(){
* 如果使用中间人的类,则会暴露中间人的私有方法
worker.qianxian();
worker.privateWork();
* 通过接口来避免中间人的所有方法被调用
publicWork.qianxian();
* 对公的类不可调用中间人的私有方法
publicWork.privateWork();
}
}~~~
实例二:模拟音乐播放器 -- 混合启动服务
public interface ControllerInterface {
void play();
void pause();
}
public class MusicService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new MusicController();
}
class MusicController extends Binder implements ControllerInterface{
@Override
public void play() {
MusicService.this.play();
}
@Override
public void pause() {
MusicService.this.pause();
}
}
public void play(){
System.out.println("音乐在播放");
}
public void pause(){
System.out.println("音乐已暂停");
}
}
public class MusicActivity extends Activity{
private ControllerInterface controller;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this,MusicService.class);
* service的混合启动,让Music拥有startService和bindService的共同特点
* 混合启动的生命周期为:onCreate - onStart() - onBind() - onUnBind() - onDestory()
startService(intent);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
controller = (ControllerInterface) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, BIND_AUTO_CREATE);
}
public void playClick(){
controller.play();
}
public void pauseClick(){
controller.pause();
}
}~~~
# 启动远程服务
- 启动自己项目中的Servie叫做本地服务;启动另外一个项目中的Service叫做远程服务。
- 启动远程服务需要使用隐式启动,使用setAction("a.b.c")
- 远程服务需要指定action
<service android:name=".RemoteService">
<intent-filter>
<action android:name="a.b.c">
</intent-filter>
</Service>
- 启动远程服务
Intent intent = new Intent();
intent.setAction("a.b.c");
startService(intent);~~~
远程服务之进程间通信(AIDL)
AIDL:Android interface definition language 安卓接口定义语言
实现AIDL的详细步骤:
- 将远程服务中接口文件的后缀名 .java改为 .aidl,这时在gen目录中自动生成 .java文件
- 远程服务中aidl文件中所有的东西都是public,不能自己定义访问修饰符。
- 远程服务中中间人对象只需要继承Stub即可,这个Stub对象已经继承了Binder并实现了PublickBusiness接口。
- 本地服务中,新建一个与远程服务中aidl的所在包一样的包,然后将这个aidl文件复制到这个包下。(aidl的包名不能改变)。
- 本地服务中用新方式进行强转。
class MyBindConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
publicWork = (PublicWork) service;
* 使用新方式强转
publicWork = Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
网友评论