文档章节

Android Service详解

fengsehng
 fengsehng
发布于 2016/11/09 09:11
字数 2934
阅读 1
收藏 0

service作为四大组件值得我们的更多的关注

在Android中,Activity主要负责前台页面的展示,Service主要负责需要长期运行的任务。例如,一个从service播放音乐的音乐播放器,应被设置为前台运行,因为用户会明确地注意它的运行.在状态栏中的通知可能会显示当前的歌曲并且允许用户启动一个activity来与音乐播放器交互。

Service的两种实现形式

1.非绑定

通过调用应用程序组件(例如Activity)的startService()方法来启动一个服务.一旦启动,服务就会在后台一直运行,即使应用程序组件此时被关闭.通常,已经启动的服务会处理一些单一功能,并且也不需要返回结果给调用者.例如,在网络上下载或上传文件.当服务的工作处理结束,才会自己关闭服务.

2.绑定(bind)

通过调用应用程序组件的bindService()方法来绑定一个服务.已绑定的服务会提供一个客户端-服务端交互接口.该接口主要用来与应用程序交互,发送请求,获取结果,甚至通过IPC来访问进程.只要一个程序组件绑定服务就会运行绑定服务,多个应用程序组件可以同时时间绑定一个服务.当所有的应用程序组件都解除绑定,该绑定服务器就会被销毁.

实现service的方法介绍

onStartCommand()

  系统在其它组件比如activity通过调用startService()请求service启动时调用这个方法.一旦这个方法执行,service就启动并且在后台长期运行.如果你实现了它,你需要负责在service完成任务时停止它,通过调用stopSelf()或stopService().(如果你只想提供绑定,你不需实现此方法).

OnBind()

  当组件调用bindService()想要绑定到service时(比如想要执行进程间通讯)系统调用此方法.在你的实现中,你必须提供一个返回一个IBinder来以使客户端能够使用它与service通讯,你必须总是实现这个方法,但是如果你不允许绑定,那么你应返回null.

OnCreate()

  系统在service第一次创建时执行此方法,来执行只运行一次的初始化工作(在调用它方法如onStartCommand()或onBind()之前).如果service已经运行,这个方法不会被调用.

OnDestroy()

  系统在service不再被使用并要销毁时调用此方法.你的service应在此方法中释放资源,比如线程,已注册的侦听器,接收器等等.这是service收到的最后一个调用.
  
  如果一个组件通过调用startService()启动一个service(最终导致onStartCommand()被调用),之后service会保持运行,直到它通过stopSelf()停止自己或另外的组件调用stopService()停止它.

service实现代码

1.非绑定

新建一个MyService继承自Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法

public class MyIntentService extends IntentService {   

    public MyIntentService() {   
        super("MyIntentService");   
    }   

    @Override   
    protected void onHandleIntent(Intent intent) {   
        // IntentService会使用单独的线程来执行该方法的代码 
        // 该方法内执行耗时任务,比如下载文件,此处只是让线程等待20秒 
        long endTime = System.currentTimeMillis() + 20 * 1000;   
        System.out.println("onStart");   
        while (System.currentTimeMillis() < endTime) {   
            synchronized (this) {   
                try {   
                    wait(endTime - System.currentTimeMillis());   
                } catch (InterruptedException e) {   
                    e.printStackTrace();   
                }   
            }   
        }   
        System.out.println("----耗时任务执行完成---");   
    }   
}   

我们在布局文件中加入了两个按钮,一个用于启动Service,一个用于停止Service。
然后打开或新建MainActivity作为程序的主Activity,在里面加入启动Service和停止Service的逻辑,代码如下所示:

public class MainActivity extends Activity implements OnClickListener {  

    private Button startService;  

    private Button stopService;  

    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        startService = (Button) findViewById(R.id.start_service);  
        stopService = (Button) findViewById(R.id.stop_service);  
        startService.setOnClickListener(this);  
        stopService.setOnClickListener(this);  
    }  

    @Override  
    public void onClick(View v) {  
        switch (v.getId()) {  
        case R.id.start_service:  
            Intent startIntent = new Intent(this, MyService.class);  
            startService(startIntent);  
            break;  
        case R.id.stop_service:  
            Intent stopIntent = new Intent(this, MyService.class);  
            stopService(stopIntent);  
            break;  
        default:  
            break;  
        }  
    }  

项目中的每一个Service都必须在AndroidManifest.xml中注册才行,所以还需要编辑AndroidManifest.xml文件,代码如下所示:

<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" >  

    ……  

        <service android:name="com.example.servicetest.MyService" >  
        </service>  
    </application>  

