Android开发--Lesson08--服务Service

Service的定义

Service(服务)就是Android里的一个“后台工作者”,它专门用来处理那些不需要用户盯着看的任务。它可以一直运行,即使你切换到其他应用或者关掉屏幕,它也不会停下来。

它的特点很简单:

  1. 没界面:你看不到它,但它确实在干活。
  2. 两种模式:
    • 一种是自己默默运行,比如播放音乐、下载文件。
    • 另一种是可以跟其他组件互动,比如给Activity提供数据。
  3. 可能会被系统停掉:如果手机内存不够了,系统会优先关掉不重要的服务,所以重要任务需要特别标记为“前台服务”。
  4. 安全可控:你可以设置权限,只让特定的应用使用它。

总之,Service就是一个能在后台长时间工作的工具,适合处理耗时任务或者需要持续运行的功能。

服务的作用

  1. 后台操作:即使应用程序不在前台显示,Service也能继续在后台执行任务,比如播放音乐、文件下载等。

  2. 跨进程通信(IPC):通过AIDL(Android Interface Definition Language),Service可以实现不同应用之间的通信。

  3. 绑定服务:允许其他组件(如Activity)与Service进行交互,甚至可以通过绑定的方式获取Service返回的结果。

  4. 处理周期性任务或定时任务:例如,在特定时间点执行某些操作或者定期更新数据。

  5. 执行网络操作:当需要进行网络请求时,使用Service可以在后台完成这些操作,不影响用户的其他操作。

服务的特点

  • 独立于UI线程:Service默认运行在主线程中,但其主要设计目的是为了执行不需要用户界面的长期操作。如果需要执行耗时操作,应该在Service内部创建新的线程来避免阻塞主线程。

  • 生命周期管理:Service具有比Activity更简单的生命周期,主要包括onCreate()onStartCommand()onDestroy()等方法。对于绑定服务,还有onBind()方法用于客户端和服务端的连接。

  • 两种启动方式:

    • Started Service(启动状态的服务):当一个组件(如Activity)通过调用startService()启动服务时,该服务即处于“启动”状态。这种服务可以无限期地运行,并且必须显式调用stopSelf()或由外部组件调用stopService()才能停止。
    • Bound Service(绑定状态的服务):当一个组件通过调用bindService()与服务绑定时,该服务即处于“绑定”状态。这种服务允许其他组件与之交互,交换数据等。所有绑定到服务的组件断开连接后,系统会销毁该服务。
  • 优先级较低:当系统资源紧张时,可能会销毁后台Service以回收内存。为避免这种情况影响用户体验,可以将Service设置为前台服务,通过调用startForeground()方法并在通知栏显示持续的通知。

  • 安全性:可以通过声明权限限制哪些应用可以与你的Service交互,保护敏感数据不被未授权的应用访问。

创建一个服务类

 新建一个服务类之后,可以就会弹窗出服务的默认配置,直接默认就好

点击Finsh之后就可以获得一个继承于 android.app.Service包下面的Service的抽象类,此类就是提供了相关的后台服务的相关方法

生成的service类会在android的清单文件中完成注册

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true" />

 

Service的相关方法

 一个基本的service类会重写父类的以下方法:

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 {
    // 构造方法
    public MyService() {
    }

    // 当客户端绑定到服务时调用
    @Override
    public IBinder onBind(Intent intent) {
        Log.i("MyService","myBinder调用");
        return new myBinder();
    }

    // 当客户端解除绑定到服务时调用
    @Override
    public boolean onUnbind(Intent intent) {
        Log.i("MyService","onUnbind调用");
        return super.onUnbind(intent);
    }

    // 在服务创建时调用
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("MyService","onCreate启动");
    }

    // 在服务启动时调用
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("MyService","onStartCommand启动");
        return super.onStartCommand(intent, flags, startId);
    }

    // 在服务销毁时调用
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("MyService","onDestroy启动");
    }
    // 自定义方法
    public void myMethod(){
        Log.i("MyService","myMethod方法");
    }
    // 继承Binder类,用于在客户端和服务之间进行通信
    class myBinder extends Binder{
        // 调用自定义方法
        public void callMethod(){
            myMethod();
        }
    }
}

 

首先是构造方法,这是用来初始化服务的,肯定是必须拥有的

然后是onBind方法和onUnBind方法,这两个方法一个是用于绑定服务启动之后需要做的事情,需要一个Binder类的对象来描述服务需要后台运行的事情,而onUnBind方法则是取消绑定,也就是取消当前服务在做的事情

