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 中可用的公共方法。如果我们的服务只是自有应用的后台工作线程,则优先采用这种方法。

不采用该方式创建接口的唯一原因是,服务被其他应用或不同的进程调用。

  1. 在Service子类中创建一个扩展 Binder 的类(OneServiceBinder),在类中声明了一个getService方法,客户端可访问该方法获取 Service子类 对象的实例,只要客户端获取到 OneServiceBinder 对象的实例就可调用服务端的公共方法。
  2. 创建一个实现IBinder 接口的实例对象并提供公共方法给客户端调用
  3. 从 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。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/40000.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

万字长文|关于 OpenAI 接口开发你应该知道的一切

这篇文章中个人结合自己的实践经验把 OpenAI 官方文档解读一遍。但是原文档涉及内容众多&#xff0c;包括微调&#xff0c;嵌入&#xff08;Embeddings&#xff09;等众多主题&#xff0c;我这里重点挑选自己开发高频使用到的&#xff0c;需要详细了解的可以自行前往官网阅读。…

2024科技文化节程序设计竞赛

补题链接 https://www.luogu.com.cn/contest/178895#problems A. 签到题 忽略掉大小为1的环&#xff0c;答案是剩下环的大小和减环的数量 #include<bits/stdc.h> #include<iostream> #include<cstdio> #include<vector> #include<map> #incl…

c进阶篇(四):内存函数

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

多模态融合算法应用:CT + 临床文本数据 + pyradiomics提取到的图像特征

多模态融合算法应用 CT 临床文本数据 pyradiomics提取图像特征 单模态建模临床数据建模pyradiomics提取图像特征建模CT建模 多模态建模前融合为什么能直接合并在一起&#xff1f; 后融合Med-CLIP&#xff1a;深度学习 可解释性 单模态建模 临床数据建模 临床文本数据&…

WPF Menu实现快捷键操作

很多小伙伴说&#xff0c;在Menu中&#xff0c;实现单个快捷键操作很简单&#xff0c;怎么实现多个快捷键操作和&#xff0c;组合快捷键呢&#xff0c;今天他来了。 上代码和效果图 一、Ctrl Shift 任意子母键实现快捷键组合 <Window x:Class"XH.TemplateLesson.M…

【测试开发】【postman】按顺序循环执行接口

