我的安卓第一课:四大组件之一Service

引言

Service的主要作用,在 Android 开发中,Service 是一种用于在后台执行操作的组件。与 Activity 不同,Service 并不提供用户界面,而是专注于处理长时间运行的任务或执行需要在后台进行的工作。Activity生活在前台,而Service生活在后台

使用场景

  1. 当需要执行可能需要较长时间才能完成的任务(例如网络请求、文件下载等),或者需要持续运行的任务(记录日志等),使用 Service 可以避免阻塞主线程(UI 线程),从而保证应用的响应性。

  2. 即使应用程序不在前台显示,Service 也可以继续在后台运行。这对于需要持续更新数据的应用来说非常有用,比如音乐播放器即使退出了主界面也能继续播放音乐。(但如果不是前台Service,则随时会被回收)

TIP: 虽然Service运行在后台,但实际上Service并不会自动开启新线程,所有的代码都是默认运行在主线程当中的 。也就是说,需要在Service的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况(导致停止响应)。

Service的创建与使用

我们可以直接在Android Studio中创建Service

其中Exported属性表示是否将这个Service暴露给 外部其他程序访问,Enabled属性表示是否启用这个Service

java 复制代码
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");
    }
}

使用Service 启用Service与启用新的Activity一样,都需要Intent的支持。比如:

java 复制代码
Intent intent = new Intent(this, MyService.class);
startService(intent);

但有不同的是,Service支持暂停

java 复制代码
Intent intent = new Intent(this, MyService.class);
stopService(intent);

TIP: 从Android 8.0系统开始,应用的后台功能被大幅削减。现在只有当应用保持在前台可见状态的情况下,Service才能保证稳定运行,一旦应用进入后台之后,Service随时都有可能被系统回收。 之所以做这样的改动,是为了防止许多恶意的应用程序长期在后台占用手机资源,从而导致手机变得越来越卡。当然,如果真的非常需要长期在后台执行一些任务,可以使用前台Service或者WorkManager

(听说早期有APP把自己缩小为一个像素来保活)

Service与Activity通信

我们在extends Service后,会强制要求实现onBind。而onBind也正是实现Service与Activity通信的基础。onBind的方法签名如下所示

java 复制代码
@Override
public IBinder onBind(Intent intent) {
    return new MyBind();
}

主要是接收一个Intent,响应一个IBinder。此时我们可以编写一个类实现IBinder接口或继承Binder。然后在绑定后返回。同时,在Service与Activity绑定时一共需要两个类,一个ServiceConnection类以及一个IBinder类

ServiceConnection

该类主要是在连接成功与断开时使用。具有两个主要方法 ServiceConnected(ComponentName name, IBinder service),onServiceDisconnected(ComponentName name)

  1. 前者,当 Activity 与 Service 成功绑定后调用,可以通过 service 参数获取到 Service 的通信接口。
  2. 后者,当连接意外断开时调用(如 Service 崩溃或被杀死)。

IBinder

IBinder是一个接口,它是 Android 中进程间通信 (IPC) 的基础 。在 Service 与 Activity 绑定的场景中,Service 需要通过IBinder向 Activity 提供交互接口。他是交互的实际执行对象。 ,通常我们不选择implements IBinder接口,而是选择extends Binder。之后编写自己的方法。

Service与Activity的绑定

bindService(intent, mConnection, Context.BIND_AUTO_CREATE);用于两者的绑定

unbindService(mConnection);用于两者的解绑 下方是部分示例代码

java 复制代码
public class MyService extends Service {
    // 自定义Binder类,继承自Binder并实现我们需要的方法
    private final IBinder mBinder = new LocalBinder();
    
    public class LocalBinder extends Binder {
        // 返回当前Service的实例,这样Activity就能调用Service中的公共方法
        MyService getService() {
            return MyService.this;
        }
        
