Android --- Service
出自于此,写得很清楚。
关于Android Service真正的完全详解,你需要知道的一切_android service-CSDN博客
出自【zejian的博客】
什么是Service?
Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。
服务可由其他应用组件启动(如Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。
此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。
Service基本上分为两种形式:
- 启动状态
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。
- 绑定状态
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
如何使用Service?
清单文件声明
使用Service前会在清单文件中声明配置。
<service android:enabled=["true" | "false"]android:exported=["true" | "false"]android:icon="drawable resource"android:isolatedProcess=["true" | "false"]android:label="string resource"android:name="string"android:permission="string"android:process="string" >. . .
</service>
- android:exported:代表是否能被其他应用隐式调用,其默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法打开,即无法被其他应用隐式调用。
- android:name:对应Service类名
- android:permission:是权限声明
- android:process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote。
- android:isolatedProcess :设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(bind and start)。
- android:enabled:是否可以被系统实例化,默认为 true因为父标签 也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活。
创建Service子类
首先要创建服务,必须创建 Service 的子类(或使用它的一个现有子类如IntentService)。
在实现中,我们需要重写一些回调方法(其中onBind()方法必须重写),以处理服务生命周期的某些关键过程。
package com.example.androidstudiostudy.service;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;//创建 Service 的子类(或使用它的一个现有子类如IntentService),重写一些回调方法
public class OneService extends Service {public OneService() {}// 绑定服务时调用@Overridepublic IBinder onBind(Intent intent) {// TODO: Return the communication channel to the service.throw new UnsupportedOperationException("Not yet implemented");}// 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 之前)。// 如果服务已在运行,则不会调用此方法,该方法只调用一次@Overridepublic void onCreate() {super.onCreate();Log.d("服务","首次创建服务调用此方法来执行一次性设置程序,该方法只调用一次");}// 每次通过startService()方法启动Service时都会被回调。@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d("服务","onStartCommand");return super.onStartCommand(intent, flags, startId);}// 服务销毁时回调@Overridepublic void onDestroy() {super.onDestroy();Log.d("服务","销毁服务");}
}
- onBind()
当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null。
- onCreate()
首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 之前)。如果服务已在运行,则不会调用此方法,该方法只调用一次
- onStartCommand()
当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,通过调用 stopSelf() 或 stopService() 来停止服务。(在绑定状态下,无需实现此方法。)
- onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。
启动Service
使用 startService(intent);
停止Serviece
使用 stopService(intent);
通过Demo测试一下Service启动状态方法的调用顺序,依次点击启动和停止。StudyService 代码如下:
public class StudyService extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_study_service);}public void serviceAction(View view) {int id = view.getId();Intent intent = new Intent(this,OneService.class);if(id == R.id.bindService){// 绑定service} else if (id == R.id.stopService) {stopService(intent); // 停止服务} else {startService(intent); // 启动服务}}
}
此时的清单文件:
<applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/study"android:label="@string/app_name"android:networkSecurityConfig="@xml/network_security_config"android:roundIcon="@mipmap/study"android:supportsRtl="true"android:theme="@style/Theme.AndroidStudioStudy"tools:targetApi="31"><activityandroid:name=".service.StudyService"android:exported="false" /><activityandroid:name=".service.studyService"android:exported="false" /><serviceandroid:name=".service.OneService"android:enabled="true"android:exported="true"android:permission=".service.OneService" />
日志打印:
绑定Service
绑定服务是Service的另一种变形,当Service处于绑定状态时,其代表着客户端-服务器接口中的服务器。
当其他组件(如 Activity)绑定到服务时,组件(如Activity)可以向Service(也就是服务端)发送请求,或者调用Service(服务端)的方法,此时被绑定的Service(服务端)会接收信息并响应,甚至可以通过绑定服务进行执行进程间通信 。
有时我们可能需要从Activity组件中去调用Service中的方法,此时Activity以绑定的方式挂靠到Service后,我们就可以轻松地方法到Service中的指定方法
与启动服务不同的是绑定服务的生命周期通常只在为其他应用组件(如Activity)服务时处于活动状态,不会无限期在后台运行,也就是说宿主(如Activity)解除绑定后,绑定服务就会被销毁。
那么在提供绑定的服务时,该如何实现呢?
实际上我们必须提供一个 IBinder接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口可以通过三种方法定义接口:
-
扩展 Binder 类
如果服务是提供给自有应用专用的,并且Service(服务端)与客户端相同的进程中运行(常见情况),则应通过扩展 Binder 类并从 onBind() 返回它的一个实例来创建接口。
客户端收到 Binder 后,可利用它直接访问 Binder 实现中以及Service 中可用的公共方法。如果我们的服务只是自有应用的后台工作线程,则优先采用这种方法。
不采用该方式创建接口的唯一原因是,服务被其他应用或不同的进程调用。
- 在Service子类中创建一个扩展 Binder 的类(OneServiceBinder),在类中声明了一个getService方法,客户端可访问该方法获取 Service子类 对象的实例,只要客户端获取到 OneServiceBinder 对象的实例就可调用服务端的公共方法。
- 创建一个实现IBinder 接口的实例对象并提供公共方法给客户端调用
- 从 onBind() 回调方法返回此 Binder 实例。
private OneServiceBinder oneServiceBinder = new OneServiceBinder();// 当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。 // 在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。 // 无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null。 @Override public IBinder onBind(Intent intent) {return oneServiceBinder;}/*** 创建Binder对象,返回给客户端即Activity使用,提供数据交换的接口*/public class OneServiceBinder extends Binder {// 声明一个方法,getService。(提供给客户端调用)OneService getService() {// 返回当前对象LocalService,这样我们就可在客户端端调用Service的公共方法了return OneService.this;}}
完整service代码
package com.example.androidstudiostudy.service;import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;//创建 Service 的子类(或使用它的一个现有子类如IntentService),重写一些回调方法
public class OneService extends Service {private OneServiceBinder oneServiceBinder = new OneServiceBinder();private Thread thread;// 当另一个组件想通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。// 在此方法的实现中,必须返回 一个IBinder 接口的实现类,供客户端用来与服务进行通信。// 无论是启动状态还是绑定状态,此方法必须重写,但在启动状态的情况下直接返回 null。@Overridepublic IBinder onBind(Intent intent) {return oneServiceBinder;}/*** 创建Binder对象,返回给客户端即Activity使用,提供数据交换的接口*/public class OneServiceBinder extends Binder {// 声明一个方法,getService。(提供给客户端调用)OneService getService() {// 返回当前对象LocalService,这样我们就可在客户端端调用Service的公共方法了return OneService.this;}}// 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或onBind() 之前)。// 如果服务已在运行,则不会调用此方法,该方法只调用一次private int count = 0;private boolean quit = false;@Overridepublic void onCreate() {super.onCreate();Log.d("服务", "首次创建服务调用此方法来执行一次性设置程序,该方法只调用一次");thread = new Thread(new Runnable() {@Overridepublic void run() {while (!quit) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}count++;}}});thread.start();}// 当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。// 一旦执行此方法,服务即会启动并可在后台无限期运行。 如果自己实现此方法,则需要在服务工作完成后,通过调用 stopSelf() 或 stopService() 来停止服务。// (在绑定状态下,无需实现此方法。)@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d("服务", "onStartCommand");return super.onStartCommand(intent, flags, startId);}// 当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。@Overridepublic void onDestroy() {super.onDestroy();this.quit = true;Log.d("服务", "销毁服务");}//--------------------公共方法------------------public int getCount() {return count;}//--------------------解除绑定时调用------------------@Overridepublic boolean onUnbind(Intent intent) {Log.d("服务", "解除绑定");return super.onUnbind(intent);}
}
客户端绑定到服务步骤:
1.ServiceConnection代表与服务的连接,它只有两个方法,实现ServiceConnection,重写这两个回调方法。
- onServiceConnected()—系统会调用该方法以传递服务的onBind()返回的IBinder;
- onServiceDisconnected()—Android系统会在服务崩溃或被杀死导致的连接中断时调用(或者随着activity 的生命周期stop)时调用该方法,当客户端取消绑定的时候,不会回调该方法
private ServiceConnection serviceConnection;private OneService myService;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_study_service);serviceConnection = new ServiceConnection() {// 绑定成功时调用@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {Log.d("绑定服务","成功绑定服务");OneService.OneServiceBinder oneServiceBinder = (OneService.OneServiceBinder) iBinder;myService = oneServiceBinder.getService();}// Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法@Overridepublic void onServiceDisconnected(ComponentName componentName) {Log.d("绑定服务","与服务的连接意外中断");myService = null;}};}
2.调用bindService(),传递ServiceConnection
3.当系统调用onServiceConnected()的回调方法时,可以使用接口定义的方法开始调用服务
4.要断开与服务的连接,请调用unBindService()
如果应用在客户端与服务仍然绑定的状态下被销毁了,则销毁会导致客户端取消绑定。
Activity代码
public class StudyService extends AppCompatActivity {private ServiceConnection serviceConnection;private OneService myService;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_study_service);serviceConnection = new ServiceConnection() {// 绑定成功时调用@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {Log.d("绑定服务","成功绑定服务");OneService.OneServiceBinder oneServiceBinder = (OneService.OneServiceBinder) iBinder;myService = oneServiceBinder.getService();}// Android 系统会在与服务的连接意外中断时(例如当服务崩溃或被终止时)调用该方法@Overridepublic void onServiceDisconnected(ComponentName componentName) {Log.d("绑定服务","与服务的连接意外中断");myService = null;}};}public void serviceAction(View view) {int id = view.getId();Intent intent = new Intent(this,OneService.class);if(id == R.id.bindService){// 绑定servicebindService(intent,serviceConnection,Service.BIND_AUTO_CREATE);if (myService != null) {// 通过绑定服务传递的Binder对象,获取Service暴露出来的数据Log.d("获取绑定数据", "从服务端获取数据:" + myService.getCount());} else {Log.d("获取绑定数据", "还没绑定呢,先绑定,无法从服务端获取数据");}} else if (id == R.id.stopService) {stopService(intent);} else {startService(intent);}}
}
打印数据
-
使用 Messenger
前面了解到应用内同一进程的通信可以使用IBinder,而不同进程间的通信,最简单的方式则是使用 Messenger 服务提供通信接口,利用此方式,我们无需使用 AIDL 便可执行进程间通信 (IPC)。Messenger底层也是通过aidl实现,不过封装了一层,AIDL 支持多线程并发。messenger是同步,如果没有多线程并发要求,就可以使用轻量级的Messenger。
以下是使用 Messenger 绑定Service的主要步骤:
主要步骤
1.创建一个服务子类(MessengerService )并在里面实现一个 Handler,由其接收来自客户端的每个调用的回调
// 用于接收从客户端传递过来的数据class ServiceReciveHandle extends Handler {@Overridepublic void handleMessage(@NonNull Message msg) {switch (msg.what) {case MSG_SAY_HELLO:Log.i(TAG, "thanks,Service had receiver message from client!");break;default:super.handleMessage(msg);}}}
2.将该Handler 用于创建 Messenger 对象(对 Handler 的引用)
3.Messenger 会创建一个 IBinder,MessengerService 通过 onBind() 返回这个Messenger对象的底层Binder。
final Messenger messenger = new Messenger(new ServiceReciveHandle());@Overridepublic IBinder onBind(Intent intent) {Log.i(TAG, "服务绑定");return messenger.getBinder();}
4.客户端使用 IBinder 将 Messenger(引用MessengerService 的 Handler)实例化,然后使用Messenger将 Message 对象发送给服务。
// 实现与服务端链接的对象private final ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {// 通过服务端传递的IBinder对象,创建相应的Messenger// 通过该Messenger对象与服务端进行交互Log.i(TAG, "服务链接绑定");myService = new Messenger(iBinder);mBound = true;}@Overridepublic void onServiceDisconnected(ComponentName componentName) {Log.i(TAG, "服务链接绑定取消");myService = null;mBound = false;}};
5.MessengerService 在其 Handler 中(在 handleMessage() 方法中)接收每个 Message。
完整Service代码:
package com.example.androidstudiostudy.service;import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;import androidx.annotation.NonNull;public class MessengerService extends Service {static final int MSG_SAY_HELLO = 1;private static final String TAG = "MessengerService";// 用于接收从客户端传递过来的数据class ServiceReciveHandle extends Handler {@Overridepublic void handleMessage(@NonNull Message msg) {switch (msg.what) {case MSG_SAY_HELLO:Log.i(TAG, "服务器接收到来自客户端的消息");break;default:super.handleMessage(msg);}}}final Messenger messenger = new Messenger(new ServiceReciveHandle());@Overridepublic IBinder onBind(Intent intent) {Log.i(TAG, "服务绑定");return messenger.getBinder();}@Overridepublic void onCreate() {Log.i(TAG, "服务onCreate");super.onCreate();}@Overridepublic void onDestroy() {Log.i(TAG, "服务Destroy");super.onDestroy();}
}
Activity代码
package com.example.androidstudiostudy.service;public class MessengerServiceActivity extends AppCompatActivity {private static final String TAG = "MessengerService-Activity";// 与服务端交互的Messengerprivate Messenger myService = null;// 是否绑定boolean mBound = false;// 实现与服务端链接的对象private final ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {// 通过服务端传递的IBinder对象,创建相应的Messenger// 通过该Messenger对象与服务端进行交互Log.i(TAG, "服务链接绑定");myService = new Messenger(iBinder);mBound = true;}@Overridepublic void onServiceDisconnected(ComponentName componentName) {Log.i(TAG, "服务链接绑定取消");myService = null;mBound = false;}};private Button sendMsg, bindService, unbindService, createService, destoryService;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_messenger_service);sendMsg = findViewById(R.id.sendMessageToService);sendMsg.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (!mBound)return;Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);try {// 发送消息myService.send(msg);} catch (RemoteException e) {throw new RuntimeException(e);}}});Intent intent = new Intent(MessengerServiceActivity.this, MessengerService.class);unbindService = findViewById(R.id.unbindMessengerService);unbindService.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {unbindService(mConnection);}});bindService = findViewById(R.id.bindMessengerService);bindService.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Log.i(TAG, "bd");bindService(intent, mConnection, Context.BIND_AUTO_CREATE);}});createService = findViewById(R.id.startMessengerService);createService.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {startService(intent);}});destoryService = findViewById(R.id.destoreyMessengerService);destoryService.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {stopService(intent);}});}
}
服务器与客户端的双向通信
上述代码能够实现客户端向服务器的通信,如果想要服务器向客户端通信,则需要在客户端也创建一个接收消息的Messenger和Handler,改造 MessengerService 中的handler,在接受到信息时发送信息。
MessengerService:
在服务器端的handler中发送返回消息
// 用于接收从客户端传递过来的数据private static class ServiceReciveHandle extends Handler {@Overridepublic void handleMessage(@NonNull Message msg) {if (msg.what == MSG_SAY_HELLO) {Log.i(TAG, "服务器接收到来自客户端的消息");Messenger replyMessenger = msg.replyTo;Message replyMessenge = Message.obtain(null, MessengerService.MSG_SAY_HELLO);Bundle bundle=new Bundle();bundle.putString("reply","ok~,I had receiver message from you! ");replyMessenge.setData(bundle);try {replyMessenger.send(replyMessenge);} catch (RemoteException e) {throw new RuntimeException(e);}} else {super.handleMessage(msg);}}}
Activity:
1.创建一个用于接收服务器端消息的Messenger和Handler
2.在发送消息时,把接收服务器端的回复的Messenger通过Message的replyTo参数传递给服务端
private final Messenger activityRecevierMessenger = new Messenger(new activityRecevierHandler());private static class activityRecevierHandler extends Handler{@Overridepublic void handleMessage(@NonNull Message msg) {if (msg.what == MessengerService.MSG_SAY_HELLO) {Log.i(TAG, "客户端接收到来自服务的消息" + msg.getData().getString("reply"));} else {super.handleMessage(msg);}}}
sendMsg = findViewById(R.id.sendMessageToService);sendMsg.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (!mBound)return;Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);// 把接收服务器端的回复的Messenger通过Message的replyTo参数传递给服务端msg.replyTo =activityRecevierMessenger;try {// 发送消息myService.send(msg);} catch (RemoteException e) {throw new RuntimeException(e);}}});
绑定服务的注意点
1.多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至任何其他绑定的客户端。当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。
2.通常情况下我们应该在客户端生命周期(如Activity的生命周期)的引入 (bring-up) 和退出 (tear-down) 时刻设置绑定和取消绑定操作,以便控制绑定状态下的Service,一般有以下两种情况:
- 如果只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
- 如果希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。需要注意的是,这意味着 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当提高该进程的权重时,系统很可能会终止该进程。
3.通常情况下(注意),切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,这样反复绑定与解绑是不合理的。此外,如果应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一次绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务,因此服务的绑定不应该发生在 Activity 的 onResume() 和 onPause()中。
4.我们应该始终捕获 DeadObjectException DeadObjectException 异常,该异常是在连接中断时引发的,表示调用的对象已死亡,也就是Service对象已销毁,这是远程方法引发的唯一异常,DeadObjectException继承自RemoteException,因此我们也可以捕获RemoteException异常。
5.应用组件(客户端)可通过调用 bindService() 绑定到服务,Android 系统随后调用服务的 onBind() 方法,该方法返回用于与服务交互的 IBinder,而该绑定是异步执行的。
关于启动服务与绑定服务间的转换问题
通过前面对两种服务状态的分析,相信大家已对Service的两种状态有了比较清晰的了解,那么现在我们就来分析一下当启动状态和绑定状态同时存在时,又会是怎么的场景?
虽然服务的状态有启动和绑定两种,但实际上一个服务可以同时是这两种状态,也就是说,它既可以是启动服务(以无限期运行),也可以是绑定服务。有点需要注意的是Android系统仅会为一个Service创建一个实例对象,所以不管是启动服务还是绑定服务,操作的是同一个Service实例,而且由于绑定服务或者启动服务执行顺序问题将会出现以下两种情况:
先绑定服务后启动服务
如果当前Service实例先以绑定状态运行,然后再以启动状态运行,那么绑定服务将会转为启动服务运行,这时如果之前绑定的宿主(Activity)被销毁了,也不会影响服务的运行,服务还是会一直运行下去,指定收到调用停止服务或者内存不足时才会销毁该服务。
先启动服务后绑定服务
如果当前Service实例先以启动状态运行,然后再以绑定状态运行,当前启动服务并不会转为绑定服务,但是还是会与宿主绑定,只是即使宿主解除绑定后,服务依然按启动服务的生命周期在后台运行,直到有Context调用了stopService()或是服务本身调用了stopSelf()方法抑或内存不足时才会销毁服务。
以上两种情况显示出启动服务的优先级确实比绑定服务高一些。不过无论Service是处于启动状态还是绑定状态,或处于启动并且绑定状态,我们都可以像使用Activity那样通过调用 Intent 来使用服务(即使此服务来自另一应用)。 当然,我们也可以通过清单文件将服务声明为私有服务,阻止其他应用访问。
最后这里有点需要特殊说明一下的,由于服务在其托管进程的主线程中运行(UI线程),它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。
这意味着,如果服务将执行任何耗时事件或阻止性操作(例如 MP3 播放或联网)时,则应在服务内创建新线程来完成这项工作,简而言之,耗时操作应该另起线程执行。只有通过使用单独的线程,才可以降低发生“应用无响应”(ANR) 错误的风险,这样应用的主线程才能专注于用户与 Activity 之间的交互, 以达到更好的用户体验。
前台服务以及通知发送
前台服务被认为是用户主动意识到的一种服务,因此在内存不足时,系统也不会考虑将其终止。 前台服务必须为状态栏提供通知,状态栏位于“正在进行”标题下方,这意味着除非服务停止或从前台删除,否则不能清除通知。
例如将从服务播放音乐的音乐播放器设置为在前台运行,这是因为用户明确意识到其操作。 状态栏中的通知可能表示正在播放的歌曲,并允许用户启动 Activity 来与音乐播放器进行交互。如果需要设置服务运行于前台, 我们该如何才能实现呢?Android官方给我们提供了两个方法,分别是startForeground()和stopForeground(),这两个方式解析如下:
- startForeground(int id, Notification notification)
该方法的作用是把当前服务设置为前台服务,其中id参数代表唯一标识通知的整型数,需要注意的是提供给 startForeground() 的整型 ID 不得为 0,而notification是一个状态栏的通知。
- stopForeground(boolean removeNotification)
该方法是用来从前台删除服务,此方法传入一个布尔值,指示是否也删除状态栏通知,true为删除。 注意该方法并不会停止服务。
但是,如果在服务正在前台运行时将其停止,则通知也会被删除。
服务Service与线程Thread的区别
两者的真正关系 = 没有关系。
两者概念的迥异
Thread 是程序执行的最小单元,它是分配CPU的基本单位,android系统中UI线程也是线程的一种,当然Thread还可以用于执行一些耗时异步的操作。
Service是Android的一种机制,服务是运行在主线程上的,它是由系统进程托管。它与其他组件之间的通信类似于client和server,是一种轻量级的IPC通信,这种通信的载体是binder,它是在linux层交换信息的一种IPC,而所谓的Service后台任务只不过是指没有UI的组件罢了。
两者的执行任务迥异
在android系统中,线程一般指的是工作线程(即后台线程),而主线程是一种特殊的工作线程,它负责将事件分派给相应的用户界面小工具,如绘图事件及事件响应,因此为了保证应用 UI 的响应能力主线程上不可执行耗时操作。如果执行的操作不能很快完成,则应确保它们在单独的工作线程执行。
Service 则是android系统中的组件,一般情况下它运行于主线程中,因此在Service中是不可以执行耗时操作的,否则系统会报ANR异常,之所以称Service为后台服务,大部分原因是它本身没有UI,用户无法感知(当然也可以利用某些手段让用户知道),但如果需要让Service执行耗时任务,可在Service中开启单独线程去执行。
两者使用场景
当要执行耗时的网络或者数据库查询以及其他阻塞UI线程或密集使用CPU的任务时,都应该使用工作线程(Thread),这样才能保证UI线程不被占用而影响用户体验。
在应用程序中,如果需要长时间的在后台运行,而且不需要交互的情况下,使用服务。比如播放音乐,通过Service+Notification方式在后台执行同时在通知栏显示着。
两者的最佳使用方式
在大部分情况下,Thread和Service都会结合着使用:
- 比如下载文件:一般会通过Service在后台执行+Notification在通知栏显示+Thread异步下载;
- 再如应用程序会维持一个Service来从网络中获取推送服务。
在Android官方看来也是如此,所以官网提供了一个Thread与Service的结合来方便我们执行后台耗时任务,它就是IntentService,当然 IntentService并不适用于所有的场景,但它的优点是使用方便、代码简洁,不需要我们创建Service实例并同时也创建线程,某些场景下还是非常赞的!由于IntentService是单个worker thread,所以任务需要排队,因此不适合大多数的多任务情况。
管理服务生命周期
- 左图显示了使用 startService() 所创建的服务的生命周期。
- 右图显示了使用 bindService() 所创建的服务的生命周期。
通过图中的生命周期方法,我们可以监控Service的整体执行过程,包括创建,运行,销毁
- 服务的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。与 Activity 类似,服务也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源。例如,音乐播放服务可以在 onCreate() 中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程。
- 无论服务是通过 startService() 还是 bindService() 创建,都会为所有服务调用 onCreate() 和 onDestroy() 方法。
- 服务的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始。每种方法均有 Intent 对象,该对象分别传递到 startService() 或 bindService()。
- 对于启动服务,有效生命周期与整个生命周期同时结束(即便是在 onStartCommand() 返回之后,服务仍然处于活动状态)。对于绑定服务,有效生命周期在 onUnbind() 返回时结束。
从执行流程图来看,服务的生命周期比 Activity 的生命周期要简单得多。但是,我们必须密切关注如何创建和销毁服务,因为服务可以在用户没有意识到的情况下运行于后台。管理服务的生命周期(从创建到销毁)有以下两种情况:
- 启动服务
该服务在其他组件调用 startService() 时创建,然后无限期运行,且必须通过调用 stopSelf() 来自行停止运行。此外,其他组件也可以通过调用 stopService() 来停止服务。服务停止后,系统会将其销毁。
- 绑定服务
该服务在另一个组件(客户端)调用 bindService() 时创建。然后,客户端通过 IBinder 接口与服务进行通信。客户端可以通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。 (服务不必自行停止运行)
- 启动服务与绑定服务的结合体
我们可以绑定到已经使用 startService() 启动的服务。例如,可以通过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务。随后,可能在用户需要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用 bindService() 绑定到服务。在
这种情况下,除非所有客户端均取消绑定,否则 stopService() 或 stopSelf() 不会真正停止服务。
如何保证服务不被杀死
- 因内存资源不足而杀死Service
这种情况比较容易处理,可将onStartCommand() 方法的返回值设为 START_STICKY或START_REDELIVER_INTENT ,该值表示服务在内存资源紧张时被杀死后,在内存资源足够时再恢复。也可将Service设置为前台服务,这样就有比较高的优先级,在内存资源紧张时也不会被杀掉。
- 用户通过 settings -> Apps -> Running -> Stop 方式杀死Service
这种情况是用户手动干预的,不过幸运的是这个过程会执行Service的生命周期,也就是onDestory方法会被调用,这时便可以在 onDestory() 中发送广播重新启动。这样杀死服务后会立即启动。这种方案是行得通的,但为程序更健全,我们可开启两个服务,相互监听,相互启动。服务A监听B的广播来启动B,服务B监听A的广播来启动A。
相关文章:

Android --- Service
出自于此,写得很清楚。关于Android Service真正的完全详解,你需要知道的一切_android service-CSDN博客 出自【zejian的博客】 什么是Service? Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。 服务可由其他应用组件…...

Vue3从入门到精通(三)
vue3插槽Slots 在 Vue3 中,插槽(Slots)的使用方式与 Vue2 中基本相同,但有一些细微的差异。以下是在 Vue3 中使用插槽的示例: // ChildComponent.vue <template><div><h2>Child Component</h2&…...

【FreeRTOS】同步与互斥通信-有缺陷的互斥案例
目录 同步与互斥通信同步与互斥的概念同步与互斥并不简单缺陷分析汇编指令优化过程 - 关闭中断时间轴分析 思考时刻 参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》 同步与互斥通信 同步与互斥的概念 一句话理解同步与互斥:我等你用完厕所,我再…...
Docker 安装 Python
Docker 安装 Python 在当今的软件开发领域,Docker 已成为一项关键技术,它允许开发人员将应用程序及其依赖环境打包到一个可移植的容器中。Python,作为一种广泛使用的高级编程语言,经常被部署在 Docker 容器中。本文将详细介绍如何在 Docker 中安装 Python,以及如何配置环…...

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——4.预后相关外泌体基因确定单因素cox回归(2)
内容如下: 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…...
C++: Map数组的遍历
在C中,map是一个关联容器,它存储的元素是键值对(key-value pairs),其中每个键都是唯一的,并且自动根据键来排序。遍历map的方式有几种,但最常用的两种是使用迭代器(iterator…...

【Windows】Bootstrap Studio(网页设计)软件介绍及安装步骤
软件介绍 Bootstrap Studio 是一款专为前端开发者设计的强大工具,主要用于快速创建现代化的响应式网页和网站。以下是它的主要特点和功能: 直观的界面设计 Bootstrap Studio 提供了直观的用户界面,使用户能够轻松拖放元素来构建网页。界面…...
二维舵机颜色追踪,使用树莓派+opencv+usb摄像头+两个舵机实现颜色追踪,采用pid调控
效果演示 二维云台颜色追踪 使用树莓派opencvusb摄像头两个舵机实现颜色追踪,采用pid调控 import cv2 import time import numpy as np from threading import Thread from servo import Servo from pid import PID# 初始化伺服电机 pan Servo(pin19) tilt Serv…...

c进阶篇(四):内存函数
内存函数以字节为单位更改 1.memcpy memcpy 是 C/C 中的一个标准库函数,用于内存拷贝操作。它的原型通常定义在 <cstring> 头文件中,其作用是将一块内存中的数据复制到另一块内存中。 函数原型:void *memcpy(void *dest, const void…...

新手入门:无服务器函数和FaaS简介
无服务器(Serverless)架构的价值在于其成本效益、弹性和扩展性、简化的开发和部署流程、高可用性和可靠性以及使开发者能够专注于业务逻辑。通过自动化资源调配和按需计费,无服务器架构能够降低成本并适应流量变化,同时简化开发流…...

基于Transformer的端到端的目标检测 | 读论文
本文正在参加 人工智能创作者扶持计划 提及到计算机视觉的目标检测,我们一般会最先想到卷积神经网络(CNN),因为这算是目标检测领域的开山之作了,在很长的一段时间里人们都折服于卷积神经网络在图像处理领域的优势&…...

6.8应用进程跨网络通信
《计算机网络》第7版,谢希仁 理解socket通信...
redis布隆过滤器原理及应用场景
目录 原理 应用场景 优点 缺点 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用位数组和哈希函数来判断一个元素是否存在于集合中。 原理 数据结构: 位数组:一个由0和1组成的数组,初始…...

vue+openlayers之几何图形交互绘制基础与实践
文章目录 1.实现效果2.实现步骤3.示例页面代码3.基本几何图形绘制的关键代码 1.实现效果 绘制点、线、多边形、圆、正方形、长方形 2.实现步骤 引用openlayers开发库。加载天地图wmts瓦片地图。在页面上添加几何图形绘制的功能按钮,使用下拉列表(sel…...

「多模态大模型」解读 | 突破单一文本模态局限
编者按:理想状况下,世界上的万事万物都能以文字的形式呈现,如此一来,我们似乎仅凭大语言模型(LLMs)就能完成所有任务。然而,理想很丰满,现实很骨感——数据形态远不止文字一种&#…...

Redis深度解析:核心数据类型与键操作全攻略
文章目录 前言redis数据类型string1. 设置单个字符串数据2.设置多个字符串类型的数据3.字符串拼接值4.根据键获取字符串的值5.根据多个键获取多个值6.自增自减7.获取字符串的长度8.比特流操作key操作a.查找键b.设置键值的过期时间c.查看键的有效期d.设置key的有效期e.判断键是否…...

C语言 指针和数组——指针的算术运算
目录 指针的算术运算 指针加上一个整数 指针减去一个整数 指针相减 指针的关系比较运算 小结 指针的算术运算 指针加上一个整数 指针减去一个整数 指针相减 指针的关系比较运算 小结 指针变量 – 指针类型的变量,保存地址型数据 指针变量与其他类型…...

[C++][CMake][CMake基础]详细讲解
目录 1.CMake简介2.大小写?3.注释1.注释行2.注释块 4.日志 1.CMake简介 CMake是一个项目构建工具,并且是跨平台的 问题 – 解决 如果自己动手写Makefile,会发现,Makefile通常依赖于当前的编译平台,而且编写Makefile的…...
CCD技术指标
CCD尺寸,即摄象机靶面。原多为1/2英寸,现在1/3英寸的已普及化,1/4英寸和1/5英寸也已商品化。CCD像素,是决定了显示图像的清晰程度,。CCD是由面阵感光元素组成,每一个元素称为像素,像素越多&…...

SpringBoot系列——使用Spring Cache和Redis实现查询数据缓存
文章目录 1. 前言2. 缓存2.1 什么是缓存2.2 使用缓存的好处2.3 缓存的成本2.4 Spring Cache和Redis的优点 3. Spring Cache基础知识3.1 Spring Cache的核心概念3.2 Spring Cache的注解3.2.1 SpEL表达式3.2.2 Cacheable3.2.3 CachePut3.2.4 CacheEvict 4. 实现查询数据缓存4.1 准…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...

Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
day51 python CBAM注意力
目录 一、CBAM 模块简介 二、CBAM 模块的实现 (一)通道注意力模块 (二)空间注意力模块 (三)CBAM 模块的组合 三、CBAM 模块的特性 四、CBAM 模块在 CNN 中的应用 一、CBAM 模块简介 在之前的探索中…...

【计算机网络】SDN
SDN这种新型网络体系结构的核心思想:把网络的控制层面与数据层面分离,而让控制层面利用软件来控制数据层面中的许多设备。 OpenFlow协议可以被看成是SDN体系结构中控制层面与数据层面之间的通信接口。 在SDN中取代传统路由器中转发表的是“流表”&…...