当前位置: 首页 > news >正文

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。 

相关文章:

Android --- Service

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

Vue3从入门到精通(三)

vue3插槽Slots 在 Vue3 中&#xff0c;插槽&#xff08;Slots&#xff09;的使用方式与 Vue2 中基本相同&#xff0c;但有一些细微的差异。以下是在 Vue3 中使用插槽的示例&#xff1a; // ChildComponent.vue <template><div><h2>Child Component</h2&…...

【FreeRTOS】同步与互斥通信-有缺陷的互斥案例

目录 同步与互斥通信同步与互斥的概念同步与互斥并不简单缺陷分析汇编指令优化过程 - 关闭中断时间轴分析 思考时刻 参考《FreeRTOS入门与工程实践(基于DshanMCU-103).pdf》 同步与互斥通信 同步与互斥的概念 一句话理解同步与互斥&#xff1a;我等你用完厕所&#xff0c;我再…...

Docker 安装 Python

Docker 安装 Python 在当今的软件开发领域,Docker 已成为一项关键技术,它允许开发人员将应用程序及其依赖环境打包到一个可移植的容器中。Python,作为一种广泛使用的高级编程语言,经常被部署在 Docker 容器中。本文将详细介绍如何在 Docker 中安装 Python,以及如何配置环…...

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——4.预后相关外泌体基因确定单因素cox回归(2)

内容如下&#xff1a; 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…...

C++: Map数组的遍历

在C中&#xff0c;map是一个关联容器&#xff0c;它存储的元素是键值对&#xff08;key-value pairs&#xff09;&#xff0c;其中每个键都是唯一的&#xff0c;并且自动根据键来排序。遍历map的方式有几种&#xff0c;但最常用的两种是使用迭代器&#xff08;iterator&#xf…...

【Windows】Bootstrap Studio(网页设计)软件介绍及安装步骤

软件介绍 Bootstrap Studio 是一款专为前端开发者设计的强大工具&#xff0c;主要用于快速创建现代化的响应式网页和网站。以下是它的主要特点和功能&#xff1a; 直观的界面设计 Bootstrap Studio 提供了直观的用户界面&#xff0c;使用户能够轻松拖放元素来构建网页。界面…...

二维舵机颜色追踪,使用树莓派+opencv+usb摄像头+两个舵机实现颜色追踪,采用pid调控

效果演示 二维云台颜色追踪 使用树莓派opencvusb摄像头两个舵机实现颜色追踪&#xff0c;采用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 中的一个标准库函数&#xff0c;用于内存拷贝操作。它的原型通常定义在 <cstring> 头文件中&#xff0c;其作用是将一块内存中的数据复制到另一块内存中。 函数原型&#xff1a;void *memcpy(void *dest, const void…...

新手入门:无服务器函数和FaaS简介

无服务器&#xff08;Serverless&#xff09;架构的价值在于其成本效益、弹性和扩展性、简化的开发和部署流程、高可用性和可靠性以及使开发者能够专注于业务逻辑。通过自动化资源调配和按需计费&#xff0c;无服务器架构能够降低成本并适应流量变化&#xff0c;同时简化开发流…...

基于Transformer的端到端的目标检测 | 读论文

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

6.8应用进程跨网络通信

《计算机网络》第7版&#xff0c;谢希仁 理解socket通信...

redis布隆过滤器原理及应用场景

目录 原理 应用场景 优点 缺点 布隆过滤器&#xff08;Bloom Filter&#xff09;是一种空间效率很高的随机数据结构&#xff0c;它利用位数组和哈希函数来判断一个元素是否存在于集合中。 原理 数据结构&#xff1a; 位数组&#xff1a;一个由0和1组成的数组&#xff0c;初始…...

vue+openlayers之几何图形交互绘制基础与实践

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

「多模态大模型」解读 | 突破单一文本模态局限

编者按&#xff1a;理想状况下&#xff0c;世界上的万事万物都能以文字的形式呈现&#xff0c;如此一来&#xff0c;我们似乎仅凭大语言模型&#xff08;LLMs&#xff09;就能完成所有任务。然而&#xff0c;理想很丰满&#xff0c;现实很骨感——数据形态远不止文字一种&#…...

Redis深度解析:核心数据类型与键操作全攻略

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

C语言 指针和数组——指针的算术运算

目录 指针的算术运算 指针加上一个整数 指针减去一个整数 指针相减 指针的关系比较运算 小结 指针的算术运算 指针加上一个整数 指针减去一个整数 指针相减 指针的关系比较运算 小结  指针变量 – 指针类型的变量&#xff0c;保存地址型数据  指针变量与其他类型…...

