Android安卓四大组件之Service
Posted woodwhale
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android安卓四大组件之Service相关的知识,希望对你有一定的参考价值。
【android】安卓四大组件之Service
1、Service的介绍
1.1 什么是service
Service即服务,用大白话讲就是——长期运行在后台的程序,如果我们说的官方一点,那么就是用于执行长期运行的任务,并且没有与用户交互的功能。
每一个service都和activity一样,需要在manifest.xml中配置,赋予其生命。使用<service>
标签即可配置
在activity类中,可以使用Context.startService()方法来开启服务,使用Context.stopService()方法来关闭服务。同时,我们还可以通过绑定的方式来开启服务,通过解除绑定的方式来关闭服务。
1.2 为什么需要使用service
第一个原因:服务适用于执行长期后台运行的操作,有时候我们没有用户交互的界面,但是该程序仍然需要执行。
最常见的例子有如下几种:
音乐APP
,我们播放歌曲的时候,可以进行看小说、浏览网页等等操作。后台下载
,我们在后台下载的时候,并没有明显的交互,但是仍然可以挂在后台下载。
在了解第二个原因之前,我们需要给几个概念:
前台进程
:最顶部直接跟用户交互的进程。比如说我们操作的Activity界面.可见进程
:可见但不操作的进程,比如说我们在一个Activity的顶部弹出一个Dialog,这个Dialog就是前台进程,但是这个Activity则是可见进程。服务进程
:服忙碌的后台进程,虽然是在后台,但是它在运行。后台进程
:后台进程就是退隐到后台,不做事的进程。空进程
:没有任何东西在上面跑着的进程,仅作缓存作用。
如果内存不够了,首先杀的是空进程,要是还不够就杀后台进程,要是还不够,那么就杀服务,但是服务被杀死以后,等内存够用了,服务又会自动跑起来了。
如果我们需要长期后台操作的任务,使用Service就可以了
2、Service的生命周期方法
和activity一样,service也有它的生命周期
2.1 最基本的生命周期
我们这里使用最基本的三个生命周期方法:
onCreate()
,创建服务时调用onStartCommand(Intent intent, int flags, int startId)
,开启服务时调用onDestroy()
,销毁服务时调用
我们来模拟服务开启和停止:
- 开启service的方法是:startService()
- 停止service的方法是:stopService()
首先创建一个带有两个button的页面,这两个button分别绑定开启服务和停止服务的方法。
记得要去manifest.xml中注册service噢,我这里创建了一个FirstService类extends了Service
<service android:name=".study.test.services.FirstService"/>
前端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"
android:orientation="vertical"
tools:context=".study.test.ServiceActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点我开启服务"
android:id="@+id/bt_startService"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点我关闭服务"
android:id="@+id/bt_stopService"/>
</LinearLayout>
然后是activity代码
public class ServiceActivity extends AppCompatActivity implements OnClickListener
private static final String TAG = "ServiceActivity";
private Button stop;
private Button start;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service);
initView();
public void initView()
start = this.findViewById(R.id.bt_startService);
stop = this.findViewById(R.id.bt_stopService);
start.setOnClickListener(this);
stop.setOnClickListener(this);
@Override
public void onClick(View v)
if (v == start)
Log.d(TAG,"114514");
Intent serviceIntent = new Intent();
serviceIntent.setClass(this, FirstService.class);
this.startService(serviceIntent);
else if (v == stop)
Log.d(TAG,"1919810");
Intent serviceIntent = new Intent();
serviceIntent.setClass(this, FirstService.class);
this.stopService(serviceIntent);
最后是service的代码:
// 继承service
public class FirstService extends Service
private static final String TAG = "FirstService";
@Nullable
@Override
public IBinder onBind(Intent intent)
return null;
// create的周期方法
@Override
public void onCreate()
Log.d(TAG,"create !");
super.onCreate();
// start的周期方法
@Override
public int onStartCommand(Intent intent, int flags, int startId)
Log.d(TAG,"start !");
return super.onStartCommand(intent, flags, startId);
// destroy的周期方法
@Override
public void onDestroy()
Log.d(TAG,"destroy !");
super.onDestroy();
我们来测试一下,效果如下:(注意控制台的log输出)
通过观察可以发现,第一次开启服务会调用onCreate()方法和onStartCommand()方法,第二次开启,仅仅调用onStartCommand()方法,因为该服务已经被create过了。点击停止服务,会调用onDestroy()方法,第二次暂停没有反映,因为服务已经被销毁过了!
2.2 Service绑定的生命周期
我们可以发现,在实现Service的类中,会默认重写一个onBind()
的方法:
- 这个方法的作用就是,用来绑定服务,接下来的一小节我们会讲。
- 我们只需要知道,这个方法是activity类中使用
bindService()
方法后的回调方法,会让我们activity中的ServiceConnection类调用onServiceConnected()
方法,从而获取一个实现IBinder
接口的service对象。
public IBinder onBind(Intent intent)
return null;
还有一个onUnBinde()的方法,就如同字面意思一样,就是解除绑定
的回调方法:
- 这个方法就是在activity类中调用
unbindService()
方法后的一个回调方法
public boolean onUnbind(Intent intent)
Log.d(TAG, "开始解除绑定!onUnbind...");
return super.onUnbind(intent);
3、Service的绑定
前面的开启服务方式
,我们会发现——无法进行各个类之间的交互,这就是一个缺点。
所以,我们可以是用绑定服务
的方式来进行交互:
- 使用
bindService(Intent service, ServiceConnection conn, int flags)
方法来进行绑定 - 使用
unbindService(ServiceConnection conn)
来解除绑定
我们来讲一下ServiceConnection
这个类,这就是一个服务连接类,如果new一个对象,需要重写两个方法:
-
onServiceConnected(ComponentName name, IBinder service)
方法- 该方法是service绑定成功的回调方法,会给一个实现IBinder接口的service对象
- 还会返回一个ComponentName对象,可以获取service的name信息
-
onServiceDisconnected(ComponentName name)
方法- 该方法时service非正常解除绑定的回调方法,在
连接正常关闭
的情况下是不会被调用的。
- 该方法时service非正常解除绑定的回调方法,在
- 该方法只在Service 被破坏了或者被杀死的时候调用。 例如, 系统资源不足, 要关闭一些Services, 刚好连接绑定的 Service 是被关闭者之一, 这个时候onServiceDisconnected() 就会被调用。
绑定服务的方式和开启服务的方式的区别:
bindService
开启的服务,在系统里是看不到服务
在运行的:startService
启动的服务,则会在设置-应用
里看到
最后注意一点,使用绑定服务的方式,在Context(Activity)被销毁之前,一定要解除绑定,否则会泄露!
3.1 尝试绑定一个Service类中的内部类?
我们来写一个例子,来实现调用服务类内部的一个private方法
首先还是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"
android:orientation="vertical"
tools:context=".study.test.ServiceActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点我开启服务"
android:id="@+id/bt_startService"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点我关闭服务"
android:id="@+id/bt_stopService"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点我绑定服务"
android:id="@+id/bt_bindService"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点我解绑服务"
android:id="@+id/bt_unbindService"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="调用服务内部方法"
android:id="@+id/bt_serviceMethod"/>
</LinearLayout>
然后是我们的service类:
- 重点是我们创建了一个内部类CommunicateBinder继承了Binder,然后写了一个callInnerMethod()方法调用内部private的innerMethod()方法
- 我们的目的是在activity启动服务后可以获取到这个内部类CommunicateBinder的实例化对象,从而调用callInnerMethod()方法来实现调用FirstService中的private的innerMethod()方法
- 需要注意的是我们在onBind()触发方法中return了一个new CommunicateBinder()——一个实例化对象
public class FirstService extends Service
private static final String TAG = "FirstService";
public class CommunicateBinder extends Binder
public void callInnerMethod()
innerMethod();
private void innerMethod()
Log.d(TAG,"内部方法!innerMethod...");
Toast.makeText(this, "inner method!", Toast.LENGTH_SHORT).show();
@Nullable
@Override
public IBinder onBind(Intent intent)
Log.d(TAG,"开始绑定!onBind...");
return new CommunicateBinder();
// create的周期方法
@Override
public void onCreate()
Log.d(TAG,"服务创建!onCreate...");
super.onCreate();
// start的周期方法
@Override
public int onStartCommand(Intent intent, int flags, int startId)
Log.d(TAG,"服务开启!onStart...");
return super.onStartCommand(intent, flags, startId);
// destroy的周期方法
@Override
public void onDestroy()
Log.d(TAG,"服务销毁!destroy...");
super.onDestroy();
@Override
public boolean onUnbind(Intent intent)
Log.d(TAG, "开始解除绑定!onUnbind...");
return super.onUnbind(intent);
然后是activity类:
- 我们在类中写入了一个成员变量,是ServiceConnection类的对象conn
- 在conn的重写的onServiceConnected()方法中,我们可以知道其中的第二个参数service其实就是我们刚刚在Service类中的return的new CommunicateBinder()的一个实例化对象,我们创建一个成员变量private FirstService.CommunicateBinder communicateBinder并给他赋值
- 我们在绑定服务按钮触发时,启动了bindService(serviceIntent, conn,BIND_AUTO_CREATE);这个方法,用来绑定一个service,这时候就会去调用conn中的onServiceConnected()方法了
- 我们在解除绑定服务按钮触发的时候,使用unbindService(conn)方法关闭service连接,并且将我们的communicateBinder对象置空
public class ServiceActivity extends Activity implements OnClickListener
private static final String TAG = "ServiceActivity";
private Button stop;
private Button start;
private Button bind;
private Button unbind;
private Button method;
private boolean isService = false;
private FirstService.CommunicateBinder communicateBinder;
private ServiceConnection conn = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName name, IBinder service)
if (service instanceof FirstService.CommunicateBinder)
Log.d(TAG,name.toString()+"服务绑定!onServiceConnected...");
communicateBinder = (FirstService.CommunicateBinder) service;
@Override
public void onServiceDisconnected(ComponentName name)
Log.d(TAG,"服务在不知情情况下解绑!onServiceDisconnected...");
communicateBinder = null;
;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service);
initView();
public void initView()
start = this.findViewById(R.id.bt_startService);
stop = this.findViewById(R.id.bt_stopService);
bind = this.findViewById(R.id.bt_bindService);
unbind = this.findViewById(R.id.bt_unbindService);
method = this.findViewById(R.id.bt_serviceMethod);
start.setOnClickListener(this);
stop.setOnClickListener(this);
bind.setOnClickListener(this);
unbind.setOnClickListener(this);
method.setOnClickListener(this);
@Override
public void onClick(View v)
Intent serviceIntent = new Intent();
serviceIntent.setClass(this, FirstService.class);
if (v == start)
this.startService(serviceIntent);
else if (v == stop)
this.stopService(serviceIntent);
else if (v == bind)
isService = this.bindService(serviceIntent, conn,BIND_AUTO_CREATE);
else if (v == unbind)
if (isService && conn != null)
isService = false;
this.unbindService(conn);
communicateBinder = null;
else if (v == method)
if (communicateBinder != null)
communicateBinder.callInnerMethod();
效果如下:(注意看控制台的输出)
- 我们点击绑定服务后,可以调用服务内部的方法
- 解除绑定服务后,无法调用服务内部方法
3.2 用接口来减少暴露风险
我们会发现,在上面的代码中,绑定的一个服务内部类是public的,也就是说,我们可以在任何地方new一个内部类出来,这当然是非常不安全
的。
如果我们把这个内部类改为一个private的类
,让其更安全,那么我们如何调用其中的方法呢?
答案不难想到,就是使用接口
我们写一个接口类:
public interface ICommunication
void sayHello();
之后再让我们的内部类implements这个ICommunication接口,并让他成为private的内部类,这样我们进行重写sayHello()方法,用雷调用Service类中的内部方法就可以啦!
private class CommunicateBinder extends Binder implements ICommunication
@Override
public void sayHello()
innerMethod();
完整的代码如下:
Service类
public class FirstService extends Service
private static final String TAG = 安卓四大组件之服务服务的生命周期和启动方式