android 学习笔记9-服务 启动停止 调用服务方法 远程服务 AIDL进程通信

来源:互联网 发布:淘宝考试答案50题2016 编辑:程序博客网 时间:2024/05/22 01:39
1、服务的概念
    就是默默运行在后台的组件,可以理解为是没有前台的activity,适合用来运行不需要前台界面的代码,比如说下载文件
    服务可以被手动关闭,不会重启;但是如果被自动关闭,内存充足就会重启
    
    startService启动服务的生命周期
        onCreate-onStartCommand-onDestroy
    重复的调用startService会导致onStartCommand被重复调用,但是不会一直create
    
    
    注意:在内存充足的情况下,你退出应用程序,你的下载在子线程中还在继续执行,但是在内存不足的情况下就很容易进程被杀死,下载也就停止了,所以我们要在服务中运行
    
    
2、5个进程优先级
    1. 前台进程:拥有一个正在与用户交互的activity(onResume方法被调用)的进程
    2. 可见进程:拥有一个非前台,但是对用户可见的activity(onPause方法被调用)的进程
    3. 服务进程:拥有一个通过startService方法启动的服务的进程
    4. 后台进程:拥有一个后台activity(onStop方法被调用)的进程(比如你按home键退出到主页了)
    5. 空进程:没有拥有任何活动的应用组件的进程,也就是没有任何服务和activity在运行。(比如你按返回键退出了,但是进程不会被销毁,便于下一次启动更快,但是非常容易被杀死)
    
3、服务的启动与停止:系统杀死服务进程会自动重启,但是用户手动停止就不能启动了。
    Service和activity很类似,也是个上下文,也需要在清单文件中配置,也可以显示或隐式启动。
    但是服务在内存中只有一个实例,不像activity那样可以启动多个实例。
    
    a,定一个服务 MyService.java 继承Service。
    b,配置下清单文件:
        因为我们显示启动,不用配置intent-filter和action
        <service android:name="com.example.runservice.MyService"></service>
    
    代码演示:
    MainActivity.java
        public class MainActivity extends Activity {
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
            }


            public void start(View v){//绑定一个按钮事件,在xml文件中定义
                //启动服务
                Intent intent = new Intent(this, MyService.class);
                startService(intent);
            }
            public void stop(View v){
                //停止服务
                Intent intent = new Intent(this, MyService.class);
                stopService(intent);
            }
        }
        
    MyService.java
        public class MyService extends Service {


            @Override
            public IBinder onBind(Intent intent) {//暂时先不用,这个是绑定服务会调用
                System.out.println("bind");
                return null;
            }


            @Override
            public int onStartCommand(Intent intent, int flags, int startId) {//启动服务系统调用
                System.out.println("start");
                return super.onStartCommand(intent, flags, startId);
            }


            @Override
            public void onCreate() {//创建服务系统调用
                System.out.println("create");
                super.onCreate();
            }
            
            @Override
            public void onDestroy() {//停止服务系统调用
                System.out.println("destroy");
                super.onDestroy();
            }
        }
    
4、服务的案例-电话侦听
    电话状态:
    空闲 TelephonyManager.CALL_STATE_IDLE
    响铃 TelephonyManager.CALL_STATE_OFFHOOK
    接听 TelephonyManager.CALL_STATE_RINGING
    
    获取电话管理器,设置侦听
        TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
tm.listen(new MyPhoneStateListener(), PhoneStateListener.LISTEN_CALL_STATE);
    
    代码演示:
    MainActivity.java
        public class MainActivity extends Activity {


            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
            }


            public void click(View v){//点击按钮启动服务
                Intent intent = new Intent(this, RecorderService.class);
                startService(intent);
            }
            
        }
    
    RecorderService.java代码:
    public class RecorderService extends Service {
        private MediaRecorder recorder;//定义一个录音机
        @Override
        public IBinder onBind(Intent intent) {//暂时不用
            return null;
        }


        @Override
        public void onCreate() {//在创建服务的时候就开始监听电话
            super.onCreate();
            //获取电话管理器
            TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
            //监听电话状态
            tm.listen(new MyListener(), PhoneStateListener.LISTEN_CALL_STATE);
        }
        
        class MyListener 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;
                }
            }
        }
    }


    
    