onCreate方法只有服务第一次进行调用,也就是一个服务的生命周期只会被调用一次,如果是bind后,然后unBind,再次bind看似换了一个服务,但其实onCreate不会被调用,换而言之就是没有新建服务

onStartCommand方法也是在服务启动的时候会被调用,但是和onCreate不同的是每次startService()方法调用时,都会运行onStartCommand,而onCreate则不会

onDestory方法则是在服务销毁的时候会被调用

后面的方法和内部类则是因为onBind方法需要的,故而简单测试,打印相关的调用信息看看

自定义方法就输出一个日志,而内部类则是提供一个方法来调用自定义方法,后面的自定义方法可以作为服务需要处理的具体的事情而修改

测试相关方法的注册状态

测试service的生命周期可以使用按钮来确定每次绑定一个操作(开启服务和关闭服务),然后查看控制台的日志就可以清楚的看到每个方法调用的时机了

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <Button
        android:id="@+id/bt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="启动"/>

    <Button
        android:id="@+id/bt2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="暂停"/>
</LinearLayout>

 

页面代码就两个按钮,负责启动服务和关闭服务,接着就是上下文对象MainActivity来负责启动和暂停服务:

在绑定Service之前,我们需要学习一个新的接口:ServiceConnection ;使用它可以描述当前上下文和service的连接状态,并且可以通过内置的一些方法定义当和service产生连接或者断开连接后需要执行那些操作

这也是MainActivity上下文对象连接bindService的必要参数对象

    class MyConnection implements ServiceConnection{

        //当服务连接成功时调用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i("MyService","onServiceConnected");
            //将IBinder转换为MyService.myBinder类型
            MyService.myBinder binder = (MyService.myBinder) service;
            //调用MyService中的callMethod方法
            binder.callMethod();
        }

        //当服务断开连接时调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
            //异常终端
            Log.i("MyService","onServiceDisconnected");
        }
    }

 

当连接成功时的方法中,IBinder的参数值就是我们定义的MyService类中的内部类binder,它会有一个绑定方法用来定义服务执行需要做什么

在上下文对象中可以将其取出,然后调用其的方法,实现在上下文启动服务并执行其方法

接下来就是绑定具体的服务了,绑定服务也需要Intent对象,来指明意图,这里的意图就是当前上下文和service类的具体连接

        //获取按钮
        Button bt1 = findViewById(R.id.bt1);
        Button bt2 = findViewById(R.id.bt2);
        //为按钮1添加点击事件
        bt1.setOnClickListener(v->{
            //添加意图
            Intent intent = new Intent(MainActivity.this,MyService.class);
            //启动服务
            startService(intent);
            //如果连接为空
            if (connection==null){
                connection = new MyConnection();
                //绑定服务,并执行其具体的方法
                bindService(intent,connection,BIND_AUTO_CREATE);
            }
        });
        //为按钮2添加点击事件
        bt2.setOnClickListener(v->{
            Intent intent = new Intent(MainActivity.this,MyService.class);
            //停止服务
            stopService(intent);
            //如果连接不为空
            if (connection!=null){
                //解除绑定服务
                unbindService(connection);
                //将连接置为空
                connection=null;
            }
        });

 

上面的代码就是启动服务和停止服务,并且为服务启动后的连接执行方法

bindService(intent,connection,BIND_AUTO_CREATE); 实现当前上下文和service的绑定,第一个intent参数就是意图,它创建的时候就表明了当前上下文和那个service产生连接,connection就是连接服务需要的对象,可以指定当前上下文和服务连接成功或终止后应该做什么,第三个参数就是常量标识绑定的模式为自自动绑定,也就是intent指定的服务没有启动则自动创建服务

以下是完整的MainActivity的代码:

public class MainActivity extends AppCompatActivity  {
    private MyConnection connection;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        //获取按钮
        Button bt1 = findViewById(R.id.bt1);
        Button bt2 = findViewById(R.id.bt2);
        //为按钮1添加点击事件
        bt1.setOnClickListener(v->{
            //添加意图
            Intent intent = new Intent(MainActivity.this,MyService.class);
            //启动服务
            startService(intent);
            //如果连接为空
            if (connection==null){
                connection = new MyConnection();
                //绑定服务,并执行其具体的方法
                bindService(intent,connection,BIND_AUTO_CREATE);
            }
        });
        //为按钮2添加点击事件
        bt2.setOnClickListener(v->{
            Intent intent = new Intent(MainActivity.this,MyService.class);
            //停止服务
            stopService(intent);
            //如果连接不为空
            if (connection!=null){
                //解除绑定服务
                unbindService(connection);
                //将连接置为空
                connection=null;
            }
        });

    }
    class MyConnection implements ServiceConnection{

        //当服务连接成功时调用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.i("MyService","onServiceConnected");
            //将IBinder转换为MyService.myBinder类型
            MyService.myBinder binder = (MyService.myBinder) service;
            //调用MyService中的callMethod方法
            binder.callMethod();
        }

        //当服务断开连接时调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
            //异常终端
            Log.i("MyService","onServiceDisconnected");
        }
    }
  
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(connection);
    }
}

 

