Android移动应用开发之服务


一、服务的创建

服务是一个长期运行在后台的用户组件,例如下载数据、播放音乐等。
程序包->New->Service->Service
在这里插入图片描述

package edu.hzuapps.test;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MyService extends Service {
    
    
    public MyService() {
    
    
    }

    @Override
    public IBinder onBind(Intent intent) {
    
    
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

onBind用于绑定服务。

二、服务的生命周期

在这里插入图片描述

  • onCreate:第一次创建服务时执行的方法;
  • onDestory:服务被销毁时执行的方法;
  • onStartCommand:客户端通过startService方法启动服务时执行该方法;
  • onBind:客户端通过调用bindService方法启动服务时执行该方法;
  • onUnbind:客户端通过调用unBindService断开服务时执行该方法。

三、服务的启动方式

1、startService方式启动

该启动模式下服务会长期在后台运行,并且服务的状态和开启者的状态没有关系,即使启动服务的组件已经被销毁,服务也会依旧运行。
activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/btn_stop"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="100dp"
        android:background="#B0E0E6"
        android:onClick="start"
        android:text="开启服务"
        android:textColor="#6C6C6C"
        android:textSize="18sp" />

    <Button
        android:id="@+id/btn_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignStart="@+id/btn_start"
        android:layout_alignLeft="@+id/btn_start"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="365dp"
        android:background="#F08080"
        android:onClick="stop"
        android:text="关闭服务"
        android:textColor="#6C6C6C"
        android:textSize="18sp" />
</RelativeLayout>

重写Service生命周期中的方法MyService.java:

package edu.hzuapps.test;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
    
    
    @Override
    public IBinder onBind(Intent intent) {
    
    
        return null;
    }
    public void onCreate() {
    
    
        super.onCreate();
        Log.i("StartService", "onCreate()");
    }
    public int onStartCommand(Intent intent, int flags, int startId) {
    
    
        Log.i("StartService", "onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }
    public void onDestroy() {
    
    
        super.onDestroy();
        Log.i("StartService", "onDestroy()");
    }
}

交互界面MainActivity.java:

package edu.hzuapps.test;

import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    
    
    private Button start;
    private Button stop;   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.start = (Button) findViewById(R.id.btn_start);
        this.stop = (Button) findViewById(R.id.btn_stop);
    }
    //开启服务的方法
    public void start(View view) {
    
    
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }
    //关闭服务的方法
    public void stop(View view) {
    
    
        Intent intent = new Intent(this, MyService.class);
        stopService(intent);
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、bindService方式启动

当一个组件通过bindService启动服务时,服务会与组件绑定,一个被绑定的服务提供一个客户端与服务器的接口,允许组件与服务交互,发送请求,得到结果,多个组件可以绑定一个服务,当调用onUnbind方法时,这个服务就会被摧毁。bindService的三个参数含义:

  • Intent对象用于指定要启动的Service;
  • ServiceConnection对象用于监听调用者与Service之间的连接状态,当调用者与Service连接成功时,将回调该对象的onServiceConnect方法,断开连接时将回调该对象的onServiceDisconnected方法;
  • flags指定绑定时是否自动创建Service(如果还未创建),该参数可指定为0,即不自动创建,也可指定为BIND_AUTO_CREATE自动创建。
    布局页面activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <Button
        android:layout_above="@+id/btn_call"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/btn_call"
        android:layout_alignStart="@+id/btn_call"
        android:layout_marginBottom="50dp"
        android:background="#F5F5DC"
        android:onClick="btnBind"
        android:text="绑定服务"
        android:textSize="16sp" />
    <Button
        android:id="@+id/btn_call"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/btn_unbind"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginBottom="50dp"
        android:layout_marginEnd="100dp"
        android:layout_marginRight="30dp"
        android:background="#F5F5DC"
        android:onClick="btnCall"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:text="调用服务中的方法"
        android:textSize="16sp" />
    <Button
        android:id="@+id/btn_unbind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/btn_call"
        android:layout_alignParentBottom="true"
        android:layout_alignStart="@+id/btn_call"
        android:layout_marginBottom="300dp"
        android:background="#F5F5DC"
        android:onClick="btnUnbind"
        android:text="解绑服务"
        android:textSize="16sp" />
</RelativeLayout>

Service类MyService.java:

package edu.hzuapps.bindservice;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
    
    
    //创建服务的代理,调用服务中的方法
    class MyBinder extends Binder {
    
    
        public void callMethodInService() {
    
    
            methodInService();
        }
    }
    public void onCreate() {
    
    
        Log.i("MyService", "创建服务,调用onCreate()");
        super.onCreate();
    }
    public IBinder onBind(Intent intent) {
    
    
        Log.i("MyService", "绑定服务,调用onBind()");
        return new MyBinder();
    }
    public void methodInService() {
    
    
        Log.i("MyService", "自定义方法methodInService()");
    }
    public boolean onUnbind(Intent intent) {
    
    
        Log.i("MyService", "解绑服务,调用onUnbind()");
        return super.onUnbind(intent);
    }
}

界面交互代码MainActivity.java:

package edu.hzuapps.bindservice;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    
    
    private MyService.MyBinder myBinder;
    private Myconn myconn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    //创建MyConn类,用于实现连接服务
    private class Myconn implements ServiceConnection {
    
    
        //当成功绑定到服务时调用的方法,返回MyService里面的Ibinder对象
        public void onServiceConnected(ComponentName name, IBinder service) {
    
    
            myBinder = (MyService.MyBinder) service;
            Log.i("MainActivity", "服务成功绑定,内存地址为"+myBinder.toString());
        }
        //当服务失去连接时调用的方法
        public void onServiceDisconnected(ComponentName name) {
    
    
            Log.i("MainActivity", "服务失去连接");
        }
    }
    //绑定服务
    public void btnBind(View view) {
    
    
        if (myconn == null) {
    
    
            myconn = new Myconn();
            Intent intent = new Intent(this, MyService.class);
            bindService(intent, myconn, BIND_AUTO_CREATE);
        }
    }
    //解绑服务
    public void btnUnbind(View view) {
    
    
        if (myconn != null) {
    
    
            unbindService(myconn);
            myconn = null;
        }
    }
    //调用服务中的方法
    public void btnCall(View view) {
    
    
        myBinder.callMethodInService();
    }
}

在这里插入图片描述
在这里插入图片描述

四、服务的通信

1、通信方式

(1)本地服务通信

绑定方式开启服务的案例实际上就是用来本地服务通信,首先创建一个Service类,该类会提供一个onBind方法,onBind方法返回一个IBinder对象,作为参数传递给ServiceConnection中的onServiceConnected,即通过IBinder对象与Service进行通信。

(2)远程服务通信

在Android中,每个应用程序都运行在自己的进程中,如果想要完成不同进程之间的通信,就需要使用远程服务通信,远程服务通信是通过AIDL:

  • AIDL定义接口的源代码必须以.aidl结尾
  • AIDL接口中用到的数据类型,除了基本的数据类型及String、List、Map、CharSequence外,其他类型需要导入包。

2、实战演练:音乐播放器

布局文件activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <EditText
        android:id="@+id/et_inputpath"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Music/失眠症.mp3" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_gravity="center_horizontal"
        android:gravity="center"
        android:orientation="horizontal">
        <TextView
            android:id="@+id/tv_play"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:drawablePadding="3dp"
            android:drawableTop="@drawable/play"
            android:gravity="center"
            android:text="播放" />
        <TextView
            android:id="@+id/tv_stop"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:drawablePadding="3dp"
            android:drawableTop="@drawable/stop"
            android:gravity="center"
            android:text="暂停" />
    </LinearLayout>
</LinearLayout>

服务类MusicService.java:

package edu.hzuapps.musicplayer;

import android.app.Service;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;

public class MusicService extends Service {
    
    
    private static final String TAG = "MusicService";
    public MediaPlayer mediaPlayer;
    class MyBinder extends Binder {
    
    
        //播放音乐
        public void play(String path) {
    
    
            try {
    
    
                if (mediaPlayer == null) {
    
    
                    //创建一个MediaPlayer播放器
                    mediaPlayer = new MediaPlayer();
                    //指定参数为音频文件
                    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                    //指定播放路径
                    mediaPlayer.setDataSource(path);
                    //准备播放
                    mediaPlayer.prepare();
                    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    
    
                        @Override
                        public void onPrepared(MediaPlayer mediaPlayer) {
    
    
                            //开始播放
                            mediaPlayer.start();
                        }
                    });
                } else {
    
    
                    int position = getCurrentProgress();
                    mediaPlayer.seekTo(position);
                    try {
    
    
                        mediaPlayer.prepare();
                    } catch (Exception e) {
    
    
                        e.printStackTrace();
                    }
                    mediaPlayer.start();
                }
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
        //暂停播放
        public void pause() {
    
    
            if (mediaPlayer != null && mediaPlayer.isPlaying()) {
    
    
                mediaPlayer.pause();
            } else if (mediaPlayer != null && (!mediaPlayer.isPlaying())) {
    
    
                mediaPlayer.start();
            }
        }
    }
    public void onCreate() {
    
    
        super.onCreate();
    }
    //获取当前进度
    public int getCurrentProgress() {
    
    
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
    
    
            return mediaPlayer.getCurrentPosition();
        } else if (mediaPlayer != null && (!mediaPlayer.isPlaying())) {
    
    
            return mediaPlayer.getCurrentPosition();
        }
        return 0;
    }
    public void onDestroy() {
    
    
        if (mediaPlayer != null) {
    
    
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
        super.onDestroy();
    }
    @Override
    public IBinder onBind(Intent intent) {
    
    
        return new MyBinder();
    }
}
  • setAudioStreamType:指定音频文件的类型,必须在prepare之前调用;
  • setDataSource:设置要播放的音频文件的位置,URI路径;
  • prepare:准备播放;
  • start:开始或继续播放音频;
  • pause:暂停播放音频;
  • seekTo:从指定位置开始播放音频;
  • release:释放掉与MediaPlayer对象相关的资源;
  • isPlaying:判断当前MediaPlayer是否正在播放音频;
  • getCurrentPosition:获取当前播放音频文件的位置。
    交互界面MainActivity.java:
package edu.hzuapps.musicplayer;

import androidx.appcompat.app.AppCompatActivity;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
    
    private EditText path;
    private Intent intent;
    private myConn conn;
    MusicService.MyBinder binder;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        path = (EditText) findViewById(R.id.et_inputpath);
        findViewById(R.id.tv_play).setOnClickListener(this);
        findViewById(R.id.tv_stop).setOnClickListener(this);
        conn = new myConn();
        intent = new Intent(this, MusicService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);
    }
    private class myConn implements ServiceConnection {
    
    
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    
    
            binder = (MusicService.MyBinder) iBinder;
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
    
    
        }
    }
    @Override
    public void onClick(View view) {
    
    
        String pathway = path.getText().toString().trim();
        File SDpath = Environment.getExternalStorageDirectory();
        File file = new File(SDpath, pathway);
        String path = file.getAbsolutePath();
        switch (view.getId()) {
    
    
            case R.id.tv_play:
                if (file.exists() && file.length()>0) {
    
    
                    binder.play(path);
                } else {
    
    
                    Toast.makeText(this, "找不到音乐文件!", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.tv_stop:
                binder.pause();
                break;
            default:
                break;
        }
    }
    protected void onDestroy() {
    
    
        unbindService(conn);
        super.onDestroy();
    }
}

在这里插入图片描述
在这里插入图片描述
[!]Android版本为6.0时注意权限问题,需要在Activity中动态申请敏感权限。

猜你喜欢

转载自blog.csdn.net/weixin_44047795/article/details/112259582