我的安卓第一课:四大组件之一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的生命周期相对来说比较复杂。主要有两种启用方式,startServicebindService。具体执行流程可以如下图所示。

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()只会执行一次。

相关推荐
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android