android service 精辟解说(摘)
本地服务
所谓本地服务,其实就是完全服务于一个进程的组件。本地服务的这种特性决定了它有特别的启动方式。通常这类服务的典型案例,就是邮件轮询。
- 调用Context.startService()启动服务
package net.bpsky;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class DoudingService extends Service {
private NotificationManager notificationMgr;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate(){
super.onCreate();
notificationMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
displayNotificationMessage("Starting Background Service!");
Thread thr = new Thread(null,new ServiceWorker(),"BackgroundService");
thr.start();
}
class ServiceWorker implements Runnable{
public void run(){
// bla bla bla...
}
}
@Override
public void onDestroy(){
displayNotificationMessage("Stopping Background Service");
super.onDestroy();
}
@Override
public void onStart(Intent intent,int startId){
super.onStart(intent, startId);
}
private void displayNotificationMessage(String message){
Notification notification = new Notification(R.drawable.note,message,System.currentTimeMillis());
PendingIntent contentIntent = PendingIntent.getActivity(this,0,new Intent(this,FeedActivity.class),0);
notification.setLatestEventInfo(this, "background Service", message, contentIntent);
notificationMgr.notify(R.id.app_notification_id,notification);
}
}
远程服务
支持onBind()方法的,就是远程服务。远程服务并不是说一定要远程访问,而是支持(RPC,Remote Procedure Call,远程过程调用)。这类服务的典型案例就是应用程序之间执行通信。比如:路由服务,将信息转发至其它应用程序。远程服务最重要的一个特性就是需要AIDL(Android Interface Definition Language)向客户端定义自身。- 定义AIDL接口
- 调用Context.bindService()绑定调用
- 优势在于允许不同进程进行绑定调用
- 在使用bindService时是异步操作,目标服务如果未在后台运行,那么在链接过程中会被启动。
服务端(RPCService)
- 定义AIDL接口并不复杂,只要写个以aidl后缀的文件放在src/package/目录下,而系统就会自动生成一个文件在gen/package/目录下(这个功能是eclipse ADT 自行完成的)。
- 当生成了绑定文件后,我们需要写个一Service并实现接口(byron认为书中的例子是在Service中实现了一个内部类,并在内部类中实现了接口方法,再由Service.onBind将实例返回)
package net.bpsky.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class RPCService extends Service {
private static final String TAG = "RPCService";
public class RPCServiceImpl extends IRPCService.Stub{
@Override
public int getValue(String text) throws RemoteException{
Log.v(TAG,"getValue called for "+text);
return 5;
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.v(TAG,"onBind() called");
return new RPCServiceImpl();
}
@Override
public void onCreate(){
super.onCreate();
Log.v(TAG, "onCreate() called");
}
@Override
public void onDestroy(){
super.onDestroy();
Log.v(TAG,"onDestroy() called");
}
@Override
public void onStart(Intent intent,int startId){
super.onStart(intent,startId);
Log.v(TAG,"onStart() called");
}
}
- 因为服务本身是没有任何界面的,所以在本模拟中activity是多余的存在,有了服务当然还要在AndroidManifest.xml申明。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.bpsky.service"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="10" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".RPCServiceActivity"
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="RPCService">
<intent-filter >
<action android:name="net.bpsky.service.IRPCService" />
</intent-filter>
</service>
</application>
</manifest>
- 现在我们完成了一个服务端模拟推送中心。但它并没有任何作用,在本例中业务处理只是简单的在getValue方法中返回了int(5)。
- 首先,你需要模拟一个目标服务相同的包(目录结构),并且将AIDL文件复制在该包中,这样Client就获得了连接Service的契约(AIDL)。
- 接下来就要开始调用服务了。
package net.bpsky.client;
import net.bpsky.service.*;
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;
import android.widget.Toast;
public class RPClientActivity extends Activity {
protected static final String TAG = "Client";
private IRPCService serviceInstance = null;
private Button bindBtn;
private Button callBtn;
private Button unbindBtn;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bindBtn = (Button)findViewById(R.id.bindService);
bindBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v){
bindService(new Intent(IRPCService.class.getName()),serviceConn,Context.BIND_AUTO_CREATE);
bindBtn.setEnabled(false);
callBtn.setEnabled(true);
unbindBtn.setEnabled(true);
}
});
callBtn = (Button)findViewById(R.id.callService);
callBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v){
callService();
}
});
callBtn.setEnabled(false);
unbindBtn = (Button)findViewById(R.id.unbindService);
unbindBtn.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v){
unbindService(serviceConn);
bindBtn.setEnabled(true);
callBtn.setEnabled(false);
unbindBtn.setEnabled(false);
}
});
unbindBtn.setEnabled(false);
}
private ServiceConnection serviceConn = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name,IBinder service){
Log.v(TAG,"onServiceConnected() called");
serviceInstance = IRPCService.Stub.asInterface(service);
callService();
}
@Override
public void onServiceDisconnected(ComponentName name){
Log.v(TAG,"onServiceDisconnected() called");
serviceInstance = null;
}
};
private void callService(){
try{
int val = serviceInstance.getValue("test");
Toast.makeText(RPClientActivity.this, "Value from service is "+val, Toast.LENGTH_LONG).show();
}catch(RemoteException ee){
Log.e(TAG,ee.getMessage(),ee);
}
}
}
- 华丽的去测试吧。