postman按顺序循环执行接口 新建接口接口排序执行请求集合 新建接口 Request 001 Request 002 Request 003 接口排序 在Request 001的Tests中添加代码 postman.setNextRequest("Request 002");在Request 002的Tests中添加代码 postman.setNextRequest("Requ…

Redis 7.x 系列【17】四种持久化策略

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 概述2. 案例演示2.1 无持久化2.2 RDB2.3 AOF2.4 混合模式2.4.1 方式一&#xff1a;…

线性代数|机器学习-P21概率定义和Markov不等式

文章目录 1. 样本期望和方差1.1 样本期望 E ( X ) \mathrm{E}(X) E(X)1.2 样本期望 D ( X ) \mathrm{D}(X) D(X) 2. Markov 不等式&Chebyshev不等式2.1 Markov不等式公式 概述2.2 Markov不等式公式 证明&#xff1a;2.3 Markov不等式公式 举例&#xff1a;2.4 Chebyshev不…

AI绘画 Stable Diffusion图像的脸部细节控制——采样器全解析

大家好&#xff0c;我是画画的小强 我们在运用AI绘画 Stable Diffusion 这一功能强大的AI绘图工具时&#xff0c;我们往往会发现自己对提示词的使用还不够充分。在这种情形下&#xff0c;我们应当如何调整自己的策略&#xff0c;以便更加精确、全面地塑造出理想的人物形象呢&a…

域环境提权

域内提权漏洞(1) Netlogon域权限提升 1.查看域控主机名称 net group "domain controllers" /domain 2.检测漏洞是否存在 https://github.com/SecuraBV/CVE-2020-1472.git python zerologon_tester.py OWA 192.168.52.138 3.漏洞利用&#xff0c;对域账号重置 ht…

《简历宝典》01 - 一文带你学会如何写一份糟糕透顶的简历

我们每个人几乎都会面对找工作这件事&#xff0c;而找工作或者说求职首先就是要写一份简历。今天狗哥将以一个不同的视角带你写一份无与伦比&#xff0c;糟糕透顶的求职简历&#xff0c;说实话&#xff0c;其实几年前&#xff0c;我就是这么写的。 目录 1. 文件名 2. 基本信…

【项目管理】项目风险管理(Word原件)

风险和机会管理就是在一个项目开发过程中对风险进行识别、跟踪、控制的手段。风险和机会管理提供了对可能出现的风险进行持续评估&#xff0c;确定重要的风险机会以及实施处理的策略的一种规范化的环境。包括识别、分析、制定处理和减缓行动、跟踪 。合理的风险和机会管理应尽力…

Lesson 47 A cup of coffee

Lesson 47 A cup of coffee 词汇 like v. 喜欢&#xff0c;想要 用法&#xff1a;like 物品 / 人 喜欢……    like 动词ing 喜欢做……&#xff08;习惯性&#xff09;    like to 动词原形 喜欢做……&#xff08;一次性&#xff09; 例句&#xff1a;我喜欢小狗…

opencv-yolo-tiny车辆检测 ----20240705

opencv-yolo-tiny 实现车辆检测 opencv.dnn模块已经支持大部分格式的深度学习模型推理,该模块可以直接加载tensorflow、darknet、pytorch等常见深度学习框架训练出来的模型,并运行推理得到模型输出结果。opecnv.dnn模块已经作为一种模型部署方式,应用在工业落地实际场景中。…

持续交付:自动化测试与发布流程的变革

目录 前言1. 持续交付的概念1.1 持续交付的定义1.2 持续交付的核心原则 2. 持续交付的优势2.1 提高交付速度2.2 提高软件质量2.3 降低发布风险2.4 提高团队协作 3. 实施持续交付的步骤3.1 构建自动化测试体系3.1.1 单元测试3.1.2 集成测试3.1.3 功能测试3.1.4 性能测试 3.2 构建…

鸿蒙系统的开发与学习

1.开发工具的下载 DevEco Studio-HarmonyOS Next Beta版-华为开发者联盟 安装、环境配置时&#xff0c;建议 自定义目录 注意&#xff1a;路径中不要有 中文、特殊字符。 2.ArkTS基础总结 1&#xff09;三种数据类型 ① string 字符串&#xff1a;描述信息 ② number 数…

Java后端每日面试题(day1)

目录 JavaWeb三大组件依赖注入的方式Autowire和Resurce有什么区别&#xff1f;Spring Boot的优点Spring IoC是什么&#xff1f;说说Spring Aop的优点Component和Bean的区别自定义注解时使用的RetentionPolicy枚举类有哪些值&#xff1f;如何理解Spring的SPI机制&#xff1f;Spr…

听说现在AI产品经理薪资30k起步?0基础可以转行AI产品吗?

2024年&#xff0c;还有什么新风口&#xff1f; AI、元宇宙、NFT… 很多人不知道&#xff0c;其实不管是元宇宙还是NFT&#xff0c;它们本质上就是人工智能领域。 AI自身应用领域非常广泛&#xff0c;大批高薪岗位随之涌了出来&#xff0c;包括AI产品经理。 AI产品经历具体工…

【LeetCode:841. 钥匙和房间 + DFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

安卓手机已删除短信如何恢复?这2个技巧,找回离家出走的短信

手机宛如一座丰富的宝库&#xff0c;珍藏着生活中的点滴回忆。其中&#xff0c;短信作为沟通的桥梁&#xff0c;记录着我们与亲朋好友间的温情脉脉&#xff0c;承载着无数珍贵的瞬间。然而&#xff0c;有时&#xff0c;我们却会不慎触发宝库中的机关&#xff0c;使得这些宝贵的…