周期分析

onCreate()方法只会在Service第一次被创建的时候调用,如果当前Service已经被创建过了,不管怎样调用startService()方法,onCreate()方法都不会再执行。因此你可以再多点击几次Start Service按钮试一次,每次都只会有onStartCommand()方法中的打印日志。

2.绑定的service

public class LocalService extends Service {  
    // Binder given to clients 
    private final IBinder mBinder = new LocalBinder();  
    // Random number generator 
    private final Random mGenerator = new Random();  

    public class LocalBinder extends Binder {  
        LocalService getService() {  
            // 返回本service的实例到客户端,于是客户端可以调用本service的公开方法 
            return LocalService.this;  
        }  
    }  

    @Override  
    public IBinder onBind(Intent intent) {  
        return mBinder;  
    }  

    /**客户端所要调用的方法*/  
    public int getRandomNumber() {  
      return mGenerator.nextInt(100);  
    }  
}  

下面是一个绑定到LocalService并且在按钮按下时调用getRandomNumber()的actvity的例子:

public class BindingActivity extends Activity {  
    LocalService mService;  
    boolean mBound = false;  

    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
    }  

    @Override  
    protected void onStart() {  
        super.onStart();  
        // 绑定到类LocalService的实例 
        Intent intent = new Intent(this, LocalService.class);  
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
    }  

    @Override  
    protected void onStop() {  
        super.onStop();  
        // 从service解除绑定 
        if (mBound) {  
            unbindService(mConnection);  
            mBound = false;  
        }  
    }  

    /** 当按钮按下时调用(在layout文件中定义的button并用android:onClick 属性指定响应到本方法) */  
    public void onButtonClick(View v) {  
        if (mBound) {  
            // 调用LocalService的一个方法 
            // 然而,如果这个调用中有挂起操作,那么这个请求应发 
            // 生在另一个线程来避免拉低activity的性能. 
            int num = mService.getRandomNumber();  
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();  
        }  
    }  

    /** 定义service绑定的回调,传给bindService() 的*/  
    private ServiceConnection mConnection = new ServiceConnection() {  

        @Override  
        public void onServiceConnected(ComponentName className,  
                IBinder service) {  
            //我们已经绑定到了LocalService,把IBinder进行强制类型转换并且获取LocalService实例. 
            LocalBinder binder = (LocalBinder) service;  
            mService = binder.getService();  
            mBound = true;  
        }  

        @Override  
        public void onServiceDisconnected(ComponentName arg0) {  
            mBound = false;  
        }  
    };  
}  

service的周期函数

1、当采用Context.startService()方法启动服务,与之有关的生命周期方法

onCreate()–> onStart()–> onDestroy()
onCreate()该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。
onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。
onDestroy()该方法在服务被终止时调用。

2、 当采用Context.bindService()方法启动服务,与之有关的生命周期方法

onCreate()–> onBind() –> onUnbind() –> onDestroy()
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。

