第10回 这Service是怎么个思想感情啊?

孔明详细讲解了一下Intent,这时的刘备才恍然大悟,捶胸顿足,后悔道:“早知听完军师讲解再去曹操那儿了,这下被曹操那些手下各种看不起呀,不行,我得回去再跟他们好好讲讲Intent是啥!”

关羽一把拉住刘备,劝道:“算了吧,大哥,你现在去谁还会听你说呀,不要再去被鄙视了,这回咱们只能认栽了。”

刘备叹道:“我就是咽不下这口气呀,那个面试的气死我了,长得贼眉鼠眼的,装作道貌岸然的样子,我看他才啥也不会才是!想起我就气不打一处来啊!”

关羽拉拉刘备,说:“大哥,面试你的那个貌似就是曹孟德……”

刘备气说:“曹什么孟德,我还刘玄德呢,姓曹了不起呀,还是有孟了不起呀!”

关羽又拉拉刘备,说:“大哥……曹孟德就是曹操。”

刘备这才反应过来,说:“啊?他就是传说中的小曹?唉,早知道管他要个签名再走了。”

关羽说:“大哥,你好没节操……”

那边孔明听他们越扯越远,越扯越没边,问道:“云长,你为啥也悲剧了呢?按理说你学的不错,开发功底也比主公要好,咋会被鄙视呢?问你的又是什么题目呀?”

关羽立马趴到地上,抱住孔明的大腿,哭道:“军师啊,说起我那更是惨上加惨呀!”

“二哥,你也好没节操啊……”张飞不知从哪里拿了根鸡腿出来,一边啃一边说道。

关羽捂着心口,一把胡子耷拉到地上,说:“正因为我比大哥有着更加深厚的基础,被问崩的时候才更伤心啊。试想我写代码这么长时间,还没被这么鄙视过!”

张飞一边咬鸡骨头一边问道:“是什么面试官如此伤害二哥?”

关羽咬牙道:“我这辈子都不会忘了他的嘴脸,他竟然还蒙住一只眼睛,不知道装的是什么酷,别人都管他叫‘惇哥’,不知道是不是也跟军师似的特别喜欢蹲厕所?”

孔明用脚踹了踹趴在地上的关羽,“骂他归骂他,不要讽刺我的兴趣爱好。废话少说,他到底问的你啥啊?”

关羽答道:“问的我Service相关概念。”

刘备也一脸迷茫道:“军师,这Service是怎么个思想感情啊?”

1.1. Service简介

前面我们一起学习了Android的常用控件、Dialog、PopupWindow、Activity等知识。这些都是看得见摸得到的内容,它们共同构建起了Android应用的界面。而这一回要介绍的Service却是一种看不见的服务。虽然看不见,但Service是Android非常重要的组件之一。

1.1.1. 什么是Service?

由于手机屏幕的限制,通常情况下在同一时间内仅有一个应用程序处于激活状态,并且只能显示该应用程序的某个界面,因此,应用程序往往需要一种能够长期在后台运行的机制,在没有用户界面的情况下,实现应用程序的特定功能,并能够处理事件或更新数据。这种机制就是Service。

Service适用于无需用户干预,且需要长期运行的后台程序。Service没有用户界面,有利于降低系统资源。Service比Activity具有更高的优先级,因此在系统资源紧张的时候,Service不会轻易被Android系统终止。即使Service被系统终止了,在系统资源恢复后Service也将自动恢复运行状态,因此可以认为Service是在系统中永久运行的组件。Service除了实现后台服务功能,还可以解决两个不同Activity应用程序进程之间的调用和通信问题。

 

Service与进程、线程之间的关系

l  一个进程可以包含多个Service。一般情况下,Service与所属程序是在同一个进程中运行的。例如音乐播放器的播放Service,与音乐播放器是在同一个进程中的。

l  一个Service可以包含多个线程。Service是运行在主线程上的,而不是运行在另一个线程中,如果想在Service中处理很占时间的操作,可以在Service中开一个新的线程,这样可以降低Activity没有响应的风险。

 

