Service

作者: 沅兮 | 来源:发表于2017-07-11 14:52 被阅读0次

    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) {
                
        }
    }
    

    相关文章

      网友评论

        本文标题:Service

        本文链接:https://www.haomeiwen.com/subject/dacmhxtx.html