Android四大组件之一:Service是一个可以在后台执行长时间运行 *** 作而不提供用户界面的应用组件;
服务是Android中实现程序后台的解决方案,不依赖任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务还能保持运行,服务非常适合那些不需要和用户交互而且还要求长期运行的任务。
服务像Activity那样也是默认运行在主线程中,如果有耗时任务还是要在服务内部创建子线程,不然程序会ANR。(默认是耗时 *** 作超过20秒就会报ANR,这个待会可以验证)
二、Service的分类Service分为本地服务(LocalService)和远程服务(RemoteService):
1、本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相对于bindService会方便很多。主进程被Kill后,服务便会终止。
2、远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。
三、Service的使用场景1、用于处理网络事务(下载文件)
2、播放音乐(音乐播放器)
3、执行文件I/O(读写文件)
四、Service的启动方式1、startService 启动的服务:
主要用于启动一个服务执行后台任务,不进行通信。停止服务需要主动使用stopService;
2、bindService 启动的服务:
该方法启动的服务可以进行通信。停止服务使用unbindService;
3、startService 同时也 bindService 启动的服务:
停止服务应同时使用stopService与unbindService;
五、代码验证1、验证ANR的问题:
public class MyService extends Service {//创建一个普通服务 private static final String TAG = MyService.class.getSimpleName(); @Override public void onCreate() { super.onCreate(); Log.i("Kathy", "onCreate - Thread ID = " + Thread.currentThread().getId() + " ,Thread Name = " + Thread.currentThread().getName()); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("Kathy", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId() + ", data = " + intent.getStringExtra("data")); try { Thread.sleep(30000); } catch (InterruptedException e) { e.printStackTrace(); } 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(); } }
如果没有在清单文件中注册,服务启动不起来的。
使用:一个启动按钮,一个停止按钮(服务是可以不用停止的,不主动停止就会跟着主进程的销毁而销毁)
public class ServiceDemoActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_service_demo); findViewById(R.id.startService).setonClickListener(v -> { Intent intent = new Intent(this, MyService.class); intent.putExtra("data", "Kathy"); startService(intent); }); findViewById(R.id.stopService).setonClickListener(v -> { Intent intent = new Intent(this, MyService.class); stopService(intent); }); } }
结果:因为我在service的onStartCommand处理了20秒以上的耗时 *** 作(The application may be doing too much work on its main thread)。
解决:加了一个异步线程处理耗时 *** 作
@Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("Kathy", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId() + ", data = " + intent.getStringExtra("data")); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(30000); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); return super.onStartCommand(intent, flags, startId); }六、两种启动方式和生命周期分析
1、方式一:startService启动服务
这种方式通常一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,已启动的服务通常是执行单一 *** 作,而且不会将结果返回给调用方。(通俗的说就是逻辑控制硬编码在Service里了,不能用别的应用组件控制),如果系统把服务杀了也就结束了。
针对同一个服务(全局的服务),可以在主线程的任何Context的地方启动,只有第一次启动需要走onCreate生命周期,只要没stopService,后续的启动不会走这个方法了。
onCreate()-->onStartCommand()-->onDestroy()
第一次启动:onCreate()-->onStartCommand()
再次启动:onStartCommand()
停止:onDestroy()
再重新启动:onCreate()-->onStartCommand()
上面启动方式验证:
上面的ServiceDemoActivity的startService按一次,stopService按一次:
上面的ServiceDemoActivity的startService按两次,stopService按一次:
可以看出:onStartCommand()方法很重要,我们在该方法中根据传入的Intent参数进行实际的 *** 作,比如会在此处创建一个线程用于下载数据或播放音乐等。
注意一个点就是onStartCommand的返回值:
START_NOT_STICKY:表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service,如果想重新实例化该Service,就必须重新调用startService来启动。
使用场景:表示当Service在执行工作中被中断几次无关紧要或者对Android内存紧张的情况下需要被杀掉且不会立即重新创建这种行为也可接受的话,这是可以在onStartCommand()返回值中设置该值。例如在Service中定时从服务器中获取最新数据。
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:表示Service运行的进程被Android系统强制杀掉之后,与返回START_STICKY的情况类似,Android系统会将再次重新创建该Service,并执行onStartCommand()回调方法,但是不同的是,Android系统会再次将Service在被杀掉之前最后一次传入onStartCommand()方法中的Intent再次保留下来并再次传入到重新创建后的Service的onStartCommand方法中,这样我们就能读取到intent参数。
使用场景:如果我们的Service需要依赖具体的Intent才能运行(需要从Intent中读取相关数据信息等),并且在强制销毁后有必要重新创建运行,那么这样的Service就适合返回START_REDELIVER_INTENT。
2、方式二:bindService绑定服务
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会自行销毁。
代码验证:
public class MyBinderService extends Service { private static final String TAG = "Kathy"; MyBinder myBinder; @Override public void onCreate() { super.onCreate(); myBinder = new MyBinder(); Log.i("Kathy", "onCreate - Thread ID = " + Thread.currentThread().getId() + " ,Thread Name = " + Thread.currentThread().getName()); } @Nullable @Override public IBinder onBind(Intent intent) { Log.i("Kathy", "onBind - Thread ID = " + Thread.currentThread().getId() + " ,Thread Name = " + Thread.currentThread().getName()); return myBinder; } @Override public boolean onUnbind(Intent intent) { Log.i("Kathy", "onUnbind"); return super.onUnbind(intent); } @Override public void onDestroy() { Log.i("Kathy", "onDestroy"); super.onDestroy(); } public class MyBinder extends Binder { public void StartBB() { new Thread(new Runnable() { @Override public void run() { //处理具体的逻辑 stopSelf(); } }).start(); Log.d(TAG, "StartBB:BBBBBBBBBB"); } } }
public class ServiceDemoActivity2 extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_service_demo2); findViewById(R.id.BinderService).setonClickListener(v -> { Intent bindIntent = new Intent(ServiceDemoActivity2.this, MyBinderService.class); //这里的三个参数后面会细说 bindService(bindIntent, mConnection, BIND_AUTO_CREATE); }); findViewById(R.id.unBinderService).setonClickListener(v -> { //多次取消绑定程序会crash unbindService(mConnection); }); } private final ServiceConnection mConnection = new ServiceConnection() { //bind服务,onCreate之后 @Override public void onServiceConnected(ComponentName name, IBinder binder) { MyBinderService.MyBinder binder1 = (MyBinderService.MyBinder) binder; binder1.StartBB(); Log.d("Kathy", "onServiceConnected"); } //unBind服务时,在onDestroy之前 @Override public void onServiceDisconnected(ComponentName name) { Log.d("Kathy", "onServiceDisconnected"); } }; }
按下绑定按钮先启动服务:
再按下解绑按钮或者返回上一个页面:
生命周期也是onCreate-->onBind-->onServiceConnected-->onUnbind-->onDestory
可以看出ServiceConnection在client和service其中起到了纽带的作用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)