测试结果:

启动服务按钮点击之后:

 停止服务按钮点击:

音乐播放案例

引入Mp3资源

需要在res包下面新建一个raw包,用于存放需要播放的音乐:

 新建一个服务项目

此服务需要提供三个方法,分别需要指定媒体元素的开始,暂停,和停止

musicService:

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

public class MusicService extends Service {
    //声明一个MediaPlayer对象
    private MediaPlayer mediaPlayer;
    //构造方法
    public MusicService() {
    }

    //绑定服务
    @Override
    public IBinder onBind(Intent intent) {
        return new MusicBinder();
    }
    //声明一个内部类,继承Binder
    class MusicBinder extends Binder{
        //播放
        public void play(){
            //如果mediaPlayer为空,则创建MediaPlayer对象并开始播放
            if (mediaPlayer == null){
                mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.gentlemen);
                mediaPlayer.start();
            }else {
                //如果mediaPlayer不为空,且正在播放,则继续播放
                if (!mediaPlayer.isPlaying()){
                    mediaPlayer.start();
                }
            }
        }
        //暂停
        public void pause(){
            //如果mediaPlayer不为空,且正在播放,则暂停播放
            if (mediaPlayer !=null && mediaPlayer.isPlaying()){
                mediaPlayer.pause();
            }
        }
        //结束
        public void stop(){
            if (mediaPlayer !=null){
                mediaPlayer.stop();
                mediaPlayer =null;
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mediaPlayer !=null){
            //停止播放
            mediaPlayer.stop();
            //释放资源
            mediaPlayer.release();
            mediaPlayer =null;
        }
    }
}

 

上面的service的实现代码其实和第二节讲解是一样的,这个时候就是在Binder类中指定了服务需要实现的三个方法,即开始音乐,暂停音乐和关闭音乐

界面XML文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    <Button
        android:id="@+id/bt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="播放"/>

    <Button
        android:id="@+id/bt2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="暂停"/>

    <Button
        android:id="@+id/bt3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止"/>
</LinearLayout>

 

界面也只有三个按钮对应三个事件

接着就是MainActivity了,需要定义的就是具体的事件绑定:

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    //声明MusicConnecet对象
    private MusicConnecet musicConnecet;
    //声明MusicService.MusicBinder对象
    private MusicService.MusicBinder musicBinder;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //启用EdgeToEdge
        EdgeToEdge.enable(this);
        //设置布局
        setContentView(R.layout.activity_main);
        //设置窗口边距
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        //获取按钮
        Button bt1 = findViewById(R.id.bt1);
        Button bt2 = findViewById(R.id.bt2);
        Button bt3 = findViewById(R.id.bt3);
        //设置按钮点击事件
        bt1.setOnClickListener(this);
        bt2.setOnClickListener(this);
        bt3.setOnClickListener(this);
        //创建Intent对象,指定要启动的服务
        Intent intent = new Intent(MainActivity.this,MusicService.class);
        //创建MusicConnecet对象
        musicConnecet = new MusicConnecet();
        //绑定服务
        bindService(intent,musicConnecet,BIND_AUTO_CREATE);
    }

    @Override
    public void onClick(View v) {
        //根据按钮id执行不同的操作
        if (v.getId()==R.id.bt1){
            musicBinder.play();
        }
        if(v.getId()==R.id.bt2){
            musicBinder.pause();
        }
        if (v.getId()==R.id.bt3){
            musicBinder.stop();
        }
    }

    //创建MusicConnecet类,实现ServiceConnection接口
    class MusicConnecet implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //将service转换为MusicBinder对象
            musicBinder=(MusicService.MusicBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除服务绑定
        unbindService(musicConnecet);
    }
}

 

最后实现的效果就是完成了音乐播放,暂停和停止

 

-----END----

 

posted @ 2025-04-28 17:51  回忆也交给时间  阅读(37)  评论(0)    收藏  举报