您的位置:首页 > 其它

关于Service的基本探究

2015-05-01 00:28 239 查看
资料一 http://www.cnblogs.com/zhangdongzi/archive/2012/01/08/2316711.html
android中服务是运行在后台的东西,级别与activity差不多。既然说service是运行在后台的服务,那么它就是不可见的,没有界面的东西。你可以启动一个服务Service来播放音乐,或者记录你地理信息位置的改变,或者启动一个服务来运行并一直监听某种动作。

Service和其他组件一样,都是运行在主线程中,因此不能用它来做耗时的请求或者动作。你可以在服务中开一一个线程,在线程中做耗时动作。


一.基础知识

服务一般分为两种:

1:本地服务, Local Service 用于应用程序内部。在Service可以调用Context.startService()启动,调用Context.stopService()结束。在内部可以调用Service.stopSelf()
或 Service.stopSelfResult()来自己停止。无论调用了多少次startService(),都只需调用一次stopService()来停止。

2:远程服务, Remote Service 用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。客户端建立到服务对象的连接,并通过那个连接来调用服务。调用Context.bindService()方法建立连接,并启动,以调用 Context.unbindService()关闭连接。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。

提供给可被其他应用复用,比如定义一个天气预报服务,提供与其他应用调用即可。

那么先来看Service的生命周期吧:如图:



context.startService()
->onCreate()- >onStart()->Service running--调用context.stopService()
->onDestroy()

context.bindService()->onCreate()->onBind()->Service
running--调用>onUnbind() ->
onDestroy()
从上诉可以知道分别对应本地的,,以及远程的,也对应不同的方式启动这个服务。


二.实战

我们可以定义一个本地服务继承Service,然后在这个服务里播放媒体播放器或者记录地理位置变化。通常有时候我们的Service要与Activity交互,那么可以可以定义一个内部类,返回这个Service,当然我们要考虑到如果是以绑定方式启动服务,那么内部类可以定义为继承Binder,然后返回本地服务,具体代码如下。



package com.dongzi;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class LocalService extends Service {

private static final String TAG = "LocalService";
private IBinder binder=new LocalService.LocalBinder();

@Override
public IBinder onBind(Intent intent) {

return binder;
}
MediaPlayer mediaPlayer=null;
@Override
public void onCreate() {
Log.i(TAG, "onCreate");
//这里可以启动媒体播放器
// if(mediaPlayer==null)
//     mediaPlayer=MediaPlayer.create(this, uri);
super.onCreate();
}

@Override
public void onStart(Intent intent, int startId) {
Log.i(TAG, "onStart");
super.onStart(intent, startId);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand");
return START_STICKY;
}

@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
super.onDestroy();
}

//定义内容类继承Binder
public class LocalBinder extends Binder{
//返回本地服务
LocalService getService(){
return LocalService.this;
}
}

}


我们可以从上面知道

//定义内容类继承Binder

public class LocalBinder extends Binder{

//返回本地服务

LocalService getService(){

return LocalService.this;

}

}

可以返回这个服务,然后activity可以通过服务调用服务的方法了。

那么如何启动服务呢?从上面基础知识中,我们知道有2种方法,如下:



//启动服务
private void startCustomService(){
Intent intent=new Intent(this,LocalService.class);
startService(intent);
}


第2种绑定方式:



LocalService localService=null;
//用bindService方法启动服务
private void BinderService(){
Intent intent=new Intent(this,LocalService.class);
bindService(intent, new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
//调用bindService方法启动服务时候,如果服务需要与activity交互,
//则通过onBind方法返回IBinder并返回当前本地服务
localService=((LocalService.LocalBinder)binder).getService();
//这里可以提示用户,或者调用服务的某些方法
}

@Override
public void onServiceDisconnected(ComponentName componentName) {
localService=null;
//这里可以提示用户
}
}, Context.BIND_AUTO_CREATE);
}


在绑定服务的时候,需要一个服务连接对象,ServiceConnection,服务一旦连接,就会调用onServiceConnected方法,我们可以在这个方法里面返回我们的本地服务对象,具体看代码;而在服务断开时候会调用onServiceDisconnected方法,我们可以清理一些服务资源。

