温故而知新,可以为师矣

至美不过回首,往事重重,顾来时,荆棘漫漫,血浸途中。
今铜衣铁靴,再行来路,任荆棘漫漫,唯落绿叶残枝。
                             ----张风捷特烈 
复制代码

零、前言

在此之前希望你已经阅读了:Android 点将台:颜值担当 [-Activity-]
在此之前希望你已经阅读了:Android 点将台:外交官 [-Intent-]

1. 本文的知识点

1).Service 的简单介绍及使用
2).Service 的绑定服务实现音乐播放器(条)
3). 使用aidl实现其他 app 访问该 Service, 播放音乐


2.Service 总览

Service.png

类名:Service      父类:ContextWrapper      修饰:public abstract
实现的接口:[ComponentCallbacks2]
包名:android.app   依赖类个数:16
内部类/接口个数:0
源码行数:790       源码行数(除注释):171
属性个数:3       方法个数:21       public方法个数:20
复制代码

Service继承关系.png


一、Service 初步认识

1. 简述

Service 和 Activity 同属一家,一暗一明,Android 作为颜值担当,Service 做后台工作 (如图)
他不见天日,却要忠诚地执行任务,Service 这个类的本身非常小,裸码 171 行
是什么让它成为 "新手的噩梦",一个单词:Binder, 曾经让多少人闻风丧胆的首席杀手

as.png


2.Service 的开启与关闭

最简单的Service使用.png

2.1:Service 测试类
/**
 * 作者:张风捷特烈<br></br>
 * 时间:2019/1/17/017:21:30<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:Service测试
 */
class MusicService : Service() {
/**
 * 绑定Service
 * @param intent 意图
 * @<span class="hljs-built_in">return</span> IBinder对象
 */
override fun onBind(intent: Intent): IBinder? {
    Log.e(TAG, <span class="hljs-string">"onBind: "</span>)
    <span class="hljs-built_in">return</span> null
}

/**
 * 创建Service
 */
override fun <span class="hljs-function"><span class="hljs-title">onCreate</span></span>() {
    super.onCreate()
    Log.e(TAG, <span class="hljs-string">"onCreate: "</span>)
}

/**
 * 开始执行命令
 * @param intent 意图
 * @param flags 启动命令的额外数据
 * @param startId id
 * @<span class="hljs-built_in">return</span>
 */
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    Log.e(TAG, <span class="hljs-string">"onStartCommand: "</span>)
    Toast.makeText(this, <span class="hljs-string">"onStartCommand"</span>, Toast.LENGTH_SHORT).show()
    <span class="hljs-built_in">return</span> super.onStartCommand(intent, flags, startId)
}


/**
 * 解绑服务
 * @param intent 意图
 * @<span class="hljs-built_in">return</span>
 */
override fun onUnbind(intent: Intent): Boolean {
    Log.e(TAG, <span class="hljs-string">"onUnbind: 成功解绑"</span>)
    <span class="hljs-built_in">return</span> super.onUnbind(intent)
}

/**
 * 销毁服务
 */
override fun <span class="hljs-function"><span class="hljs-title">onDestroy</span></span>() {
    super.onDestroy()
    Log.e(TAG, <span class="hljs-string">"onDestroy: 销毁服务"</span>)
}

companion object {
    private val TAG = <span class="hljs-string">"MusicService"</span>
}

}

复制代码

2.2:ToastSActivity 测试类

就两个按钮,点一下

//开启服务
id_btn_start.setOnClickListener {
    toastIntent = Intent(this, MusicService::class.java)
    startService(toastIntent)
}
//销毁服务
id_btn_kill.setOnClickListener {
    stopService(toastIntent)
}
复制代码

2.3: 测试类结果

点一下开启会执行onCreateonStartCommand方法

点一下开启.png

多次点击开启,onCreate只会执行一次,onStartCommand方法每次都会执行

多次点击开启.png

点击开启与销毁

点击开启与销毁.png


3.Activity 与 Service 的数据传递

onStartCommand 中有 Intent,和 BroadcastReciver 的套路有点像

Service的数据传递.png

---->[ToastSActivity#onCreate]----------------------
id_btn_start.setOnClickListener {
    toastIntent = Intent(this, MusicService::class.java)
    toastIntent?.putExtra("toast_data", id_et_msg.text.toString())
    startService(toastIntent)
}

—->[MusicService#onStartCommand]———————-
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int
Log.e(TAG, "onStartCommand:")
val data = intent.getStringExtra(“toast_data”)
//data?:“NO MSG”表示如果 data 是空,就取“NO MSG”
Toast.makeText(this, data?:“NO MSG”, Toast.LENGTH_SHORT).show()
return super.onStartCommand(intent, flags, startId)
}

复制代码

4. 在另一个 App 中使用其他 app 的 Service

创建另一个 App, 进行测试 ActivityBroadcastReciverService是四大组件的三棵顶梁柱
Intent 可以根据组件包名及类名开启组件,ActivityBroadcastReciver可以,Service自然也可以,

创建另一个App.png

两个app.png

Service在其他应用中访问.png


局限性:

1.需要添加android:exported="true",否则会崩
<service android:name=".service.service.ToastService" android:exported="true"/> 

2. 大概一分钟后会自动销毁,自动销毁后再用就会崩…所以约等于无用

复制代码

异常.png

自动销毁.png


4. 关于隐式调用 Service

Android5.0+ 明确指出不能隐式调用:ContextImpl 的validateServiceIntent方法中

---->[ContextImpl#validateServiceIntent]---------------------------
private void validateServiceIntent(Intent service) {
    //包名、类名为空,即隐式调用,跑异常
    if (service.getComponent() == null && service.getPackage() == null) {
    //从LOLLIPOP(即5.0开始)
        if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
            IllegalArgumentException ex = new IllegalArgumentException(
                    "Service Intent must be explicit:" + service);
            throw ex;
        } else {
            Log.w(TAG, "Implicit intents with startService are not safe:" + service
                    + " " + Debug.getCallers(2, 3));
        }
    }
}
复制代码

隐式问题.png


二、绑定服务

前面的都是组件的日常,接下来才是 Service 的要点
为了不让本文看起来太 low, 写个布局吧 (效果摆出来了,可以仿着做。不嫌丑的话用 button 也可以)

T-M-001.png


1. 实现的效果

为了方便管理,这里写了一个 IPlayer 接口规定一下 MusicPlayer 的几个主要方法
暂时都是无返回值,无入参的方法,以后有需要再逐步完善

绑定服务.png

绑定服务.gif


2. 播放接口
/**
 * 作者:张风捷特烈<br></br>
 * 时间:2018/10/31 0031:23:32<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:播放接口
 */
interface IPlayer {
    fun create()// 诞生
fun start()// 开始

fun resume()// 复苏

fun stop()// 停止

fun pause()// 暂停

fun release()//死亡

}

复制代码

3. 播放的核心类
/**
 * 作者:张风捷特烈<br></br>
 * 时间:2019/1/17/017:21:57<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:播放核心类
 */
class MusicPlayer(private val mContext: Context) : Binder(), IPlayer {
    override fun create() {
        Toast.makeText(mContext, "诞生", Toast.LENGTH_SHORT).show()
    }
override fun <span class="hljs-function"><span class="hljs-title">start</span></span>() {
    Toast.makeText(mContext, <span class="hljs-string">"开始播放"</span>, Toast.LENGTH_SHORT).show()
}

override fun <span class="hljs-function"><span class="hljs-title">resume</span></span>() {
    Toast.makeText(mContext, <span class="hljs-string">"恢复播放"</span>, Toast.LENGTH_SHORT).show()

}

override fun <span class="hljs-function"><span class="hljs-title">stop</span></span>() {
    Toast.makeText(mContext, <span class="hljs-string">"停止播放"</span>, Toast.LENGTH_SHORT).show()

}

override fun <span class="hljs-function"><span class="hljs-title">pause</span></span>() {
    Toast.makeText(mContext, <span class="hljs-string">"暂停播放"</span>, Toast.LENGTH_SHORT).show()
}

override fun <span class="hljs-function"><span class="hljs-title">release</span></span>() {
    Toast.makeText(mContext, <span class="hljs-string">"销毁"</span>, Toast.LENGTH_SHORT).show()
}

}

复制代码

4. 播放的服务
/**
 * 作者:张风捷特烈<br></br>
 * 时间:2019/1/17/017:21:30<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:播放Service测试
 */
class MusicService : Service() {
override fun onBind(intent: Intent): IBinder? {
    Log.e(TAG, <span class="hljs-string">"onBind: "</span>)
    Toast.makeText(this, <span class="hljs-string">"Bind OK"</span>, Toast.LENGTH_SHORT).show()
    <span class="hljs-built_in">return</span> MusicPlayer(this)
}

override fun <span class="hljs-function"><span class="hljs-title">onCreate</span></span>() {
    super.onCreate()
    Log.e(TAG, <span class="hljs-string">"onCreate: "</span>)
}

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
    Log.e(TAG, <span class="hljs-string">"onStartCommand: "</span>)
    <span class="hljs-built_in">return</span> super.onStartCommand(intent, flags, startId)
}

override fun onUnbind(intent: Intent): Boolean {
    Toast.makeText(this, <span class="hljs-string">"onUnbind: 成功解绑"</span>, Toast.LENGTH_SHORT).show()
    Log.e(TAG, <span class="hljs-string">"onUnbind: 成功解绑"</span>)
    <span class="hljs-built_in">return</span> super.onUnbind(intent)
}

override fun <span class="hljs-function"><span class="hljs-title">onDestroy</span></span>() {
    super.onDestroy()
    Log.e(TAG, <span class="hljs-string">"onDestroy: 销毁服务"</span>)
}

companion object {
    private val TAG = <span class="hljs-string">"MusicService"</span>
}

}