ServiceActivity的相同点与不同点

l  不同点:Activity是与用户交互的组件,用户可以直观的看到。而Service是后台运行的,执行应用程序的后台操作。

l  相同点:Acivitiy和Service都属于Android四大组件。在使用这两者时,都需要在配置文件中添加标签进行声明。

 

Service的分类

l  本地服务(Local Service):用于应用程序内部。

l  远程服务(Remote Service):用于Android系统内部的应用程序之间的进程通信。

1.1.2. Service生命周期

Service是不能被自己启动的,只有通过Context对象调用startService()和bindService()方法来启动。这两种方法的Service生命周期如图10-1所示:

图 10-1  Service的生命周期

如上图10-1所示,Service有两种启动方式:

 

startService()方法启动

此时启动的Service与调用者Activity之间没有关联,即Activity已经退出,Service仍然可以继续运行,而且调用者和Service之间无法进行数据交换和通信。如果需要停止Service的运行,只能调用Context类的stopService()方法,或者由Service本身调用其stopSelf()方法。当第一次启动Service时,系统会调用onCreate()、onStart()两个方法;当停止Service服务时,系统会调用onDestory()方法;如果Service已经启动了,再启动同一个服务时,系统就只调用 onStart() 这个方法了。

 

bindService()方法启动

此方法调用Service时,调用者Activity与Service绑定在一起,如果Activity退出,则Service也随之退出,而且调用者Activity和Service之间可以进行数据交换或通信。

1.2.音乐播放服务实例

关羽听完军师介绍的Service,不觉手指发痒,马上开始一个简单音乐播放实例的开发,来详细的学习Service的使用。关羽新建一个Android工程,创建一个MainActivity,代码如下所示:

MainActivity.java代码清单10-1:

/**

* @author 关羽:大哥最近身体偶感不适,让贤弟为你高歌一曲,疏通经脉:“我是女生,可爱的

女生……”

张飞:大哥!冷静!

刘备:不要拦我!

*/

public classMainActivity extends Activity {

    // author 关羽 我要证明自己!

    //LocalServiceDemo Activity

    private static String TAG ="service";

    private Button startButton;

    private Button stopButton;

    private Button bindButton;

    private Button unbindButton;

    private Button timeButton;

    private MyBinder binder = null;

    @Override

    public void onCreate(BundlesavedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        initViews();

    }

//定义ServiceConnection用于在Activity和Service中传递数据

    final ServiceConnection conn = newServiceConnection() {

        @Override

        public voidonServiceConnected(ComponentName name, IBinder service) {

            // MyService中的onBinder()方法的返回值实际上是一个MyBinder对象

//因此可以使用强制转换

//MyBinder继承于IBinder在代码清单10-2中定义

            binder = (MyBinder)service;

            Log.e(TAG, "MainActivityonSeviceConnected");

        }

        @Override

        public voidonServiceDisconnected(ComponentName name) {

            // 注意该方法只在Service非正常中断时调用

            binder = null;

            Log.e(TAG, "MainActivityonSeviceDisconnected");

        }

    };

    private void initViews() {

        startButton =(Button)findViewById(R.id.start_button);

        stopButton =(Button)findViewById(R.id.stop_button);

        bindButton =(Button)findViewById(R.id.bind_button);

        unbindButton =(Button)findViewById(R.id.unbind_button);

        timeButton = (Button)findViewById(R.id.time_button);

        // 定义按钮监听器

        OnClickListener ocl = newOnClickListener() {

            @Override

            public void onClick(View v) {

                Intent i = newIntent(MainActivity.this, MusicPlayService.class);

                switch (v.getId()) {

case  R.id.start_button:

                        // 开始播放按钮响应事件

                        startService(i);

                        break;

case  R.id.stop_button:

                        // 停止播放按钮响应事件

                        stopService(i);

                        break;

case  R.id.bind_button:

                        // 绑定按钮响应事件

                        bindService(i, conn,Context.BIND_AUTO_CREATE);

                        break;

case  R.id.unbind_button:

                        // 解除绑定按钮响应事件

                        unbindService(conn);

                        binder = null;

                        break;

case  R.id.time_button:

                        // 查看播放时间按钮响应事件

                        if (binder != null) {

                           Toast.makeText(MainActivity.this, "播放了" + binder.getRunTime() + "毫秒",Toast.LENGTH_LONG).show();

                        } else {

                           Toast.makeText(MainActivity.this, "没有绑定服务,无法查询播放时间", Toast.LENGTH_LONG).show();

                        }

                        break;

                }

            }

        };

        // 设置按钮监听器

        startButton.setOnClickListener(ocl);

        stopButton.setOnClickListener(ocl);

        bindButton.setOnClickListener(ocl);

        unbindButton.setOnClickListener(ocl);

        timeButton.setOnClickListener(ocl);

    }

}

