-
2022-03-10 17:08:47
启动service的两种方式
1. 通过StartService启动Service
通过startService启动后,service会一直无限期运行下去,
- 当外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁
- 当系统资源不足时, 会回收一些不重要的service,service被系统回收也会停止运行并被销毁
生命周期
-
onCreate()
1.如果service没被创建过,调用startService()后会执行onCreate()回调;
2.如果service已处于运行中,调用startService()不会执行onCreate()方法。
此方法适合完成一些初始化工作。 -
onStartCommand()
如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用。 -
onBind()
Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。 -
onDestory()
在销毁的时候会执行Service该方法。
代码实例
MyActivity.java
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 启动service Intent mIntent=new Intent(MainActivity.this,MyService.class) ; startService(mIntent); } }
MyServvice.java
public class MyService extends Service { private static final String TAG = "MyService"; private NotificationManager notificationManager; private String notificationId = "channel_Id"; private String notificationName = "channel_Name"; public MyService() { } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } @Override public void onCreate() { Log.d(TAG, "onCreate: ..."); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand: ..."); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { Log.d(TAG, "onDestroy: ...."); super.onDestroy(); }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.iauto.demo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MainActivity "> <service android:name=".MyService" android:enabled="true" android:exported="true" ></service> <activity android:name=".MainActivity" android:exported="true" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
2. 通过bindService启动Service
bindService启动服务特点:
- bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
- client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
- bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁
生命周期
-
onCreate()
当服务通过onStartCommand()和onBind()被第一次创建的时候,系统调用该方法。该调用要求执行一次性安装。 -
onBind()
当其他组件想要通过bindService()来绑定服务时,系统调用该方法。如果你实现该方法,你需要返回IBinder对象来提供一个接口,以便客户来与服务通信。你必须实现该方法,如果你不允许绑定,则直接返回null。 -
onUnbind()
当客户中断所有服务发布的特殊接口时,系统调用该方法。 -
onRebind()
当新的客户端与服务连接,且此前它已经通过onUnbind(Intent)通知断开连接时,系统调用该方法。 -
onDestroy()
当服务不再有用或者被销毁时,系统调用该方法。你的服务需要实现该方法来清理任何资源,如线程,已注册的监听器,接收器等。
代码实例
MainAcivity.java
public class MainAcivity extends Activity{ private Myservice = null; private boolean isBind = false; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { isBind = true; TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder) binder; service = myBinder.getService(); int num = service.getRandomNumber(); } @Override public void onServiceDisconnected(ComponentName name) { isBind = false; } }; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_a); Intent intent = new Intent(this, TestTwoService.class); intent.putExtra("from", "MainAcivity"); bindService(intent, conn, BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(conn); } }
MyService.java
public class MyService extends Service{ //client 可以通过Binder获取Service实例 public class MyBinder extends Binder { public MyService getService() { return MyService .this; } } //通过binder实现调用者client与Service之间的通信 private MyBinder binder = new MyBinder(); @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_NOT_STICKY; } @Nullable @Override public IBinder onBind(Intent intent) { return binder; } @Override public boolean onUnbind(Intent intent) { return false; } @Override public void onDestroy() { super.onDestroy(); } //getRandomNumber是Service暴露出去供client调用的公共方法 public int getRandomNumber() { return generator.nextInt(); } }
如何保证service不被杀死
之前说过:当系统资源不足时, 会回收一些不重要的service,service被系统回收也会停止运行并被销毁,那么如何保证service不被杀死呢
1. onStartCommand方式中,返回START_STICKY
表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,但是onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息。如果你的Service可以在任意时刻运行或结束都没什么问题,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY
2. 提高Service的优先级
在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低
*: 但是我在service中设置intent-filter,设置优先级build报错,有兴趣的可以另行查证
3. 提升Service进程的优先级
前台进程foreground_app优先级相对较高,可以将service设置为前台进程
代码实例:
MainActivity.javapackage com.iauto.helloword; import androidx.appcompat.app.AppCompatActivity; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 启动service Intent mIntent=new Intent(MainActivity.this,MyService.class) ; Log.d("activity", "onCreate: to start service"); startForegroundService(mIntent); Log.d("activity", "onCreate: start service end"); finish(); } }
MyService.java
package com.iauto.helloword; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.BitmapFactory; import android.os.Build; import android.os.IBinder; import android.util.Log; import androidx.core.app.NotificationCompat; public class MyService extends Service { private static final String TAG = "MyService"; private NotificationManager notificationManager; private String notificationId = "channel_Id"; private String notificationName = "channel_Name"; public MyService() { } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } @Override public void onCreate() { Log.d(TAG, "onCreate: ..."); notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); //创建NotificationChannel if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(notificationId, notificationName, NotificationManager.IMPORTANCE_HIGH); // 必须创建notifychannel, 不然会抛异常Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notificationManager.createNotificationChannel(channel); } startForeground(1, getNotification()); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand: ..."); return START_STICKY; } @Override public void onDestroy() { Log.d(TAG, "onDestroy: ...."); super.onDestroy(); } private Notification getNotification() { Notification.Builder builder = new Notification.Builder(this) .setContentTitle("ScenarioEngineLite正在后台运行") .setContentText(""); //设置Notification的ChannelID,否则不能正常显示 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId(notificationId); } Notification notification = builder.build(); return notification; } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.iauto.helloword"> <!-- 必须设置以下权限,否则会抛异常RemoteException--> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.MainActivity" android:persistent="true"> <service android:name=".MyService" android:enabled="true" android:exported="true"> <!-- <intent-filter android:priority = "1000"/>--> </service> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
4.在onDestroy方法里重启Service
当service走到onDestroy()时,发送一个自定义广播,当收到广播时,重新启动service。
5.系统广播监听Service状态
6.将APK安装到/system/app,变身为系统级应用
更多相关内容 -
android防止service多次执行onStartCommand
2018-04-09 14:23:32原地址:... service的启动方式有两种 1)使用Context.startService(Intent)启动 2)通过bindService启动(该方式service的生命周期跟activity的生命周期相关联...原地址:https://blog.csdn.net/yuzhiqiang_1993/article/details/78211385
service作为Android四大组件之一,我们会经常使用它。
service的启动方式有两种
1)使用Context.startService(Intent)启动
2)通过bindService启动(该方式service的生命周期跟activity的生命周期相关联)需要注意的是:
service的onCreate只会执行一次,onBind也只会执行一次,onStartCommand可以执行多次也就是说一旦service创建过了,而又没有被销毁的话,不管是再次调用bindService还是startService都不会再走onCreate, onBind也只会执行一次,多次调用startService会多次执行onstartCommand。
我们可以通过打印日志来看一下:
图片看不清楚的话可以把图片直接拉到一个新窗口看。那么问题来了,在有些业务上比如版本更新之类的服务,我们一般是通过startService的方式来启动服务,因为下载是个耗时操作,我们不希望跟activity的生命周期绑定到一起。
下载的逻辑是在onStartCommand中实现的,如果多次调用onStartCommand很可能会出现问题,那么我们就只希望服务启动后只走一次onStartCommand。解决思路:
我们可以直接判断service是否正在运行,如果正在运行,就不再startService/** * 判断服务是否运行 */ private boolean isServiceRunning(final String className) { ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningServiceInfo> info = activityManager.getRunningServices(Integer.MAX_VALUE); if (info == null || info.size() == 0) return false; for (ActivityManager.RunningServiceInfo aInfo : info) { if (className.equals(aInfo.service.getClassName())) return true; } return false; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
/*如果服务正在运行,直接return*/ if (isServiceRunning("com.yzq.servicedemo.MyService")){ Log.i("服务正在运行","return"); return; } /*启动服务*/ startServiceIntent = new Intent(this, MyService.class); startService(startServiceIntent);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
来看看打印日志:
图片看不清楚的话可以把图片直接拉到一个新窗口看。
-
android笔记--处理started service的多次启动请求
2018-05-01 21:49:11所谓的started service, 是我对以startService()方法启动的service的叫法. Service运行在所在进程的main thread中. 启动一个service时, 不会自动为该service创建新的thread. 这意味着开发者通常需要为service...所谓的started service, 是我对以startService()方法启动的service的叫法. Service运行在所在进程的main thread中. 启动一个service时, 不会自动为该service创建新的thread. 这意味着开发者通常需要为service开启新的线程, 以执行耗时或者阻塞操作—否则可能导致ANR错误的发生. 既然如此, 为何不在activity中直接开启新的线程执行耗时操作或者阻塞操作呢? 原因在于一个包含正在运行的service的进程具有更安全的进程优先级--它的进程优先级至少是service process, 而activity处于后台运行时它的进程优先级为background process, 在系统内存不足时,处于后台运行的activity更有可能被系统杀死. 关于进程优先级, 在我的另一篇博文中有过介绍 http://coolxing.iteye.com/blog/1279170 .如果没有调用stopService()方法或者stopSelf()方法, 就算onStartCommand()方法执行完成, 该service仍然处于active状态, 但onStartCommand()方法返回后, 系统可能处于内存不足的缘故摧毁这个service, 如果发生这种情形, 那么系统将尽快重建这个service, 而onStartCommand()方法的返回值用来定义系统该如何重建service, 返回值的可以是以下3个int值中的一个:
- START_NOT_STICKY, 表明不要重建service. 这可以避免在非必要的情况下浪费系统的资源.
- START_STICKY, 表明需要重建service, 并在重建service之后调用onStartCommand()方法, 传递给该方法的intent为null.
- START_REDELIVER_INTENT, 表明需要重建service, 并在重建service之后调用onStartCommand()方法, 传递给该方法的intent为service被摧毁之前接收到的最后一个intent.
当service第一次被启动时会调用onCreate()方法, 然后再调用onStartCommand()方法. 在该service的生命周期内,如果再次启动这个service, 就会直接调用onStartCommand()方法了.
onStartCommand(Intent intent, int flags, int startid)方法的第一个参数intent就是用于启动该service的intent, 第二个参数flags的值通常为0, 第三个参数startid标识此次启动请求, 通常用于stopSelf(int)方法.
通常情况下, started service应该使用单线程处理多个启动请求, 此时继承IntentService是一个更好的选择, 当然也可以继承Service类, 只是会导致更多的代码. 不过如果started service需要并发处理多个启动请求, 那么只能继承Service类.
继承Service--以单线程的方式处理多个启动请求
单线程方式处理多个启动请求是大多数时候的选择, 因为该方式可以有效的处理所有请求, 却有着不需要考虑线程安全的优势. 下面的例子使用了HandlerThread和Handler类构建了单线程模型, 关于Handler, Looper, Message等线程相关的概念, 请参考我的另一篇博文http://coolxing.iteye.com/blog/1208371.
- public class SingleService extends Service {
- private static final int NEW_INTENT_COMMING = 0;
- private Handler handler;
- private final class WorkThreadHanlder extends Handler {
- // 使用指定Looper创建Handler
- public WorkThreadHanlder(Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == NEW_INTENT_COMMING) {
- Log.d("SingleService", Thread.currentThread().getName());
- try {
- Thread.sleep(5 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- // arg1中存储的是onStartCommand()方法的startId参数
- stopSelf(msg.arg1);
- }
- }
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Message msg = handler.obtainMessage();
- msg.arg1 = startId;
- msg.what = NEW_INTENT_COMMING;
- msg.sendToTarget();
- return START_STICKY;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- HandlerThread thread = new HandlerThread("work thread",
- Process.THREAD_PRIORITY_BACKGROUND);
- // thread启动之后才能调用getLooper()方法获取thread中的Looper对象
- thread.start();
- // 使用子线程的Looper创建handler, 该handler绑定在子线程的消息队列上
- handler = new WorkThreadHanlder(thread.getLooper());
- }
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
- }
继承IntentService--以单线程的方式处理多个启动请求
从上面的例子可以看到, 在Service中构建单线程模型需要编写大量代码, 为了简化编程, android提供了IntentService类. IntentService类Service类的子类, 查看源代码可以知道IntentService只是将上例的单线程模型进行了简单的包装, 在handleMessage()方法中调用onHandleIntent()方法进行具体的请求处理, 该方法是一个抽象方法, 需要由开发者提供实现. 以下是IntentService对Service增强之处:
- 在onCreate()方法中创建了一个子线程, 用于处理所有发送给onStartcommand()方法的intent.
- 在onStartCommand()方法中将intent和startId存储在Message对象中, 并将该Message发送给子线程的消息队列. 因此同时存在多个启动请求时, 就会将这些请求的intent以Message的形式加入到子线程的消息队列中, 然后子线程从队列中不断的取出和处理消息.
- 由子线程的Handler对象处理消息, Handler对象的handleMessage()方法调用onHandleIntent方法后调用了stopSelf(int)方法, 因此开发者无需考虑service的退出问题.
- onHandleIntent方法是一个抽象方法, 留待开发者提供具体实现.
因此继承IntentService可以方便的构建单线程处理启动请求的service, 开发者不需要考虑创建线程, 创建Handler, service的退出等复杂的问题, 仅需要提供一个构造函数和实现onHandleIntent方法.
使用IntentService重写上面的例子:
- public class SingleIntentService extends IntentService {
- public SingleIntentService() {
- // 设置子线程名称
- super("work thread");
- }
- @Override
- protected void onHandleIntent(Intent intent) {
- Log.d("SingleService", Thread.currentThread().getName());
- try {
- Thread.sleep(5 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
继承Service--并发处理多个请求
为了并发处理多个请求, 可以在考虑在onStartCommand()方法中启动新的线程处理intent. 这样, 每调用一次startService()方法就会启动一个新的线程处理请求.
- public class MultipleService extends Service {
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
- @Override
- public int onStartCommand(Intent intent, int flags, final int startId) {
- new Thread() {
- public void run() {
- Log.d("SingleService", Thread.currentThread().getName());
- try {
- Thread.sleep(5 * 1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- stopSelf(startId);
- };
- }.start();
- return super.onStartCommand(intent, flags, startId);
- }
- }
原文地址:http://coolxing.iteye.com/blog/1284476
-
Service的两种启动方式
2019-03-18 15:13:53通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。 要创建一个这样的Service,你需要让该类继承Service类,然后重写以下方法...Service生命周期.png
第一种方式:通过StartService启动Service
通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。
要创建一个这样的Service,你需要让该类继承Service类,然后重写以下方法:
-
onCreate()
1.如果service没被创建过,调用startService()后会执行onCreate()回调;
2.如果service已处于运行中,调用startService()不会执行onCreate()方法。
也就是说,onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate(),此方法适合完成一些初始化工作。 -
onStartCommand()
如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用。onStartCommand()方法很重要,我们在该方法中根据传入的Intent参数进行实际的操作,比如会在此处创建一个线程用于下载数据或播放音乐等。 -
onBind()
Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。 -
onDestory()
在销毁的时候会执行Service该方法。
这几个方法都是回调方法,且在主线程中执行,由android操作系统在合适的时机调用。
startService代码实例
创建TestOneService,并在manifest里注册。
在MainActivty中操作TestOneService,code如下:/** * Created by Kathy on 17-2-6. */ public class TestOneService extends Service{ @Override public void onCreate() { Log.i("Kathy","onCreate - Thread ID = " + Thread.currentThread().getId()); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("Kathy", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId()); return super.onStartCommand(intent, flags, startId); } @Nullable @Override public IBinder onBind(Intent intent) { Log.i("Kathy", "onBind - Thread ID = " + Thread.currentThread().getId()); return null; } @Override public void onDestroy() { Log.i("Kathy", "onDestroy - Thread ID = " + Thread.currentThread().getId()); super.onDestroy(); } }
在MainActivity中三次startService,之后stopService。
/** * Created by Kathy on 17-2-6. */ public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("Kathy", "Thread ID = " + Thread.currentThread().getId()); Log.i("Kathy", "before StartService"); //连续启动Service Intent intentOne = new Intent(this, TestOneService.class); startService(intentOne); Intent intentTwo = new Intent(this, TestOneService.class); startService(intentTwo); Intent intentThree = new Intent(this, TestOneService.class); startService(intentThree); //停止Service Intent intentFour = new Intent(this, TestOneService.class); stopService(intentFour); //再次启动Service Intent intentFive = new Intent(this, TestOneService.class); startService(intentFive); Log.i("Kathy", "after StartService"); } }
打印出的Log如下:
02-06 15:19:45.090 8938-8938/? I/Kathy: Thread ID = 1 02-06 15:19:45.090 8938-8938/? I/Kathy: before StartService 02-06 15:19:45.233 8938-8938/? I/Kathy: onCreate - Thread ID = 1 02-06 15:19:45.234 8938-8938/? I/Kathy: onStartCommand - startId = 1, Thread ID = 1 02-06 15:19:45.234 8938-8938/? I/Kathy: onStartCommand - startId = 2, Thread ID = 1 02-06 15:19:45.235 8938-8938/? I/Kathy: onStartCommand - startId = 3, Thread ID = 1 02-06 15:19:45.236 8938-8938/? I/Kathy: onDestroy - Thread ID = 1 02-06 15:19:45.237 8938-8938/? I/Kathy: onCreate - Thread ID = 1 02-06 15:19:45.237 8938-8938/? I/Kathy: onStartCommand - startId = 1, Thread ID = 1 02-06 15:19:45.238 8938-8938/? I/Kathy: after StartService
分析:
1.主线程打印出是1,所有回调方法中打印出的执行线程ID都是1,证明回调方法都是在主线程中执行的。
2.三次调用startService,只触发一次onCreate回调,触发了三次onStartCommand回调,且startId分别为1,2,3。证明 多次startService不会重复执行onCreate回调,但每次都会执行onStartCommand回调。第二种方式:通过bindService启动Service
bindService启动服务特点:
1.bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
2.client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
3.bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁。bindService代码实例
交互界面设计如下:
ActivityA界面布局.png
ActivityB界面布局.png
1.创建一个TestTwoService继承Service(Server)
2.创建ActivityA,可以通过bindService绑定服务(client)
3.创建ActivityB,可以通过bindService绑定服务(client)
4.ActivityA可以跳转到ActivityBTestTwoService创建如下:
要想让Service支持bindService调用方式,需要做以下事情:
1.在Service的onBind()方法中返回IBinder类型的实例。
2.onBInd()方法返回的IBinder的实例需要能够返回Service实例本身。通常,最简单的方法就是在service中创建binder的内部类,加入类似getService()的方法返回Service,这样绑定的client就可以通过getService()方法获得Service实例了。/** * Created by Kathy on 17-2-6. */ public class TestTwoService extends Service{ //client 可以通过Binder获取Service实例 public class MyBinder extends Binder { public TestTwoService getService() { return TestTwoService.this; } } //通过binder实现调用者client与Service之间的通信 private MyBinder binder = new MyBinder(); private final Random generator = new Random(); @Override public void onCreate() { Log.i("Kathy","TestTwoService - onCreate - Thread = " + Thread.currentThread().getName()); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("Kathy", "TestTwoService - onStartCommand - startId = " + startId + ", Thread = " + Thread.currentThread().getName()); return START_NOT_STICKY; } @Nullable @Override public IBinder onBind(Intent intent) { Log.i("Kathy", "TestTwoService - onBind - Thread = " + Thread.currentThread().getName()); return binder; } @Override public boolean onUnbind(Intent intent) { Log.i("Kathy", "TestTwoService - onUnbind - from = " + intent.getStringExtra("from")); return false; } @Override public void onDestroy() { Log.i("Kathy", "TestTwoService - onDestroy - Thread = " + Thread.currentThread().getName()); super.onDestroy(); } //getRandomNumber是Service暴露出去供client调用的公共方法 public int getRandomNumber() { return generator.nextInt(); } }
client端要做的事情:
1.创建ServiceConnection类型实例,并重写onServiceConnected()方法和onServiceDisconnected()方法。
2.当执行到onServiceConnected回调时,可通过IBinder实例得到Service实例对象,这样可实现client与Service的连接。
3.onServiceDisconnected回调被执行时,表示client与Service断开连接,在此可以写一些断开连接后需要做的处理。创建ActivityA,代码如下:
/** * Created by Kathy on 17-2-6. */ public class ActivityA extends Activity implements Button.OnClickListener { private TestTwoService service = null; private boolean isBind = false; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { isBind = true; TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder) binder; service = myBinder.getService(); Log.i("Kathy", "ActivityA - onServiceConnected"); int num = service.getRandomNumber(); Log.i("Kathy", "ActivityA - getRandomNumber = " + num); } @Override public void onServiceDisconnected(ComponentName name) { isBind = false; Log.i("Kathy", "ActivityA - onServiceDisconnected"); } }; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_a); Log.i("Kathy", "ActivityA - onCreate - Thread = " + Thread.currentThread().getName()); findViewById(R.id.btnBindService).setOnClickListener(this); findViewById(R.id.btnUnbindService).setOnClickListener(this); findViewById(R.id.btnStartActivityB).setOnClickListener(this); findViewById(R.id.btnFinish).setOnClickListener(this); } @Override public void onClick(View v) { if (v.getId() == R.id.btnBindService) { //单击了“bindService”按钮 Intent intent = new Intent(this, TestTwoService.class); intent.putExtra("from", "ActivityA"); Log.i("Kathy", "----------------------------------------------------------------------"); Log.i("Kathy", "ActivityA 执行 bindService"); bindService(intent, conn, BIND_AUTO_CREATE); } else if (v.getId() == R.id.btnUnbindService) { //单击了“unbindService”按钮 if (isBind) { Log.i("Kathy", "----------------------------------------------------------------------"); Log.i("Kathy", "ActivityA 执行 unbindService"); unbindService(conn); } } else if (v.getId() == R.id.btnStartActivityB) { //单击了“start ActivityB”按钮 Intent intent = new Intent(this, ActivityB.class); Log.i("Kathy", "----------------------------------------------------------------------"); Log.i("Kathy", "ActivityA 启动 ActivityB"); startActivity(intent); } else if (v.getId() == R.id.btnFinish) { //单击了“Finish”按钮 Log.i("Kathy", "----------------------------------------------------------------------"); Log.i("Kathy", "ActivityA 执行 finish"); this.finish(); } } @Override protected void onDestroy() { super.onDestroy(); Log.i("Kathy", "ActivityA - onDestroy"); } }
创建ActivityB,代码如下:
/** * Created by Kathy on 17-2-6. */ public class ActivityB extends Activity implements Button.OnClickListener { private TestTwoService service = null; private boolean isBind = false; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { isBind = true; TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder)binder; service = myBinder.getService(); Log.i("Kathy", "ActivityB - onServiceConnected"); int num = service.getRandomNumber(); Log.i("Kathy", "ActivityB - getRandomNumber = " + num); } @Override public void onServiceDisconnected(ComponentName name) { isBind = false; Log.i("Kathy", "ActivityB - onServiceDisconnected"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_b); findViewById(R.id.btnBindService).setOnClickListener(this); findViewById(R.id.btnUnbindService).setOnClickListener(this); findViewById(R.id.btnFinish).setOnClickListener(this); } @Override public void onClick(View v) { if(v.getId() == R.id.btnBindService){ //单击了“bindService”按钮 Intent intent = new Intent(this, TestTwoService.class); intent.putExtra("from", "ActivityB"); Log.i("Kathy", "----------------------------------------------------------------------"); Log.i("Kathy", "ActivityB 执行 bindService"); bindService(intent, conn, BIND_AUTO_CREATE); }else if(v.getId() == R.id.btnUnbindService){ //单击了“unbindService”按钮 if(isBind){ Log.i("Kathy", "----------------------------------------------------------------------"); Log.i("Kathy", "ActivityB 执行 unbindService"); unbindService(conn); } }else if(v.getId() == R.id.btnFinish){ //单击了“Finish”按钮 Log.i("Kathy", "----------------------------------------------------------------------"); Log.i("Kathy", "ActivityB 执行 finish"); this.finish(); } } @Override public void onDestroy(){ super.onDestroy(); Log.i("Kathy", "ActivityB - onDestroy"); } }
测试步骤1
step1: 点击ActivityA的bindService按钮
step2: 再点击ActivityA的unbindService按钮
Log输出:02-07 14:09:38.031 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main 02-07 14:09:39.488 1738-1738/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:09:39.488 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA 执行 bindService 02-07 14:09:39.496 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main 02-07 14:09:39.497 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main 02-07 14:09:39.500 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected 02-07 14:09:39.500 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -1046987376 02-07 14:09:50.866 1738-1738/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:09:50.866 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA 执行 unbindService 02-07 14:09:50.870 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA 02-07 14:09:50.871 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main
总结调用bindService之后发生的事情:
1.client执行bindService()
2.如果Service不存在,则Service执行onCreate(),onBind()
3.client实例ServiceConnection执行onServiceConnected()方法总结调用unbindService之后发生的事情:
1.client执行unbindService()
2.client与Service解除绑定连接状态
3.Service检测是否还有其他client与其连接,如果没有Service执行onUnbind()和onDestroy()测试步骤2
step1: 点击ActivityA的bindService按钮
step2: 再点击ActivityA的Finish按钮
Log 输出:02-07 14:49:16.626 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main 02-07 14:49:18.102 12566-12566/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:49:18.102 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 执行 bindService 02-07 14:49:18.105 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main 02-07 14:49:18.110 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main 02-07 14:49:18.112 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected 02-07 14:49:18.112 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -318399886 02-07 14:49:19.540 12566-12566/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:49:19.540 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 执行 finish 02-07 14:49:19.789 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onDestroy 02-07 14:49:19.798 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA 02-07 14:49:19.798 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main
总结:如果client销毁,那么client会自动与Service解除绑定。
测试步骤3
step1: 点击ActivityA的bindService按钮
step2: 点击ActivityA的startActivity B按钮,切换到ActivityB
step3: 点击ActivityB中的bindService按钮
step4: 点击ActivityB中的unbindService按钮
step5: 点击ActivityB中的Finish按钮
step6: 点击ActivityA中的unbindService按钮
得到Log:02-07 14:55:04.390 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main 02-07 14:55:08.191 12566-12566/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:55:08.191 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 执行 bindService 02-07 14:55:08.197 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main 02-07 14:55:08.198 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main 02-07 14:55:08.205 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected 02-07 14:55:08.205 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -706215542 02-07 14:55:23.261 12566-12566/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:55:23.261 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 启动 ActivityB 02-07 14:55:29.239 12566-12566/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:55:29.239 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 执行 bindService 02-07 14:55:29.241 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - onServiceConnected 02-07 14:55:29.241 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - getRandomNumber = 1827572726 02-07 14:55:33.951 12566-12566/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:55:33.951 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 执行 unbindService 02-07 14:55:36.645 12566-12566/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:55:36.645 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 执行 finish 02-07 14:55:36.852 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - onDestroy 02-07 14:55:43.137 12566-12566/com.demo.kathy.demo I/Kathy: ---------------------------------------------------------------------- 02-07 14:55:43.137 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 执行 unbindService 02-07 14:55:43.143 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA 02-07 14:55:43.143 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main
总结bindService的生命周期:
1.点击ActivityA的bindService按钮
第一次调用bindService会实例化TestTwoService,然后执行其onBind()方法,得到IBinder类型的实例,将其作为参数传入ActivityA的ServiceConnection的onServiceConnected方法中,标志着ActivityA与TestTwoService建立了绑定。2.点击ActivityB中的bindService按钮
由于TestTwoService已处于运行状态,所以再次调用bindService不会重新创建它的实例,所以也不会执行TestTwoService的onCreate()方法和onBind()方法。ActivityB与ActivityA共享IBinder实例。此时有两个client与TestTwoService绑定。3.点击ActivityB中的unbindService按钮
ActivityB与TestTwoService解除了绑定,当没有任何client与Service绑定时,才会执行Service的onUnbind()方法。此时,ActivityA还在绑定连接中,所以不会执行Service的解绑方法。4.点击ActivityA中的unbindService按钮
ActivityA执行unbindService之后,ActivityA与TestTwoService就解除绑定了,这样就没有client与TestTwoService绑定,这时候Android会销毁TestTwoService,在销毁前会先执行TestTwoService的onUnbind()方法,然后才会执行其onDestroy()方法,这样TestService就销毁了。如何保证Service不被杀死?
1. onStartCommand方式中,返回START_STICKY
首先我们来看看onStartCommand都可以返回哪些值:
调用Context.startService方式启动Service时,如果Android面临内存匮乏,可能会销毁当前运行的Service,待内存充足时可以重建Service。而Service被Android系统强制销毁并再次重建的行为依赖于Service的onStartCommand()方法的返回值。
-
START_NOT_STICKY
如果返回START_NOT_STICKY,表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service。当然如果在其被杀掉之后一段时间又调用了startService,那么该Service又将被实例化。那什么情境下返回该值比较恰当呢?
如果我们某个Service执行的工作被中断几次无关紧要或者对Android内存紧张的情况下需要被杀掉且不会立即重新创建这种行为也可接受,那么我们便可将 onStartCommand的返回值设置为START_NOT_STICKY。
举个例子,某个Service需要定时从服务器获取最新数据:通过一个定时器每隔指定的N分钟让定时器启动Service去获取服务端的最新数据。当执行到Service的onStartCommand时,在该方法内再规划一个N分钟后的定时器用于再次启动该Service并开辟一个新的线程去执行网络操作。假设Service在从服务器获取最新数据的过程中被Android系统强制杀掉,Service不会再重新创建,这也没关系,因为再过N分钟定时器就会再次启动该Service并重新获取数据。 -
START_STICKY
如果返回START_STICKY,表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,但是onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息。如果你的Service可以在任意时刻运行或结束都没什么问题,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY,比如一个用来播放背景音乐功能的Service就适合返回该值。 -
START_REDELIVER_INTENT
如果返回START_REDELIVER_INTENT,表示Service运行的进程被Android系统强制杀掉之后,与返回START_STICKY的情况类似,Android系统会将再次重新创建该Service,并执行onStartCommand回调方法,但是不同的是,Android系统会再次将Service在被杀掉之前最后一次传入onStartCommand方法中的Intent再次保留下来并再次传入到重新创建后的Service的onStartCommand方法中,这样我们就能读取到intent参数。只要返回START_REDELIVER_INTENT,那么onStartCommand重的intent一定不是null。如果我们的Service需要依赖具体的Intent才能运行(需要从Intent中读取相关数据信息等),并且在强制销毁后有必要重新创建运行,那么这样的Service就适合返回START_REDELIVER_INTENT。
2.提高Service的优先级
在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。3.提升Service进程的优先级
当系统进程空间紧张时,会依照优先级自动进行进程的回收。
Android将进程分为6个等级,按照优先级由高到低依次为:- 前台进程foreground_app
- 可视进程visible_app
- 次要服务进程secondary_server
- 后台进程hiddena_app
- 内容供应节点content_provider
- 空进程empty_app
可以使用startForeground将service放到前台状态,这样低内存时,被杀死的概率会低一些。
4.在onDestroy方法里重启Service
当service走到onDestroy()时,发送一个自定义广播,当收到广播时,重新启动service。5.系统广播监听Service状态
6.将APK安装到/system/app,变身为系统级应用 -
-
深入浅出Service启动流程
2019-01-12 11:52:09Service启动方式有两种,一种是通过Context的startService启动Service,另一种是通过Context的bindService绑定Service,下面对这两种启动方式的启动流程进行详细的讲解。 startService的启动流程 通过startService... -
多进程导致Application多次启动
2017-08-16 10:36:42前几天碰到一个问题,application多次启动,开始并不清楚是什么原因,后来看到在我们自己的插件里也有一个进程启动了这个application 所以我的application多次启动是因为多进程 那我们如何来避免这个问题呢 ... -
三 、 service生命周期 启动方式 Android 8.0后台service
2021-11-14 14:40:00Service启动后默认是运行在主线程中,在执行具体耗时任务过程中要手动开启子线程,应用程序进程被杀死,所有依赖该进程的Service服务也会停止运行。 图片和部分文字转载于... -
service的onStartCommand和防止service多次执行onStartCommand
2019-04-08 04:58:57也就是说一旦service创建过了,而又没有被销毁的话,不管是再次调用bindService还是startService都不会再走onCreate, onBind也只会执行一次,多次调用startService会多次执行onstartCommand。 那么问题来了,在... -
android启动Service和ForegroundService
2022-03-22 22:00:16之前用的都是Service class SocketService : Service() { private var webSockethandler: WebSocketHandler? = null private var pushObserve: Observer<String>? = null private var taskLog: Int = 0 ... -
Linux systemd启动守护进程,service启动顺序分析及调整service启动顺序
2019-05-15 22:48:39相较于之前被采用的init进程串行启动,systemd进程采用并行启动且为系统启动管理提供了成套的方案。 探索systemd启动的问题背景是,团队使用树莓派zero W+做的产品在系统启动后需要启动声卡播放音频文件,但声卡... -
Android Service启动(一) startService()启动过程以及原理解析
2019-01-06 14:37:22Service分为两种工作状态,启动状态和绑定状态,通过调用startService()可进入启动状态,通过bindService()可进入绑定状态,本篇文章主要讲解startService()启动Service的过程。(额,android的版本还是26) 我们... -
Android Service两种启动方式及 生命周期
2021-05-27 05:23:28onStart()后调用bindService该service则调用onBind(),调用onBind以后调stopService将无法释放该... 使用startService启动服务的生命周期方法:使用这种方法启动一个Service,在Service启动后该Service和启动该S... -
在Application中启动一个IntentService会创建并执行两次Service
2019-04-28 16:26:53InitDataService是一个IntentService,Log日志显示InitDataService的onCreate方法和onHandleIntent执行了两次,但是startService只是调用了一次 放在MainActivity中就只执行一次了 搞不明白是什么原因 -
Android 11 添加系统开机启动的Service方案
2022-03-16 15:37:45android11 添加了开机启动的service,涉及添加过程中遇到的编译,安全规则语法等多个问题 -
Service的两种启动方式和区别?
2018-03-28 20:58:43第一种启动方式:startService(Intent)启动服务 生命周期方法流程:onCreate --- > onStartCommand 关闭服务:stopService(Intent)关闭服务 生命周期方法流程 :onDestory第二种启动方式:bindService... -
一篇文章看明白 Android Service 启动过程
2018-04-02 10:36:25Android - Service 启动过程 概述 Service 启动过程与 Activity 启动过程比较相似,不了解 Activity 启动过程的可以先看一下:Activity 启动过程。 Service 的启动分两种情况:startService,bindService。 ... -
Android10.0 Service启动源码解析
2019-11-14 09:24:37Service的启动流程的还是比较简单的,不像Activity那么复杂,相关联的类和方法都少了很多,不像Activity启动流程里面走着走着就会迷失方向,源码分析这是加强Android内功修炼的必修课,分析了几个流程之后,你就会... -
Service启动流程总结-start和stop service
2020-06-07 18:38:09文章目录回顾概述源码探究startService过程onCreate阶段onStartCommand阶段Service超时机制设置超时移除超时触发超时当目标Service进程未启动时stopService过程总结 回顾 Activity启动系列总结: Activity启动流程... -
Service的两种启动模式
2018-06-12 15:48:05Service有两种启动模式,startService 和bindService两种模式下的生命周期如下:(借用了Big不吃鱼的https://www.jianshu.com/p/4c798c91a613)一、startService模式通过这种方式调用startService,onCreate只会被... -
Android(O) 8.0 怎样在后台启动service
2018-03-23 19:15:00在Android8.0之后, google对后台启动service进行了更加严格的限制, 具体可以参考官网文档, 上不了外网的朋友看这个网址:android中文官网, 这里摘出和后台service相关的内容: 后台服务限制 在后台中运行的服务会... -
Android中服务(Service)的两种启动方式
2019-09-03 23:03:35Service是安卓四大组件之一,也是可执行的程序,也又自己的生命周期。创建、配置service和创建、配置Activity相似。... 一、Service的第一种启动方式:start方式 步骤: 1、定义一个类继承Service 2、在Ma... -
Service的生命周期onStartCommand开启两次
2018-09-17 22:42:281.第一次正常启动: 服务的生命周期方法:onStartCommand---22874-22874 2.呼出多任务后滑动APP结束任务: 重启后的进程PID 一样 服务的生命周期方法:onStartCommand---23094-23094 服务的生命周期方法:... -
Android Service两种启动方式详解
2019-09-01 15:16:301. 概念 开始,先稍稍讲一点android中Service的概念和用途吧~ Service分为本地服务(LocalService)和远程服务(RemoteService): ...相应bindService会方便很多。主进程被Kill后,服务便会终止。 2、远... -
Android Service和IntentService(启动方式startService和bindService)
2018-09-17 18:32:33Service Android四大组件之一,Service 是长期运行在后台的应用程序组件。 Service 不是进程,也不是线程,它和应用程序在同一个进程中 Service中不能做耗时操作,他是寄托于主线程的。运行在主线程中 ... -
Service的生命周期、两种启动方法的区别
2020-03-06 00:57:34Service的生命周期、两种启动方法的区别 一、A started service onCreate, onStartCommand, onBind 和 onDestroy。这几个方法都是回调方法,都是由Android操作系统在合适的时机调用的,并且需要注意的是这几个回调... -
Android Studio使用onCreate(),onStartCommand()和stopService()方法来启动和停止服务
2021-12-11 20:05:45这种启动方式的特点: 1、一旦服务开启跟调用者(开启者)就没有任何关系了。 2、开启者退出了,开启者挂了,服务还在后台长期的运行。 3、开启者不能调用服务里面的方法。