资料二 http://www.cnblogs.com/zhangdongzi/archive/2012/01/09/2317197.html AIDL的一些知识。这些与服务息息相关。


一.基础知识

AIDL的作用

在Android平台,每个应用程序App都运行在自己的进程空间。通常一 个进程不能访问另一个进程的内存空间(一个应用不能访问另一个应用),如果想沟通,需要将对象分解成操作系统可以理解的基本单元,Android提供了AIDL来处理。

AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参 数。换句比较浅显的话来说,就是我这个App应用的activity,需要调用其他App应用的Service.当然同一App应用的activity
与service也可以在不同进程间,这可以设置Service配置中,android:process=":remote"

AIDL的使用


官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。(太生硬了,不同进程的组件调用吧。)

那么怎么制作AIDL呢?下面步骤

1:创建.aidl文件。新建立个文件并且以.aidl作为后缀保存,在这个文件中编写接口以及方法,这个我们普通java接口声明是一样的,不同的是要显示import
复杂类型,即便复杂类型对象在同一个包内。Java基本数据类型 (int, long, char, boolean等),String和CharSequence,集合接口类型List和Map,不需要import 。

比如:

package com.dongzi;

interface IStockQuoteService{

double getPrice(String ticker);

}

2:创建好AIDL文件后,刷新下工程,你会发现在gen包下,对应的包下面出现一个与AIDL文件相同的java文件。如:



/*
* This file is auto-generated.  DO NOT MODIFY.
* Original file: D:\\mywordspace\\MyPhone\\src\\com\\dongzi\\IStockQuoteService.aidl
*/
package com.dongzi;

public interface IStockQuoteService extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.dongzi.IStockQuoteService {
private static final java.lang.String DESCRIPTOR = "com.dongzi.IStockQuoteService";

/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.dongzi.IStockQuoteService
* interface, generating a proxy if needed.
*/
public static com.dongzi.IStockQuoteService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface) obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.dongzi.IStockQuoteService))) {
return ((com.dongzi.IStockQuoteService) iin);
}
return new com.dongzi.IStockQuoteService.Stub.Proxy(obj);
}

public android.os.IBinder asBinder() {
return this;
}

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getPrice: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
double _result = this.getPrice(_arg0);
reply.writeNoException();
reply.writeDouble(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.dongzi.IStockQuoteService {
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
mRemote = remote;
}

public android.os.IBinder asBinder() {
return mRemote;
}

public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}

public double getPrice(java.lang.String ticker) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
double _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(ticker);
mRemote.transact(Stub.TRANSACTION_getPrice, _data, _reply, 0);
_reply.readException();
_result = _reply.readDouble();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}

static final int TRANSACTION_getPrice = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

public double getPrice(java.lang.String ticker) throws android.os.RemoteException;
}


AIDL工具自动生成了那么多代码,其实我们只需要知道3个就够了。

public static abstract class Stub extends android.os.Binder implements com.dongzi.IStockQuoteService 静态抽象内部类Stub

private static class Proxy implements com.dongzi.IStockQuoteService AIDL服务代理类

public double getPrice(java.lang.String ticker) throws android.os.RemoteException; AIDL公布出的接口,就是我们定义的接口方法

3:把AIDL文件存放在其他客户端应用中,我们这个作为服务端。当然我们也可以方便的把这个应用作为客户端以及服务端。其实根本区别就是为了使调用者与被调用者在不同进程中,于是在服务中添加android:process=":remote"即可。省去了再建立应用客户端调用。

4:AIDL只是请定义一个契约,我们这里需要一个服务来提供服务。于是建立服务MyService.


二.实战

既然我们了解一些基础知识后,现在我们开始来代码吧。假设我们需要在一个进程中调用其他应用的服务,这个服务提供一个股票价格查询,或者GPS定位的服务。

并且定义一个类,继承我们AIDL生成的Stub内部类,并实现我们AIDL定义的方法

代码如下:



package com.dongzi;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class MyService extends Service {
static final String TAG="MyService";

//定义内部类MyServiceImpl继承我们的AIDL文件自动生成的内部类,
//并且实现我们AIDL文件定义的接口方法
private class MyServiceImpl extends IStockQuoteService.Stub{

@Override
public double getPrice(String ticker) throws RemoteException {
Log.e(TAG, "getPrice");
return 10.5;
}

}

@Override
public IBinder onBind(Intent arg0) {
//返回AIDL实现
return new MyServiceImpl();
}

@Override
public void onDestroy(){
Log.e(TAG, "Release MyService");
super.onDestroy();
}
}


我们需要在onBind方法中返回我们的AIDL接口实现对象,以便其他进程调用。

当然了,现在AIDL,以及Service都定义好了,就需要在mainfest.xml中设置

<service android:name=".MyService"
android:process=":remote"
>
<intent-filter>
<action android:name="com.dongzi.IStockQuoteService"/>
</intent-filter>
</service>


在客户端服务端在同个App中,android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。

那么现在客户端来调用我们的服务了,代码如下:



package com.dongzi;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MYyActivity extends Activity {
static final String TAG="MYyActivity";
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btnCall=(Button)findViewById(R.id.btnCall);
if(btnCall!=null)
btnCall.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
//绑定一个服务
bindMyService();
}
});
}

IStockQuoteService iService=null;
private ServiceConnection  conn=new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//返回AIDL接口对象,然后可以调用AIDL方法
iService=IStockQuoteService.Stub.asInterface(service);
double value=0.0;
try {
value=iService.getPrice("");
}
catch (RemoteException e) {
Log.e(TAG,"调用出错!");
e.printStackTrace();
}
Log.e(TAG, "返回数值为:"+value);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "释放Service");
}
};

private void bindMyService(){
// Intent intent=new Intent("com.dongzi.IStockQuoteService");
Intent intent=new Intent(this,MyService.class);
startService(intent);

bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
}


在按钮点击时候启动service,然后再绑定这个Service.在连接到服务后,我们会发现,调用AIDL中定义的方法成了,打印如下:



项目结构为:



资料三 原document译文 http://blog.csdn.net/ryantang03/article/details/7770939
Service是Android中四大组件之一,在Android开发中起到非常重要的作用,先来看一下官方对Service的定义:

A
Service
is
an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another
application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service might handle network transactions, play music, perform file I/O, or interact with a content provider,
all from the background.

翻译过来就是:Service(服务)是一个没有用户界面的在后台运行执行耗时操作的应用组件。其他应用组件能够启动Service,并且当用户切换到另外的应用场景,Service将持续在后台运行。另外,一个组件能够绑定到一个service与之交互(IPC机制),例如,一个service可能会处理网络操作,播放音乐,操作文件I/O或者与内容提供者(content
provider)交互,所有这些活动都是在后台进行。

Service有两种状态,“启动的”和“绑定”

Started
A service is "started" when an application component (such as an activity) starts it by calling
startService()
.
Once started, a service can run in the background indefinitely, even if the component that started it is destroyed. Usually, a started service performs a single operation and does not return a result to the caller. For example, it might download or upload
a file over the network. When the operation is done, the service should stop itself.
Bound

A service is "bound" when an application component binds to it by calling
bindService()
.
A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application
component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed.

通过startService()启动的服务处于“启动的”状态,一旦启动,service就在后台运行,即使启动它的应用组件已经被销毁了。通常started状态的service执行单任务并且不返回任何结果给启动者。比如当下载或上传一个文件,当这项操作完成时,service应该停止它本身。

还有一种“绑定”状态的service,通过调用bindService()来启动,一个绑定的service提供一个允许组件与service交互的接口,可以发送请求、获取返回结果,还可以通过夸进程通信来交互(IPC)。绑定的service只有当应用组件绑定后才能运行,多个组件可以绑定一个service,当调用unbind()方法时,这个service就会被销毁了。

另外,在官方的说明文档中还有一个警告:

Caution: A service runs in the main thread of its hosting process—the service does not create its own thread and does not run in a separate process (unless you specify otherwise). This means
that, if your service is going to do any CPU intensive work or blocking operations (such as MP3 playback or networking), you should create a new thread within the service to do that work. By using a separate thread, you will reduce the risk of Application
Not Responding (ANR) errors and the application's main thread can remain dedicated to user interaction with your activities.

意思是service与activity一样都存在与当前进程的主线程中,所以,一些阻塞UI的操作,比如耗时操作不能放在service里进行,比如另外开启一个线程来处理诸如网络请求的耗时操作。如果在service里进行一些耗CPU和耗时操作,可能会引发ANR警告,这时应用会弹出是强制关闭还是等待的对话框。所以,对service的理解就是和activity平级的,只不过是看不见的,在后台运行的一个组件,这也是为什么和activity同被说为Android的基本组件。

Service生命周期中的一些方法:



通过这个图可以看到,两种启动service的方式以及他们的生命周期,bind service的不同之处在于当绑定的组件销毁后,对应的service也就被kill了。service的声明周期相比与activity的简单了许多,只要好好理解两种启动service方式的异同就行。

service生命周期也涉及一些回调方法,这些方法都不用调用父类方法,具体如下:

[java] view
plaincopy

<span style="font-family:Comic Sans MS;font-size:18px;">public class ExampleService extends Service {

int mStartMode; // indicates how to behave if the service is killed

IBinder mBinder; // interface for clients that bind

boolean mAllowRebind; // indicates whether onRebind should be used

@Override

public void onCreate() {

// The service is being created

}

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

// The service is starting, due to a call to startService()

return mStartMode;

}

@Override

public IBinder onBind(Intent intent) {

// A client is binding to the service with bindService()

return mBinder;

}

@Override

public boolean onUnbind(Intent intent) {

// All clients have unbound with unbindService()

return mAllowRebind;

}

@Override

public void onRebind(Intent intent) {

// A client is binding to the service with bindService(),

// after onUnbind() has already been called

}

@Override

public void onDestroy() {

// The service is no longer used and is being destroyed

}

}</span>

关于Service生命周期还有一张比较易懂的图(来源于网络)



另外,这里要说明Service的一个子类,IntentService,首先看下官方文档的说明:

IntentService


This is a subclass of
Service
that
uses a worker thread to handle all start requests, one at a time. This is the best option if you don't require that your service handle multiple requests simultaneously. All you need to do is implement
onHandleIntent()
,
which receives the intent for each start request so you can do the background work.

IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent,对于异步的startService请求,IntentService会处理完成一个之后再处理第二个,每一个请求都会在一个单独的worker thread中处理,不会阻塞应用程序的主线程,这里就给我们提供了一个思路,如果有耗时的操作与其在Service里面开启新线程还不如使用IntentService来处理耗时操作。而在一般的继承Service里面如果要进行耗时操作就必须另开线程,但是使用IntentService就可以直接在里面进行耗时操作,因为默认实现了一个worker
thread。对于异步的startService请求,IntentService会处理完成一个之后再处理第二个。

看下IntentService的具体实现:

[java] view
plaincopy

<span style="font-family:Comic Sans MS;font-size:18px;color:#222222;">public class HelloIntentService extends IntentService {

/**

* A constructor is required, and must call the super IntentService(String)

* constructor with a name for the worker thread.

*/

public HelloIntentService() {

super("HelloIntentService");

}

/**

* The IntentService calls this method from the default worker thread with

* the intent that started the service. When this method returns, IntentService

* stops the service, as appropriate.

*/

@Override

protected void onHandleIntent(Intent intent) {

// Normally we would do some work here, like download a file.

// For our sample, we just sleep for 5 seconds.

long endTime = System.currentTimeMillis() + 5*1000;

while (System.currentTimeMillis() < endTime) {

synchronized (this) {

try {

wait(endTime - System.currentTimeMillis());

} catch (Exception e) {

}

}

}

}

}</span>

关于停止Service,如果service是非绑定的,最终当任务完成时,为了节省系统资源,一定要停止service,可以通过stopSelf()来停止,也可以在其他组件中通过stopService()来停止,绑定的service可以通过onUnBind()来停止service。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: