All of my articles are collected in this article, and will be updated in time: Knowledge is long, the road of the walker will end in no words (My Programming Road)


Zero, preface,

1. Knowledge points of this article
1).Service 'simple' introduction and use '2).Service' binding Service 'implementation' music player (bar) '3). Use 'aidl' to enable other apps to access the Service and play musicCopy the code

2. The Service overview

[ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] 790 number of source lines (except comments):171 Number of attributes: 3 number of methods :21 number of public methods :20Copy the code


1. Preliminary understanding of Service

1. A brief introduction

The Service class itself is very small, with 171 lines of code naked. What makes it a “beginner’s nightmare” is one word: Binder, the chief killer who terrorized so many people


2. Enable or disable the Service

2.1: the Service test class
/ * * * the author: packer jet fierce < br > < br > * time: 2019/1/17/017: < br > and < / br > * E-mail: 1981462002 @qq.com < br > < br > * note: */ class MusicService:Service() {/** * Bind Service * @param IntentreturnIBinder */ Override Fun onBind(Intent: Intent): IBinder? { Log.e(TAG,"onBind: ")
        returnNull} /** * Create Service */ Override funonCreate() {
        super.onCreate()
        Log.e(TAG, "onCreate: ")} /** * Start the command * @param Intent * @param Flags Extra data for the start command * @param startId ID * @return
     */
    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Log.e(TAG, "onStartCommand: ")
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
        returnSuper.onstartcommand (Intent, flags, startId)} /** * Unbind service * @param intent * @return
     */
    override fun onUnbind(intent: Intent): Boolean {
        Log.e(TAG, "OnUnbind: unbind successfully")
        returnSuper.onunbind (Intent)} /** * Destroy service */ Override funonDestroy() {
        super.onDestroy()
        Log.e(TAG, "OnDestroy: Destroy service")
    }

    companion object {
        private val TAG = "MusicService"}}Copy the code

2.2: ToastSActivity test class

Just two buttons. Click it

Id_btn_start. setOnClickListener {toastIntent = Intent(this, MusicService::class.java) startService(toastIntent)} // Id_bTN_kill.setonClickListener {stopService(toastIntent)}Copy the code

2.3: Test results

Clicking On Start executes the onCreate and onStartCommand methods

If you click on it multiple times,onCreate will only execute once, onStartCommand will execute every time

Click open and Destroy


3. Data transfer between Activity and Service

OnStartCommand contains Intent, which is similar to BroadcastReciver

---->[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"If data is null, take"NO MSG"Toast.makeText(this, data? :"NO MSG", Toast.LENGTH_SHORT).show()
    return super.onStartCommand(intent, flags, startId)
}
Copy the code

4. Use the Service of another App in another App

Activity, BroadcastReciver, and Service are the three main components of an Intent. An Intent can start a component based on its package name and class name. We can also use Service,


Limitations:

Android :exported="true"<service android:name=".service.service.ToastService" android:exported="true"/> 2. It will self-destruct after about a minute. So it's about uselessCopy the code


4. About implicitly calling a Service

Android5.0+ explicitly states that it cannot implicitly call :ContextImpl in the validateServiceIntent method