复制代码

5.Activity 中的使用
/**
 * 绑定服务
 */
private fun bindMusicService() {
    musicIntent = Intent(this, MusicService::class.java)
    mConn = object : ServiceConnection {
        // 当连接成功时候调用
        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            mMusicPlayer = service as MusicPlayer
        }
        // 当连接断开时候调用
        override fun onServiceDisconnected(name: ComponentName) {
        }
    }
    //[2]绑定服务启动
    bindService(musicIntent, mConn, BIND_AUTO_CREATE);
}
复制代码

三、音乐播放条的简单实现

接下来实现一个播放条,麻雀虽小,五脏俱全,完善了一下 UI, 如下

添加进度条.png


1. 歌曲准备和修改接口

这里为了简洁些,直接用四个路径, 判断存在什么的自己完善 (非本文重点)
关于MediaPlayer 的相关知识详见这篇,这里就直接上代码了
在 create 时传入播放的列表路径字符串

准备四首音乐.png

/**
 * 作者:张风捷特烈<br></br>
 * 时间:2018/10/31 0031:23:32<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:播放接口
 */
interface IPlayer {
    fun create(musicList: ArrayList<String>)// 诞生
    fun start()// 开始
    fun stop()// 停止
    fun pause()// 暂停
    fun release()//死亡
    fun next()//下一曲
    fun prev()//上一曲
    fun isPlaying(): Boolean 是否播放
    fun seek(pre_100: Int)//拖动进度
}
复制代码

2.create 方法和 start 方法的实现

MusicActivity 中通过ServiceConnectiononServiceConnected方法回调IBinder对象
MusicPlayer对象传入 MusicActivity 中,对应的 UI 点击调用对应的方法即可

---->[MusicPlayer]--------------
private lateinit var mPlayer: MediaPlayer
private var isInitialized = false//是否已初始化