5、服务两种启动方式:
    startService
        开始服务,会使进程变成为服务进程
        启动服务的activity和服务不再有任何关系
    bindService
        绑定服务不会使进程变成服务进程(那么为什么还要绑定服务呢?绑定服务可以让activity与sercice通信,可以调用服务里面的方法,我们可以混合启动服务就达到了可以在后台运行又可以通信的目的)
        绑定服务,是activity与服务建立连接,如果activity销毁了,服务也会被解绑并销毁,但是如果服务被销毁,activity不会被销毁
    绑定服务和解绑服务的生命周期方法:onCreate->onBind->onUnbind->onDestroy
    
    代码演示下两种启动:
    清单文件就不显示了,和之前的一样。
    MainActivity.java:
        public class MainActivity extends Activity {
            private MyConn conn;//自定义的一个ServiceConnection对象,绑定服务的时候需要传的一个参数


            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                
                conn = new MyConn();
            }




            public void start(View v){//界面按钮的绑定事件,在xml文件定义了
                Intent intent = new Intent(this, MyService.class);
                startService(intent);//显示启动
            }
            public void stop(View v){//界面按钮的绑定事件,在xml文件定义了
                Intent intent = new Intent(this, MyService.class);
                stopService(intent);//显示启动
            }
            public void bind(View v){//界面按钮的绑定事件,在xml文件定义了
                Intent intent = new Intent(this, MyService.class);
                //绑定服务
                //需要传一个ServiceConnection对象,因为ServiceConnection是一个接口,所以我们实现它,conn也可以使用匿名内部类。
                //但是我们解绑还要用,所以就不用,
                //BIND_AUTO_CREATE表示如果服务不存在,就创建服务
                bindService(intent, conn, BIND_AUTO_CREATE);
            }
            
            public void unbind(View v){//界面按钮的绑定事件,在xml文件定义了
                unbindService(conn);//解绑服务,记住解绑的一定要是conn这个对象,不然会报错的
            }
            
            //创建一个类,实现ServiceConnection接口
            class MyConn implements ServiceConnection{


                //到服务的连接被建立了,系统回调此方法
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    
                }


                //到服务的连接中断了,系统回调此方法
                @Override
                public void onServiceDisconnected(ComponentName name) {
                    
                }
            }
            
        }
            
    
    MyService.java//定义一个服务,重写下里面的一些回调方法
        public class MyService extends Service {
            @Override
            public IBinder onBind(Intent intent) {
                System.out.println("绑定");//绑定服务的时候调用
                return null;
            }


            @Override
            public boolean onUnbind(Intent intent) {
                System.out.println("解绑");
                return super.onUnbind(intent);
            }
            
            @Override
            public void onCreate() {
                System.out.println("创建");
                super.onCreate();
            }
            
            @Override
            public int onStartCommand(Intent intent, int flags, int startId) {
                System.out.println("开始");
                return super.onStartCommand(intent, flags, startId);
            }
            
            @Override
            public void onDestroy() {
                System.out.println("摧毁");
                super.onDestroy();
            }
        }


    
    特别注意:
    如果一个服务没有绑定,你去解绑的话,程序会崩溃的,报一个非法参数异常IllegalArgumentException
    
    
    
