【Android】安卓四大组件之Service用法

文章目录

使用Handler更新UI

  1. 主线程创建Handler对象,重写handlerMessage方法
  2. 子线程创建Message对象,使用Handler对象调用sendMessage方法发送消息,发到MessageQueue
  3. Looper一直尝试从MessageQueue中取出待处理消息,分发给Handler的handlerMessage方法中
java 复制代码
public class MainActivity extends AppCompatActivity {
    // 创建一个 Handler 实例,用于在主线程处理消息
    private Handler handler = new Handler(Looper.myLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            // 当接收到的消息的 what 属性为 1 时执行以下代码
            if (msg.what == 1) {
                // 获取消息中的数据并转换为字符串
                String data = (String) msg.obj;
                // 将 TextView 的文本设置为接收到的数据
                tv.setText(data);
            }
        }
    };

    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 设置活动的布局
        setContentView(R.layout.activity_main);

        // 获取布局文件中的 TextView 控件
        tv = findViewById(R.id.tv_response);
        // 获取布局文件中的 Button 控件
        Button button = findViewById(R.id.btn_send);
        // 为按钮设置点击事件监听器
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建一个新的 Message 对象
                Message message = new Message();
                // 设置消息的内容
                message.obj = "数据";
                // 设置消息的标识符
                message.what = 1;
                // 通过 handler 发送消息
                handler.sendMessage(message);
            }
        });
    }
}

Service

基本特点

  • 后台执行 : Service 主要用于在后台执行一些长时间运行的操作,比如音乐播放、文件下载等,而不会影响用户界面的交互。
  • 没有界面 : Service 不像 Activity 那样有界面。它运行在后台,通常没有用户界面。
  • 主线程 : Service 运行在主线程中,因此在 Service 中执行耗时操作(如网络请求或文件操作)会阻塞主线程,可能会导致应用无响应。为了避免这种情况,通常需要使用异步机制,如 AsyncTaskHandlerExecutorService 等。

启动方式

非绑定式服务

(Started Service)

  • 启动方式 :通过 startService() 方法启动。
  • 生命周期 :服务的生命周期与启动它的组件无关。服务一旦启动,即使启动它的组件被销毁,服务仍会继续在后台运行,直到它自己被停止(通过 stopSelf()stopService())。
  • 交互 :启动的服务不能被组件直接调用其方法。如果需要与服务进行交互,需要通过广播、MessengerAIDL 等方式。
  • 用途:适用于需要在后台执行长时间操作的情况,例如,下载文件、播放音乐等。
使用步骤
  1. 定义服务

定义一个继承自 Service 的类,并实现它的生命周期方法。

java 复制代码
public class MyService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("MyService", "onCreate()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("MyService", "onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyService", "onDestroy()");
    }
}
  1. AndroidManifest.xml 中声明服务

确保在你的 AndroidManifest.xml 文件中注册服务:

xml 复制代码
<service android:name=".MyBoundService" />
  1. 启动服务
java 复制代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void startService(View view) {
        Intent intent = new Intent(this, MyService.class);
        startService(intent);
    }
}
  1. 关闭服务

  2. 由启动者(通常是Activity)调用 stopService 方法。

  3. 服务内部调用 stopSelf 方法。

  • 由启动者关闭服务

在Activity中,可以通过调用 stopService 来停止服务:

java 复制代码
Intent intent = new Intent(this, MyService.class);
stopService(intent);
  • 服务内部关闭自己

服务可以在内部调用 stopSelf 方法来停止自己:

java 复制代码
public class MyService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 某些逻辑操作完成后,停止服务
        stopSelf();
        return START_NOT_STICKY;
    }
}

绑定式服务

(Bound Service)

  • 启动方式 :通过 bindService() 方法启动。
  • 生命周期 :服务的生命周期与绑定它的组件(如 Activity)有关。绑定服务的存在时间是基于组件的生命周期,如果所有绑定它的组件都被销毁,服务也会被销毁。
  • 交互 :允许组件与服务进行交互,组件可以通过 ServiceConnection 接口获取到服务的引用,从而调用服务中的方法。
  • 用途:适用于需要与服务进行长期交互的情况,例如,组件需要获取服务的数据或调用服务中的方法。
步骤
  1. 定义服务

