【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);


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


相关推荐
0白露32 分钟前
Apifox Helper 与 Swagger3 区别
开发语言
Tanecious.1 小时前
机器视觉--python基础语法
开发语言·python
叠叠乐2 小时前
rust Send Sync 以及对象安全和对象不安全
开发语言·安全·rust
战族狼魂2 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
Tttian6223 小时前
Python办公自动化(3)对Excel的操作
开发语言·python·excel
xyliiiiiL3 小时前
ZGC初步了解
java·jvm·算法
杉之3 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
hycccccch4 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq
独好紫罗兰4 小时前
洛谷题单2-P5713 【深基3.例5】洛谷团队系统-python-流程图重构
开发语言·python·算法
每次的天空5 小时前
Android学习总结之算法篇四(字符串)
android·学习·算法