android之service,你应该知道的一切

来源:互联网 发布:nginx 支持ipv6 编辑:程序博客网 时间:2024/05/21 09:16

    今天写一篇关于service的文章。准备从以下几方面进行阐述,有理解错误的地方,欢迎指正。

1. service和Thread是否有关系
2. 什么时候使用service
3. service的生命周期及启动方式对比
4. android的IPC通信(service访问相关)的几种实现方式及对比

service和Thread是否有关系

有人会问service是线程吗,答案是service和线程没什么直接关系。

    thread是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。而serviceService是android的一种机制,如果运行的是Local Service(service和应用在同一进程),那么该Service 是运行在主进程的 main 线程上的。如果是RemoteService(service和应用不在同一进程),那么对应的 Service 则是运行在独立进程的 main 线程上。因此说Service肯定不会线程,二者无直接关系。


什么时候使用service

有人会问什么时候使用service,大家都知道如果需要后台长期运行的任务可以交给service来完成,但是如果在activity中开启一个子线程同样能完成这样的任务,那么为什么要用service呢?

    其实这点要从android系统进程管理是具有一定的优先级规则的。
android系统的内存清理机制会按照进程的优先级去回收系统的内存。进程的优先级分为以下五个等级。按照回收优先级由高到低的顺序
Foreground process 前台进程
很好理解,就是用户正在操作的应用程序对应的进程
Visible process 可视进程
代表着用户仍然可以看到这个进程的界面。仍处于可视状态,只是失去了焦点,比如一个activity处于onPause()状态,如弹出个对话框等。
Service process服务进程
应用程序有服务组件在后台运行,比如后台播放音乐等。
Background process 后台进程
应用程序没有服务在运行并且处于不可见的状态,如按下home键,则调用了activity的onStop()方法。已经比较容易被回收了,对用户体验几乎无影响。
Empty process 空进程
没有运行任何component的进程,保留这个进程主要是为了缓存的需要。比如,我们在浏览器搜索框中输入了一些关键字,然后关闭了浏览器,为了下一次启动后,搜索框还有上次的关键字,需要使用缓存将这些关键字存起来,建立空进程来保存这些关键字,从而当再次启动浏览器时,可以从空进程中获得关键字数据。
所以利用service来进行后台操作而不是直接开启子线程可以提升进程的优先级,不容易被系统回收。但是我们要注意默认情况下,service是运行在进程的主线程上,即UI线程上,所以如果service里面的任务是耗时操作,不要直接放在service中进行,最好在service中开启子线程处理,否则可能会出现有名的ANR异常。


service的生命周期及启动方式对比

接下来讨论一下service的生命周期。service的生命周期中比较重要的方法有这么几个:onCreate,onStartCommand,onBind,onUnbind,onDestroy等方法。至于几个方法的调用顺序,我们结合启动方式来说明。
service的启动方式分为以下两种,采用start的方法开启服务采用绑定的方法开启服务

采用start的方法开启服务

首先新建一个TestService类

public class TestService extends Service {    @Override    public void onCreate() {        Log.i("StartService","onCreate");        Log.i("StartService","PID:"+android.os.Process.myPid());        super.onCreate();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i("StartService","onStartCommand");        return super.onStartCommand(intent, flags, startId);    }    @Override    public IBinder onBind(Intent arg0) {        Log.i("StartService","onBind");        return null;    }    @Override    public boolean onUnbind(Intent intent) {        Log.i("StartService","onUnbind");        return super.onUnbind(intent);    }    @Override    public void onDestroy() {        Log.i("StartService","onDestroy");        super.onDestroy();    }}

太在MainActivity中新建一个Button,代码太简单,就不贴了,点击按钮时,启动服务:

Intent intent=new Intent(MainActivity.this,TestService.class);                startService(intent);

在AndroidManifest.xml中,加入:

<service android:name="com.example.binderclient.TestService"            />

第一次点击button时,观看打印结果:

这里写图片描述
可见只调用了onCreate和onStartCommand方法,再次点击button,则onCreate不会被调用,只调用onStartCommand方法,也就是只会启动一个service实例。
如果想停止service,则必须调用stopService(intent)方法。或者在service中显示的调用stopSelf()方法,此时,service的onDestroy函数会被调用,值得注意的是,调用stopSelf()并不会立即中断当前方法的执行。比如:在onStartCommand方法中调用stopSelf()方法,则方法体内stopSelf()语句后面仍然可以得到执行。


采用绑定的方法开启服务
此种方式开启service,需要了解一点Binder通信方面的东西,关于Binder通信的介绍放在下一节,本节只关注service生命周期。

在Mainactivity中新建一个ServiceConnection,在这里只需知道ServiceConnection用于获取service的代理对象即可。

 private ServiceConnection conn=new ServiceConnection() {        @Override        public void onServiceDisconnected(ComponentName name) {            // TODO Auto-generated method stub        }        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            // TODO Auto-generated method stub        }    };

在button中加入:

 Intent intent = new Intent(MainActivity.this, TestService.class);                   bindService(intent, conn, Context.BIND_AUTO_CREATE);

点击按钮后,输出结果:
这里写图片描述

    可见通过绑定的方式开启service,则只会调用onCreate()和onBind()方法,只会建立和绑定service一次,再次点击则不会在调用onCreate()和onBind()方法。
    如果想解除绑定,则必须调用unbindService(conn);此时会调用onUnbind()和onDestroy()方法,此外,由于此时service和启动它的Context进行了绑定,在本例中,调用的是MainActivity的bindService方法,所以如果MainActivity结束即activity的onDestroy()方法得到调用时,也会结束Service。在应用中,我们最好是调用unbindService()方法来结束对应的service。

    最后要说明的一点是:如果某个service两种启动方式都调用了,那必须两种结束方式都被调用才可以结束service。


android的IPC通信(service访问相关)的几种实现方式及对比

在编写跨进程service通信机制之前,先说一说进程间通信IPC(Inter-Process Communication)。在Linux下IPC通信有管道,共享内存,信号量等,在android下进程间通信主要是Binder机制。下面就来大致介绍一下Binder的工作原理。
    Binder机制实际上就是一个类似于C/S的构架:客户端进程要想与服务端进程通信就必须在客户端获取一个服务端进程代理对象,然后将请求发送到代理对象上;代理对象通过Binder驱动将请求转发给服务端进程处理;当处理完成之后,再次通过Binder驱动传回给代理对象,客户端从代理对象获取响应信息。
    Android对Binder机制进行了抽象,定义了IBinder接口,该接口是对跨进程对象的抽象,在C/C++和Java层都有定义。IBinder定义了一套使用Binder机制来实现客户程序与服务器的通信协议。
     一个普通对象只能在当前进程中被访问,如果希望它能被其他进程访问,就必须实现IBinder接口。IBinder接口可以指向本地对象,也可以指向远程对象,关键就在于IBinder接口中的transact函数。如果IBinder指向的是一个服务端代理,那么transact只是负责把请求发送给服务器;如果IBinder指向的是一个服务端,那么transact只负责提供服务即可。因此,不管是服务端还是服务端代理对象,都必须实现该接口,这样才能进行Binder通信。
Binder架构包括服务器接口、Binder驱动、客户端接口三个模块。

Binder服务端:一个Binder服务端实际上就是Binder类的对象,该对象一旦创建,内部则会启动一个隐藏线程,会接收Binder驱动发送的消息,收到消息后,会执行Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务器端代码。onTransact函数的参数是客户端调用transact函数的输入。

Binder驱动:任意一个服务端Binder对象被创建时,同时会在Binder驱动中创建一个mRemote对象,该对象也是一个Binder类。客户端访问远程服务端都是通过该mRemote对象。

客户端:获取远程服务在Binder驱动中对应的mRemote引用,然后调用它的transact方法即可向服务端发送消息。

未完待续

0 0