Service
Service
-
Service既不是一个线程,Service通常运行在当成宿主进程的主线程中,所以在Service中进行一些耗时操作就需要在Service内部开启线程去操作,否则会引发ANR异常。
-
也不是一个单独的进程。除非在清单文件中声明时指定进程名,否则Service所在进程就是application所在进程。
生命周期
-
服务要在清单文件中声明
-
如果组件通过调用
startService()
启动服务(这会引起对onStartCommand()
的调用),则服务会一直运行,直到其使用stopSelf()
自行停止运行,或由其他组件通过调用stopService()
将其停止为止。 -
如果组件通过调用
bindService()
来创建服务,且未调用onStartCommand()
,则服务只会在该组件与其绑定时运行。当该服务与其所有组件取消绑定后,系统便会将其销毁。 -
onStartCommand()返回值
- START_STICKY = 1:service所在进程被kill之后,系统会保留service状态为开始状态。系统尝试重启service,当服务被再次启动,传递过来的intent可能为null,需要注意。
- START_NOT_STICKY = 2:service所在进程被kill之后,系统不再重启服务
- START_REDELIVER_INTENT = 3:系统自动重启service,并传递之前的intent
默认返回START_STICKY。
前台服务
- 请求前台权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
- MainActivity.java
package com.example.myService; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import androidx.appcompat.app.AppCompatActivity; import com.example.myService.service.MyService; public class MainActivity extends AppCompatActivity { private Button btn_start; private Button btn_stop; private static final String TAG = "wmj"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_start = findViewById(R.id.btn_start); btn_stop = findViewById(R.id.btn_stop); Intent intent = new Intent(MainActivity.this, MyService.class); btn_start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startService(intent); } }); btn_stop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { stopService(intent); } }); } }
- MyForegroundService.java
package com.example.myService.service; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; import android.graphics.BitmapFactory; import android.graphics.Color; import android.os.IBinder; import android.util.Log; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import com.example.myService.activity.BindingActivity; import com.example.myService.R; public class MyForegroundService extends Service { private static final int NOTIFICATION_ID = 1; // 不能为0 public static final String CHANNEL_ID = "channel_id"; private static final String TAG = "wmj"; @Override public void onCreate() { super.onCreate(); Log.d(TAG, "MyForegroundService onCreate: "); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "MyForegroundService onStartCommand: "); // 获取通知管理器类对象 NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // 通知渠道,参数3是通知重要程度 NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, "测试通知", NotificationManager.IMPORTANCE_HIGH); notificationManager.createNotificationChannel(notificationChannel); // 点击通知后触发的Intent Intent notificationIntent = new Intent(this, BindingActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent , 0); // RemoteViews remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification_layout); // Builder构造器创建Notification对象 Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("通知标题") .setContentText("通知详情") .setAutoCancel(true) // 点击通知后自动消失 .setSmallIcon(R.drawable.ic_launcher_background) .setContentIntent(pendingIntent) // 点击后触发的Intent .setColor(Color.parseColor("#FF0000")) // 小图标颜色 .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.dog)) // 通知右侧大图标 // .setContent(remoteViews) .build();// id和上面一致 // 参数一是唯一的通知标识 startForeground(NOTIFICATION_ID, notification); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "MyForegroundService onDestroy: "); stopForeground(true); // 停止前台服务 } @Nullable @Override public IBinder onBind(Intent intent) { Log.d(TAG, "MyForegroundService onBind: "); return null; } }
- NotificationActivity.java
package com.example.myService; import android.app.Activity; import android.os.Bundle; import android.util.Log; import androidx.annotation.Nullable; public class NotificationActivity extends Activity { private static final String TAG = "wmj"; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate: NotificationActivity"); } }
绑定服务
- 如果您确实允许服务同时具有已启动和已绑定状态,那么服务启动后,系统不会在所有客户端均与服务取消绑定后销毁服务,而必须由您通过调用
stopSelf()
或stopService()
显式停止服务。 - 可以将多个客户端同时连接到某项服务。但是,系统会缓存
IBinder
服务通信通道。换言之,只有在第一个客户端绑定服务时,系统才会调用服务的onBind()
方法来生成IBinder
。然后,系统会将该IBinder
传递至绑定到同一服务的所有其他客户端,无需再次调用onBind()
。 - 当最后一个客户端取消与服务的绑定时,系统会销毁该服务(除非还通过
startService()
启动了该服务)。
扩展Binder类
- 服务仅供本地应用使用,且无需跨进程工作,应该实现自有
Binder
类,让客户端通过该类直接访问服务中的公共方法。 - 使用
- 在您的服务中,创建可执行以下某种操作的Binder实例:
- 包含客户端可调用的公共方法。
- 返回当前的
Service
实例,该实例中包含客户端可调用的公共方法。 - 返回由服务承载的其他类的实例,其中包含客户端可调用的公共方法。
- 从
onBind()
回调方法返回此Binder
实例。 - 在客户端中,从
onServiceConnected()
回调方法接收Binder
,并使用提供的方法调用绑定服务。
- 在您的服务中,创建可执行以下某种操作的Binder实例:
- 只有当客户端和服务处于同一应用和进程内(最常见的情况)时,此方式才有效。
- BindingActivity.java
package com.example.myService; 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 android.view.View; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.example.myService.service.MyBindService; public class BindingActivity extends AppCompatActivity { private static final String TAG = "wmj"; private MyBindService mService; private boolean mBound = false; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_binding); } @Override protected void onStart() { super.onStart(); // 绑定服务 Intent intent = new Intent(this, MyBindService.class); // 参数2是回调函数 bindService(intent, connection, Context.BIND_AUTO_CREATE); } // 通过ServiceConnection接口来取得建立连接与连接意外丢失的回调 private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder service) { MyBindService.MyBinder binder = (MyBindService.MyBinder) service; mService = binder.getService(); mBound = true; Log.d(TAG, "onServiceConnected: "); } @Override public void onServiceDisconnected(ComponentName arg0) { mBound = false; Log.d(TAG, "onServiceDisconnected: "); } }; public void onButtonClick(View v) { if (mBound) { // 调用服务的public方法 // 如果此调用可能会挂起,则此请求应该发生在单独的线程中,以避免降低活动性能。 int num = mService.getRandomNumber(); Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); } } @Override protected void onResume() { super.onResume(); } @Override protected void onPause() { super.onPause(); } @Override protected void onStop() { super.onStop(); // 解除绑定 unbindService(connection); mBound = false; } @Override protected void onDestroy() { super.onDestroy(); } }
- MyBindService.java
package com.example.myService.service; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; import java.util.Random; public class MyBindService extends Service { private final Random mGenerator = new Random(); private static final String TAG = "wmj"; // 服务总是运行在和客户端相同的进程中,除非在清单文件中设置 public class MyBinder extends Binder { public MyBindService getService() { // 返回service以便客户端可以调用service的public方法 return MyBindService.this; } } @Override public void onCreate() { super.onCreate(); Log.d(TAG, "MyBindService onCreate: "); } @Override public IBinder onBind(Intent intent) { Log.d(TAG, "MyBindService onBind: "); return new MyBinder(); } @Override public boolean onUnbind(Intent intent) { Log.d(TAG, "MyBindService onUnbind: "); return super.onUnbind(intent); } @Override public void onDestroy() { Log.d(TAG, "MyBindService onDestroy: "); super.onDestroy(); } public int getRandomNumber() { return mGenerator.nextInt(100); } }
Messenger
- 如果您需要让服务与远程进程通信,则可使用
Messenger
为您的服务提供接口。 - 对于大多数应用,服务无需执行多线程处理,因此使用
Messenger
可让服务一次处理一个调用。如果您的服务必须执行多线程处理,请使用 AIDL来定义接口。 - 使用
- 服务实现一个
Handler
,由其接收来自客户端的每个调用的回调。 - 服务使用
Handler
来创建Messenger
对象(该对象是对Handler
的引用)。 Messenger
创建一个IBinder
,服务通过onBind()
将其返回给客户端。- 客户端使用
IBinder
将Messenger
(它引用服务的Handler
)实例化,然后再用其将Message
对象发送给服务。 - 服务在其
Handler
中(具体而言,是在handleMessage()
方法中)接收每个Message
。
- 服务实现一个
- 服务会在
Handler
的handleMessage()
方法中接收传入的Message
,并根据what
成员决定下一步操作。客户端只需根据服务返回的IBinder
创建Messenger
,然后使用send()
发送消息。 - MessengerActivity.java
package com.example.myService.activity; 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.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.view.View; import androidx.appcompat.app.AppCompatActivity; import com.example.myService.R; import com.example.myService.service.MessengerService; public class MessengerActivity extends AppCompatActivity { private boolean bound = false; // 是否已绑定 private Messenger messenger = null; private ServiceConnection connection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { messenger = new Messenger(service); bound = true; } public void onServiceDisconnected(ComponentName className) { messenger = null; bound = false; } }; public void sayHello(View v) { if (!bound) return; Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); try { messenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); } @Override protected void onStart() { super.onStart(); // 绑定服务 Intent intent = new Intent(this, MessengerService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // 如果绑定了就解绑 if (bound) { unbindService(connection); bound = false; } } @Override protected void onDestroy() { super.onDestroy(); } }
- MessenagerService.java
package com.example.myService.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; public class MessengerService extends Service { // 给客户端的信使以向IncomingHandler发送消息。 private Messenger mMessenger; private static final String TAG = "wmj"; public static final int MSG_SAY_HELLO = 1; static class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_SAY_HELLO: Log.d(TAG, "MessengerService handleMessage:"); break; default: super.handleMessage(msg); } } } @Override public IBinder onBind(Intent intent) { mMessenger = new Messenger(new IncomingHandler()); Log.d(TAG, "MessengerService onBind: "); return mMessenger.getBinder(); } }
绑定到服务
-
应用组件(客户端)可通过调用
bindService()
绑定到服务。然后,Android 系统会调用服务的onBind()
方法,该方法会返回用于与服务交互的IBinder
。 -
绑定是异步操作,并且
bindService()
可立即返回,无需将IBinder
返回给客户端。如要接收IBinder
,客户端必须创建一个ServiceConnection
实例,并将其传递给bindService()
。ServiceConnection
包含一个回调方法,系统通过调用该回调方法来传递IBinder
。 -
只有 Activity、服务和内容提供程序可以绑定到服务
-
使用
-
实现ServiceConnection。
您的实现必须替换两个回调方法:
-
onServiceConnected()
系统会调用该方法以传递服务的
onBind()
方法所返回的IBinder
。 -
onServiceDisconnected()
当与服务的连接意外中断时,例如服务崩溃或被终止时,Android 系统会调用该方法。当客户端取消绑定时,系统不会调用该方法。
-
-
调用bindService(),从而传递ServiceConnection实现。
注意:如果该方法返回 false,说明您的客户端与服务之间并无有效连接。不过,您的客户端仍应调用
unbindService()
;否则,您的客户端会使服务无法在空闲时关闭。 -
当系统调用
onServiceConnected()
回调方法时,您可以使用接口定义的方法开始调用服务。 -
如要断开与服务的连接,请调用unbindService()。
当应用销毁客户端时,如果客户端仍与服务保持绑定状态,销毁会导致客户端取消绑定。更好的做法是在客户端与服务交互完成后,就立即取消客户端的绑定。这样做可以关闭空闲的服务。
-
生命周期
-
当服务与所有客户端之间的绑定全部取消时,Android 系统会销毁该服务(除非还使用
startService()
调用启动了该服务)。因此,如果您的服务是纯粹的绑定服务,则无需对其生命周期进行管理,Android 系统会根据它是否绑定到任何客户端代您管理。 -
不过,如果您选择实现
onStartCommand()
回调方法,就必须显式停止服务,因为系统现在将服务视为已启动状态。在此情况下,服务将一直运行,直到其通过stopSelf()
自行停止,或其他组件调用stopService()
(与该服务是否绑定到任何客户端无关)。 -
此外,如果您的服务已启动并接受绑定,那么当系统调用您的
onUnbind()
方法时,如果您想在客户端下一次绑定到服务时接收onRebind()
调用,可以选择返回true
。onRebind()
返回空值,但客户端仍会在其onServiceConnected()
回调中接收IBinder
。下图说明了这种生命周期的逻辑。
本文作者:n1ce2cv
本文链接:https://www.cnblogs.com/sprinining/p/15379474.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步