如果先采用startService()方法启动服务,然后调用bindService()方法绑定到服务,再调用unbindService()方法解除绑定,最后调用bindService()方法再次绑定到服务,触发的生命周期方法如下:
onCreate()–>onStart()–>onBind()–>onUnbind()[重载后的方法需返回true–>onRebind()

那么如果我们既点击了Start Service按钮,又点击了Bind Service按钮会怎么样呢?

这个时候你会发现,不管你是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将两个按钮都点击一下,Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。

Service和Thread的区别

主要就是因为Service的后台概念。Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是,如果我告诉你Service其实是运行在主线程里的,所以是没有任何关系的。

Service又有何用呢?

其实大家不要把后台和子线程联系在一起就行了,这是两个完全不同的概念。

Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。

比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,前面不是刚刚验证过Service是运行在主线程里的么?在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。

使用service创建线程和activity直接创建线程的区别

Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

IntentService介绍

IntentService是Service的子类,比普通的Service增加了额外的功能。

先看Service本身存在两个问题:

Service不会专门启动一条单独的进程,Service与它所在应用位于同一个进程中;

Service也不是专门一条新线程,因此不应该在Service中直接处理耗时的任务;

二、IntentService特征

会创建独立的worker线程来处理所有的Intent请求;

会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题;

所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;

为Service的onBind()提供默认实现,返回null;

为Service的onStartCommand提供默认实现,将请求Intent添加到队列中;

实现

public class MyIntentService extends IntentService {   

    public MyIntentService() {   
        super("MyIntentService");   
    }   

    @Override   
    protected void onHandleIntent(Intent intent) {   
        // IntentService会使用单独的线程来执行该方法的代码 
        // 该方法内执行耗时任务,比如下载文件,此处只是让线程等待20秒 
        long endTime = System.currentTimeMillis() + 20 * 1000;   
        System.out.println("onStart");   
        while (System.currentTimeMillis() < endTime) {   
            synchronized (this) {   
                try {   
                    wait(endTime - System.currentTimeMillis());   
                } catch (InterruptedException e) {   
                    e.printStackTrace();   
                }   
            }   
        }   
        System.out.println("----耗时任务执行完成---");   
    }   
}   

activity代码

 public void startIntentService(View source) {   
        // 创建需要启动的IntentService的Intent 
        Intent intent = new Intent(this, MyIntentService.class);   
        startService(intent);   
    }   

参考:
http://blog.csdn.net/guolin_blog/article/details/11952435
http://blog.csdn.net/u011067360/article/details/24523491
http://blog.csdn.net/p106786860/article/details/17885115

我的微信二维码如下,欢迎交流讨论

这里写图片描述

欢迎关注《IT面试题汇总》微信订阅号。每天推送经典面试题和面试心得技巧

微信订阅号二维码如下:

这里写图片描述

© 著作权归作者所有

共有 人打赏支持
fengsehng
粉丝 4
博文 284
码字总数 214494
作品 0
朝阳
程序员
私信 提问
AndroidManifest.xml配置详解

AndroidManifest.xml配置文件对于Android应用开发来说是非常重要的基础知识,本文旨在总结该配置文件中重点的用法,以便日后查阅。下面是一个标准的AndroidManifest.xml文件样例。 [html]vi...

蓝狐乐队
2013/12/07
0
0
AndroidManifest.xml配置文件详解

AndroidManifest.xml配置文件对于Android应用开发来说是非常重要的基础知识,本文旨在总结该配置文件中重点的用法,以便日后查阅。下面是一个标准的AndroidManifest.xml文件样例。 [html] v...

sflfqx
2015/01/16
0
0
一份关于 Java、Kotlin 与 Android 的学习笔记

JavaKotlinAndroidLearn 这是一份关于 Java 、Kotlin 、Android 的学习笔记,既包含对基础知识点的介绍,也包含对一些重要知识点的源码解析,笔记的大纲如下所示: Java 重拾Java(0)-基础知...

叶应是叶
08/08
0
0
[无线] AndroidManifest.xml配置文件详解

AndroidManifest.xml配置文件对于Android应用开发来说是非常重要的基础知识,本文旨在总结该配置文件中重点的用法,以便日后查阅。下面是一个标准的AndroidManifest.xml文件样例。 从以上示例...

长平狐
2012/11/19
75
0
个帖子学会Android开发四大组件

黑色幽默Lion 方向比努力重要,能力比知识重要,情商比智商重要! 首页 新闻 新随笔 管理 随笔- 43 文章- 0 评论- 12 一个帖子学会Android开发四大组件 注:本文来自“友盟杯”,仅在此阅读,学...

wsl_Mr
2015/09/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Java面试题:面向对象,类加载器,JDBC, Spring 基础概念

1. 为什么说Java是一门平台无关语言? 平台无关实际的含义是“一次编写到处运行”。Java 能够做到是因为它的字节码(byte code)可以运行在任何操作系统上,与底层系统无关。 2. 为什么 Java...

Java干货分享
15分钟前
0
0
LeetCode算法题-Range Sum Query Immutable(Java实现)

01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第70题(顺位题号是303)。给定整数数组nums,找到索引i和j(i≤j)之间的元素之和,包括端点。例如: 给定nums = [-2,0,3,-5,2,-1] s...

qwergkp
20分钟前
0
0
慎用延时初始化(71)

延迟初始化是延时到需要域的值的时候才进行初始化 如果不需要,永远不初始化 既适用于静态域、也适用于实例域 延时初始化是一种优化 除非绝对必要,请尽量不要使用 降低了初始化开销、增加了...

Java搬砖工程师
20分钟前
1
0
中介者模式 调停者 Mediator 行为型 设计模式(二十一)

  用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散      而且可以独立地改变它们之间的交互。      中介者模式又称为调停...

SEOwhywhy
31分钟前
4
0
大数据Spark优化读取Hbase--region 提高并行数过程详细解析

一. Hbase 的 region 我们先简单介绍下 Hbase 的 架构和 region : 从物理集群的角度看,Hbase 集群中,由一个 Hmaster 管理多个 HRegionServer,其中每个 HRegionServer 都对应一台物理机器...

金铭鼎IT教育
31分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部