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内的方法
- 先以 start方式开启服务:。目的:让服务可以在后台运行。
- 再以 bind方式开启服务。目的:能够调用到服务中的方法。
- unbind 解绑服务。(解绑后切记把ServiceConnection对象置null,避免运行异常)
- stopService终止服务。(用户主动关闭服务执行该操作)
PS:
- 混合方式开启的服务虽然可以做到后台运行,但并不是不能被Kill掉,假如整个应用程序的进程被Kill掉了,那么服务就会结束。
- 平常我们操作退出应用,应用的进程只是从前台进程变成了后台进程,并没有直接被kill掉,所以我们的服务还能够运行。
- 如果想要服务即便是在应用进程被kill掉的情况下依然可以运行,那么就需要做"服务后台保活"的操作。
这里先提供一个方案:在代码中定义两个Service,如:ServiceA,ServiceB,当ServiceA执行onDestroy时,让ServiceA开启ServiceB,当ServiceB执行onDestroy时,再让ServiceB开启ServiceA。这样两个服务相互开启,就会一直有一个服务在运行中。(具体的代码操作,后面我抽时间会写一个案例贴出了,大家一起探讨)
Android服务之混合方式开启服务的更多相关文章
- Android(java)学习笔记231:服务(service)之混合方式开启服务
1. 前面我们已经讲过可以使用两种方式开启服务 startService----stopService: oncreate() ---> onstartCommand() ---& ...
- Android(java)学习笔记174:服务(service)之混合方式开启服务
1. 前面我们已经讲过可以使用两种方式开启服务 startService----stopService: oncreate() ---> onstartCommand() ---& ...
- Android -- service的开启方式, start开启和绑定开启服务,调用服务的的方法, aidl调用远程服务
1. 概述 bindService() 绑定服务 可以得到服务的代理人对象,间接调用服务里面的方法. 绑定服务: 间接调用服务里面的方法. 如果调用者activity被销毁了, ...
- [android] 绑定方式开启服务&调用服务的方法
需求:后台开启一个唱歌服务,这个服务里面有个方法切换歌曲 新建一个SingService继承系统Service 重写onCreate()和onDestory()方法 填一个自定义的方法changeSi ...
- Android使用bindService作为中间人对象开启服务
Android使用bindService作为中间人对象开启服务 项目结构如下: MyService: package com.demo.secondservice; import android.ap ...
- Android入门:绑定本地服务
一.绑定服务介绍 前面文章中讲过一般的通过startService开启的服务,当访问者关闭时,服务仍然存在: 但是如果存在这样一种情况:访问者需要与服务进行通信,则我们需要将访问者与服务进行绑定: ...
- Android 程式开发:(廿二)服务 —— 22.1 自定义服务
服务,就是跑在后台的“程序”,不需要和用户进行交互.举个例子,当使用一款应用的时候,可能同时想在后台播放一些音乐.在这种情况下,后来播放音乐的代码不需要和用户进行交互,所以,它就可能被当成一个服务.当 ...
- Android ListView分页载入(服务端+android端)Demo
Android ListView分页载入功能 在实际开发中经经常使用到,是每一个开发人员必须掌握的内容,本Demo给出了服务端+Android端的两者的代码,并成功通过了測试. 服务端使用MyEcli ...
- start方式开启服务的特点&bindService 方式开启服务的特点
服务是在后台运行 可以理解成是没有界面的activity 定义四大组件的方式都是一样的 定义一个类继承Service start方式开启服务的特点 特点: (1)服务通 ...
随机推荐
- 如何用python进行邮件发送
使用Python调用邮件服务器发送邮件,使用的协议是SMTP(Simple Mail Transfer Protocol),下图为使用TCP/IP基于SMTP发送邮件的过程示意图: SMTP协议工作原 ...
- Python之基本数据类型概览
Python之基本数据类型概览 什么是数据类型? 每一门编程语言都有自己的数据类型,例如最常见的数字1,2,3.....,字符串'小明','age','&D8'...,这些都是数据类型中的某一 ...
- Hive的安装及配置
title: Hive的安装及配置 summary: 关键词:Hive ubuntu 安装和配置 Derby MySQL PostgreSQL 数据库连接 date: 2019-5-19 13:25 ...
- java四种引用及在LeakCanery中应用
java 四种引用 Java4种引用的级别由高到低依次为: StrongReference > SoftReference > WeakReference > PhantomRefe ...
- C++ 并发编程之互斥锁和条件变量的性能比较
介绍 本文以最简单生产者消费者模型,通过运行程序,观察该进程的cpu使用率,来对比使用互斥锁 和 互斥锁+条件变量的性能比较. 本例子的生产者消费者模型,1个生产者,5个消费者. 生产者线程往队列里放 ...
- javascript之操作数组方法
掌握如何操作数组,会让你的开发变得更加高效 1.栈和队列方法(以下四个方法都改变原数组) arr.push() //接受任意类型的参数,逐个添加到数组的末尾,并返回数组的长度 改变原数组 arr.po ...
- 初尝RabbitMQ消息队列
RabbitMQ 是什么? 消息中间件 作用? 用于分布式项目中的模块解耦 用法? 创建队列 创建消息工厂并设置 (生产者额外步骤 : 创建消息) 创建连接,通道 声明队列 生产者 : 发送消 ...
- JVM体系结构详解
每个Java开发人员都知道字节码将由JRE (Java运行时环境)执行.但是很多人不知道JRE是Java Virtual Machine(JVM)的实现,它分析字节码.解释代码并执行代码.作为开发者, ...
- Mysql分区实战
一,什么是数据库分区 前段时间写过一篇关于MySQL分表的的文章,下面来说一下什么是数据库分区,以mysql为例.mysql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面 ...
- 背包形动态规划 fjutoj2375 金明的预算方案
金明的预算方案 TimeLimit:1000MS MemoryLimit:128MB 64-bit integer IO format:%lld Problem Description 金明今天 ...