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()一次和多次执行,生命周期该怎么走,了解这些就可以了!!!这些在开发中已经够用了!!!剩下的,如果有特殊的,在开发中慢慢实践就好了!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值