该音乐播放器界面包含5个按钮分别用于:开启音乐播放服务、停止音乐播放服务、绑定音乐播放服务、解除绑定、查看播放时间。

l 开启音乐播放服务:调用startService()方法启动MusicPlayService服务。

l 停止音乐播放服务:调用stopService()方法来停止服务。

l 绑定音乐服务:调用bindService(Intentservice, ServiceConnection conn, int flags)方法来绑定Activity与Service。参数Intent service是指向需要绑定服务的Intent;ServiceConnection是一个接口,重写其中的onServiceConnected()的方法可以通过Binder类来达到Activity与Service之间传递数据的目的;flags用来标明绑定中的操作,不想指定时设为0即可,通常设置为BIND_AUTO_CREATE,这样就会在service不存在时创建一个。上段代码中就利用onServiceConnected()方法得到了service中的Binder数据。

孔明:需要注意的是,ServiceConnection的onServiceDisconnected()方法仅在服务非正常中断时调用,如果正常解除Service绑定不会调用该语句。

 

 

 

 


l  解除绑定服务:调用unbindService(ServiceConnectionconn)方法解除服务绑定,服务绑定解除后服务停止。

l  查看播放时间:通过绑定服务返回的Binder类来获取所需的服务运行时间,如果Binder为空,则提示需要绑定服务。

运行程序,结果如图10-2所示:

图 10-2 音乐播放服务主界面

除了MainActivity.java之外,本程序还包含一个继承自Service的MusicPlayService类。该类包含一个MediaPlayer和一个long成员变量,用于播放音乐和标记播放开始时间。

MusicPlayService.java代码清单10-2-0:

/**

* @author 孔明:小羽羽昨天去精神病院给人唱歌,病人好了,大夫疯了。

*/

public class MusicPlayService extends Service {

    // 用来播放音乐的Service类

    private MediaPlayermPlayer;

    private static StringTAG = "service";

    private MyBinder binder= null;

    // Log TAG

    private long startTime;

    // 标记播放开始时间;

    @Override

    public void onCreate() {

        Log.e(TAG,"Service onCreate");

        // 初始化播放器,并设置为循环播放

        mPlayer =MediaPlayer.create(this, R.raw.bgmusic);

       mPlayer.setLooping(true);

        super.onCreate();

    }

    @Override

    public void onDestroy(){

        // 服务终止

        Log.e(TAG,"Service onDestory");

        mPlayer.stop();

        super.onDestroy();

    }

    @Override

    public voidonStart(Intent intent, int startId) {

        // 服务启动

        Log.e(TAG,"Service onStart");

        mPlayer.start();

       super.onStart(intent, startId);

    }

    @Override

    public IBinderonBind(Intent intent) {

        // 绑定服务

        Log.e(TAG,"Service onBind");

        mPlayer.start();

        startTime =System.currentTimeMillis();

        binder = newMyBinder();

        return binder;

    }

    @Override

    public booleanonUnbind(Intent intent) {

        // 解除绑定

        Log.e(TAG,"Service onUnbind");

        mPlayer.stop();

        return super.onUnbind(intent);

    }