[C++][CMake][CMake基础]详细讲解

目录 1.CMake简介2.大小写&#xff1f;3.注释1.注释行2.注释块 4.日志 1.CMake简介 CMake是一个项目构建工具&#xff0c;并且是跨平台的 问题 – 解决 如果自己动手写Makefile&#xff0c;会发现&#xff0c;Makefile通常依赖于当前的编译平台&#xff0c;而且编写Makefile的…...

CCD技术指标

CCD尺寸&#xff0c;即摄象机靶面。原多为1/2英寸&#xff0c;现在1/3英寸的已普及化&#xff0c;1/4英寸和1/5英寸也已商品化。CCD像素&#xff0c;是决定了显示图像的清晰程度&#xff0c;。CCD是由面阵感光元素组成&#xff0c;每一个元素称为像素&#xff0c;像素越多&…...

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 准…...

【算法】(C语言):冒泡排序、选择排序、插入排序

冒泡排序 从第一个数据开始到第n-1个数据&#xff0c;依次和后面一个数据两两比较&#xff0c;数值小的在前。最终&#xff0c;最后一个数据&#xff08;第n个数据&#xff09;为最大值。从第一个数据开始到第n-2个数据&#xff0c;依次和后面一个数据两两比较&#xff0c;数值…...

iOS项目怎样进行二进制重排

什么是二进制重排 &#xff1f; 在iOS项目中&#xff0c;二进制重排&#xff08;Binary Reordering 或者 Binary Rearrangement&#xff09;是一种优化技术&#xff0c;主要目的是通过重新组织应用程序的二进制文件中的代码和数据段&#xff0c;来提高应用程序的性能&#xff…...

CentOS中使用SSH远程登录

CentOS中使用SSH远程登录 准备工作SSH概述SSH服务的安装与启动建立SSH连接SSH配置文件修改SSH默认端口SSH文件传输 准备工作 两台安装CentOS系统的虚拟机 客户机&#xff08;192.168.239.128&#xff09; 服务器&#xff08;192.168.239.129&#xff09; SSH概述 Secure S…...

spring @Autowire注解作用

终于有人把Autowired注解讲清楚了&#xff0c;赞&#xff01;&#xff01;&#xff01;_autowired-CSDN博客...

密码学原理精解【5】

这里写目录标题 移位密码概述代码 希尔密码( Z 256 Z_{256} Z256​)待加密长度被3整除待加密长度不一定被3整除加解密文件 移位密码 概述 以 z 26 运算为例 , k 为密钥 加密&#xff1a; e k ( x ) ( x k ) m o d 26 解密&#xff1a; d k ( x ) ( x − k ) m o d 26 以z_{…...

Unity3D 资源管理YooAsset原理分析与详解

引言 Unity3D 是一款广泛应用于游戏开发、虚拟现实&#xff08;VR&#xff09;、增强现实&#xff08;AR&#xff09;等领域的强大游戏开发引擎。在开发过程中&#xff0c;资源管理是一项至关重要的任务&#xff0c;它直接影响到游戏的性能和用户体验。YooAsset 是一个基于 Un…...

npm install puppeteer 报错 npm ERR! PUPPETEER_DOWNLOAD_HOST is deprecated解决办法

npm install puppeteer 报错如下&#xff1a; npm ERR! PUPPETEER_DOWNLOAD_HOST is deprecated. Use PUPPETEER_DOWNLOAD_BASE_URL instead. npm ERR! Error: ERROR: Failed to set up Chrome v126.0.6478.126! Set "PUPPETEER_SKIP_DOWNLOAD" env variable to sk…...

浙大版PTA《Python 程序设计》题目集 参考答案

浙大版PTA《Python 程序设计》题目集 参考答案 本答案配套详解教程专栏&#xff0c;欢迎订阅&#xff1a; PTA浙大版《Python 程序设计》题目集 详解教程_少侠PSY的博客-CSDN博客 01第1章-1 从键盘输入两个数&#xff0c;求它们的和并输出 aint(input()) # 输入a的值 bint(…...

“拆分盘投资:机遇与风险并存

一、引言 随着互联网技术的日新月异&#xff0c;金融投资领域迎来了前所未有的变革&#xff0c;其中拆分盘作为一种新兴的投资模式&#xff0c;正逐渐进入公众的视野。其独特的价值增长逻辑和创新的投资机制&#xff0c;为投资者开辟了新的财富增值渠道。本文旨在深入探讨拆分…...

