目录
Android Service (服务)
线程
Service (服务)
Service 相关方法
Android 非绑定 Service
startService() 启动 Service
验证 startService() 启动 Service 的调用顺序
Android 绑定 Service
bindService() 启动 Service
验证 BindService 启动 Service 的顺序
startService() 启动 Service 后 bindService() 绑定
Android IntentService 耗时操作
IntentService
范例
Android Service (服务)
在 Android 中,Service 主要用于在后台执行长时间运行的操作,并且提供了与其他组件交互的机制。Service 和 Thread 有一些相似之处,但也存在着明显的区别。 Thread 在应用程序的进程内创建的轻量级执行单元。一个进程可以有多个线程,它们共享相同的内存空间和资源,是 Java 中用于实现并发的基本单位。
线程
我们先来看看 Thread (线程) 有关的一些概念:
- 程序(Program):指的是为了完成特定任务而用某种编程语言编写的一组指令集合,通常是静态的代码文件。
- 进程(Process):是操作系统中的一个独立执行单位,运行中的程序。每个进程都有独立的内存空间,操作系统为每个进程分配一段内存空间,用于存储程序的代码、数据以及运行时的状态信息。进程是操作系统进行资源分配和调度的基本单位,一个进程可以包含多个线程。
- 线程(Thread):是比进程更小的执行单元,是程序执行流的最小单元。一个进程中可以有多个线程,它们共享相同的内存空间和资源,但拥有各自的执行路径。线程由程序负责管理,而进程则由操作系统进行调度。
- 多线程的理解:多线程是指在一个程序中同时执行多个线程,每个线程都独立运行,并且可能同时访问共享的内存空间。虽然在单核处理器上实际上是分时执行的,但由于线程切换的速度非常快,用户会感觉到线程是同时执行的。
- 线程的生命周期:线程的生命周期包括创建、就绪、运行、阻塞和销毁等状态。线程的状态会随着执行过程中的不同操作而发生变化。
关于创建线程的方式,有三种常见的方法:
- 继承 Thread 类:定义一个类继承自 Thread,并重写其 run() 方法来定义线程执行的任务。
- 实现 Runnable 接口:定义一个类实现 Runnable 接口,并实现其 run() 方法,然后将该类的实例传递给 Thread 的构造方法。
- 实现 Callable 接口:定义一个类实现 Callable 接口,并实现其 call() 方法,然后将该类的实例传递给 ExecutorService 的 submit() 方法。
在 Android 开发中,通常推荐使用第二种方式,即实现 Runnable 接口来创建线程。常见的做法是通过创建一个 Runnable 对象,并将其传递给 Thread 类的构造方法来创建新的线程。另外,也可以使用匿名类的方式来创建线程,这在 Android 开发中更为常见。
Service (服务)
在 Android 中,Service 是一种用于执行长时间运行操作的组件,可以在后台执行任务而不需要用户界面。Service 的生命周期包括以下方法:
1.onCreate():当 Service 第一次创建时调用。在此方法中,通常进行一次性的初始化操作,例如创建线程、初始化变量等。
2. onStartCommand(Intent, int, int):当通过 startService() 方法启动 Service 时调用。在此方法中,Service 可以处理启动请求并执行相应的操作。该方法会返回一个整数值,用于指示系统在 Service 终止后是否应该尝试重新启动该 Service。
3. onBind(Intent):当通过 bindService() 方法启动 Service 时调用。在此方法中,Service 返回一个 IBinder 对象,允许客户端与 Service 进行通信。如果不想允许绑定,则可以返回 null。
4. onUnbind(Intent):当所有客户端与 Service 解绑时调用。在此方法中,通常执行一些资源清理或其他必要的操作。
5. onDestroy():当 Service 即将销毁时调用。在此方法中,通常释放资源、停止线程等清理工作。
Android 中使用 Service 的方式有两种:
1. startService():通过调用 Context 的 startService() 方法来启动 Service。调用此方法后,Service 将会在后台运行,即使启动它的组件已经销毁。通常用于执行一次性任务,例如下载文件、播放音乐等。
2. bindService():通过调用 Context 的 bindService() 方法来绑定 Service。绑定 Service 后,客户端可以与 Service 进行通信,直到所有客户端解绑。通常用于需要与 Service 进行交互的场景,例如绑定到音乐播放器 Service 并控制播放状态。
这两种方式可以结合使用,例如通过 bindService() 方法绑定到 Service 并获取其实例,然后再调用其方法执行任务。
Service 相关方法
1、onCreate():
- 说明:当 Service 第一次被创建后立即回调该方法。该方法在整个生命周期中只会调用一次。
- 用途:通常在这个方法中进行一次性的初始化操作,比如创建线程、初始化变量等。
2、onDestroy():
- 说明:当 Service 被关闭时会回调该方法。该方法只会在 Service 被销毁时调用一次。
- 用途:通常在这个方法中执行一些清理工作,释放资源、停止线程等。
3、onStartCommand(intent, flag, startId)(在早期版本是 onStart(intent, startId)):
- 说明:当客户端调用 startService(Intent) 方法时会回调。可多次调用 startService() 方法,但不会再创建新的 Service 对象,而是继续复用前面产生的 Service 对象,会继续回调onStartCommand() 方法。
- 用途:用于处理客户端对 Service 的启动请求,执行相应的操作。方法的参数包括 Intent 对象(携带启动服务的请求信息)、flag(标志位)和 startId(启动命令的唯一标识)。
4、onBind(intent):
- 说明:Service 必须实现的方法,该方法会返回一个 IBinder 对象,应用程序通过该对象与 Service 组件进行通信。
- 用途:用于实现与客户端绑定的通信接口,返回的 IBinder 对象用于客户端与 Service 进行通信。
5、onUnbind(intent):
- 说明:Service 上绑定的所有客户端都断开连接时会回调该方法。
- 用途:通常在这个方法中执行一些资源清理或其他必要的操作。
Android 非绑定 Service
startService() 启动 Service
startService() 方法用于启动一个 Service。第一次启动 Service 时,会创建一个新的 Service 实例,并按顺序调用 onCreate() 和 onStartCommand() 方法。
一旦 Service 进入运行状态,再次调用 startService() 时,不会创建新的 Service 实例,而是系统会直接复用之前创建的 Service 实例,并调用其 onStartCommand() 方法。
这种 Service 与其调用者之间没有必然的联系,即使调用者的生命周期结束了,只要没有调用 stopService() 方法,Service 仍然会继续运行。
不论调用了多少次 startService(),只需调用一次 stopService() 就可以停止 Service 的运行。
验证 startService() 启动 Service 的调用顺序
1、MyService.java:
package com.example.myapplication2;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate()已调用");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "使用startId调用onStartCommand(): " + startId);
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// 不是绑定式服务,返回 null
return null;
}
}
2、MainActivity.java:
package com.example.myapplication2;
import android.os.Bundle;
import android.content.Intent;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "正在启动MyService。。。");
Intent serviceIntent = new Intent(this, MyService.class);
startService(serviceIntent);
}
}
3、确保在 AndroidManifest.xml 文件中注册了 MyService:
<service android:name=".MyService"/>
4、运行效果如下
Android 绑定 Service
bindService() 启动 Service
bindService(Intent Service,ServiceConnection conn,int flags)
bindService() 方法是用于在 Android 应用程序中绑定服务的方法。下面是对参数的解释:
- service:通过这个 Intent 参数指定要启动的服务。Intent 中包含了服务的标识符和其他必要的信息。
- conn:ServiceConnection 对象,用于监听访问者与服务之间的连接情况。当连接成功时,会回调该对象中的 onServiceConnected(ComponentName, IBinder) 方法。当服务由于异常终止或其他原因与访问者断开连接时,会调用 onServiceDisconnected(ComponentName) 方法。需要注意的是,主动通过 unBindService() 方法断开连接并不会调用 onServiceDisconnected() 方法。
- flags:指定绑定时是否自动创建服务。可以使用以下两个常量之一:0(不自动创建)或 BIND_AUTO_CREATE(自动创建)。如果服务还未创建且设置为自动创建,则在绑定时会自动创建服务。
通过调用 bindService() 方法,应用程序可以与服务建立连接,并通过 ServiceConnection 监听连接状态的变化。这样可以实现应用程序与服务之间的交互和通信。
注意:
- 使用 bindService() 方法来启动服务,并通过参数传递要启动的服务以及一个 ServiceConnection 对象,用于监听连接情况。
- 在 ServiceConnection 对象中,可以通过 onServiceConnected() 方法来处理与服务连接成功的情况,并在其中获取 IBinder 对象,以实现与服务之间的通信。
- 如果服务所在的宿主异常终止或其他原因终止,会调用 onServiceDisconnected() 方法。
- 解除与服务的绑定可以通过调用 unbindService() 方法来实现,此时会调用服务的 onUnbind() 和 onDestroy() 方法。
- 多个客户端绑定同一个服务时,当所有客户端都解除绑定后,系统会销毁服务,除非服务也被 startService() 方法启动。
- 在自定义服务中,一般需要继承 Binder 类并实现自己的 IBinder 对象,并在 onBind() 方法中返回该对象。
综上所述,通过绑定服务,应用程序能够与服务之间建立更为紧密的连接,并进行双向通信。
验证 BindService 启动 Service 的顺序
1、MainActivity.java:
package com.example.myapplication2;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private TimerService timerService;
private boolean isBound = false;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
TimerService.TimerBinder binder = (TimerService.TimerBinder) service;
timerService = binder.getService();
isBound = true;
Log.d(TAG, "onServiceConnected:TimerService已连接");
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
Log.d(TAG, "onServiceDisconnected:TimerService已断开连接");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 启动并绑定服务
Intent intent = new Intent(this, TimerService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (isBound) {
unbindService(serviceConnection);
isBound = false;
}
}
}
2、MyService.java:
package com.example.myapplication2;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class TimerService extends Service {
private static final String TAG = "TimerService";
private final IBinder binder = new TimerBinder();
private boolean isRunning = false;
public class TimerBinder extends Binder {
TimerService getService() {
return TimerService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind:TimerService绑定");
return binder;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate:TimerService已创建");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "StartCommand:TimerService已启动");
return START_NOT_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy:TimerService已销毁");
}
// 启动计时器的方法
public void startTimer() {
Log.d(TAG, "StartTimer:计时器已启动");
isRunning = true;
}
// 停止计时器的方法
public void stopTimer() {
Log.d(TAG, "stopTimer:计时器已停止");
isRunning = false;
}
}
3、确保在 AndroidManifest.xml 文件中注册了 TimerService:
<service android:name=".TimerService"/>
startService() 启动 Service 后 bindService() 绑定
当一个服务已经通过 startService() 方法启动后,即使有其他客户端通过 bindService() 方法绑定到该服务,服务仍然会保持运行状态,直到所有客户端都调用了 unbindService() 方法来解除绑定,并且没有其他客户端通过 startService() 方法启动了该服务,才会触发服务的销毁。
在这种情况下,如果调用 bindService() 来绑定到一个已经启动的服务,系统只是将服务的内部 IBinder 对象传递给客户端,不会将服务的生命周期与客户端绑定。因此,即使客户端调用 unbindService() 方法解除绑定,服务也不会被销毁。
总结起来,对于已经通过 startService() 启动的服务,即使有其他客户端通过 bindService() 方法绑定到该服务,服务也只有在所有客户端都调用了 unbindService() 方法解除绑定,并且没有其他客户端通过 startService() 方法启动了该服务时才会被销毁。
Android IntentService 耗时操作
IntentService
IntentService 是 Android 平台提供的一种服务,用于处理异步请求。它是 Service 类的一个子类,但相比普通的 Service,IntentService 更适合用来执行需要较长时间的操作,而且它可以在后台自动停止,无需手动控制。
下面是 IntentService 的一般用法:
- 启动 IntentService:客户端通过调用 startService(Intent) 方法来启动 IntentService。这个 Intent 包含了要执行的操作的描述。
- 自动停止:IntentService 在执行完所有的请求后会自动停止。这意味着我们不需要手动去停止 IntentService。
- 多次启动:IntentService 可以被启动多次。每次启动时,请求的 Intent 会被加入到工作队列中,然后按照队列顺序依次执行。注意,虽然可以多次启动 IntentService,但是每次只会执行一个工作线程,确保操作的顺序性和线程安全性。
- onHandleIntent() 回调方法:每个请求的操作会在 IntentService 的 onHandleIntent(Intent) 回调方法中执行。这个方法在工作线程中执行,因此可以执行耗时操作,而不会阻塞主线程。
通过这些特性,IntentService 提供了一种方便的方式来处理异步操作,尤其适合那些需要按顺序执行的任务或者需要在后台执行的长时间操作。
范例
1、MyIntentService.java
package com.example.myapplication2;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
// 打印一些日志
for (int i = 0; i < 5; i++) {
Log.d(TAG, "正在执行任务 " + i);
try {
// 模拟耗时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 任务完成后,IntentService 会自动停止
}
}
2、确保在 AndroidManifest.xml 文件中注册了 MyIntentService:
<service
android:name=".MyIntentService"
android:exported="false" />
3、接下来,在应用中启动这个 IntentService:
package com.example.myapplication2;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MyIntentService.class);
startService(intent);
}
}