Writing android Services

Keywords: Android

Links to the original text: https://my.oschina.net/fuyajun1983cn/blog/263932


Service is one of the four components of Android. It uses the underlying Binder mechanism to implement RPC mechanism. A service can exist either in an independent process or in an existing process. Services can be invoked either by Active in the same process or by an Activity in a different process. This paper mainly discusses how to write a remote service interface. For demonstration purposes, this article does not introduce the process of automatically generating service interface code through AIDL language.

First, we define interfaces:

public interface IRemoteService extends IInterface {

	/**
	 *  serviceMethod
	 */
	 public void serviceMethod(int arg0, float arg1, long arg2, boolean arg3, double arg4, String arg5) throws RemoteException;
			
}

It should be noted that, due to the constraints of Binder mechanism, in addition to the basic data types, the Parceable interface must be implemented for the customized data types in order to be used as the parameters of service methods. This article will not consider the case where custom types are passed as parameters.

The next focus is to implement the service interface defined above. Here, in order to support the same process and activities of different processes can call the service interface, we need two interface implementations, one is local implementation, the other is remote implementation. Before introducing these two implementation codes, let's look at the implementation of the corresponding RemoteService class:

public RemoteService extends Service {

	    @Override
		public void onCreate() {

		}
		
		@Override
		public void onDestroy() {
		
        }
	
		@Override
		public IBinder onBind(Intent intent) {
			// Select the interface to return.  If your service only implements
			// a single interface, you can just return it here without checking
			// the Intent.
			if (IRemoteService.class.getName().equals(intent.getAction())) {
				return mBinder;
			}
			return null;
		}
		
		/**
		 * The IRemoteInterface is defined through IDL
		 */
		private final RemoteServiceStub mBinder = new RemoteServiceStub() {
			public void serviceMethod(int arg0, float arg1, long arg2, boolean arg3, double arg4, String arg5) 
			{
				//do something here
			}
		};
}

In Android App development, defining your own services needs to be derived from the Service class. Starting a service can be done either by startService or by bindService. For the way to call a service interface through bindService, you also need to implement the onBind() method, which returns the IBinder corresponding to what we will do next. The service implementation class introduced.

First, the local interface implementation class, the code is as follows:

/** Local-side IPC implementation stub class. */
public abstract class RemoteServiceStub extends Binder implements	IRemoteService {
		private static final String DESCRIPTOR = "com.fyj.test.IRemoteService";

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

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

		public IBinder asBinder() {
			return this;
		}

		@Override
		public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
				throws RemoteException {
			switch (code) {
			case INTERFACE_TRANSACTION: {
				reply.writeString(DESCRIPTOR);
				return true;
			}
			case TRANSACTION_methodService: {
				data.enforceInterface(DESCRIPTOR);
				int _arg0;
				_arg0 = data.readInt();
				float _arg1;
				_arg1 = data.readFloat();
				long _arg2;
				_arg2 = (0!=data.readLong());
				boolean _arg3;
				_arg3 = data.readInt()?true:false;
				double _arg4;
				_arg4 = data.readDouble();
				String _arg5;
				_arg5 = data.readString();
				this.serviceMethod(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
				reply.writeNoException();
				return true;
			}
			}
			return super.onTransact(code, data, reply, flags);
		}

		 public void serviceMethod(int arg0, float arg1, long arg2, boolean arg3, double arg4, String arg5) throws RemoteException;
		
		static final int TRANSACTION_methodService = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

It is implemented as an abstract class, delaying the implementation of the interface to RemoteService. In this class, we mainly implement the onTransact() method, which handles the customer's call request. The asInterface() interface will automatically determine whether the current call is local or remote, and if it is remote, the service will be invoked using the interface of an instance of the RemoteServiceProxy class. Its implementation code is as follows:

/** Local-side IPC implementation stub class. */
public class RemoteServiceProxy implements IRemoteService {

	private static final String DESCRIPTOR = "com.fyj.test.IRemoteService";
	private IBinder mRemote;

	RemoteServiceProxy(IBinder remote) {
		mRemote = remote;
	}

	public IBinder asBinder() {
		return mRemote;
	}

	public String getInterfaceDescriptor() {
		return DESCRIPTOR;
	}
	
	 public void serviceMethod(int arg0, float arg1, long arg2, boolean arg3, double arg4, String arg5) throws RemoteException
	 {
		Parcel _data = Parcel.obtain();
		Parcel _reply = Parcel.obtain();
		try {
			_data.writeInterfaceToken(DESCRIPTOR);
			_data.writeInt(anInt);
			_data.writeFloat(aFloat);
			_data.writeLong(aLong);
			_data.writeInt(((aBoolean)?(1):(0)));
			_data.writeDouble(aDouble);
			_data.writeString(aString);
			mRemote.transact(TRANSACTION_methodService, _data, _reply, 0);
			_reply.readException();
		}
		finally {
		_reply.recycle();
		_data.recycle();
		}
	 }
	
	
		static final int TRANSACTION_methodService = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

This class is a proxy class, transparent to the caller of the service.


In the process of invoking bindService, we need to pass a Service Connection object, which will receive the object of the service class after the service connection is successful, so that we can judge whether it should be implemented by the local service class or by the remote service class. We will analyze the specific implementation details in another topic. The following is the code for creating the ServiceConnection object implementation:

private ServiceConnection mConnection = new ServiceConnection() {

		public void onServiceConnected(ComponentName className, IBinder service) {
			// TODO Auto-generated method stub
			mService = RemoteServiceStub.asInterface(service);
			...
		}

		public void onServiceDisconnected(ComponentName className) {
			// TODO Auto-generated method stub
			mService = null;
			...
		}

	};





Reproduced in: https://my.oschina.net/fuyajun1983cn/blog/263932

Posted by Cleanselol on Tue, 17 Sep 2019 01:25:38 -0700