安卓四大组件之服务
一、服务是什么?
1.服务是什么?
用俗话说服务就是长期运行在后台的程序,如果官方一点,首先它是一个组件,用于执行长期运行的任务,并且与用户没有交互。
每一个服务都需要在配置文件AndroidManifest.xml文件下进行声明,怎么声明呢?
使用标签,其实和前面的activity和广播接收者receiver一样声明。
通过Context.startService()来开启服务,通过Context.stop()来停止服务。
还有一种启动形式就是通过Context.bindService()的方法
2.为什么要使用服务呢?
原因一:
从上面的文字说,我们可以知道服务就是执行于后台长期运行的操作。有些时候,我们没有界面,但是服务还是要工作。比如我们播放音乐,在后台播放音乐。比如,我们下载任务,在后台下载文件。这些没有界面的后台运行程序,这些都是用服务做得。
在讲第二个原因之前,先介绍几个概念:
- 前台进程:可以理解为最顶部的,直接跟用户进行交互的,比如我们的操作界面Activity;
- 可见进程:可以见到的,但是不进行操作。比如在一个Activity的顶部弹出一个Dialog,这个Dialog就是前台进程,但是这个Activity是可见进程;
- 服务进程:服务可以理解为忙碌的后台进程,虽然在后台,但是很忙碌;
- 后台进程:后台进程就是退隐到后台,不做事的进程;
- 空进程:空进程是不做事的,没有任何东西在上面跑着,仅仅做缓存作用。
假设内存不够用了,会先杀谁呢?
首先杀空进程,要是还不够就杀后台进程,要是还不够,就杀服务,服务被杀后,等内存够用了就会跑起来了。
如果面试问到,服务用语执行耗时操作,这是对的吗?
如时服务直接执行耗时操作,也会出现anr.
在这里给大家补充一下anr的时长知识。首先ANR的意思是android no response,也就是无相应或者理解为操作超时。
在android系统中广播的ANR时长为:
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
前台广播为10秒,后台广播为60秒。
按键操作的anr时长为:
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
如果在服务中直接做耗时操作,也是会出现ANR异常的。服务可以长期在后台运行,所以你可以这么做:如果要做耗时操作,比如说网络的访问,数据库的读写之类的,可以开线程去做。
3.服务的生命周期
1.基本生命周期
(1)创建一个新的活动,在xml文件下面添加两个按钮,用来开启服务和结束服务
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:onClick="startServiceClick"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始服务"
tools:ignore="OnClick" />
<Button
android:onClick="stopServiceClick"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="停止服务"
tools:ignore="OnClick" />
</LinearLayout>
(2).新建一个服务类
package com.example.service_learning.services;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
/**
* @author 23737
* @version 1.0 2021.5.17
*/
public class FirstService extends Service {
private static final String TAG = "FirstService";
public FirstService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate:·····");
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestory:·····");
}
}
如上所示:oncreate()用来开启服务;onDestroy()用来停止服务;
onStart()现在已经过时,替换的是onStartCommand(),每次开启服务,都会开启此方法
(3)在主活动的.java文件内添加代码
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//开启服务
public void startServiceClick(View view){
//第一步呢还是要创建一个intent(意图)
Intent intent = new Intent();
//第二步开启服务,setClass()里面传入两个参数,分别是当前活动和服务类
intent.setClass(this, FirstService.class);
//第三步就是把意图作为参数传给startService()方法,这样服务就开启了
startService(intent);
}
//停止服务
public void stopServiceClick(View view){
Intent intent = new Intent();
intent.setClass(this, FirstService.class);
stopService(intent);
}
}
点击发送和停止服务后,log显示首先调用oncreate,然后是onStartCommand()。停止服务时调用了ondestory()
可以看出,我们在startService之后,就执行了onCreate方法,接着是onStartCommand方法。当我们执行stopService的时候,就会走onDestroy方法了。
这就是一个服务的基本生命周期
4.服务的绑定和解绑
1.绑定服务
在服务类里面编辑代码,创建一个内部类和内部方法
public class FirstService extends Service {
private static final String TAG = "FirstService";
public FirstService() {
}
//内部类调用服务方法
public class InnerBinder extends Binder {
public void callServiceInnerMethod(){
sayHello();
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new InnerBinder();
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate:·····");
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestory:·····");
}
public void sayHello(){
Toast.makeText(this,"hello",Toast.LENGTH_LONG).show();
}
}
然后在.java文件下编辑代码
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//开启服务
public void startServiceClick(View view){
//第一步呢还是要创建一个intent(意图)
Intent intent = new Intent();
//第二步开启服务,setClass()里面传入两个参数,分别是当前活动和服务类
intent.setClass(this, FirstService.class);
//第三步就是把意图作为参数传给startService()方法,这样服务就开启了
startService(intent);
}
//停止服务
public void stopServiceClick(View view){
Intent intent = new Intent();
intent.setClass(this, FirstService.class);
stopService(intent);
}
//调用函数内部方法
public void CallServiceMethod(View view){
if (mCommunicateBinder != null) {
//调用服务内部的方法
mCommunicateBinder.callInnerMethod();
}
}
//绑定服务
public void bindServiceClick(View view){
Intent intent = new Intent();
intent.setClass(this, FirstService.class);
boolean bindService = bindService(intent, mConnection, BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
private FirstService.InnerBinder mRemoteBinder;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
mRemoteBinder = (FirstService.InnerBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected: ");
}
};
}
我理解的绑定流程:
- 首先在服务类里边创建一个内部类,使之继承Binder类,然后在里面创一个方法用来调用内部方法,记得在onBind()方法里边return内部类的对象
//内部类调用服务方法 public class InnerBinder extends Binder { public void callServiceInnerMethod(){ sayHello(); } } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. return new InnerBinder(); }
- 在activity中,设置意图,然后实例化ServiceConnection类
//绑定服务 public void bindServiceClick(View view){ Intent intent = new Intent(); intent.setClass(this, FirstService.class); boolean bindService = bindService(intent, mConnection, BIND_AUTO_CREATE); } private ServiceConnection mConnection = new ServiceConnection() { private FirstService.InnerBinder mRemoteBinder; @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG, "onServiceConnected: "); mRemoteBinder = (FirstService.InnerBinder) service; } @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG, "onServiceDisconnected: "); } };
2.解绑服务
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//开启服务
public void startServiceClick(View view){
//第一步呢还是要创建一个intent(意图)
Intent intent = new Intent();
//第二步开启服务,setClass()里面传入两个参数,分别是当前活动和服务类
intent.setClass(this, FirstService.class);
//第三步就是把意图作为参数传给startService()方法,这样服务就开启了
startService(intent);
}
//停止服务
public void stopServiceClick(View view){
Intent intent = new Intent();
intent.setClass(this, FirstService.class);
stopService(intent);
}
//调用函数内部方法
public void CallServiceMethod(View view){
}
//绑定服务
public void bindServiceClick(View view){
Intent intent = new Intent();
intent.setClass(this, FirstService.class);
boolean bindService = bindService(intent, mConnection, BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
private FirstService.InnerBinder mRemoteBinder;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
mRemoteBinder = (FirstService.InnerBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected: ");
}
};
//解绑服务
public void unbindServiceClick(View view){
if (mConnection != null) {
unbindService(mConnection);
}
}
}
和绑定服务基本一致的流程,少了调用内部方法和内部类
二、服务的两种启动方式及其优缺点
1.startService
(1).创建一个类,继承Service
public class FirstService extends Service{
private static final String TAG = "FirstService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate...");
super.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();
}
}
2.接着写一个类去控制服务
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startService(View view) {
Log.d(TAG, "start service ... ");
startService(new Intent(this, FirstService.class));
}
public void stopService(View view) {
Log.d(TAG, "stop service....");
stopService(new Intent(this, FirstService.class));
}
}
startService(new Intent(this, FirstService.class)) 服务就启动完成了哈
2.bindService
(1).创建一个服务
public class SecondService extends Service {
private static final String TAG = "SecondService";
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return null;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.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();
}
}
(2).写一个主界面
public class BindServiceActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bind_service);
}
public void bindServiceClick(View view) {
//创建意图对象
Intent intent = new Intent(this, SecondService.class);
//第一个是参数是意图对象,第二个参数是回调,第三个参数是标记,这个是自动创建的意,如果服务没有start,那么会自己创建。
//automatically create the service as long as the binding exists
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
public void unBindServiceClick(View view) {
//解绑服务
if (mServiceConnection != null) {
unbindService(mServiceConnection);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:onClick="bindServiceClick"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="绑定服务"/>
<Button
android:onClick="unBindServiceClick"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="解绑服务"/>
</LinearLayout>
bindService(intent, mServiceConnection, BIND_AUTO_CREATE) 也能启动服务
绑定启动服务的特点:
- 绑定服务在系统设置里是没有显进服务正在跑着的;
- 如果onBind方法返回的是null,那么onServiceConnected方法不会被调用;
- 绑定服务的生命周期跟Activity是不求同时生,但求同时死,Activity没了,服务也要解绑;
- 服务在解除绑定以后会停止运行,执行unBind方法—>onDestroy方法;
- 绑定服务开启的服务,只可以解绑一次,多次解绑会抛异常;
- 绑定的connection要跟解绑的connection要对应着,否则没法解绑。
3.两种启动方式的区别是什么?
1、startService这个方法来启动服务的话,是长期运行的,只有stopService才会停止服务。而bindService来启动服务,不用的时候,需要调用unBindService,否则会导致context泄漏,所以bindService不是长期运行的。当context销毁的时候,则会停止服务运行。
2、startService来启动服务可以长期运行,但是不可以通讯,而bindService的方式来启动服务则可以通讯,两者都有优缺点,所以我们就有了混合起来使用的方法。
三、混合开发服务
1.创建一个活动,设置UI布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Mixed_life_cycle.MyActivity"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="startService"
android:onClick="startServiceClick"
tools:ignore="OnClick" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="bindService"
android:onClick="bindServiceClick"
tools:ignore="OnClick" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="callServiceInnerMethod"
android:onClick="callServiceClick"
tools:ignore="OnClick" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="unbindService"
android:onClick="unbindServiceClick"
tools:ignore="OnClick" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="stopService"
android:onClick="stopServiceClick"
tools:ignore="OnClick" />
</LinearLayout>
2.在activity中完成点击事件
public class MyActivity extends AppCompatActivity {
private boolean bindService;
public static final String TAG = "MyActivity";
private ICommunication iCommunication;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
}
/**
* 开启服务
*
* @param view
*/
public void startServiceClick(View view) {
//开启服务
Intent intent = new Intent(this, SecondService.class);
startService(intent);
}
/**
* 绑定服务
*
* @param view
*/
public void bindServiceClick(View view) {
//如果服务已经启动,不会再启动服务。如果没有启动,则启动服务.
bindService = bindService(new Intent(this, SecondService.class), serviceConnection, BIND_AUTO_CREATE);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
iCommunication = (ICommunication)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
iCommunication = null;
}
};
/**
* 调用服务内部方法
*
* @param view
*/
public void callServiceClick(View view) {
if (iCommunication != null) {
iCommunication.callServiceInnerMethod();
}
}
/**
* 解绑服务
*
* @param view
*/
public void unbindServiceClick(View view) {
if (bindService && serviceConnection != null) {
unbindService(serviceConnection);
}
}
/**
* 关闭服务
*
* @param view
*/
public void stopServiceClick(View view) {
//停止服务
stopService(new Intent(this, SecondService.class));
}
}
3.创建一个服务
public class SecondService extends Service {
public static final String TAG = "SecondService";
public SecondService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
Log.d(TAG, "onBind: ");
InnerBinder innerBinder = new InnerBinder();
return innerBinder;
}
//调用内部类方法
private class InnerBinder extends Binder implements ICommunication {
@Override
public void callServiceInnerMethod() {
serviceInnerMethod();
}
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate: ");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind: ");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy: ");
super.onDestroy();
}
/**
* 服务内部方法
*/
private void serviceInnerMethod(){
Toast.makeText(this,"内部方法被调用",Toast.LENGTH_LONG).show();
}
}
是不是根本没get到怎么进行混合开发服务的点呢?别着急,大菜马上就来!
4.精心大菜——怎么进行混合开发服务?
(1)首先得有个接口,里边定义一个抽象方法
public interface ICommunication {
void callServiceInnerMethod();
}
(2)使用startService开启服务
/**
* 开启服务
*
* @param view
*/
public void startServiceClick(View view) {
//开启服务
Intent intent = new Intent(this, SecondService.class);
startService(intent);
}
(3)绑定启动服务bindService
/**
* 绑定服务
*
* @param view
*/
public void bindServiceClick(View view) {
//如果服务已经启动,不会再启动服务。如果没有启动,则启动服务.
bindService = bindService(new Intent(this, SecondService.class), serviceConnection, BIND_AUTO_CREATE);
}
**你且看看,第二个参数里边是不是需要传一个serviceConnection?**所以下一步就是创建一个serviceConnection
(4个serviceConnection出来
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
iCommunication = (ICommunication)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
iCommunication = null;
}
};
(5)在服务里边设一个内部类,使之继承Binder类,并且实现我们创建的那个接口,以完成活动和服务之间的通信
实现接口的那个方法里边要实现自定义的那个方法
//调用内部类方法
private class InnerBinder extends Binder implements ICommunication {
@Override
public void callServiceInnerMethod() {
serviceInnerMethod();
}
}
(6)在服务里边创一个自定义方法,作为服务内部方法
/**
* 服务内部方法
*/
private void serviceInnerMethod(){
Toast.makeText(this,"内部方法被调用",Toast.LENGTH_LONG).show();
}
(7)在activity中实现“实现服务内部类”的方法
/**
* 调用服务内部方法
*
* @param view
*/
public void callServiceClick(View view) {
if (iCommunication != null) {
iCommunication.callServiceInnerMethod();
}
}
巨详细笔记地址:
https://www.sunofbeach.net/a/1186588611802320896