Java面试题系列 - 第2天

题目&#xff1a;Java中的线程池模型及其配置策略 背景说明&#xff1a;在Java多线程编程中&#xff0c;线程池是一种高效的线程复用机制&#xff0c;能够有效管理和控制线程的创建与销毁&#xff0c;避免频繁创建和销毁线程带来的性能开销。理解和掌握线程池的配置策略对于优…...

AGI|Transformer自注意力机制超全扫盲攻略,建议收藏!

一、前言 2017年&#xff0c;谷歌团队推出一篇神经网络的论文&#xff0c;首次提出将“自注意力”机制引入深度学习中&#xff0c;这一机制可以根据输入数据各部分重要性的不同而分配不同的权重。当ChatGPT震惊世人时&#xff0c;Transformer也随之进入大众视野。一夜之间&…...

QT+OpenCV在Android上实现人脸实时检测与目标检测

一、功能介绍 在当今的移动应用领域&#xff0c;随着技术的飞速发展和智能设备的普及&#xff0c;将先进的计算机视觉技术集成到移动平台&#xff0c;特别是Android系统中&#xff0c;已成为提升用户体验、拓展应用功能的关键。其中&#xff0c;目标检测与人脸识别作为计算机视…...

常见网络攻击方式及防御方法

1. DDOS攻击&#xff08;分布式拒绝服务攻击&#xff09; 概念&#xff1a;借助于C/S&#xff08;客户端/服务器&#xff09;技术&#xff0c;将多个计算机联合起来作为攻击平台&#xff0c;对一个或多个目标发动DDOS攻击&#xff0c;从而成倍地提高拒绝服务攻击的威力。防护方…...

使用 ESP32 实现无线对讲机功能涉及音频采集、音频传输以及音频播放等多个方面。实现无线对讲机功能的基本步骤和示例代码。

硬件准备 两个 ESP32 开发板两个 MAX9814 麦克风模块&#xff08;或其他兼容的模拟麦克风模块&#xff09;两个 MAX98357A DAC 模块&#xff08;或其他兼容的音频放大器模块&#xff09;扬声器 接线 麦克风模块 -> ESP32 ADC 引脚ESP32 DAC 引脚 -> 音频放大器模块 -&…...

SpringBoot项目,配置文件pom.xml的结构解析

pom.xml 是 Maven 项目对象模型&#xff08;Project Object Model&#xff09;的配置文件&#xff0c;它定义了 Maven 项目的基本设置和构建过程。以下是 pom.xml 文件的基本结构和一些常见元素的解析&#xff1a; 项目声明 (<project>): <modelVersion>: 通常设置…...

教程:Spring Boot中集成Memcached的详细步骤

教程&#xff1a;Spring Boot中集成Memcached的详细步骤 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在现代应用开发中&#xff0c;缓存是提升性能和扩展性…...

Websocket通信实战项目(图片互传应用)+PyQt界面+python异步编程(async) (上)服务器端python实现

Rqtz : 个人主页 ​​ 共享IT之美&#xff0c;共创机器未来 ​ Sharing the Beauty of IT and Creating the Future of Machines Together 目录 项目背景 ​编辑​专有名词介绍 服务器GUI展示 功能(位置见上图序号) 客户端GUI展示&#xff08;h5cssjs&#xf…...

实验一 MATLAB \ Python数字图像处理初步

一、实验目的&#xff1a; 1&#xff0e;熟悉及掌握在MATLAB\Python中能够处理哪些格式图像。 2&#xff0e;熟练掌握在MATLAB\Python中如何读取图像。 3&#xff0e;掌握如何利用MATLAB\Python来获取图像的大小、颜色、高度、宽度等等相关信息。 4&#xff0e;掌握如何在M…...

echarts柱状选中shadow阴影背景宽度设置

使用line&#xff0c;宽度增大到所需要的宽度&#xff0c;设置下颜色透明度就行 tooltip: {trigger: axis,//把阴影的层级往下降z:-15,axisPointer: {type: line,lineStyle: {color: rgba(150,150,150,0.3),width: 44,type: solid,},}, }, series: [{type: bar,barWidth:20,//…...

ArrayBuffer 对象常见的几个用途