private var mCurrentPos = 0// 当前播放第几个音乐
private lateinit var mMusicList: ArrayList<String>// 当前播放第几个音乐

—->[MusicPlayer#create]————–
override fun create(musicList: ArrayList<String>) {
mMusicList = musicList
val file = File(musicList[mCurrentPos])
val uri = Uri.fromFile(file)
mPlayer = MediaPlayer.create(mContext, uri)
isInitialized = true
Log.e(TAG, “诞生”)
}

—->[MusicPlayer#start]————–
override fun start() {
if (!isInitialized && mPlayer.isPlaying) {
return
}
mPlayer.start();
Log.e(TAG, “开始播放”)
}

复制代码

这样歌曲就能播放了


3. 上一曲和下一曲的实现及自动播放下一曲
---->[MusicPlayer]--------------

override fun next() {
mCurrentPos++
judgePos()// 如果越界则置 0
changMusicByPos(mCurrentPos)
}

override fun prev() {
mCurrentPos–
judgePos()// 如果越界则置 0
changMusicByPos(mCurrentPos)
}

/**
* 越界处理
*/
private fun judgePos() {
if (mCurrentPos >= mMusicList.size) {
mCurrentPos = 0
}
if (mCurrentPos < 0) {
mCurrentPos = mMusicList.size - 1
}
}

/**
* 根据位置切歌
* @param pos 当前歌曲 id
*/
private fun changMusicByPos(pos: Int) {
mPlayer.reset()// 重置
mPlayer.setDataSource(mMusicList[pos])// 设置当前歌曲
mPlayer.prepare()// 准备
start()
Log.e(TAG, “当前播放歌曲 pos:$pos:, 路径:${mMusicList[pos]} )
}

—->[MusicPlayer#create]————–
mPlayer.setOnCompletionListener {
next()// 播放完成,进入下一曲
}

复制代码

播放的日志.png


4. 进度拖拽和监听处理

这里每隔一秒更新一下进度,通过 Timer 实现,当然实现方式有很多

---->[MusicPlayer]--------------

override fun seek(pre_100: Int) {
pause()
mPlayer.seekTo((pre_100 * mPlayer.duration / 100))
start()
}

—->[MusicPlayer#create]————–
mTimer = Timer()// 创建 Timer
mHandler = Handler()// 创建 Handler
mTimer.schedule(timerTask {
if (isPlaying()) {
val pos = mPlayer.currentPosition;
val duration = mPlayer.duration;
mHandler.post {
if (mOnSeekListener != null) {
mOnSeekListener.onSeek((pos.toFloat() / duration * 100).toInt());
}
}
}
}, 0, 1000)

//————设置进度监听———–
interface OnSeekListener {
fun onSeek(per_100: Int);
}
private lateinit var mOnSeekListener: OnSeekListener
fun setOnSeekListener(onSeekListener: OnSeekListener) {
mOnSeekListener = onSeekListener;
}

复制代码

5. 绑定服务的意义何在?

估计很多新手都有一个疑问,我直接在 Activity 中 new 一个 MediaPlayer 多好
为什么非要通过 Service 来绕一圈得到 MediaPlayer 对象呢?

cs.png

比如:一台服务器S上运行着一个游戏业务,一个客户端C连接到服务器便能够玩游戏  
没有人会想把服务器上的业务移植到客户端,如果这样就真的一人一区了

Service 相当于提供服务,此时 Activity 相当于客户端,通过 conn 连接服务
MediaPlayer(Binder 对象) 相当于核心业务,通过绑定获取服务,是典型的 client-server 模式
client-server 模式的特点是一个 Service 可以为多个客户端服务

client 可以通过 IBinder 接口获取服务业务的实例这里是 MediaPlayer(Binder 对象)
从而实现在 client 端直接调用服务业务 (MediaPlayer) 中的方法以实现灵活交互
但是现在只能在一个 app 里玩,如何让其他 app 也可以连接服务,这就要说到 aidl 了

还有很重要的一点:Service 存活力强,记得上次在 Activity 中 new MediaPlayer 来播放音乐
切切应用一会就停了。今天在 Service 里,玩了半天音乐也没停

复制代码

四、安卓接口定义语言aidl在 Service 中的使用

这个服务端有点弱,现在想办法让外部也能用它
不知道下图你里看出了什么,我看的挺兴奋,前几天看 framework 源码,感觉挺相似
你可以看一下ActivityManagerNative的源码和这里 AS 自动生成的,你会有所感触

aidl生成文件.png


1.aidl 文件的书写

还记得上面的 IPlayer 的接口吧,aidl 内容就是这个接口的方法
只不过书写的语法稍稍不同,下面是 IMusicPlayerService 的 aidl
写完后记得点小锤子,他会使用sdk\build-tools\28.0.3\aidl.exe生成代码

自动生成.png

// IMusicPlayerService.aidl
package com.toly1994.tolyservice;

// Declare any non-default types here with import statements

interface IMusicPlayerService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void stop();
void pause();
void start();
void prev();
void next();
void release();
boolean isPlaying();
void seek(int pre_100);
// 加in
void create(in List<String> filePaths);
}

复制代码

2. 自动生成的代码使用

本文只是说一下生成的 IMusicPlayerService 如何使用,下一篇将详细分析它 可以看出 IMusicPlayerService 中有一个内部类 Stub 继承自 Binder 还实现了IMusicPlayerService
刚才我们是自定义MusicPlayer继承Binder并实现IPlayer
现在有个现成的 IMusicPlayerService.Stub, 我们继承它就行了, 为避免看起来乱
新建了一个MusicPlayerServiceMusicPlayerStub,可以上面的方式图对比一下

aidl.png

---->[IMusicPlayerService$Stub]------------
public interface IMusicPlayerService extends android.os.IInterface{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
com.toly1994.tolyservice.IMusicPlayerService
复制代码

3.MusicPlayerStub 的实现 (Binder 对象)

实现上和上面的MusicPlayer一模一样,这里用 java 实现

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/23/023:17:11<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:MusicPlayerStub--Binder对象
 */
public class MusicPlayerStub extends IMusicPlayerService.Stub {
    private MediaPlayer mPlayer;
    private boolean isInitialized = false;//是否已初始化
    private int mCurrentPos = 0;//当前播放第几个音乐
    private List<String> mMusicList;//音乐列表
    private Context mContext;
    private Timer mTimer;
    private Handler mHandler;
public MusicPlayerStub(Context mContext) {
    this.mContext = mContext;
}

@Override
public void create(List&lt;String&gt; filePaths) throws RemoteException {
    mMusicList = filePaths;
    File file = new File(mMusicList.get(mCurrentPos));
    Uri uri = Uri.fromFile(file);
    mPlayer = MediaPlayer.create(mContext, uri);
    isInitialized = <span class="hljs-literal">true</span>;

    //构造函数中
    mTimer = new Timer();//创建Timer
    mHandler = new Handler();//创建Handler

    //开始方法中
    mTimer.schedule(new <span class="hljs-function"><span class="hljs-title">TimerTask</span></span>() {
        @Override
        public void <span class="hljs-function"><span class="hljs-title">run</span></span>() {
            <span class="hljs-keyword">if</span> (mPlayer.isPlaying()) {
                int pos = mPlayer.getCurrentPosition();
                int duration = mPlayer.getDuration();
                mHandler.post(() -&gt; {
                    <span class="hljs-keyword">if</span> (mOnSeekListener != null) {
                        mOnSeekListener.onSeek((int) (pos * 1.f / duration * 100));
                    }
                });
            }
        }
    }, 0, 1000);

    mPlayer.setOnCompletionListener(mp -&gt; {
        try {
            next();//播放完成,进入下一曲
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    });
}

@Override
public void start() throws RemoteException {
    <span class="hljs-keyword">if</span> (!isInitialized &amp;&amp; mPlayer.isPlaying()) {
        <span class="hljs-built_in">return</span>;
    }
    mPlayer.start();
}

@Override
public void stop() throws RemoteException {

}

@Override
public void pause() throws RemoteException {
    <span class="hljs-keyword">if</span> (mPlayer.isPlaying()) {
        mPlayer.pause();
    }
}

@Override
public void prev() throws RemoteException {
    mCurrentPos--;
    judgePos();//如果越界则置0
    changMusicByPos(mCurrentPos);
}

@Override
public void next() throws RemoteException {
    mCurrentPos++;
    judgePos();//如果越界则置0
    changMusicByPos(mCurrentPos);
}

@Override
public void release() throws RemoteException {

}

@Override
public boolean isPlaying() throws RemoteException {
    <span class="hljs-built_in">return</span> mPlayer.isPlaying();
}

@Override
public void seek(int pre_100) throws RemoteException {
    pause();
    mPlayer.seekTo((pre_100 * mPlayer.getDuration() / 100));
    start();
}

/**
 * 越界处理
 */
private void <span class="hljs-function"><span class="hljs-title">judgePos</span></span>() {
    <span class="hljs-keyword">if</span> (mCurrentPos &gt;= mMusicList.size()) {
        mCurrentPos = 0;
    }

    <span class="hljs-keyword">if</span> (mCurrentPos &lt; 0) {
        mCurrentPos = mMusicList.size() - 1;
    }
}

/**
 * 根据位置切歌
 *
 * @param pos 当前歌曲id
 */
private void changMusicByPos(int pos) {
    mPlayer.reset();//重置
    try {
        mPlayer.setDataSource(mMusicList.get(pos));//设置当前歌曲
        mPlayer.prepare();//准备
        start();
    } catch (IOException | RemoteException e) {
        e.printStackTrace();
    }
}


//------------设置进度监听-----------
public interface OnSeekListener {
    void onSeek(int per_100);
}

private OnSeekListener mOnSeekListener;

public void <span class="hljs-built_in">set</span>OnSeekListener(OnSeekListener onSeekListener) {
    mOnSeekListener = onSeekListener;
}

}

复制代码

4.MusicPlayerService中返回 MusicPlayerStub 对象

一般都把 MusicPlayerStub 作为 MusicPlayerService 的一个内部类
本质没有区别,为了和上面对应,看起来舒服些,我把 MusicPlayerStub 提到了外面

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/23/023:16:32<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:音乐播放服务idal版
 */
public class MusicPlayerService extends Service {
    private MusicPlayerStub musicPlayerStub;
@Override
public void <span class="hljs-function"><span class="hljs-title">onCreate</span></span>() {
    super.onCreate();
    ArrayList&lt;String&gt; musicList = new ArrayList&lt;&gt;();
    musicList.add(<span class="hljs-string">"/sdcard/toly/此生不换_青鸟飞鱼.aac"</span>);
    musicList.add(<span class="hljs-string">"/sdcard/toly/勇气-梁静茹-1772728608-1.mp3"</span>);
    musicList.add(<span class="hljs-string">"/sdcard/toly/草戒指_魏新雨.aac"</span>);
    musicList.add(<span class="hljs-string">"/sdcard/toly/郭静 - 下一个天亮 [mqms2].flac"</span>);

    musicPlayerStub = new MusicPlayerStub(this);
    try {
        musicPlayerStub.create(musicList);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    <span class="hljs-built_in">return</span> musicPlayerStub;
}

}

复制代码

5. 在本项目中的使用

如果只在本项目中用,将两个类换下名字就行了和刚才没本质区别

/**
 * 绑定服务
 */
private fun bindMusicService() {
    musicIntent = Intent(this, MusicPlayerService::class.java)
    mConn = object : ServiceConnection {
        // 当连接成功时候调用
        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            mMusicPlayer = service as MusicPlayerStub
            mMusicPlayer.setOnSeekListener {
                    per_100 -> id_pv_pre.setProgress(per_100) }
        }
        // 当连接断开时候调用
        override fun onServiceDisconnected(name: ComponentName) {
        }
    }
    //[2]绑定服务启动
    bindService(musicIntent, mConn, BIND_AUTO_CREATE);
}
复制代码

话说回来,搞了一大圈,aidl 的优势在哪里? 现在貌似还没看出来哪里厉害,接着看
在此之前先配置一下服务app/src/main/AndroidManifest.xml

<service android:name=".service.service.MusicPlayerService">
    <intent-filter>
        <action android:name="www.toly1994.com.music.player"></action>
    </intent-filter>
</service>
复制代码

五、基于aidl在另一个项目中使用别的项目 Service

这就是 aidl 的牛掰的地方,跨进程间通信,以及 Android 的系统级 Service 都基于此
下面进入另一个 app 里:anotherapp, 核心点就是获取 IMusicPlayerService 对象
注意一点:常识问题,在客户端连接服务端时,服务端要先打开...

aidl在另一个应用中使用.png

aidl绑定服务.png

class ServiceTestActivity : AppCompatActivity() {
    private var mConn: ServiceConnection? = null
    private lateinit var mMusicPlayer: IMusicPlayerService
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    <span class="hljs-built_in">set</span>ContentView(R.layout.ac_br)
    title=<span class="hljs-string">"另一个App"</span>
    <span class="hljs-built_in">bind</span>MusicService()
    id_btn_send.text=<span class="hljs-string">"播放音乐"</span>
    id_btn_send.setOnClickListener {
        mMusicPlayer.start()
    }
}

/**
 * 绑定服务
 */
private fun <span class="hljs-function"><span class="hljs-title">bindMusicService</span></span>() {
    val intent = Intent()
    //坑点:5.0以后要加 服务包名,不然报错
    intent.setPackage(<span class="hljs-string">"com.toly1994.tolyservice"</span>)
    intent.action = <span class="hljs-string">"www.toly1994.com.music.player"</span>
    mConn = object : ServiceConnection {
        // 当连接成功时候调用
        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            //核心点获取IMusicPlayerService对象
            mMusicPlayer = IMusicPlayerService.Stub.asInterface(service)
        }

        // 当连接断开时候调用
        override fun onServiceDisconnected(name: ComponentName) {
        }
    }
    //[2]绑定服务启动
    <span class="hljs-built_in">bind</span>Service(intent, mConn, BIND_AUTO_CREATE);
}

}

复制代码

当点击时音乐响起,一切就通了,如果你了解 client-server 模式,你应该明白这有多重要
framework 的众多 service 就是这个原理,所以不明白 aidl,framework 的代码看起来会很吃力
下一篇将会结合 framework,详细讨论 aidl 以及 Binder 的机制的第一层。


后记:捷文规范

1. 本文成长记录及勘误表
项目源码 日期 附录
V0.1-- 无 2018-1-23

发布名:Android点将台:绝命暗杀官[-Service-]
捷文链接:juejin.im/post/5c4a7e…

2. 更多关于我
笔名 QQ 微信
张风捷特烈 1981462002 zdl1994328

我的 github:github.com/toly1994328
我的简书:www.jianshu.com/u/e4e52c116…
我的简书:www.jianshu.com/u/e4e52c116…
个人网站:www.toly1994.com

3. 声明

1---- 本文由张风捷特烈原创, 转载请注明
2---- 欢迎广大编程爱好者共同交流
3---- 个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
4---- 看到这里,我在此感谢你的喜欢与支持

icon_wx_200.png

  • Android

    开放手机联盟(一个由 30 多家科技公司和手机公司组成的团体)已开发出 Android,Android 是第一个完整、开放、免费的手机平台。

    293 引用
感谢    赞同    分享    收藏    关注    反对    举报    ...