展会信息港展会大全

Android四大组件之 Service详解
来源:互联网   发布日期:2016-01-13 22:18:33   浏览:2431次  

导读:一 Service简介 Service是运行在后台的,没有界面的,用来处理耗时比较长的。Service不是一个单独的进程,不是一个单独的线程。 Service有两种类型:本地服务(Local Service):用于应用程序内部远程服务......

一Service简介

Service是运行在后台的,没有界面的,用来处理耗时比较长的。Service不是一个单独的进程,不是一个单独的线程。

Service有两种类型:

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

远程服务(Remote Sercie):用于android系统内部的应用程序之间

前者用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较 好。

后者可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。

二Service的生命周期

Service有startService()和bindService()两种启动Service方法,每种方法Service的生命周期是不一样的。

1 通过startService()

Service会经历 onCreate --> onStartCommand()

stopService的时候直接onDestroy

如果是 调用者 直接退出而没有调用stopService的话,Service会一直在后台运行。

下次调用者再起来仍然可以stopService。

2 通过bindService()

Service只会运行onCreate()-->onBind()

这个时候 调用者和Service绑定在一起

unbindService的时候onUnbind()-->onDestroyed()

调用者退出了,Srevice就会调用onUnbind()-->onDestroyed()

所谓绑定在一起就共存亡了。

注意:Service的onCreate的方法只会被调用一次,

就是你无论多少次的startService又 bindService,Service只被创建一次。

如果先是bind了,那么start的时候就直接运行Service的onStart方法,

如果先是start,那么bind的时候就直接运行onBind方法。如果你先bind上了,就stop不掉了,

只能先UnbindService, 再StopService,所以是先start还是先bind行为是有区别的。

Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的 程序。

服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。

这两个方法都可以启动Service,但是它们的使用场合有所不同。使用startService()方法启用服务,调用者与服务之间没有关连,

即使调用者退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有 不求同 时生,必须同时死 的特点。

如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,

接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次 创建服务,

但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结 束服务,服务结束时会调用onDestroy()方法。

如果打算采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,

接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会先调用服务的onUnbind()方法,

接着调用onDestroy()方法。如果调用bindService()方法前服务已经被绑定,

多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说onCreate()和onBind()方法并不会被多次调用)。

如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的 onUnbind()-->onDestroy()方法.

三代码实例

编写不需和Activity交互的本地服务示例

本地服务编写比较简单。首先,要创建一个Service类,该类继承android的Service类。这里写了一个计数服务的类,每秒钟为计数器 加一。在服务类的内部,还创建了一个线程,用于实现后台执行上述业务逻辑。

package com.easymorse;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.util.Log;

public class CountService extends Service {

private boolean threadDisable;

privateint count;

@Override

public IBinder onBind(Intent intent) {

returnnull;

}

@Override

public void onCreate() {

super.onCreate();

new Thread(new Runnable() {

@Override

publicvoid run() {

while (!threadDisable) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

}

count++;

Log.v("CountService","Count is " + count);

}

}

}).start();

}

@Override

public void onDestroy() {

super.onDestroy();

this.threadDisable= true;

Log.v("CountService","on destroy");

}

public int getCount() {

return count;

}

}

需要将该服务注册到配置文件AndroidManifest.xml中,否则无法找到:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.easymorse" android:versionCode="1" android:versionName="1.0">

<applicationandroid:icon="@drawable/icon" android:label="@string/app_name">

<activityandroid:name=".LocalServiceDemoActivity"

android:label="@string/app_name">

<intent-filter>

<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>

</intent-filter>

</activity>

<serviceandroid:name="CountService"/>

</application>

<uses-sdkandroid:minSdkVersion="3"/>

</manifest/>

在Activity中启动和关闭本地服务。

package com.easymorse;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

public class LocalServiceDemoActivityextends Activity {

/** Called when the activity is first created.*/

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

this.startService(new Intent(this, CountService.class));

}

@Override

protectedvoid onDestroy() {

super.onDestroy();

this.stopService(new Intent(this, CountService.class));

}

}

可通过日志查看到后台线程打印的计数内容。

编写本地服务和Activity交互的示例

上面的示例是通过startService和stopService启动关闭服务的。适用于服务和activity之间没有调用交互的情况。如果之 间需要传递参数或者方法调用。需要使用bind和unbind方法。

具体做法是,服务类需要增加接口,比如ICountService,另外,服务类需要有一个内部类,这样可以方便访问外部类的封装数据,这个内部 类 需要继承Binder类并实现ICountService接口。还有,就是要实现Service的onBind方法,不能只传回一个null了。

这是新建立的接口代码:

package com.easymorse;

public interface ICountService {

public abstract int getCount();

}

修改后的CountService代码:

package com.easymorse;

import android.app.Service;

import android.content.Intent;

import android.os.Binder;

import android.os.IBinder;

import android.util.Log;

public class CountServiceextends Serviceimplements ICountService {

privateboolean threadDisable;

privateint count;

private ServiceBinder serviceBinder=new ServiceBinder();

public class ServiceBinderextends Binderimplements ICountService{

@Override

publicint getCount() {

return count;

}

}

@Override

public IBinder onBind(Intent intent) {

return serviceBinder;

}

@Override

public void onCreate() {

super.onCreate();

new Thread(new Runnable() {

@Override

publicvoid run() {

while (!threadDisable) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

}

count++;

Log.v("CountService","Count is " + count);

}

}

}).start();

}

@Override

public void onDestroy() {

super.onDestroy();

this.threadDisable= true;

Log.v("CountService","on destroy");

}

/* (non-Javadoc)

* @see com.easymorse.ICountService#getCount()

*/