ArrayBuffer 在 JavaScript 中的用途广泛&#xff0c;主要用于处理二进制数据。 ArrayBuffer 对象、 TypedArray 视图和 DataView 视图是 JavaScript 操作二进制数据的一个接口。本文介绍ArrayBuffer 对象的常见的一些用法。 1. 网络传输二进制数据 使用方法&#xff1a;通过 …...

STC89C52RC单片机设计的FM收音机+自动搜台+存储电台(程序+原理图+PCB)

资料下载地址&#xff1a;STC89C52RC单片机设计的FM收音机自动搜台存储电台&#xff08;程序原理图PCB) 1、实物图 2、部分程序 #include <reg52.h> #include "tea5767.h" #include "delay.h" #include "lcd1602.h" //K1:上一台 K2:下一…...

【若依】关闭当前标签页并跳转路由到其他页面

使用场景如&#xff1a;当在新增/编辑路由页面提交成功后&#xff0c;需要关闭当前页&#xff0c;并跳转回列表页。 实现代码&#xff1a; this.$store.dispatch("tagsView/delView", this.$route); //关闭当前页 this.$router.replace({ path: "/xxx/xxx"…...

防爆智能手机如何解决危险环境下通信难题?

在化工厂、石油行业、矿山等危险环境中&#xff0c;通信安全一直是难题。传统手机因不具备防爆功能&#xff0c;可能引发火花、爆炸等安全风险&#xff0c;让工作人员在关键时刻难以及时沟通。但如今&#xff0c;防爆智能手机的出现彻底改变了这一现状&#xff01; 安全通信&am…...

软件测试最全面试题及答案整理(2024最新版)

1、你的测试职业发展是什么? 测试经验越多&#xff0c;测试能力越高。所以我的职业发展是需要时间积累的&#xff0c;一步步向着高级测试工程师奔去。而且我也有初步的职业规划&#xff0c;前3年积累测试经验&#xff0c;按如何做好测试工程师的要点去要求自己&#xff0c;不断…...

11 - matlab m_map地学绘图工具基础函数 - 绘制航迹、椭圆、风向玫瑰图和特定的圆形区域的有关函数及其用法

11 - matlab m_map地学绘图工具基础函数 - 绘制航迹、椭圆、风向玫瑰图和特定的圆形区域的有关函数及其用法 0. 引言1. 关于m_track2. 关于m_range_ring3. 关于m_ellipse4. 关于m_windrose5. 结语 0. 引言 本篇介绍下m_map中绘制航迹图函数&#xff08;m_track&#xff09;、绘…...

长安链安装及使用问题

1. 关于golang编译出错: Get “https://proxy.golang.org/chainmaker.org/chainmaker/common/v2/v/v2.2.0.mod“: dial 在网上查阅资料后发现是自己的golang版本太低(1.3一下),因为goalng在最初开发时,国内基本上都会遇到依赖下载不了的问题&#xff0c; 然而在1.3版本后,go…...

大学生竞赛管理系统-计算机毕业设计源码37276

大学生竞赛管理系统的设计与实现 摘 要 随着教育信息化的不断发展&#xff0c;大学生竞赛已成为高校教育的重要组成部分。传统的竞赛组织和管理方式存在着诸多问题&#xff0c;如信息不透明、效率低下、管理不便等。为了解决这些问题&#xff0c;提高竞赛组织和管理效率&#x…...

去中心化 RAG 先行者,KIP Protocol 如何保护数据所有权、激活 AI 资产

AI 时代&#xff0c;人人都应实现 KnowledgeFi 的梦想或许并不遥远&#xff0c;KIP Protocol 正在生动践行这一价值理念&#xff0c;带动去中心化数字产权的创建与盈利&#xff0c;面向 CryptoAI 的蓝海市场迈出创新探索的技术步伐&#xff0c;朝着 Web3 行业打造去中心化 AI 的…...

numpy库(python)

文章目录 1.numpy简介2.安装numpy3.ndarry : numpy库的心脏3.1 创建数组3.2数据类型3.3dtype NumPy是用Python.进行科学计算&#xff0c;尤其是数据分析时&#xff0c;所用到的一个基础库。它是大量Python 数学和科学计算包的基础&#xff0c;比如后面要讲到的pandas)库就用到了…...

AI技术在招聘行业的应用

大模型AI技术在招聘行业的应用正变得越来越广泛&#xff0c;以下是一些关键领域的应用实例。大模型AI技术在招聘行业的应用不仅提高了效率和精确度&#xff0c;还帮助企业在竞争激烈的人才市场中获得优势。随着技术的不断发展&#xff0c;预计AI将在招聘领域扮演更加重要的角色…...