【0049】Android基础-36-Android-Service相关

【1】【注意事项】

【1.1】【非常重要】四大组件都是运行在线程当中的;

【1.2】获取打气筒的三种方法

 【1.3】通过bind方式开启服务  服务不能再设置页面里面找到  相当于是一个隐形的服务

【2】进程的概念

【2.1】应用的进程列表:在设备中运行的所有的进行都会在该列表中进行显示和操作;

 【2.2】Activity中的所有生命周期都是运行在主线程中的,除非再次开启线程;

 

【2.3】进程的生命周期:具有优先级,会先关闭空进程,依次向上关闭;

Android中的服务 也是在后台运行  可以理解成是在后台运行并且是没有界面的Activity
 
  (1)Foreground process   前台进程  用户正在交互  可以理解成相 当于 Activity执行onResume方法
  (2)Visible process     可视进程 用户没有在交互 但用户还一直能看得见页面 相当于Activity执行了onPause方法 
  (3)Service Process     服务进程  通过startService()开启了一个服务
  (4)Background process    后台进程  当前用户看不见页面 相当于Activity执行了onStop方法
  (5)Empty process     空进程

 

【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】第二种开启服务的方式

     (1)当点击按钮第一次开启服务 会执行服务的onCreate方法 和 onBind()方法
     (2) 当我第二次点击按钮在调用bindservice  服务没有响应 ,即不能进行多个服务的运行;
   **(3) 当activity销毁的时候服务也销毁  不求同时生但求同时死 --非常重要!!!

【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()方法的调用;

【修改源码】

(4)通过bind方式开启服务  服务不能再设置页面里面找到  相当于是一个隐形的服务

【bindService服务的特点】

(5)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     }  

 

posted @ 2017-10-31 11:30  OzTaking  阅读(383)  评论(0)    收藏  举报