Byron's log
‹‹ back
android service 精辟解说(摘)
Create Time : 2011-07-01 09:13:56
Modify Time : 2011-07-14 10:30:44

本地服务

所谓本地服务,其实就是完全服务于一个进程的组件。本地服务的这种特性决定了它有特别的启动方式。通常这类服务的典型案例,就是邮件轮询。
  • 调用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)。
客服端(RPClient)
  • 首先,你需要模拟一个目标服务相同的包(目录结构),并且将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);
    	}
    }
}
  • 华丽的去测试吧。