public int getCount() {

return count;

}

}

服务的注册也要做改动,AndroidManifest.xml文件:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.easymorse" android:versionCode="1" android:versionName="1.0">

<applicationandroid:icon="@drawable/icon" android:label="@string/app_name">

<activityandroid:name=".LocalServiceDemoActivity"

android:label="@string/app_name">

<intent-filter>

<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>

</intent-filter>

</activity>

<serviceandroid:name="CountService">

<intent-filter>

<actionandroid:name="com.easymorse.CountService"/>

</intent-filter>

</service>

</application>

<uses-sdkandroid:minSdkVersion="3"/>

</manifest>

Acitity代码不再通过startSerivce和stopService启动关闭服务,另外,需要通过ServiceConnection的 内部类实现来连接Service和Activity。

package com.easymorse;

import android.app.Activity;

import android.content.ComponentName;

import android.content.Intent;

import android.content.ServiceConnection;

import android.os.Bundle;

import android.os.IBinder;

import android.util.Log;

public class LocalServiceDemoActivity extends Activity {

private ServiceConnection serviceConnection= new ServiceConnection() {

@Override

publicvoid onServiceConnected(ComponentName name, IBinder service) {

countService = (ICountService) service;

Log.v("CountService","on serivce connected, count is"

+ countService.getCount());

}

@Override

publicvoid onServiceDisconnected(ComponentName name) {

countService =null;

}

};

private ICountService countService;

/** Called when the activity is first created.*/

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

this.bindService(new Intent("com.easymorse.CountService"),

this.serviceConnection, BIND_AUTO_CREATE);

}

@Override

protectedvoid onDestroy() {

this.unbindService(serviceConnection);

super.onDestroy();//注意先后

}

}

编写传递基本型数据的远程服务

上面的示例,可以扩展为,让其他应用程序复用该服务。这样的服务叫远程(remote)服务,实际上是进程间通信(RPC)。

这时需要使用android接口描述语言(AIDL)来定义远程服务的接口,而不是上述那样简单的java接口。扩展名为aidl而不是 java。 可用上面的ICountService改动而成ICountSerivde.aidl,eclipse会自动生成相关的java文件。

package com.easymorse;

interface ICountService {

int getCount();

}

编写服务(Service)类,稍有差别,主要在binder是通过远程获得的,需要通过桩(Stub)来获龋桩对象是远程对象的本地代理。

package com.easymorse;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.os.RemoteException;

import android.util.Log;

public class CountService extends Service {

privateboolean threadDisable;

privateint count;

private ICountService.Stub serviceBinder= new ICountService.Stub() {

@Override

publicint getCount()throws RemoteException {

return count;

}

};

@Override

public IBinder onBind(Intent intent) {

return serviceBinder;

}

@Override

public void onCreate() {

super.onCreate();

new Thread(new Runnable() {

@Override

publicvoid run() {

while (!threadDisable) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

}

count++;

Log.v("CountService","Count is " + count);

}

}

}).start();

}

@Override

public void onDestroy() {

super.onDestroy();

this.threadDisable= true;

Log.v("CountService","on destroy");

}

}

配置文件AndroidManifest.xml和上面的类似,没有区别。

在Activity中使用服务的差别不大,只需要对ServiceConnection中的调用远程服务的方法时,要捕获异常。

private ServiceConnection serviceConnection= new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName name, IBinder service) {

countService = (ICountService) service;

try {

Log.v("CountService","on serivce connected, count is"

+ countService.getCount());

} catch (RemoteException e) {

thrownew RuntimeException(e);

}

}

@Override

public void onServiceDisconnected(ComponentName name) {

countService =null;

}

};

这样就可以在同一个应用程序中使用远程服务的方式和自己定义的服务交互了。

如果是另外的应用程序使用远程服务,需要做的是复制上面的aidl文件和相应的包构到应用程序中,其他调用等都一样。

编写传递复杂数据类型的远程服务

远程服务往往不只是传递java基本数据类型。这时需要注意android的一些限制和规定:

android支持String和CharSequence

如果需要在aidl中使用其他aidl接口类型,需要 import,即使是在相同包结构下;

android允许传递实现Parcelable接口的类,需要import;

android 支持集合接口类型List和Map,但是有一些限制,元素必须是基本型或者上述三种情况,不需要import集合接口类,但是需要对元素涉及到的类型 import;

非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和 inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。

这里将前面的例子中返回的int数据改为复杂数据类型:

package com.easymorse;

import android.os.Parcel;

import android.os.Parcelable;

public class CountBean implements Parcelable {

public static final Parcelable.Creator<CountBean> CREATOR= new Creator<CountBean>() {

@Override

public CountBean createFromParcel(Parcel source) {

CountBean bean =new CountBean();

bean.count = source.readInt();

return bean;

}

@Override

public CountBean[] newArray(int size) {

returnnew CountBean[size];

}

};

public int count;

@Override

public void writeToParcel(Parcel dest,int flags) {

dest.writeInt(this.count);

}

@Override

public int describeContents() {

return;

}

}

然后,需要在相同包下建一个同名的aidl文件,用于android生成相应的辅助文件:

package com.easymorse;

parcelable CountBean;

这一步是android 1.5后的变化,无法通过adt生成aidl,也不能用一个比如全局的project.aidl文件,

然后,需要在服务的aidl文件中修改如下:

package com.easymorse;

import com.easymorse.CountBean;

interface ICountService {

CountBean getCount();

}

其他的改动很小,只需将CountService和调用CountService的部分修改为使用CountBean即可

赞助本站

人工智能实验室

相关热词: Service 组件

AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港