android SDK提供了Service,用于类似*nix守护进程或者windows的服务。
Service有两种类型:
前者用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。
后者可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。
编写本地服务和Activity交互的示例上面的示例是通过startService和stopService启动关闭服务的。适用于服务和activity之间没有调用交互的情况。如果之间需要传递参数或者方法调用。需要使用bind和unbind方法。
具体做法是,服务类需要增加接口,比如ICountService,另外,服务类需要有一个内部类,这样可以方便访问外部类的封装数据,这个内部类需要继承Binder类并实现ICountService接口。还有,就是要实现Service的onBind方法,不能只传回一个null了。
这是新建立的接口代码:
packagecom.easymorse;
publicinterfaceICountService {
publicabstractintgetCount();
}
修改后的CountService代码:
packagecom.easymorse;
importandroid.app.Service;
importandroid.content.Intent;
importandroid.os.Binder;
importandroid.os.IBinder;
importandroid.util.Log;
publicclassCountService extendsService implementsICountService {
privatebooleanthreadDisable;
privateintcount;
privateServiceBinder serviceBinder=newServiceBinder();
publicclassServiceBinder extendsBinder implementsICountService{
@Override
publicintgetCount() {
returncount;
}
}
@Override
publicIBinder onBind(Intent intent) {
returnserviceBinder;
}
@Override
publicvoidonCreate() {
super.onCreate();
newThread(newRunnable() {
@Override
publicvoidrun() {
while(!threadDisable) {
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
}
count++;
Log.v("CountService", "Count is "+count);
}
}
}).start();
}
@Override
publicvoidonDestroy() {
super.onDestroy();
this.threadDisable =true;
Log.v("CountService", "on destroy");
}
/*(non-Javadoc)
* @see com.easymorse.ICountService#getCount()
*/
publicintgetCount() {
returncount;
}
服务的注册也要做改动,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">
<application android:icon="@drawable/icon"android:label="@string/app_name">
<activity android:name=".LocalServiceDemoActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service android:name="CountService">
<intent-filter>
<action android:name="com.easymorse.CountService"/>
</intent-filter>
</service>
</application>
<uses-sdk android:minSdkVersion="3"/>
</manifest>
Acitity代码不再通过startSerivce和stopService启动关闭服务,另外,需要通过ServiceConnection的内部类实现来连接Service和Activity。
packagecom.easymorse;
importandroid.app.Activity;
importandroid.content.ComponentName;
importandroid.content.Intent;
importandroid.content.ServiceConnection;
importandroid.os.Bundle;
importandroid.os.IBinder;
importandroid.util.Log;
publicclassLocalServiceDemoActivity extendsActivity {
privateServiceConnection serviceConnection =newServiceConnection() {
@Override
publicvoidonServiceConnected(ComponentName name, IBinder service) {
countService =(ICountService) service;
Log.v("CountService", "on serivce connected, count is "
+countService.getCount());
}
@Override
publicvoidonServiceDisconnected(ComponentName name) {
countService =null;
}
};
privateICountService countService;
/**Called when the activity is first created. */
@Override
publicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.bindService(newIntent("com.easymorse.CountService"),
this.serviceConnection, BIND_AUTO_CREATE);
}
@Override
protectedvoidonDestroy() {
this.unbindService(serviceConnection);
super.onDestroy(); //注意先后
}
}
上面的示例,可以扩展为,让其他应用程序复用该服务。这样的服务叫远程(remote)服务,实际上是进程间通信(RPC)。
这时需要使用android接口描述语言(AIDL)来定义远程服务的接口,而不是上述那样简单的java接口。扩展名为aidl而不是java。可用上面的ICountService改动而成ICountSerivde.aidl,eclipse会自动生成相关的java文件。
package com.easymorse;
interfaceICountService {
intgetCount();
}
packagecom.easymorse;
importandroid.app.Service;
importandroid.content.Intent;
importandroid.os.IBinder;
importandroid.os.RemoteException;
importandroid.util.Log;
publicclassCountService extendsService {
privatebooleanthreadDisable;
privateintcount;
privateICountService.Stub serviceBinder =newICountService.Stub() {
@Override
publicintgetCount() throwsRemoteException {
returncount;
}
};
@Override
publicIBinder onBind(Intent intent) {
returnserviceBinder;
}
@Override
publicvoidonCreate() {
super.onCreate();
newThread(newRunnable() {
@Override
publicvoidrun() {
while(!threadDisable) {
try{
Thread.sleep(1000);
} catch(InterruptedException e) {
}
count++;
Log.v("CountService", "Count is "+count);
}
}
}).start();
}
@Override
publicvoidonDestroy() {
super.onDestroy();
this.threadDisable =true;
Log.v("CountService", "on destroy");
}
}
配置文件AndroidManifest.xml和上面的类似,没有区别。
在Activity中使用服务的差别不大,只需要对ServiceConnection中的调用远程服务的方法时,要捕获异常。
privateServiceConnection serviceConnection =newServiceConnection() {
@Override
publicvoidonServiceConnected(ComponentName name, IBinder service) {
countService =(ICountService) service;
try{
Log.v("CountService", "on serivce connected, count is "
+countService.getCount());
} catch(RemoteException e) {
thrownewRuntimeException(e);
}
}
@Override
publicvoidonServiceDisconnected(ComponentName name) {
countService =null;
}
};
如果是另外的应用程序使用远程服务,需要做的是复制上面的aidl文件和相应的包构到应用程序中,其他调用等都一样。
编写传递复杂数据类型的远程服务远程服务往往不只是传递java基本数据类型。这时需要注意android的一些限制和规定:
这里将前面的例子中返回的int数据改为复杂数据类型:
packagecom.easymorse;
importandroid.os.Parcel;
importandroid.os.Parcelable;
publicclassCountBean implementsParcelable {
publicstaticfinalParcelable.Creator<CountBean>CREATOR =newCreator<CountBean>() {
@Override
publicCountBean createFromParcel(Parcel source) {
CountBean bean =newCountBean();
bean.count =source.readInt();
returnbean;
}
@Override
publicCountBean[] newArray(intsize) {
returnnewCountBean[size];
}
};
publicintcount;
@Override
publicvoidwriteToParcel(Parcel dest, intflags) {
dest.writeInt(this.count);
}
@Override
publicintdescribeContents() {
return0;
}
然后,需要在相同包下建一个同名的aidl文件,用于android生成相应的辅助文件:
packagecom.easymorse;
parcelable CountBean;
http://www.anddev.org/viewtopic.php?p=20991
然后,需要在服务的aidl文件中修改如下:
packagecom.easymorse;
importcom.easymorse.CountBean;
interfaceICountService {
CountBean getCount();
}
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。