---->[ContextImpl#validateServiceIntent]---------------------------Private void validateServiceIntent(Intent Service) {// Package name and class name are nullif(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)); }}}Copy the code


2. Binding service

In order not to make this article look too low, write a layout (the effect is displayed, you can imitate it. Button, if not ugly.)


1. Effects achieved

For ease of administration, there is an IPlayer interface that states that the main methods of MusicPlayer are no return, no input methods for the time being, and will need to be improved in the future


2. Play interface
/** * Author: Zhang Fengjie Teilie <br></br> * Time: 2018/10/31 0031:23:32<br></br> * Email: [email protected]<br></br> * Description: */ interface IPlayer {fun create()// Create fun start()// Start fun Resume ()// Restore fun stop()// Stop fun pause()// Pause fun Release ()} / / deathCopy the code

3. Core classes to play
/ * * * the author: packer jet fierce < br > < br > * time: 2019/1/17/017:21:57 < br > < br > * E-mail: 1981462002 @qq.com < br > < br > * note: */ Class MusicPlayer(Private Val mContext: Context) : Binder(), IPlayer {Override funcreate() {
        Toast.makeText(mContext, "Born", Toast.LENGTH_SHORT).show()
    }

    override fun start() {
        Toast.makeText(mContext, "Start playing.", Toast.LENGTH_SHORT).show()
    }

    override fun resume() {
        Toast.makeText(mContext, "Resume play", Toast.LENGTH_SHORT).show()

    }

    override fun stop() {
        Toast.makeText(mContext, "Stop playing.", Toast.LENGTH_SHORT).show()

    }

    override fun pause() {
        Toast.makeText(mContext, "Pause play", Toast.LENGTH_SHORT).show()
    }

    override fun release() {
        Toast.makeText(mContext, "Destroyed", Toast.LENGTH_SHORT).show()
    }
}
Copy the code

4. Play services
/ * * * the author: packer jet fierce < br > < br > * time: 2019/1/17/017: < br > and < / br > * E-mail: 1981462002 @qq.com < br > < br > * note: */ class MusicService:Service() {

    override fun onBind(intent: Intent): IBinder? {
        Log.e(TAG, "onBind: ")
        Toast.makeText(this, "Bind OK", Toast.LENGTH_SHORT).show()
        return MusicPlayer(this)
    }

    override fun onCreate() {
        super.onCreate()
        Log.e(TAG, "onCreate: ")
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Log.e(TAG, "onStartCommand: ")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onUnbind(intent: Intent): Boolean {
        Toast.makeText(this, "OnUnbind: unbind successfully", Toast.LENGTH_SHORT).show()
        Log.e(TAG, "OnUnbind: unbind successfully")
        return super.onUnbind(intent)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e(TAG, "OnDestroy: Destroy service")
    }

    companion object {
        private val TAG = "MusicService"}}Copy the code

5. Use in activities
/** * Bind service */ private funbindMusicService() { musicIntent = Intent(this, MusicService::class.java) mConn = object : ServiceConnection {// Call override fun onServiceConnected(name: ComponentName, service: connected); IBinder) {mMusicPlayer = service as MusicPlayer} override fun onServiceDisconnected(name: ComponentName (ComponentName) {}} //[2] The binding service is startedbindService(musicIntent, mConn, BIND_AUTO_CREATE);
}
Copy the code

Three, music playback bar simple implementation

The next implementation of a play bar, sparrow is small, the five internal organs, perfect the UI, as follows


1. Song preparation and modification interface

So just for the sake of simplicity, I’m just going to use the four paths, and I’m going to tell you what’s going on here. If you know more about MediaPlayer, I’m just going to go straight to the code and I’m going to pass in the playlist path string when I create it, okay

/** * Author: Zhang Fengjie Teilie <br></br> * Time: 2018/10/31 0031:23:32<br></br> * Email: [email protected]<br></br> * Description: */ interface IPlayer {fun create(musicList: ArrayList<String>)// Create fun start()// Start fun stop()// Stop fun Pause ()// Pause fun release()// Die fun Next ()// Next fun Prev ()// Last song fun isPlaying(): Boolean whether to play fun seek(pre_100: Int)// Drag progress}Copy the code

2. Implementation of create method and start method

A MusicActivity passes a MusicPlayer object to its IBinder object by calling its OnServiceconnection’s onServiceConnected method. The corresponding UI clicks to call the corresponding method

---->[MusicPlayer]--------------
private lateinit var mPlayer: MediaPlayer
private var isInitialized = falsePrivate var mCurrentPos = 0 private lateinit var mMusicList: ArrayList<String>// What music is currently playing ---->[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, "Born")
}

---->[MusicPlayer#start]--------------
override fun start() {
    if(! isInitialized && mPlayer.isPlaying) {return
    }
    mPlayer.start();
    Log.e(TAG, "Start playing.")}Copy the code

So the song can play


3. The realization of the last song and the next song and auto play the next song
---->[MusicPlayer]--------------

override fun next() {mCurrentPos++ judgePos()// changMusicByPos(mCurrentPos)} Override funprev() {mCurrentPos - judgePos () / / if the crossing the s changMusicByPos (mCurrentPos)} / cross-border deal with * * * * / private funjudgePos() {
    if (mCurrentPos >= mMusicList.size) {
        mCurrentPos = 0
    }
    if(mCurrentPos < 0) {mCurrentPos = mmusicList.size - 1}} /** * changMusicByPos(pos: Int) {mplayer.setdatasource (mMusicList[pos])// set mplayer.setdatasource (mMusicList[pos])"Currently playing song POS:$pos: the path:${mMusicList[pos]}" )
}

---->[MusicPlayer#create]--------------MPlayer. SetOnCompletionListener {next () / / play is completed, the next song}Copy the code


4. Progress drag and listen processing

I’m going to update it every second, and I’m going to do it with a Timer, but there’s a lot of ways to do it

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

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

---->[MusicPlayer#create]--------------MTimer = Timer()// Create Timer mHandler = Handler()// Create 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) / / -- -- -- -- -- -- -- -- -- -- -- -- set the schedule to monitor -- -- -- -- -- -- -- -- -- -- - interface OnSeekListener {fun onSeek (per_100: Int); } private lateinit var mOnSeekListener: OnSeekListener funsetOnSeekListener(onSeekListener: OnSeekListener) {
    mOnSeekListener = onSeekListener;
}
Copy the code

5. What’s the point of binding services?

I think a lot of people who are new to the Activity have a question, why do I have to go through the Service and go around and get the MediaPlayer object?

Such as: A server S is running a game business, a client C connected to the server can play the game, no one would want to port the server business to the client, if this is really a one-person zone, the Service is equivalent to providing a Service, at this time the Activity is equivalent to the client, The Service MediaPlayer(Binder object) is equivalent to the core business through the Conn connection. It's a typical client-server mode and the client-server mode is a Service that can serve multiple clients and the client can get an instance of the Service through the IBinder interface in this case the MediaPlayer(Binder object) But now you can only play in one app. How can other apps connect to the service? That's where AIDL comes in. The Service is very active. Remember the last time I tried to play music in my Activity with new MediaPlayer. The app stopped after a while. Today in the Service, the music didn't stop for a long timeCopy the code

4. Android Interface Definition LanguageaidlUse in a Service

This server is a little weak, now try to make it available externally. I don’t know what you can see in the picture below, I’m very excited about it. A few days ago, I looked at the framework source code, it felt very similar


1. Write the AIDL file

Remember the IPlayer interface up there, the aiDL content is just the methods of this interface with a slightly different syntax, and here’s the AIDL for the IMusicPlayerService and when you’re done with that little hammer, He will generate the code using SDK \build-tools\28.0.3\ aidL.exe

// 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 inAIDL. */ void stop(); void pause(); void start(); void prev(); void next(); void release(); boolean isPlaying(); void seek(int pre_100); / / addin
    void create(in List<String> filePaths);
}
Copy the code

2. Automatically generated code to use

This article will only show you how to use the generated IMusicPlayerService, We’ll look at it in more detail in the next article but you can see that there’s an inner class in the IMusicPlayerService that Stub inherits from Binder and implements IMusicPlayerService and we just custom MusicPlayer inherits from Binder and implements IPlayer Now, there is a ready-made IMusicPlayerService Stub, we inherit it, to avoid look messy Has built a MusicPlayerService and MusicPlayerStub can figure compare the above ways

---->[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.IMusicPlayerServiceCopy the code

3.MusicPlayerStub Implementation (Binder object)

The implementation is exactly the same as MusicPlayer above, but it’s implemented in Java

/** * Author: Zhang Feng Jieteilie <br/> * Time: 2019/1/23/023:17:11<br/> * Email: [email protected]<br/> * Description: MusicPlayerStub--Binder object */ Public Class MusicPlayerStub extends IMusicPlayerService.Stub {private MediaPlayer mPlayer; private boolean isInitialized =false; Private int mCurrentPos = 0; private int mCurrentPos = 0; Private List<String> mMusicList; 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<String> filePaths) throws RemoteException { mMusicList = filePaths; File file = new File(mMusicList.get(mCurrentPos)); Uri uri = Uri.fromFile(file); mPlayer = MediaPlayer.create(mContext, uri); isInitialized =true; MTimer = new Timer(); // Create Timer mHandler = new Handler(); // Create Handler // start method mtimer.schedule (new)TimerTask() {
            @Override
            public void run() {
                if (mPlayer.isPlaying()) {
                    int pos = mPlayer.getCurrentPosition();
                    int duration = mPlayer.getDuration();
                    mHandler.post(() -> {
                        if(mOnSeekListener ! = null) { mOnSeekListener.onSeek((int) (pos * 1.f / duration * 100)); }}); }, 0, 1000); mPlayer.setOnCompletionListener(mp -> { try { next(); } catch (RemoteException e) {e.printStackTrace(); }}); } @Override public void start() throws RemoteException {if(! isInitialized && mPlayer.isPlaying()) {return;
        }
        mPlayer.start();
    }

    @Override
    public void stop() throws RemoteException {

    }

    @Override
    public void pause() throws RemoteException {
        if(mPlayer.isPlaying()) { mPlayer.pause(); } } @Override public void prev() throws RemoteException { mCurrentPos--; judgePos(); ChangMusicByPos (mCurrentPos); } @Override public void next() throws RemoteException { mCurrentPos++; judgePos(); ChangMusicByPos (mCurrentPos); } @Override public void release() throws RemoteException { } @Override public boolean isPlaying() throws RemoteException {returnmPlayer.isPlaying(); } @Override public void seek(int pre_100) throws RemoteException { pause(); mPlayer.seekTo((pre_100 * mPlayer.getDuration() / 100)); start(); } /** * Override */ private voidjudgePos() {
        if (mCurrentPos >= mMusicList.size()) {
            mCurrentPos = 0;
        }

        if(mCurrentPos < 0) { mCurrentPos = mMusicList.size() - 1; }} /** * @param pos */ private void changMusicByPos(int pos) {mplayer.reset (); // Reset try {mplayer.setdatasource (mmusicList.get (pos)); Mplayer.prepare (); / / ready to start (); } catch (IOException | RemoteException e) { e.printStackTrace(); }} / / -- -- -- -- -- -- -- -- -- -- -- -- set the schedule to monitor -- -- -- -- -- -- -- -- -- -- - public interface OnSeekListener {void onSeek (int per_100); } private OnSeekListener mOnSeekListener; public voidsetOnSeekListener(OnSeekListener onSeekListener) { mOnSeekListener = onSeekListener; }}Copy the code

4.MusicPlayerServiceReturns the MusicPlayerStub object

There is generally no difference between using MusicPlayerStub as an inner class of MusicPlayerService, but to match the above and make it look more comfortable, I put MusicPlayerStub on the outside

/** * Author: Zhang Feng Jiete Lie <br/> * Time: 2019/1/23/023:16:32<br/> * Email: [email protected]<br/> * Description: Public Class MusicPlayerService extends Service {private MusicPlayerStub MusicPlayerStub; @Override public voidonCreate() {
        super.onCreate();
        ArrayList<String> musicList = new ArrayList<>();
        musicList.add("/sdcard/toly/ this life is not changed _ bluebird flying fish.aAC");
        musicList.add("/sdcard/ Toly/Courage - Jingru Leong -1772728608-1.mp3");
        musicList.add("/sdcard/ TOLy/Grass ring _ Weixinyu.aac");
        musicList.add("/sdcard/toly/ Guo Jing-The next morning [mqms2].flac");

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        returnmusicPlayerStub; }}Copy the code

5. Use in this project

If you’re only using it in this project, you can just change the names of the two classes and it won’t be any different

/** * Bind service */ private funbindMusicService() { musicIntent = Intent(this, MusicPlayerService::class.java) mConn = object : ServiceConnection {// Call override fun onServiceConnected(name: ComponentName, service: connected); IBinder) { mMusicPlayer = service as MusicPlayerStub mMusicPlayer.setOnSeekListener { per_100 -> Id_pv_pre.setprogress (per_100)}} Override fun onServiceDisconnected(name: ComponentName (ComponentName) {}} //[2] The binding service is startedbindService(musicIntent, mConn, BIND_AUTO_CREATE);
}
Copy the code

So, what’s the advantage of AIDL? Now seems haven’t seen where severe, then In this service before you configure the app/SRC/main/AndroidManifest. The XML

<service android:name=".service.service.MusicPlayerService">
    <intent-filter>
        <action android:name="www.toly1994.com.music.player"></action>
    </intent-filter>
</service>
Copy the code

Fifth, based onaidlUse another project Service in another project

This is where aidl is great, cross-process communication, and Android’s system-level services are all based on this to get into anotherapp: anotherapp. The core point is to get the IMusicPlayerService object. Common sense problem, when the client connects to the server, the server must first open…

class ServiceTestActivity : AppCompatActivity() {
    private var mConn: ServiceConnection? = null
    private lateinit var mMusicPlayer: IMusicPlayerService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.ac_br)
        title="Another App"
        bindMusicService()
        id_btn_send.text="Play music"Id_btn_send.setonclicklistener {mmusicPlayer.start ()}} /** * Bind service */ private funbindMusicService() {val intent = intent (); // Intent.setpackage (); // Intent.setpackage ();"com.toly1994.tolyservice")
        intent.action = "www.toly1994.com.music.player"MConn = object: ServiceConnection {// Call override fun onServiceConnected(name: ComponentName, service: connected) IBinder) {/ / emphasis for IMusicPlayerService object mMusicPlayer = IMusicPlayerService Stub. AsInterface (service)} / / called when the connection is broken Override Fun onServiceDisconnected(name: ComponentName) {}} //[2] Bind service startupbindService(intent, mConn, BIND_AUTO_CREATE); }}Copy the code

When you click on it, the music starts, and everything works, and if you know the client-server pattern, you know how important that is. That’s how a lot of the services in the framework work, so if you don’t understand aiDL, the code in the framework looks like a lot of work, and I’m going to do it with the framework in the next article, Discuss aiDL and the first layer of the Binder mechanism in detail.

All of my articles are collected in this article, and will be updated in time: Knowledge is long, the road of the walker will end in no words (My Programming Road)


Zero, preface,

1. Knowledge points of this article
1).Service 'simple' introduction and use '2).Service' binding Service 'implementation' music player (bar) '3). Use 'aidl' to enable other apps to access the Service and play musicCopy the code

2. The Service overview

[ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] [ComponentCallbacks2] 790 number of source lines (except comments):171 Number of attributes: 3 number of methods :21 number of public methods :20Copy the code


1. Preliminary understanding of Service

1. A brief introduction

The Service class itself is very small, with 171 lines of code naked. What makes it a “beginner’s nightmare” is one word: Binder, the chief killer who terrorized so many people


2. Enable or disable the Service

2.1: the Service test class
/ * * * the author: packer jet fierce < br > < br > * time: 2019/1/17/017: < br > and < / br > * E-mail: 1981462002 @qq.com < br > < br > * note: */ class MusicService:Service() {/** * Bind Service * @param IntentreturnIBinder */ Override Fun onBind(Intent: Intent): IBinder? { Log.e(TAG,"onBind: ")
        returnNull} /** * Create Service */ Override funonCreate() {
        super.onCreate()
        Log.e(TAG, "onCreate: ")} /** * Start the command * @param Intent * @param Flags Extra data for the start command * @param startId ID * @return
     */
    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Log.e(TAG, "onStartCommand: ")
        Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
        returnSuper.onstartcommand (Intent, flags, startId)} /** * Unbind service * @param intent * @return
     */
    override fun onUnbind(intent: Intent): Boolean {
        Log.e(TAG, "OnUnbind: unbind successfully")
        returnSuper.onunbind (Intent)} /** * Destroy service */ Override funonDestroy() {
        super.onDestroy()
        Log.e(TAG, "OnDestroy: Destroy service")
    }

    companion object {
        private val TAG = "MusicService"}}Copy the code

2.2: ToastSActivity test class

Just two buttons. Click it

Id_btn_start. setOnClickListener {toastIntent = Intent(this, MusicService::class.java) startService(toastIntent)} // Id_bTN_kill.setonClickListener {stopService(toastIntent)}Copy the code

2.3: Test results

Clicking On Start executes the onCreate and onStartCommand methods

If you click on it multiple times,onCreate will only execute once, onStartCommand will execute every time

Click open and Destroy


3. Data transfer between Activity and Service

OnStartCommand contains Intent, which is similar to BroadcastReciver

---->[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"If data is null, take"NO MSG"Toast.makeText(this, data? :"NO MSG", Toast.LENGTH_SHORT).show()
    return super.onStartCommand(intent, flags, startId)
}
Copy the code

4. Use the Service of another App in another App

Activity, BroadcastReciver, and Service are the three main components of an Intent. An Intent can start a component based on its package name and class name. We can also use Service,


Limitations:

Android :exported="true"<service android:name=".service.service.ToastService" android:exported="true"/> 2. It will self-destruct after about a minute. So it's about uselessCopy the code


4. About implicitly calling a Service

Android5.0+ explicitly states that it cannot implicitly call :ContextImpl in the validateServiceIntent method

---->[ContextImpl#validateServiceIntent]---------------------------Private void validateServiceIntent(Intent Service) {// Package name and class name are nullif(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)); }}}Copy the code


2. Binding service

In order not to make this article look too low, write a layout (the effect is displayed, you can imitate it. Button, if not ugly.)


1. Effects achieved

For ease of administration, there is an IPlayer interface that states that the main methods of MusicPlayer are no return, no input methods for the time being, and will need to be improved in the future


2. Play interface
/** * Author: Zhang Fengjie Teilie <br></br> * Time: 2018/10/31 0031:23:32<br></br> * Email: [email protected]<br></br> * Description: */ interface IPlayer {fun create()// Create fun start()// Start fun Resume ()// Restore fun stop()// Stop fun pause()// Pause fun Release ()} / / deathCopy the code

3. Core classes to play
/ * * * the author: packer jet fierce < br > < br > * time: 2019/1/17/017:21:57 < br > < br > * E-mail: 1981462002 @qq.com < br > < br > * note: */ Class MusicPlayer(Private Val mContext: Context) : Binder(), IPlayer {Override funcreate() {
        Toast.makeText(mContext, "Born", Toast.LENGTH_SHORT).show()
    }

    override fun start() {
        Toast.makeText(mContext, "Start playing.", Toast.LENGTH_SHORT).show()
    }

    override fun resume() {
        Toast.makeText(mContext, "Resume play", Toast.LENGTH_SHORT).show()

    }

    override fun stop() {
        Toast.makeText(mContext, "Stop playing.", Toast.LENGTH_SHORT).show()

    }

    override fun pause() {
        Toast.makeText(mContext, "Pause play", Toast.LENGTH_SHORT).show()
    }

    override fun release() {
        Toast.makeText(mContext, "Destroyed", Toast.LENGTH_SHORT).show()
    }
}
Copy the code

4. Play services
/ * * * the author: packer jet fierce < br > < br > * time: 2019/1/17/017: < br > and < / br > * E-mail: 1981462002 @qq.com < br > < br > * note: */ class MusicService:Service() {

    override fun onBind(intent: Intent): IBinder? {
        Log.e(TAG, "onBind: ")
        Toast.makeText(this, "Bind OK", Toast.LENGTH_SHORT).show()
        return MusicPlayer(this)
    }

    override fun onCreate() {
        super.onCreate()
        Log.e(TAG, "onCreate: ")
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Log.e(TAG, "onStartCommand: ")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onUnbind(intent: Intent): Boolean {
        Toast.makeText(this, "OnUnbind: unbind successfully", Toast.LENGTH_SHORT).show()
        Log.e(TAG, "OnUnbind: unbind successfully")
        return super.onUnbind(intent)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.e(TAG, "OnDestroy: Destroy service")
    }

    companion object {
        private val TAG = "MusicService"}}Copy the code

5. Use in activities
/** * Bind service */ private funbindMusicService() { musicIntent = Intent(this, MusicService::class.java) mConn = object : ServiceConnection {// Call override fun onServiceConnected(name: ComponentName, service: connected); IBinder) {mMusicPlayer = service as MusicPlayer} override fun onServiceDisconnected(name: ComponentName (ComponentName) {}} //[2] The binding service is startedbindService(musicIntent, mConn, BIND_AUTO_CREATE);
}
Copy the code

Three, music playback bar simple implementation

The next implementation of a play bar, sparrow is small, the five internal organs, perfect the UI, as follows


1. Song preparation and modification interface

So just for the sake of simplicity, I’m just going to use the four paths, and I’m going to tell you what’s going on here. If you know more about MediaPlayer, I’m just going to go straight to the code and I’m going to pass in the playlist path string when I create it, okay

/** * Author: Zhang Fengjie Teilie <br></br> * Time: 2018/10/31 0031:23:32<br></br> * Email: [email protected]<br></br> * Description: */ interface IPlayer {fun create(musicList: ArrayList<String>)// Create fun start()// Start fun stop()// Stop fun Pause ()// Pause fun release()// Die fun Next ()// Next fun Prev ()// Last song fun isPlaying(): Boolean whether to play fun seek(pre_100: Int)// Drag progress}Copy the code

2. Implementation of create method and start method

A MusicActivity passes a MusicPlayer object to its IBinder object by calling its OnServiceconnection’s onServiceConnected method. The corresponding UI clicks to call the corresponding method

---->[MusicPlayer]--------------
private lateinit var mPlayer: MediaPlayer
private var isInitialized = falsePrivate var mCurrentPos = 0 private lateinit var mMusicList: ArrayList<String>// What music is currently playing ---->[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, "Born")
}

---->[MusicPlayer#start]--------------
override fun start() {
    if(! isInitialized && mPlayer.isPlaying) {return
    }
    mPlayer.start();
    Log.e(TAG, "Start playing.")}Copy the code

So the song can play


3. The realization of the last song and the next song and auto play the next song
---->[MusicPlayer]--------------

override fun next() {mCurrentPos++ judgePos()// changMusicByPos(mCurrentPos)} Override funprev() {mCurrentPos - judgePos () / / if the crossing the s changMusicByPos (mCurrentPos)} / cross-border deal with * * * * / private funjudgePos() {
    if (mCurrentPos >= mMusicList.size) {
        mCurrentPos = 0
    }
    if(mCurrentPos < 0) {mCurrentPos = mmusicList.size - 1}} /** * changMusicByPos(pos: Int) {mplayer.setdatasource (mMusicList[pos])// set mplayer.setdatasource (mMusicList[pos])"Currently playing song POS:$pos: the path:${mMusicList[pos]}" )
}

---->[MusicPlayer#create]--------------MPlayer. SetOnCompletionListener {next () / / play is completed, the next song}Copy the code


4. Progress drag and listen processing

I’m going to update it every second, and I’m going to do it with a Timer, but there’s a lot of ways to do it

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

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

---->[MusicPlayer#create]--------------MTimer = Timer()// Create Timer mHandler = Handler()// Create 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) / / -- -- -- -- -- -- -- -- -- -- -- -- set the schedule to monitor -- -- -- -- -- -- -- -- -- -- - interface OnSeekListener {fun onSeek (per_100: Int); } private lateinit var mOnSeekListener: OnSeekListener funsetOnSeekListener(onSeekListener: OnSeekListener) {
    mOnSeekListener = onSeekListener;
}
Copy the code

5. What’s the point of binding services?

I think a lot of people who are new to the Activity have a question, why do I have to go through the Service and go around and get the MediaPlayer object?

Such as: A server S is running a game business, a client C connected to the server can play the game, no one would want to port the server business to the client, if this is really a one-person zone, the Service is equivalent to providing a Service, at this time the Activity is equivalent to the client, The Service MediaPlayer(Binder object) is equivalent to the core business through the Conn connection. It's a typical client-server mode and the client-server mode is a Service that can serve multiple clients and the client can get an instance of the Service through the IBinder interface in this case the MediaPlayer(Binder object) But now you can only play in one app. How can other apps connect to the service? That's where AIDL comes in. The Service is very active. Remember the last time I tried to play music in my Activity with new MediaPlayer. The app stopped after a while. Today in the Service, the music didn't stop for a long timeCopy the code

4. Android Interface Definition LanguageaidlUse in a Service

This server is a little weak, now try to make it available externally. I don’t know what you can see in the picture below, I’m very excited about it. A few days ago, I looked at the framework source code, it felt very similar


1. Write the AIDL file

Remember the IPlayer interface up there, the aiDL content is just the methods of this interface with a slightly different syntax, and here’s the AIDL for the IMusicPlayerService and when you’re done with that little hammer, He will generate the code using SDK \build-tools\28.0.3\ aidL.exe

// 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 inAIDL. */ void stop(); void pause(); void start(); void prev(); void next(); void release(); boolean isPlaying(); void seek(int pre_100); / / addin
    void create(in List<String> filePaths);
}
Copy the code

2. Automatically generated code to use

This article will only show you how to use the generated IMusicPlayerService, We’ll look at it in more detail in the next article but you can see that there’s an inner class in the IMusicPlayerService that Stub inherits from Binder and implements IMusicPlayerService and we just custom MusicPlayer inherits from Binder and implements IPlayer Now, there is a ready-made IMusicPlayerService Stub, we inherit it, to avoid look messy Has built a MusicPlayerService and MusicPlayerStub can figure compare the above ways

---->[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.IMusicPlayerServiceCopy the code

3.MusicPlayerStub Implementation (Binder object)

The implementation is exactly the same as MusicPlayer above, but it’s implemented in Java

/** * Author: Zhang Feng Jieteilie <br/> * Time: 2019/1/23/023:17:11<br/> * Email: [email protected]<br/> * Description: MusicPlayerStub--Binder object */ Public Class MusicPlayerStub extends IMusicPlayerService.Stub {private MediaPlayer mPlayer; private boolean isInitialized =false; Private int mCurrentPos = 0; private int mCurrentPos = 0; Private List<String> mMusicList; 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<String> filePaths) throws RemoteException { mMusicList = filePaths; File file = new File(mMusicList.get(mCurrentPos)); Uri uri = Uri.fromFile(file); mPlayer = MediaPlayer.create(mContext, uri); isInitialized =true; MTimer = new Timer(); // Create Timer mHandler = new Handler(); // Create Handler // start method mtimer.schedule (new)TimerTask() {
            @Override
            public void run() {
                if (mPlayer.isPlaying()) {
                    int pos = mPlayer.getCurrentPosition();
                    int duration = mPlayer.getDuration();
                    mHandler.post(() -> {
                        if(mOnSeekListener ! = null) { mOnSeekListener.onSeek((int) (pos * 1.f / duration * 100)); }}); }, 0, 1000); mPlayer.setOnCompletionListener(mp -> { try { next(); } catch (RemoteException e) {e.printStackTrace(); }}); } @Override public void start() throws RemoteException {if(! isInitialized && mPlayer.isPlaying()) {return;
        }
        mPlayer.start();
    }

    @Override
    public void stop() throws RemoteException {

    }

    @Override
    public void pause() throws RemoteException {
        if(mPlayer.isPlaying()) { mPlayer.pause(); } } @Override public void prev() throws RemoteException { mCurrentPos--; judgePos(); ChangMusicByPos (mCurrentPos); } @Override public void next() throws RemoteException { mCurrentPos++; judgePos(); ChangMusicByPos (mCurrentPos); } @Override public void release() throws RemoteException { } @Override public boolean isPlaying() throws RemoteException {returnmPlayer.isPlaying(); } @Override public void seek(int pre_100) throws RemoteException { pause(); mPlayer.seekTo((pre_100 * mPlayer.getDuration() / 100)); start(); } /** * Override */ private voidjudgePos() {
        if (mCurrentPos >= mMusicList.size()) {
            mCurrentPos = 0;
        }

        if(mCurrentPos < 0) { mCurrentPos = mMusicList.size() - 1; }} /** * @param pos */ private void changMusicByPos(int pos) {mplayer.reset (); // Reset try {mplayer.setdatasource (mmusicList.get (pos)); Mplayer.prepare (); / / ready to start (); } catch (IOException | RemoteException e) { e.printStackTrace(); }} / / -- -- -- -- -- -- -- -- -- -- -- -- set the schedule to monitor -- -- -- -- -- -- -- -- -- -- - public interface OnSeekListener {void onSeek (int per_100); } private OnSeekListener mOnSeekListener; public voidsetOnSeekListener(OnSeekListener onSeekListener) { mOnSeekListener = onSeekListener; }}Copy the code

4.MusicPlayerServiceReturns the MusicPlayerStub object

There is generally no difference between using MusicPlayerStub as an inner class of MusicPlayerService, but to match the above and make it look more comfortable, I put MusicPlayerStub on the outside

/** * Author: Zhang Feng Jiete Lie <br/> * Time: 2019/1/23/023:16:32<br/> * Email: [email protected]<br/> * Description: Public Class MusicPlayerService extends Service {private MusicPlayerStub MusicPlayerStub; @Override public voidonCreate() {
        super.onCreate();
        ArrayList<String> musicList = new ArrayList<>();
        musicList.add("/sdcard/toly/ this life is not changed _ bluebird flying fish.aAC");
        musicList.add("/sdcard/ Toly/Courage - Jingru Leong -1772728608-1.mp3");
        musicList.add("/sdcard/ TOLy/Grass ring _ Weixinyu.aac");
        musicList.add("/sdcard/toly/ Guo Jing-The next morning [mqms2].flac");

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        returnmusicPlayerStub; }}Copy the code

5. Use in this project

If you’re only using it in this project, you can just change the names of the two classes and it won’t be any different

/** * Bind service */ private funbindMusicService() { musicIntent = Intent(this, MusicPlayerService::class.java) mConn = object : ServiceConnection {// Call override fun onServiceConnected(name: ComponentName, service: connected); IBinder) { mMusicPlayer = service as MusicPlayerStub mMusicPlayer.setOnSeekListener { per_100 -> Id_pv_pre.setprogress (per_100)}} Override fun onServiceDisconnected(name: ComponentName (ComponentName) {}} //[2] The binding service is startedbindService(musicIntent, mConn, BIND_AUTO_CREATE);
}
Copy the code

So, what’s the advantage of AIDL? Now seems haven’t seen where severe, then In this service before you configure the app/SRC/main/AndroidManifest. The XML

<service android:name=".service.service.MusicPlayerService">
    <intent-filter>
        <action android:name="www.toly1994.com.music.player"></action>
    </intent-filter>
</service>
Copy the code

Fifth, based onaidlUse another project Service in another project

This is where aidl is great, cross-process communication, and Android’s system-level services are all based on this to get into anotherapp: anotherapp. The core point is to get the IMusicPlayerService object. Common sense problem, when the client connects to the server, the server must first open…

class ServiceTestActivity : AppCompatActivity() {
    private var mConn: ServiceConnection? = null
    private lateinit var mMusicPlayer: IMusicPlayerService

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.ac_br)
        title="Another App"
        bindMusicService()
        id_btn_send.text="Play music"Id_btn_send.setonclicklistener {mmusicPlayer.start ()}} /** * Bind service */ private funbindMusicService() {val intent = intent (); // Intent.setpackage (); // Intent.setpackage ();"com.toly1994.tolyservice")
        intent.action = "www.toly1994.com.music.player"MConn = object: ServiceConnection {// Call override fun onServiceConnected(name: ComponentName, service: connected) IBinder) {/ / emphasis for IMusicPlayerService object mMusicPlayer = IMusicPlayerService Stub. AsInterface (service)} / / called when the connection is broken Override Fun onServiceDisconnected(name: ComponentName) {}} //[2] Bind service startupbindService(intent, mConn, BIND_AUTO_CREATE); }}Copy the code

When you click on it, the music starts, and everything works, and if you know the client-server pattern, you know how important that is. That’s how a lot of the services in the framework work, so if you don’t understand aiDL, the code in the framework looks like a lot of work, and I’m going to do it with the framework in the next article, Discuss aiDL and the first layer of the Binder mechanism in detail.