zoukankan      html  css  js  c++  java
  • Android服务之混合方式开启服务

    引言
    前面介绍过了Android服务的两种开启方式:Start方式可以让服务在后台运行;bind方式能够调用到服务中的方法。
    在实际的开发工作中,有很多需求是:既要在后台能够长期运行,又要在服务中操作业务。那么就需要两种方式结合在一起,才能做到我们想要的结果。


    需求:模仿音乐后台播放案例,实现应用退出后,服务中依然可以在后台运行。

    代码如下
    AndroidManifest.xml 清单文件中配置service

     <service android:name=".service.music.MusicPlayService" />
    

    MusicPlayService.class 自定义一个服务类

    import android.app.Service;
    import android.content.Intent;
    import android.os.Binder;
    import android.os.IBinder;
    import android.os.SystemClock;
    import android.util.Log;
    /**
     * 创建一个服务:模拟后台播放音乐(以控制台打印Log代替)
     */
    public class MusicPlayService extends Service {
        //音乐播放状态(播放:true; 暂停:false)
        private boolean running = false;
    
        @Override
        public IBinder onBind(Intent intent) {
            return new MusicBindImpl();
        }
        @Override
        public void onCreate() {
            super.onCreate();
        }
        @Override
        public void onDestroy() {
            super.onDestroy();
        }
        //服务中的方法:播放音乐
        public void player() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    running = true;
                    while (true) {
                        if (running) {
                            SystemClock.sleep(500);
                            Log.e("music", "音乐播放中...");
                        }
                    }
                }
            }).start();
        }
        //服务中的方法:继续播放
        public void rePlayer() {
            player();
        }
        //服务中的方法:暂停音乐
        public void pause() {
            running = false;
        }
    
        /**
         * 创建中间帮助类对象
         */
        class MusicBindImpl extends Binder implements IMusicService {
            @Override
            public void callPlayer() {
                player();
            }
            @Override
            public void callRePlayer() {
                rePlayer();
            }
            @Override
            public void callPause() {
                pause();
            }
        }
    }
    

    IMusicService.interface 抽取一个接口封装方法

    /**
     * 定义一个接口,封装中间帮助类要实现的函数
     */
    public interface IMusicService {
        public void callPlayer();
        public void callRePlayer();
        public void callPause();
    }
    

    MusicServiceConnection.class 创建一个服务连接对象

    import android.content.ComponentName;
    import android.content.ServiceConnection;
    import android.os.IBinder;
    /**
     * 定义服务连接对象,接收中间帮助类对象
     */
    public class MusicServiceConnection implements ServiceConnection {
    
        private MusicPlayService.MusicBindImpl musicBind;
    
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取:服务绑定成功后,返回的中间帮助类对象
            this.musicBind = (MusicPlayService.MusicBindImpl) service;
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
        //暴露方法:获取中间帮助类对象
        public MusicPlayService.MusicBindImpl getMusicBindImpl(){
            return musicBind;
        }
    }
    

    MusicPlayActivity.class
    该类使用kotlin语言,模拟音乐 开、关、暂停、继续、退出 操作

    /**
     * 混合方式开启服务
     */
    class MusicPlayActivity : BaseActivity() {
        //意图,开启服务
        private var musicIntent: Intent? = null
        //服务连接对象
        private var msconn: MusicServiceConnection? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_music_play)
            startMusicService()
            tvPlayer.setOnClickListener {
                playerMethod()
            }
            tvPause.setOnClickListener {
                pauseMethod()
            }
            tvRePlayer.setOnClickListener {
                rePlayerMethod()
            }
            tvSignOut.setOnClickListener {
                signOutMethod()
            }
        }
        //开启服务
        private fun startMusicService() {
            //1. 使用start方式开启服务
            musicIntent = Intent(this, MusicPlayService::class.java)
            this.startService(musicIntent)
            //2. 通过bind方式绑定该服务(同一个Intent对象)
            if (msconn == null) {
                //保证ServiceConnection连接对象的唯一性
                msconn = MusicServiceConnection()
            }
            bindService(musicIntent, msconn, Context.BIND_AUTO_CREATE)
        }
        //播放
        private fun playerMethod() {
            msconn?.musicBindImpl?.callPlayer()
        }
        //暂停
        private fun pauseMethod() {
            msconn?.musicBindImpl?.callPause()
        }
        //继续
        private fun rePlayerMethod() {
            msconn?.musicBindImpl?.callRePlayer()
        }
        //退出(先暂停播放,在解绑,最后应用退出)
        private fun signOutMethod() {
            msconn?.musicBindImpl?.callPause()
            //3. 解绑服务并停止服务
            if (msconn != null) {
                this.unbindService(msconn)
                msconn = null
            }
            if (musicIntent != null) {
                stopService(musicIntent)
            }
            this.finish()
        }
        override fun onDestroy() {
            super.onDestroy()
            if (msconn != null) {
                this.unbindService(msconn)
                msconn = null
            }
            musicIntent = null
        }
    }
    

    代码流程补充说明:

    • 创建一个Service,定义具体的操作。并创建一个中间人对象(中间帮助类),由中间对象调用Service内的方法(内部类的使用)。
    • 创建Service连接对象,当Service绑定成功后,接收中间人对象。
    • 在Activity中开启服务,获取中间人对象,通过中间人对象执行Service内的方法
        1. 先以 start方式开启服务:。目的:让服务可以在后台运行。
        1. 再以 bind方式开启服务。目的:能够调用到服务中的方法。
        1. unbind 解绑服务。(解绑后切记把ServiceConnection对象置null,避免运行异常)
        1. stopService终止服务。(用户主动关闭服务执行该操作)

    PS:

    • 混合方式开启的服务虽然可以做到后台运行,但并不是不能被Kill掉,假如整个应用程序的进程被Kill掉了,那么服务就会结束。
    • 平常我们操作退出应用,应用的进程只是从前台进程变成了后台进程,并没有直接被kill掉,所以我们的服务还能够运行。
    • 如果想要服务即便是在应用进程被kill掉的情况下依然可以运行,那么就需要做"服务后台保活"的操作。

    这里先提供一个方案:在代码中定义两个Service,如:ServiceA,ServiceB,当ServiceA执行onDestroy时,让ServiceA开启ServiceB,当ServiceB执行onDestroy时,再让ServiceB开启ServiceA。这样两个服务相互开启,就会一直有一个服务在运行中。(具体的代码操作,后面我抽时间会写一个案例贴出了,大家一起探讨)

  • 相关阅读:
    2.Liunx 系统设置
    1.Liunx 文件管理
    Liunx 更新环境时用到的命令
    解决SSH Secure Shell 连接Liunx 有乱码情况。
    JMeter 性能测试基本过程及示例(4)
    在 macOS 中怎样获取当前文件夹的路径?
    Mac环境安装启动jmeter
    StringUtils工具类常用方法汇总1(判空、转换、移除、替换、反转)
    Json与Gson
    Quartz的基本使用之入门(2.3.0版本)
  • 原文地址:https://www.cnblogs.com/io1024/p/11568507.html
Copyright © 2011-2022 走看看