6、调用服务中的方法-通过Binder实现


    服务中有定义一个方法,前提是不是静态的,你在MainActivity中怎么调用,发现根本调不了,因为startService和bindService都没有返回值的。
    我们只能通过bindService,回调的onBind方法,里面会返回一个IBinder对象(类似一个中间人对象),可以通过Binder来调用,这就是bindService存在的意义。
    
    
    我们activity和service之间不能直接通信,需要有个中间人,IBinder就类似中间人
    代码演示:
    MainActivity.java
        public class MainActivity extends Activity {
            MyBinder = mb;
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                //服务是需要通过intent来实现的。
                Intent intent = new Intent(this, LeaderService.class);
                bindService(intent, new ServiceConnection() {//这个ServiceConnection参数是通过匿名内部类,因为我们后面不用解绑,所以没关系
                    
                    //连接因为异常而终止才会调用,正常的解绑是不会调用的
                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        
                    }
                    //onBind有返回值此方法才会调用,并不是说连接建立了就调用
                    //service:这个对象就是onBind返回的中间人对象
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        mb = (MyBinder)service;//强转下,因为service是onBind返回的中间人对象
                    }
                }, BIND_AUTO_CREATE);
            }
            
            public void click(View v){//点击按钮调用
                mb.qianXian();//这样就可以通过中间人MyBinder调用到服务中的serviceMethod方法了。
            }
        }
        
    LeaderService.java//定义的一个服务
        public class LeaderService extends Service {


            @Override
            public IBinder onBind(Intent intent) {
                // 返回中间人对象
                return new MyBinder();//我们发现IBinder是一个接口,肯定google给我们提供了它的实现类,ctrl+T发现有个Binder实现了这个接口
            }
    
            class MyBinder extends Binder{//我们定义一个中间类,继承下Binder类就行,Binder实现了IBinder这个接口
                //中间人
                public void qianXian(){
                    //调用服务中的方法(领导中方法)
                    serviceMethod();//java内部类可以直接访问外部类里面的方法
                }
            }
            
            public void serviceMethod(){//这个是服务里面的一个方法,就是我们activity想调用的方法
                System.out.println("成功调用服务中的方法");
            }
        }
    
    
    
7、服务中中间人方法抽取到接口,MyBinder类里面的方法
    为什么要抽到接口中呢?现在MyBinder里面只有一个serviceMethod方法,如果还有一些其它的方法,那么这个时候我们的activity中也可以调用了。
    为了不让activity调用,就把方法都抽到接口中(也可以私有方法,但是我们这里不这样)
    
    a,定义一个接口:
    PublicBusiness.java
        public interface PublicBusiness {
            void qianXian();//把对公业务抽取到这里
        }


    b,MyBinder类就实现接口
            class MyBinder extends Binder implements PublicBusiness{
                //中间人
                public void qianXian(){
                    serviceMethod();
                }
                
                public void takeSoap(){
                    System.out.println("不想activity调用的方法);
                }
            }
    c,在  MainActivity中就不强转为 MyBinder了,而是转为  PublicBusiness
        PublicBusiness pb;
        
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            pb = (PublicBusiness)service;//强转为接口(转为MyBinder,PublicBusiness都可以),那么就只能调用接口中实现的方法了,不能调用takeSoap了
        }
        
        public void click(View v){//点击按钮调用
            pb.qianXian();
        }
    
    
8、混合启动服务,实现音乐播放
    代码演示:
    a,MainActivity.java
        public class MainActivity extends Activity {


            ControllerInterface ci;//服务中抽取的接口类
            @Override
            protected void onCreate(Bundle savedInstanceState) {//应用启动的时候就启动服务
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
                
                Intent intent = new Intent(this, MusicService.class);
                //把进程变成服务进程
                startService(intent);
                //绑定服务获取中间人对象
                bindService(intent, new ServiceConnection() {
                    
                    @Override
                    public void onServiceDisconnected(ComponentName name) {
                        
                    }
                    @Override
                    public void onServiceConnected(ComponentName name, IBinder service) {
                        ci = (ControllerInterface) service;
                        
                    }
                }, BIND_AUTO_CREATE);
            }


            public void play(View v){//点击按钮播放
                ci.play();
            }
            public void pause(View v){//点击按钮停止播放
                ci.pause();
            }
            
        }


    b,MusicService.java//定义一个音乐播放服务
        public class MusicService extends Service {
            @Override
            public IBinder onBind(Intent intent) {
                return new MusicController();//返回一个IBinder
            }


            class MusicController extends Binder implements ControllerInterface{


                @Override
                public void play() {
                    MusicService.this.play();//this.play() 和play()都是调用自己会进入死循环,调用外部类的方法,也就是服务中的的播放方法
                }


                @Override
                public void pause() {
                    MusicService.this.pause();
                }
            }
            
            public void play(){
                System.out.println("播放音乐");
            }
            public void pause(){
                System.out.println("暂停音乐");
            }
        }
    
    c,ControllerInterface.java//抽调为接口
        public interface ControllerInterface {
            void play();
            void pause();
        }
    
    这样播放的框架就OK了
    温馨提示:混合启动就可以调用服务里面的播放方法,也是一个服务进程了,不容易被系统杀死
    
    