定义一个继承自 ServiceMyBindService类,并实现 onBind() 方法以返回一个实现IBinder 接口的类的对象。这个 IBinder 对象将用于与客户端进行通信。我们定义一个MyBinder类继承自Binder类,Binder类实现了IBinder接口

java 复制代码
public class MyBindService extends Service {
    private static final String TAG = "MyBindService";

    // 测试服务的方法
    public void testService() {
        Log.d(TAG, "服务的测试");
    }

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate executed");
        super.onCreate();
        // 在服务创建时初始化一些资源
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand executed");
        // 这里可以处理服务启动时的逻辑
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy executed");
        super.onDestroy();
        // 在服务销毁时释放资源
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind executed");
        return new MyBinder(this);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind executed");
        return super.onUnbind(intent);
        // 可以在这里处理服务解绑时的逻辑
    }

    // Binder类实现了IBinder接口
    public class MyBinder extends Binder {
        private final MyBindService mMyBindService;

        // 构造函数,用于初始化服务实例
        public MyBinder(MyBindService myBindService) {
            mMyBindService = myBindService;
        }

        // 提供一个方法来获取服务实例
        public MyBindService getService() {
            return mMyBindService;
        }

        // 测试Binder的方法
        public void test() {
            Log.d(TAG, "MyBinder测试");
            mMyBindService.testService();
        }
    }
}
  1. AndroidManifest.xml 中声明服务
xml 复制代码
<service android:name=".MyBoundService" />
  1. 绑定服务

在你的组件(如 Activity)中,通过 bindService() 方法绑定到服务。你需要实现 ServiceConnection 接口来处理与服务的连接状态。

java 复制代码
public class MainActivity extends AppCompatActivity {
    // 定义一个MyBinder对象,用于绑定服务后与服务交互
    public MyBindService.MyBinder myBinder = null;

    // 定义一个ServiceConnection对象,用于管理服务的连接和断开
    private ServiceConnection coon = new ServiceConnection() {
        // 当服务成功连接时回调该方法
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 将传入的IBinder对象转换为MyBinder
            myBinder = (MyBindService.MyBinder) service;
            // 调用MyBinder的test方法,测试服务功能
            myBinder.test();
        }

        // 当服务断开连接时回调该方法
        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 可以在这里处理服务断开时的逻辑
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    // 绑定服务的方法
    public void bindService(View view) {
        Intent intent = new Intent(this, MyBindService.class);
        bindService(intent, coon, BIND_AUTO_CREATE);
    }
}
  1. 解除绑定

通过 unbindService() 方法来完成解除绑定的操作

java 复制代码
@Override
protected void onStop() {
    super.onStop();
    if (isBound) {
        unbindService(connection);
        isBound = false;
    }
}

服务的绑定和解除绑定的注意事项

  • 绑定后:一旦绑定成功,组件可以通过服务提供的方法进行交互。
  • 解除绑定后 :如果没有其他组件绑定该服务,服务会被销毁(如果服务是绑定服务),并调用 onDestroy() 方法进行清理。
  • 线程处理:如果服务需要执行长时间运行的任务,最好在服务中使用后台线程或异步任务来避免阻塞主线程。

生命周期

Service 的生命周期主要包括以下几个阶段:

  • onCreate(): 当服务第一次被创建时调用。通常在这里进行服务初始化。
  • onStartCommand(Intent intent, int flags, int startId) : 当服务被 startService() 方法启动时调用。在这个方法中可以处理服务的任务。返回的 int 值决定了服务被系统杀掉后如何重启。
  • onBind(Intent intent) : 当 bindService() 方法被调用时触发。用于提供与 Service 的绑定接口。若 Service 不需要绑定,可以返回 null
  • onDestroy(): 当服务被销毁时调用。通常在这里进行清理工作。

非绑定式

启动阶段
  • onCreate()
    • 只会在服务首次创建时调用一次。适合在这里做一些一次性的初始化工作。
  • onStartCommand(Intent intent, int flags, int startId)
    • 每次调用 startService 时都会执行。可以在这里处理传入的 Intent。
结束阶段

启动者(Activity)调用 stopService 或服务内部调用 stopSelf

  • 当 Activity 调用 stopService(Intent intent) 或服务内部调用 stopSelf() 方法时,服务会停止并触发 onDestroy 方法。

  • onDestroy()

    • 服务销毁前的最后一个方法。适合在这里做一些资源释放的收尾工作。

      java 复制代码
      @Override
      public void onDestroy() {
          super.onDestroy();
          // 释放资源
      }