    public class MyBinderextends Binder {

        // 获取Service运行时间

        public longgetRunTime() {

            return(System.currentTimeMillis() - startTime);

        }

    }

}

关羽你以为这样就完成了整个程序的开发了吗?还没有!如同Activity一样,记得在AndroidManifest.xml中对Service进行注册,在<application>标签下,添加如下语句:

<service android:name=".MusicPlayService" />

这样,整个音乐播放服务程序才算完成。

在MusicPlayService类中,重写了Service的onCreate()、onDestroy()、onStart(Intent intent, int startId)、onBind(Intentintent)、onUnbind(Intent intent)五个方法。当服务被创建时会调用onCreate()方法,在onCreate()方法中初始化播放器,并设置循环播放。这里需要将mp3文件放到工程资源文件夹res/raw下,再在初始化时指定资源ID;当服务被终止时会调用onDestroy()方法,在onDestroy()方法中添加mPlayer.stop()用于停止音乐播放;当服务开始播放时会调用onStart()方法,在该方法中添加mPlayer.start()以开启音乐播放;当服务被绑定至某Activity时会调用onBind()方法,在该方法中添加mPlayer.start()以开启音乐播放,在onBind()方法中关羽创建了一个MyBinder类继承自Binder类, MyBinder类的作用是传递Service的运行时间,onBind()方法将MyBinder对象返回,在MainActivity中的onServiceConnected()方法得到传递过去的Binder类;当服务解除绑定时会调用onUnbind(Intentintent)方法,在该方法中添加mPlayer.stop()以停止音乐播放。

运行程序,绑定音乐服务后,点击“查看播放时间”按钮,如图10-3所示:

图 10-3 查询播放时间

当关羽点击点击“开启音乐播放服务”按钮时,发现无法查看播放时间。经过研究,发现这是因为服务并没有与Activity进行绑定,只有在绑定之后服务运行时间才能通过Binder传递给了Activity,并在界面上显示。

1.3.玄德有话说

刘备:原来Service是这么回事!貌似还是很重要的呀!

孔明:那必须的!Service可是Android的四大组件之一,跟歌坛四大天王的地位差不多的。

刘备:刚开始听你说这Service分本地的和远程的,这远程的又是什么思想感情呢?

孔明:Android系统的进程之间不能共享内存,当传递对象时,需要把对象转化为操作系统可以识别的形式。在Android中,可以采用AIDL来公开服务的接口,采用远程过程调用(Remote Procedure Call,RPC)和代理模式来实现跨进程通信。AIDL(Android InterfaceDefinition Language)即Android接口描述语言,ADT会根据AIDL文件在gen目录下生成对应的Java接口文件。我们需要手工创建一个Service的子类并实现生成的Java接口,然后在AndroidManifest.xml文件中进行配置。远程服务可以为多个客户端服务,由于涉及到数据通信,一般采用bindService的方式。

刘备:老“猪”,怎么这么专业!

 

张飞:老“猪”啊,我也有个问题。

孔明:拜托,我姓诸葛……

张飞:嗨,差不多啦。拿我们这个音乐播放服务来说,如果我先start音乐播放服务,再bind音乐播放服务。会怎么样呢?

孔明:这样的话会在start时调用onCreate()、onStart()方法播放音乐,bind时不会调用onCreate(),而会直接调用onBind()方法。需要注意的是,如果此时unBind()解除绑定服务不会调用onDestory()方法,仅调用了onUnbind()方法,Service不会停止。如果要真正的停止服务,需要再调用stop()方法触发onDestory()销毁服务。

张飞:老“猪”,你说的我好迷茫。

孔明:简而言之两种方法混合使用时,Service启动和退出服务的调用顺序为onCreate()→onStart()→onBind()→onUnbind→onStop()→onDestory()或onCreate()→onBind()→onStart()→onStop()→onUnbind→onDestory()。如果以这两种顺序调用,Service将不能正常退出。

张飞:了解!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值