9、服务的分类
    本地服务:服务和启动它的组件在同一个进程
    远程服务:服务和启动它的组件不在同一个进程
    远程服务只能隐式启动,类似隐式启动Activity,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点
    
    启动远程服务首先需要那个服务所在的进程已经启动,不然启动不了。
    配置远程服务需要在清单文件中配置 action
    例如:
    a,配置下第1个工程清单文件
        <service android:name="com.example.remoteservice.RemoteService">
<intent-filter >
<action android:name="a.b.c"/>
</intent-filter>
</service>
    b,定义服务,和之前的代码一样的。
    
    c,在另外一个工程(第2个工程)里面启动这个工程的服务
        public void start(View v){
            Intent intent = new Intent();
            intent.setAction("a.b.c");
            startService(intent);
        }
        public void stop(View v){
            Intent intent = new Intent();
            intent.setAction("a.b.c");
            stopService(intent);
        }
    
    这样就OK了
    但是有个问题就是,我们怎么调用远程服务里面的方法呢?比如说上面音乐播放的play方法和7中的serviceMethod方法?
        public void play(){
            System.out.println("播放音乐");
        }
    
10、AIDL 进程间通信


    Android interface definition language 安卓接口定义语言,google专门给android写的
    
    应用场景:远程服务中的中间人对象,其他应用是拿不到的,那么在通过绑定服务获取中间人对象时,就无法强制转换,
    使用aidl,就可以在其他应用中拿到中间人类所实现的接口
    
    比如:我们在自己的进程中是不能强转service,这个时候就需要用到AIDL 拿到别人进程的实现的接口
        bindService(intent, new ServiceConnection() {
            
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                ci = (ControllerInterface) service;//在自己的进程中是不能强转service,因为ControllerInterface这个接口不是在自己进程中
            }
        }, BIND_AUTO_CREATE);
    
    远程服务修改的具体的步骤:
        修改远程服务的后缀名称
        1. 把接口文件的后缀名改成aidl,比如ControllerInterface.java修改为ControllerInterface.aidl
        2. aidl文件中所有东西都是public的,不需要也不能自己定义访问修饰符,如果自己定义了就会报一个语法错误
            注意:如果定义了一个aidl就会在gen目录下面自动生成一个java文件,例如ControllerInterface.java
        3. 中间人对象继承Stub,这个对象已经继承了Binder并实现了PublicBusiness接口,就类似一个新的中间人,就和7里面的MyBinder功能类似。
            Stub定义可以在gen文件夹中的ControllerInterface.java查看
            
        4.中间人对象就不要像下面这样写:
            class MyBinder extends Binder implements PublicBusiness{
                //中间人
                public void qianXian(){
                    serviceMethod();
                }
            }
                            
            要写成:
            class MyBinder extends Stub{
                //中间人
                public void qianXian(){
                    serviceMethod();
                }
            }
    
    我们自己的应用修改具体步骤:(我们调用远程服务)
        1. 把远程服务工程里面的aidl文件复制到自己项目,然后aidl所在的包名必须一致(我们先建一个和它一样的包名,然后在把aidl复制过来)
        2. 把我们项目获取到的中间人对象使用Stub.asInterface强转
        例如:
            ControllerInterface ci;//这个是在MainActivity中定义的
            //摘取其中一段关键修改代码
            bindService(intent, new ServiceConnection() {
                @Override
                public void onServiceDisconnected(ComponentName name) {
                }
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    ci = Stub.asInterface(service);//在自己的进程中是不能强转service,因为ControllerInterface这个接口不是在自己进程中,使用aidl就行了
                }
            }, BIND_AUTO_CREATE);
    
    
    
0 0
原创粉丝点击