绑定式

启动阶段

**启动者(Activity)**调用 bindService(Intent intent, ServiceConnection conn, int flags)

  • onCreate()
    • 只会在服务首次创建时调用一次。适合在这里做一些一次性的初始化工作。
  • onBind(Intent intent)
    • 只会在首次绑定时执行一次。返回一个 IBinder 对象,供客户端与服务进行通信。
结束阶段

启动者(Activity)销毁或调用 unbindService 方法

  • 当 Activity 销毁或调用 unbindService(ServiceConnection conn) 方法时,会解除与服务的绑定。当没有绑定者时,服务会销毁。

  • onUnbind(Intent intent)

    • 解除绑定:当所有客户端都解除绑定时,会调用此方法。可以在这里处理一些解绑相关的逻辑。
  • onDestroy()

    • 销毁:服务销毁前的最后一个方法。适合在这里做一些资源释放的收尾工作。

注意事项

  • 服务绑定和解除绑定 :确保在 Activity 的生命周期中正确绑定和解除绑定服务,以避免内存泄漏。
  • 资源释放 :在 onDestroy 中释放所有资源,确保服务完全终止时不留下任何资源占用。

++onCreate->onBind->onUnbind->onDestory++

前台Service

前台服务 (Foreground Service) 是一种能够持续运行并与用户进行交互的服务。相比于后台服务,前台服务具有更高的优先级,通常不会被系统回收。

使用步骤

  1. 创建通知 (Notification)

前台服务必须显示一个通知来告知用户该服务正在运行。可以使用 NotificationCompat.Builder 来创建通知。

java 复制代码
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
        .setContentTitle("服务正在运行")
        .setContentText("前台服务示例")
        .setSmallIcon(R.drawable.ic_service_icon)
        .build();
  1. 调用startForeground方法

在服务的 onCreateonStartCommand 方法中调用 startForeground(notificationId, notification) 方法,将服务提升到前台状态。

java 复制代码
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("服务正在运行")
            .setContentText("前台服务示例")
            .setSmallIcon(R.drawable.ic_service_icon)
            .build();
    
    startForeground(1, notification);
    // 其他业务逻辑
    return START_STICKY;
}
  1. 申请权限
xml 复制代码
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

结束

  • 完全停止ServicestopServicestopSelf
  • 降级Service状态stopForeground(true) 将Service从前台状态退出并移除通知,但Service仍然在运行,只是优先级降低。
结束Service本身
  • 正常的结束Service方式 :外部调用 stopService 方法或自身调用 stopSelf 方法。这两种方式都会完全停止Service。当Service结束后,所有与之相关的通知也会被移除。

示例:

java 复制代码
// 通过外部调用
stopService(new Intent(context, MyService.class));

// 通过Service自身调用
stopSelf();
降级为普通Service

退出Service的前台状态,降级为普通Service

  • 调用 stopForeground(true) 方法 :这会将Service从前台状态退出,变成普通的后台Service。这样做会降低Service的优先级,在系统内存不足时可能会被回收销毁。参数 true 表示同时移除通知。

示例:

java 复制代码
// 将Service退出前台状态,同时移除通知
stopForeground(true);

elf` 方法。这两种方式都会完全停止Service。当Service结束后,所有与之相关的通知也会被移除。

示例:

java 复制代码
// 通过外部调用
stopService(new Intent(context, MyService.class));

// 通过Service自身调用
stopSelf();
降级为普通Service

退出Service的前台状态,降级为普通Service

  • 调用 stopForeground(true) 方法 :这会将Service从前台状态退出,变成普通的后台Service。这样做会降低Service的优先级,在系统内存不足时可能会被回收销毁。参数 true 表示同时移除通知。

示例:

java 复制代码
// 将Service退出前台状态,同时移除通知
stopForeground(true);


感谢您的阅读
如有错误烦请指正


相关推荐
老猿讲编程10 分钟前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk1 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*1 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue1 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man1 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟1 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
数据猎手小k2 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
P.H. Infinity2 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天2 小时前
java的threadlocal为何内存泄漏
java
caridle2 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express