        // 也可以直接在这里定义供Activity调用的方法
        public void doSomething() {
            // 实现具体功能
        }
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    
    // Service中其他的公共方法,Activity可以通过LocalBinder获取Service实例后调用
    public void publicMethod() {
        // 实现具体功能
    }
}
java 复制代码
public class MainActivity extends AppCompatActivity {
    private MyService mService;
    private boolean mBound = false;
    
    // 实现ServiceConnection接口
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            // 当与Service的连接建立后,此方法会被调用
            // 通过IBinder获取Service实例
            MyService.LocalBinder binder = (MyService.LocalBinder) service;
            mService = binder.getService();
            mBound = true;
            
            // 连接成功后可以立即调用Service中的方法
            if (mBound) {
                mService.publicMethod();
                // binder.doSomething()
            }
        }
        
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            // 当与Service的连接意外断开时(如Service崩溃或被杀死),此方法会被调用
            mBound = false;
        }
    };
    // ...
    
    @Override
    protected void onStart() {
        super.onStart();
        // 绑定Service
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        // 解除绑定
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
    
    // Activity中的方法,调用Service的功能
    public void callServiceMethod(View view) {
        if (mBound) {
            mService.doSomething();
        }
    } 
    // ...
}

在上面的示例中,MainActivity启动后,执行onStart。绑定并启动Service。同时通过onBind()获得IBinder对象,执行onServiceConnected(ComponentName, IBinder)获得IBinder对象,就可以执行IBinder中的方法了。断开则是执行去onServiceDisconnected()

onBind() -> onServiceConnected() -> onServiceDisconnected()

Service的生命周期

类似于Activity,Service也有自己的生命周期。不过Service的生命周期相对来说比较复杂。主要有两种启用方式,startService与bindService。具体执行流程可以如下图所示。

text 复制代码
               onCreate()
                   ↓
           ┌────────────────┐
           │   启动方式?   │───────────┐
           └───────┬────────┘           │
                   ↓                    │
     ┌───────────────────────┐          ↓         
     │    startService()     │    bindService()
     │  ┌─────────────────┐  │    ┌────────────┐
     │  │ onStartCommand()│  │    │  onBind()  │
     │  └─────────────────┘  │    └──────┬─────┘
     │         ↓             │           ↓
     │    运行中 Service     │       客户端绑定
     │         ↓             │           ↓
     │    stopService()/     │    unbindService()
     │     stopSelf()        │           ↓
     └───────────┬───────────┘    ┌─────────────┐
                 │                │  onUnbind() │
                 │                └──────┬──────┘
                 │                       ↓
                 └───────────┬───────────┘
                             ↓
                        onDestroy()
  1. 每调用一次startService()方法,onStartCommand()就会执行一次,但实际上每个Service只会存在一个实例 。所以不管你调用了多少次startService()方法,只需调用一次stopService()或stopSelf()方法,Service就会停止。在这一类启动方法下- Service 会在后台无限期运行,直到被系统终止 或调用stopService()/stopSelf()。

  2. 当调用bindService()时,Service 与绑定的组件(如 Activity)生命周期绑定 ,组件解除绑定时 Service 会被销毁。

  3. 当两种启动方式都使用呢,根据Android系统的机制,一个Service只要被启动或者被绑定了之后,就会处于运行状态 ,必须要让以上两种条件同时不满足 ,Service才能被销毁。所以,这种情况下要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行。

TIP:每个Service只会存在一个实例,其是单例的。所以对于同一个Service,onCreate()与onDestory()只会执行一次。

相关推荐
Dovis(誓平步青云)4 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
晚风叙码5 小时前
MySQL 数据类型详解:从数值到字符串,一篇讲透
android·mysql·adb
传奇开心果编程6 小时前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程9 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程10 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程10 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程11 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓11 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程11 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer
supabc12311 小时前
Celium:连接 Windows、Mac、Linux 与 Android,让远程访问和设备管理更简单
android·linux·windows·macos·远程访问·网络管理·celium