Android【Service组件】【基本概念及使用】
概念
1.Service是一个应用组件,它用来在后台完成一个时间跨度比较大的工作,且没有关联任何界面。
着重理解这里的后台,去区分Service和线程的区别:
Service是由主线程执行的,也就是创建主Activity的线程来执行Service,并且
是后台执行,所以它是页面无关联的组件。最重要的是,如果我们想要在Service中
执行耗时操作,那么就不能够占用主线程,我们需要开启分线程来执行!!!
和线程的区别就是,线程是重新开启了一个线程,通常这种行为叫做分线程!!!
2.一个Service通常可以完成下面的工作:
- 访问网络
- 播放音乐
- 文件IO操作
- 大数据量的数据库操作等等。。。
当然,上面的操作需要开启分线程!!!因为比较耗时间!!!
3.服务的特点:
- Service在后台运行,不用与用户进行交互。
即使应用退出,服务也不会停止。
这里需要区别一下:
我说的应用退出有两种退出:
1.back退出(没有被特殊的重写过。。。)。2.小米类似的清理工具杀死进程。back退出,Service是运行在应用程序进程中的,而back退出是不会杀死进程的,
所以Service照常运行。但是,如果你通过小米清理工具一键清理后,应用程序进程将会被销毁,而进程没有
了,Service当然也会被销毁,但是,这里有个特别的地方,那就是Service被销毁
后能够起死回生!!!这很重要!!!在默认情况下,Service运行在应用程序进程的主线程(UI进程)中,如果需要在Service中处理一些网络连接等耗时的操作,那么应该将这些任务放在分线程中处理,避免阻塞用户界面。
Service的分类:
1.Local Service(本地服务)
Service对象与Service的启动者在同个进程中运行,两者的通信是进程内通信。那么就是本地服务。
2.Remote Service(远程服务)
Service对象与Service的启动者不在同一个进程中运行,这时存在一个进程间通信的问题,Android 专门为此设计了AIDL来实现进程间通信!!!
正常情况下,Android一个应用程序只有一个进程,但是可以创建独立进程。
Service类:
如果我们想要写一个我们的服务,那么就必须继承Service类。
我们可以发现,Service是一个抽象类,而且有着一个唯一的抽象方法onBind(Intent intent)。
查看Service的继承结构可以知道,Service能够调用Activity的大部分方法,因为Service也继承了Context类!!!
通过案例讲解Service的各种特点:
案例界面:
目的:通过该案例探讨Service的各种特点。
1.创建一个安卓项目:ServiceBestTest:
2.创建一个LocalService类,继承Service,实现我们自己的服务:
创建Service
/**
* Created by FireLang on 2017/6/6.
*/
public class LocalService extends Service{
/**
* 关键任务:
*
* 查看:
* 一次点击启动Service和一次点击停止Service时,LocalService的生命周期方法执行!!!
* 多次点击启动Service和一次点击停止Service时,LocalService的生命周期方法执行!!!
*/
private static final String TAG = "LocalService";
/**
* 一般我们不将构造方法归于生命周期方法
*/
public LocalService (){
Log.d(TAG, "LocalService:");
}
/**
* 生命周期方法
*/
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: ");
}
/**
* 生命周期方法
*/
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
//唯一的抽象方法
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
在Service中,我只重写了一部分生命周期方法,onCreate()和onDestroy()方
法,还有一个onStartComment()方法没有重写!!!
其中onBind()方法是必须重写的!在每个需要的方法中,我都打印了日志,方
便我们查看最后的探讨效果!!!
其中,我在代码最上边还给出了说明,我们要怎么去探讨Service!!!
注册Service
在AndroidManifest.xml中完成Service注册,不知道你发现没有,凡是Android的4大组件都需要在AndroidManifest.xml中完成注册!!!
写布局:(修改主Activity的布局文件)
<?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:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="cn.domarvel.servicebesttest.MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Local Service"
android:gravity="center_horizontal"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal"
>
<Button
android:id="@+id/StartLocalService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="StartLocalService"
android:textAllCaps="false"
android:layout_weight="1"
android:onClick="StartLocalService"
/>
<!--
android:textAllCaps:让文本都转换为大写??true或false
android:layout_weight:设置weight的时候,最佳实践就是把
android:layout_width设置为0dp
-->
<Button
android:id="@+id/StopLocalService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="StopLocalService"
android:textAllCaps="false"
android:layout_weight="1"
android:onClick="StopLocalService"
/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Remote Service"
android:gravity="center_horizontal"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal"
>
<Button
android:id="@+id/StartRemoteService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="StartRemoteService"
android:textAllCaps="false"
android:layout_weight="1"
/>
<Button
android:id="@+id/StopRemoteService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="StopRemoteService"
android:textAllCaps="false"
android:layout_weight="1"
/>
</LinearLayout>
</LinearLayout>
完成Activity的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 StartLocalService(View view) {
//这是启动本地进程的Service方法
/**
* 启动Service有两种方式,一种是显式启动,一种是隐式启动,和启动Activity是一样的!!!
* 但是这里是启动本地进程的Service,建议用显式启动。
* 因为我们能够直接访问到类,而启动其它进程的Service,就用隐式启动。
*/
//其中把this换成getApplicationContext()也可以,因为在底层源代码中,它只是通过Content对象类型获取了下包名就完了。。。
//你可以打印包名试试看,最终打出来就是你Android在开始的时候取得名字。具体请看下面的图片。
Intent intent = new Intent(this, LocalService.class);
startService(intent);
}
public void StopLocalService(View view) {
//这是停止本地进程的Service方法
/**
* 停止Service有两种方式,一种是显式停止,一种是隐式停止!!!
*/
Intent intent = new Intent(this, LocalService.class);
stopService(intent);
}
}
在Activity代码中,我写了两个点击事件代码,StartLocalService(View view)和StopLocalService(View view)代码。相信你能够看得懂,为什么我没有实现View.OnclickListener类就能够做关于事件的事,那是因为我在布局文件中指定了哪儿个按钮onClick后调用哪儿个方法了,这个原理很简单,相信你能够很快百度到!!!
然后就是,startService(intent)和stopService(intent),这两个都是Context中的方法,所以在服务中,我们还可以启动或者停止另外一个服务。
最后运行程序!!!
当点击一次StartLocalService按钮后,你将会在控制台看到输出:
证明,Service调用构造方法后,就马上调用onCreate方法。
当点击一次StopLocalService按钮后,你将会在控制台看到输出:
调用了onDestroy方法,证明Service已经被销毁了!!!
当多次点击StartLocalService按钮后,你将会在控制台看到输出:
证明构造方法和onCreate方法只会在Service的生命周期中调用一次!!!
当多次点击StopLocalService按钮后,你将会在控制台看到输出:
证明:当Service被关闭后再次进行关闭,是不会报错的!!!
到这里,我们已经学会启动Service了,但是还有一个方法能够启动Service,那就是bind(绑定)!!!
通过bind来启动Service:
向界面xml中添加配置:
添加下面代码:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Local Service bindService"
android:gravity="center_horizontal"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal"
>
<Button
android:id="@+id/BindLocalService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="BindLocalService"
android:textAllCaps="false"
android:layout_weight="1"
android:onClick="BindLocalService"
/>
<Button
android:id="@+id/UnBindLocalService"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="UnBindLocalService"
android:textAllCaps="false"
android:layout_weight="1"
android:onClick="UnBindLocalService"
/>
</LinearLayout>
向LocalService中添加代码:
@Override
public IBinder onBind(Intent intent) {
/**
* 这里返回的IBinder对象将会在Activity的onServiceConnected中接受到该参数!!!
* 当返回值不是null的时候,才会触发Activity中的onServiceConnected方法。
*/
Log.d(TAG, "onBind: ");
return new Binder();
}
/**
* 当Activity和Service断开连接后调用该方法!!!
* 当重写了该方法后,才会触发Activity中的onServiceDisconnected方法。
* @param intent
* @return
*/
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind: ");
return super.onUnbind(intent);
}
向MainActivity中添加代码:
private ServiceConnection connection;
/**
* 绑定Service,或者叫做创建Service后,Activity再连接Service!!!
* @param view
*/
public void BindLocalService(View view) {
/**
* 我们现在是测试一个Activity和Service只进行一次连接。当然也可以进行多次连接,不同Activity
* 和同一个Service进行多次连接,或者不同的Activity和不同的Service连接每次都会执行onBind方
* 法和onServiceConnected。
* 如果是同一个Activity和同一个Service进行多次连接,连接成功后只会执行onServiceConnected方法。并且
* 也就是说,每次通过bindService来开启服务,不管是同一个Activity和同一个Service进行多次绑定还是其它的情况,都会执行onServiceConnected方法,相当于执行startService后,每次都会执行startCommand方法是一个道理。
* 进行多次连接,都成功后,你必须要每一个连接都关闭了才会执行onUnbind方法。如果有多个连接,你只关闭了一个连接,是不会执行onUnbind方法的。
*
*/
//这里我判断connection,我的目的就是,如果Activity和Service已经连接了,我们就没有必要再次连接了。
if(connection==null){
Intent intent = new Intent(getApplicationContext(),LocalService.class);
connection = new ServiceConnection() {
/**
* 当Activity和Service连接上后
* 连接时机为,Service创建成功后才开始连接,连接成功后调用该方法
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/**
* 发现这里的IBinder对象没有,该对象就是LocalService里面的onBind()方法返回的值。
*/
Log.d(TAG, "onServiceConnected: ");
}
/**
* 理论上:
* 当Activity和Service断开连接后,调用该方法。
* 但是,最佳实践告诉我们,当Activity和Service断开连接后,该方法一直都没有被调用过!!!
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected: ");
}
};
/**
* BIND_AUTO_CREATE:创建Service后自动绑定!!!绑定可以理解为连接的意思!!!
*/
Log.d(TAG, "BindLocalService: ");
bindService(intent,connection, Context.BIND_AUTO_CREATE);
}else{
Log.d(TAG, "BindLocalService: Service Bind already");
}
}
/**
* 对Service解绑,Activity对Service断开连接!!!并且销毁Service!!!
* @param view
*/
public void UnBindLocalService(View view) {
if(connection!=null){
unbindService(connection);
Log.d(TAG, "UnBindLocalService: unBind Service Success");
}else{
Log.d(TAG, "UnBindLocalService: You have't Bind Service");
}
connection = null;
}
实践:
当点击一次BindLocalService后。
可以知道,我们调用了MainActivity中的BindLocalService方法,之后就是创建了LocalService对象,执行了onCreate方法,到了这里我们就已经通过BindService创建了服务,和startService不同,bindService还在启动服务的基础上,对Activity和Service进行了连接。当连接开始时,执行onBind方法,连接成功后执行onServiceConnected方法。通过一个对象连接,方便Activity和Service之间通信!!!
当一次点击UnBindLocalService后:
可以发现,解绑一个服务就等于销毁一个服务;而给Activity绑定一个服务,就相当于创建一个服务,同时在Activity和Service中间放一个对象来建立连接。
解绑时,执行了LocalService中的onUnbind()方法,代表Activity和Service开始断开连接,成功断开后,服务随即被销毁。可以看到,在服务被销毁之前,都没有执行onServiceDisconnected方法,于是大牛给出解释说,Android设计的方法不一定非得执行,如果站在积极的态度上,这就是Android给自己留的后路,算是功能的扩展!!!但是不一定得执行,相信以后需要时,就会用到的!!现在不执行代表平时开发中用不到!!!
似乎感觉自己已经掌握了用法???
下面来看看这个:
当点击BindLocalService按钮,开启并绑定了Service后,点击手机的back键。
你会发现报错了!!!
错误的原因就是:在Activity和Service建立了连接后,Activity突然就消失不见了(我们看不见Activity,不代表被销毁,destroy销毁才是Activity被销毁,而back键默认就是destroy掉Activity),强制断开了Activity和Service之间的连接,并销毁了Service。
这就是报错的原因!!!那么怎么才会不报错呢???
那么我们就需要重写Activity的onDestroy生命周期方法,在里面主动把断开Activity和Service之间的连接,并且销毁Service!!!
/**
* 重写Activity的onDestroy方法,在里面主动解除Activity和Service之间的连接!!!并销毁Service!!!
*/
@Override
protected void onDestroy() {
super.onDestroy();
if(connection!=null){
unbindService(connection);
}
connection = null;
}
重写运行应用程序,通过绑定开启服务,再back键返回主界面。你会发现程序不报错了!!!并且运行得很完美!!!
Service的生命周期
执行流程:
当通过startService()来开启服务时:
执行一次startService方法:创建Service对象—>onCreate()—>onStartCommand()—>现在Service就是运行状态了—>通过stopService()或者在Service中stopSelf()都可以关闭服务—>onDestroy()–>服务被销毁了。
当已经执行了一次startService()方法后,再次执行startService()方法。只会执行startCommand()方法。而不会再执行onCreate()方法了。
当通过bindService()方法来开启服务时:
执行一次bindService()方法:创建Service对象—>onCreate()方法—>onBind()方法—>onServiceConnected—>此时Service已经被开启了,并且Activity和Service创建了一个连接!!!—>通过unbindService()方法断开连接,并且关闭服务—>当关闭了所有连接后—>执行onUnbind()方法—>执行onDestroy()方法—>服务被关闭了。
在同一个Activity和同一个Service中,已经执行过一次bindService()方法后,再次执行bindService()方法,你会发现onBind()方法没有再执行了,而onServiceConnected()方法却是会每次都执行。现在我们通过unbindService()方法断开连接,如果我们只断开了同一个Activity和同一个Service中的一个连接,那么不会执行onUnbind()方法和Service的onDestroy()方法,只有断开同一个Activity和同一个Service中的所有连接后,才会执行onUnbind()方法和Service的onDestroy()方法。
其实我们只需要探讨同一个Activity和同一个Service的开启服务情况就行了。
通过startService和bindService()一次和多次执行,生命周期该怎么走,了解这些就可以了!!!这些在开发中已经够用了!!!剩下的,如果有特殊的,在开发中慢慢实践就好了!!!
还没有评论,来说两句吧...