【0049】Android基础-36-Android-Service相关
【1】【注意事项】
【1.1】【非常重要】四大组件都是运行在线程当中的;
【1.2】获取打气筒的三种方法
【1.3】通过bind方式开启服务 服务不能再设置页面里面找到 相当于是一个隐形的服务
【2】进程的概念
【2.1】应用的进程列表:在设备中运行的所有的进行都会在该列表中进行显示和操作;
【2.2】Activity中的所有生命周期都是运行在主线程中的,除非再次开启线程;
【2.3】进程的生命周期:具有优先级,会先关闭空进程,依次向上关闭;
【3】startservice方式开启服务特点
start方式开启服务的特点
服务是在后台运行 可以理解成是没有界面的activity
定义四大组件的方式都是一样的
定义一个类继承Service
特点:
(1)服务通过startservice方式开启 第一次点击按钮开启服务 会执行服务的onCreate 和 onStart方法
(2)如果第二次开始在点击按钮开启服务 服务之后执行onStartt方法
(3)服务被开启后 会在设置页面里面的 running里面找得到这个服务
***(4)startservice 方式开启服务 服务就会在后台长期运行 直到用户手工停止 或者调用StopService方法 服务才会被销毁
【3.1】Activity与Service的关系
由下面的图可以看出:
Activity继承与ContextThemeWrapper,ContextThemeWrapper继承与ContexWrapper;
而Service直接继承与ContexWrapper;
因此:Service是Activity的大爷儿;
【3.2】实例
【3.2.1】新建类继承Service类
【1_服务入门/src/com/itheima/service/FirstService.java】源码
1 package com.itheima.service; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.IBinder; 6 7 /** 8 * 定义服务 需要在清单文件里面配置 9 * @author jhon 10 * 11 */ 12 public class FirstService extends Service { 13 14 @Override 15 public IBinder onBind(Intent intent) { 16 17 System.out.println("onBind"); 18 return null; 19 } 20 21 //当服务第一次被开启的时候调用 22 @Override 23 public void onCreate() { 24 System.out.println("onCreate"); 25 super.onCreate(); 26 } 27 28 @Override 29 public int onStartCommand(Intent intent, int flags, int startId) { 30 System.out.println("onStartCommand"); 31 return super.onStartCommand(intent, flags, startId); 32 } 33 34 //服务销毁的时候执行 35 @Override 36 public void onDestroy() { 37 System.out.println("onDestroy"); 38 super.onDestroy(); 39 } 40 41 }
【3.2.2】配置清单列表
【3.2.3】服务的运行
1 /** 2 * activity 你大爷 是 服务 3 * 4 * @author jhon 5 * 6 */ 7 public class MainActivity extends Activity { 8 9 private MyConn conn; 10 11 12 @Override 13 protected void onCreate(Bundle savedInstanceState) { 14 super.onCreate(savedInstanceState); 15 setContentView(R.layout.activity_main); 16 17 } 18 19 // 点击按钮开启服务 20 public void click1(View v) { 21 //参数:第一个参数:上下文;第二个参数:定义了服务的class文件; 22 Intent intent = new Intent(this, FirstService.class); 23 // 开启服务 24 startService(intent); 25 26 }
【3.2.3】服务的停止:
【方式1】点击stop之后停止了进程;
【方式2】通过代码关闭
1 // 点击按钮停止服务 2 public void click2(View v) { 3 Intent intent = new Intent(this, FirstService.class); 4 stopService(intent); 5 }
【4】电话监听器:当来电时会自动录音;
【4.1】 (1)先定义一个服务 服务用来监听电话状态 开启服务
(2)在服务的oncreate方法里面实例化TelephoneManager类的实例
(3)注册一个电话监听
【第一个参数的书写】
【第二个参数】
【4.2】增加权限 ![]()
【4.3】框架的搭建:测试电话接听是否有反应
【/3_电话监听器/src/com/itheima/phonelistener/PhoneService.java源码】
1 package com.itheima.phonelistener; 2 3 import java.io.IOException; 4 5 import android.app.Service; 6 import android.content.Intent; 7 import android.media.MediaRecorder; 8 import android.os.IBinder; 9 import android.telephony.PhoneStateListener; 10 import android.telephony.TelephonyManager; 11 import android.view.LayoutInflater; 12 import android.view.View; 13 14 public class PhoneService extends Service { 15 16 17 private MediaRecorder recorder; 18 @Override 19 public IBinder onBind(Intent intent) { 20 21 return null; 22 } 23 24 //服务第一次被开启的时候调用 25 @Override 26 public void onCreate() { 27 28 //[1]获取电话管理者的实例 29 TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); 30 31 //[2]注册一个电话状态的监听 32 tm.listen(new MyPhoneStateListenrer(), PhoneStateListener.LISTEN_CALL_STATE); 33 34 35 super.onCreate(); 36 } 37 38 39 //监听电话的状态 40 private class MyPhoneStateListenrer extends PhoneStateListener{ 41 //当设备的状态发生改变的时候调用 42 43 44 @Override 45 public void onCallStateChanged(int state, String incomingNumber) { 46 47 //[3]具体判断一下 电话是处于什么状态 48 switch (state) { 49 case TelephonyManager.CALL_STATE_IDLE: //空闲状态 50 51 break; 52 53 case TelephonyManager.CALL_STATE_OFFHOOK://接听状态 54 55 System.out.println("开始录"); 56 57 break; 58 59 case TelephonyManager.CALL_STATE_RINGING: //响铃状态 60 61 System.out.println("我准备一个录音机出来 "); 62 63 break; 64 } 65 66 super.onCallStateChanged(state, incomingNumber); 67 } 68 69 } 70 71 72 //当服务销毁的时候执行 73 @Override 74 public void onDestroy() { 75 super.onDestroy(); 76 } 77 78 }
【4.4】增加录音功能
【4.5】增加权限
【4.6】测试生成录音文件
【4.7】该服务做隐藏:写为开机启动运行在后台
【功能增加】上面写出的程序需要在按下开启服务的按钮之后才可以执行,达不到后台录音的功能,在开机时直接运行在后台
开机启动后在后台运行;
【5】使用服务注册特殊的广播接收者
(1)创建我们要注册的广播接受者
(2)创建一个服务 用来注册广播接收者 代码如下
1 package com.itheima.registerbroadcast; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.content.IntentFilter; 6 import android.os.IBinder; 7 8 public class ScreenService extends Service { 9 10 private ScreenReceiver receiver; 11 12 @Override 13 public IBinder onBind(Intent intent) { 14 return null; 15 } 16 17 //当服务第一次启动的时候调用 18 @Override 19 public void onCreate() { 20 21 //在这个方法里面注册广播接收者 22 //[1]获取ScreenReceiver实例 23 receiver = new ScreenReceiver(); 24 25 //[2]创建IntentFilter对象 26 IntentFilter filter = new IntentFilter(); 27 //[3]添加注册的事件 28 filter.addAction("android.intent.action.SCREEN_OFF"); 29 filter.addAction("android.intent.action.SCREEN_ON"); 30 //[4]通过代码的方式注册 31 registerReceiver(receiver, filter); 32 33 super.onCreate(); 34 } 35 36 //当服务销毁的时候调用 37 @Override 38 public void onDestroy() { 39 40 //当actvivity销毁的时候 取消注册广播接收者 41 unregisterReceiver(receiver); 42 43 44 super.onDestroy(); 45 } 46 47 }
(3)一定记得配置service
(4)开启服务
【/4_使用服务注册特殊的广播接收者/src/com/itheima/registerbroadcast/MainActivity.java】源码
1 package com.itheima.registerbroadcast; 2 3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.content.Intent; 6 import android.content.IntentFilter; 7 import android.view.Menu; 8 9 public class MainActivity extends Activity { 10 11 @Override 12 protected void onCreate(Bundle savedInstanceState) { 13 super.onCreate(savedInstanceState); 14 setContentView(R.layout.activity_main); 15 16 //开启服务 17 18 Intent intent = new Intent(this,ScreenService.class); 19 //开启服务 20 startService(intent); 21 22 } 23 24 25 26 27 }
【6】第二种开启服务的方式
【6.1】源码
1 // 点击按钮通过 bindservice 方式去开启服务 2 public void click3(View v) { 3 Intent intent = new Intent(this,FirstService.class); 4 5 //连接到服务FirstService 6 7 conn = new MyConn(); 8 System.out.println("bind----"+conn); 9 bindService(intent,conn, BIND_AUTO_CREATE); 10 11 12 } 13 14 //点击按钮 取消绑定服务 15 public void click4(View v) { 16 System.out.println("unbind----"+conn); 17 unbindService(conn); 18 } 19 20 //当activity销毁的时候调用 21 @Override 22 protected void onDestroy() { 23 //当activity销毁的时候 取消绑定服务 24 unbindService(conn); 25 26 super.onDestroy(); 27 } 28 29 30 31 32 //用来监听服务的状态 33 private class MyConn implements ServiceConnection{ 34 35 //连接成功后调用 36 @Override 37 public void onServiceConnected(ComponentName name, IBinder service) { 38 39 } 40 41 //失去连接调用 42 @Override 43 public void onServiceDisconnected(ComponentName name) { 44 45 } 46 47 } 48 49 }
【6.2】bind方式服务的开启
【6.3】bind方式服务的关闭
【红色日志】在点击返回退出按钮后,出现了红色日志,但是:OnDestory()方法被调用了,说明服务Service销毁了;
【报红的原因】在之前见到过,原因是需要在onDestory()中书写unbindService()方法的调用;
【修改源码】
【bindService服务的特点】
【7】Andorid为什么要引入bindService开启服务的方式?
【答】目的为了调用服务里面的方法, 通过【实例】讲解
【7.1】实例:功能在MainActivity中调用定义的服务中(TestService)的方法;
【出现的问题】空指针异常;
【说明】空指针说的是上下文的空指针;
【1】首先被调用的方法中的Toast类需要使用到Context上下文;
【2】 在普通类是没有Android设备的上下文环境的;因此上下文会出现空指针;
【3】解决该问题就是使用bind方法;
【7.2】改进:使用bindService方法调用 中间需要一个代理类:IBinder;
【7.2.1】IBinder类:接口;
【使用一个接口的方法】可以实现该接口,或者继承已经实现了该类的类;
【7.2.2】通过bindservice方式调用服务方法里面的过程
(1)定义一个服务 服务里面有一个方法需要Activity调用

(2)定义一个中间人对象(IBinder) 继承Binder;

(3)在onbind方法里面把我们定义的中间人对象返回

(4)在Activity的oncreate 方法里面调用bindservice 目的是为来获取我们定义的中间人对象

(4.1)获取中间人对象

(5)拿到中间人对象后就可以间接的调用到服务里面的方法

【8】通过接口方式调用服务里面的方法
【8.1】存在的问题:在中间人中的方法的权限不可以分类;
例如:
【8.2】通过接口方式调用服务里面的方法
接口可以隐藏代码内部的细节 让程序员暴露自己只想暴露的方法
(6)定义一个接口 把想暴露的方法都定义在接口里面
(7)我们定义的中间人对象 实现我们定义的接口
(8)在获取我们定义的中间人对象方式变了
【9】百度音乐盒服务部分的案例
【9.1】 百度音乐盒框架
需求:我既想让服务在后台长期运行(需要start开启) 又想调用服务里面的方法 (需要binde方式开启)
混合方式开启服务 :注意存在开启服务具有先后顺序的问题,否则会出现乱套的情况;
(1)先调用startService()方法 保证服务在后台长期运行
(2)调用bindservice()目的获取我们定义的中间人对象 调用服务里面的方法
(3)unbindservice() 看这时候服务会不会销毁(服务仍然在后台运行)
(4)最后调用stopservice() 停止服务
【9.2】源码
【/7_百度音乐盒/src/com/itheima/baidumusic/MusicService.java】源码
1 package com.itheima.baidumusic; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.Binder; 6 import android.os.IBinder; 7 8 //音乐播放服务 9 public class MusicService extends Service { 10 11 //[2]把我们定义的中间人对象 返回 12 @Override 13 public IBinder onBind(Intent intent) { 14 return new MyBinder(); 15 } 16 //服务第一次开启的是调用 17 @Override 18 public void onCreate() { 19 super.onCreate(); 20 } 21 22 23 //当服务销毁的时候调用 24 @Override 25 public void onDestroy() { 26 super.onDestroy(); 27 } 28 29 //专门用来播放音乐的 30 public void playMusic(){ 31 System.out.println("音乐播放了"); 32 //TODO 等讲完多媒体 把该功能完善 33 34 } 35 36 //音乐暂停了 37 public void pauseMusic(){ 38 System.out.println("音乐暂停了"); 39 } 40 41 //音乐继续播放的方法 42 public void rePlayMusic(){ 43 System.out.println("音乐继续播放了"); 44 } 45 46 //[1]定义一个中间人对象(IBinder) 47 private class MyBinder extends Binder implements Iservice{ 48 49 //调用播放音乐的方法 50 @Override 51 public void callPlayMusic() { 52 53 playMusic(); 54 } 55 56 //调用暂停音乐的方法 57 @Override 58 public void callPauseMusic() { 59 60 pauseMusic(); 61 } 62 63 //调用继续播放的方法 64 @Override 65 public void callrePlayMusic() { 66 67 rePlayMusic(); 68 } 69 70 } 71 72 73 74 }
【/7_百度音乐盒/src/com/itheima/baidumusic/MainActivity.java】源码
1 package com.itheima.baidumusic; 2 3 import android.os.Bundle; 4 import android.os.IBinder; 5 import android.app.Activity; 6 import android.content.ComponentName; 7 import android.content.Intent; 8 import android.content.ServiceConnection; 9 import android.view.Menu; 10 import android.view.View; 11 import android.view.ViewGroup; 12 import android.widget.BaseAdapter; 13 14 public class MainActivity extends Activity { 15 16 private Iservice iservice; // 这个就是我们定义的中间人对象 17 private MyConn conn; 18 19 @Override 20 protected void onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.activity_main); 23 24 //[0]先调用startservice 方法开启服务 保证服务在后台长期运行 25 Intent intent = new Intent(this, MusicService.class); 26 startService(intent); 27 28 // [1]调用bindservice 目的是为了获取我们定义的中间人对象 29 conn = new MyConn(); 30 // 连接MusicService 服务 获取我们定义的中间人对象 31 bindService(intent, conn, BIND_AUTO_CREATE); 32 33 } 34 35 // 点击按钮 进行 音乐播放 36 public void click1(View v) { 37 38 // 调用播放音乐的方法 39 iservice.callPlayMusic(); 40 } 41 42 // 暂停音乐 43 public void click2(View v) { 44 45 // 调用暂停音乐的方法 46 iservice.callPauseMusic(); 47 } 48 49 // 继续播放 50 public void click3(View v) { 51 52 // 调用继续播放 53 iservice.callrePlayMusic(); 54 } 55 56 // 当Activity销毁的时候调用 57 @Override 58 protected void onDestroy() { 59 // 在Activity销毁的时候 取消绑定服务 60 unbindService(conn); 61 62 super.onDestroy(); 63 } 64 65 private class MyConn implements ServiceConnection { 66 67 // 当连接成功时候调用 68 @Override 69 public void onServiceConnected(ComponentName name, IBinder service) { 70 // 获取我们定义的中间人对象 71 iservice = (Iservice) service; 72 73 } 74 75 @Override 76 public void onServiceDisconnected(ComponentName name) { 77 78 } 79 80 } 81 82 }
【10】AIDL
【10.1】aidl介绍
(1)远程服务 运行在其他应用里面的服务
(2)本地服务 运行在自己应用里面的服务
(3)进行进程间通信 IPC
(4)aidl Android interface Defination Language Android接口定义语言 专门是用来解决进程间通信的
【10.2】【核心操作】本地进程和远程进程中服务的关联:通过隐式调用跨进程的服务
【远程服务】通过intent-filter进行过滤;
【本地服务】通过设置setAction()设置远程服务要捕捉的action;
【10.3】远程服务中的AIDL的使用
【步骤1】
【生成的文件的本质工作】
【10.4】【本地接口的调用】
【原则】规定:在本地和远程中,同时存在相同的包名和相同的aidl文件,则认为在两个应用同时运行的时候可以直接调用相同的代理接口;
【1】在本地服务中新建一个与远程服务相同的包含有aidl文件相同的包名;
【2】同时将aidl文件拷贝一份给本地服务中,同时自动生成aidl.java文件;
【10.5】本地服务使用中间对象
【10.6】aidl 实现步骤和之前调用服务里面的方法的区别
(1)先把Iservice.java文件变成aidl文件
(2)adil 不认识public 把public 给我去掉
(3)会自动生成一个Stub类 实现ipc
(4)我们定义的中间人对象 直接继承stub
(5)想要保证2个应用程序的aidl文件是同一个 要求aidl文件所在包名相同
(6)获取中间人对象Stub.asinterface(Ibinder obj)
【10.9】源码存在位置:
https://github.com/wsxingjun/20_AIDLDemo_Client.git
https://github.com/wsxingjun/20_AIDLDemo.git
【11】Android Studio中创建AIDL Service
用startService,bindService创建的服务只能在本应用程序内访问,如果要使得本程序的服务能够被其他应用程序访问,这时候就要使用远程过程调用(Remote Procedure Call,RPC)方式来实现,安卓定义了一种接口定义语言Android Interface Definition Language,简称AIDL。今天记录一下如何在Android Studio下创建AIDL Serivce。
建立ADIL Service的步骤比建立普通Service要多一些,主要有:
1、创建AIDL文件,在这里面定义远程接口。
2、生成Java接口文件。
3、建立一个Service的子类,并且记得在AndroidManifest.xml文件中配置。
在客户端调用ADIL Servie:
1、拷贝服务器端的AIDL文件,并生成Java接口文件。
2、用BindService来调用Service,与调用普通Serivce相类似,只是获取IBinder的方式有点不一样。
下面结合例子来看看,编程环境是Android Studio v1.0.1,本例子基于《疯狂Android讲义(第二版)》10.2节的例子。 本AIDL Service只有两个方法,返回一个字符串和double型数据。
【11.1】建立AIDL文件
在项目名称上右键>NEW>AIDL>AIDL File,这样就创建了一个ADIL文件,命名为ICat
文件内容为:
1 package com.sysu.aidlclient.aidlclient; 2 3 interface ICat { 4 String getColor(); 5 double getWeight(); 6 }
然后菜单中选择Build>Rebulid Project,这样就生成了java接口文件,地址在项目文件夹/app/build/generated/aidl里面。
【11.2】编写Service子类:
这一步跟建立普通的Serivce步骤一样,同样右键>New>Service,这样会创建一个Service的子类以及在AndroidManiFest中添加Service的的内容,主要要添加action标签并且定义android:name。它的onBind方法返回的IBinder对象要是ICat.Stub的子类的实例,ICat.Stub这个类基本不用去管它,是自动生成的。
1 public class AidlService extends Service { 2 3 private CatBinder catBinder; 4 //此处要继承Stub,实现ICat和IBinder接口 5 <span style="color:#ff0000;"> public class CatBinder extends ICat.Stub 6 { 7 @Override 8 public String getColor() throws RemoteException { 9 return "get from remote service"; 10 } 11 12 @Override 13 public double getWeight() throws RemoteException { 14 return 999.9; 15 } 16 }</span> 17 18 @Override 19 public void onCreate() { 20 super.onCreate(); 21 <span style="color:#ff0000;">catBinder = new CatBinder();</span> 22 23 @Override 24 public IBinder onBind(Intent intent) { 25 <span style="color:#ff0000;">return catBinder;</span> 26 } 27 28 @Override 29 public void onDestroy() { 30 System.out.println("remote service destroy"); 31 } 32 }
然后将应用部署到手机上。
【11.3】客户端调用
首先要拷贝AIDL文件,这里要保证文件的内容一模一样,包括包的名称,比如本例子中服务器端AIDL文件所在包的名称是com.sysu.aidlclient.aidlcilent,如何做到这一点,先新建一个项目,然后在:项目文件夹/app/src/main目录下建立一个aidl文件夹,与java文件夹同级,在Android Studio中就可以看到这个目录,在这个目录上右键New>Package,建立一个com.sysu.aidlclient.aidlclient的包,再将aidl文件拷进去。这样才能保证生成的java接口文件完全一样,否则会提示找不到接口。
在MainActivity中调用,用bindService方法,记得要定义一个显式的intent,如红色代码获取Service的IBinder的代理
1 private ServiceConnection conn = new ServiceConnection() { 2 @Override 3 public void onServiceConnected(ComponentName name, IBinder service) { 4 <span style="color:#ff0000;">catService = ICat.Stub.asInterface(service);</span> 5 } 6 7 @Override 8 public void onServiceDisconnected(ComponentName name) { 9 catService = null; 10 11 } 12 }; 13 14 @Override 15 protected void onCreate(Bundle savedInstanceState) { 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.activity_main); 18 19 Intent intent = new Intent(); 20 intent.setAction("com.sysu.aidlclient.action.AIDL_SERVICE"); 21 //this is important 22 intent.setPackage("com.sysu.aidlclient.aidlclient"); 23 bindService(intent,conn, Service.BIND_AUTO_CREATE); 24 } 25 26 之后就可以用catService来调用远程service中的方法了 27 [javascript] view plain copy 28 public void getRemoteService(View view) 29 { 30 try 31 { 32 Toast.makeText(this,"the color and weight is: "+catService.getColor()+":", 33 Toast.LENGTH_LONG).show(); 34 System.out.println(catService.getColor()+"----"+catService.getWeight()); 35 }catch (RemoteException e) 36 { 37 e.printStackTrace(); 38 } 39 System.out.println("button click"); 40 }