Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中的跨進程通信方法實例及特點分析(一):AIDL Service

Android中的跨進程通信方法實例及特點分析(一):AIDL Service

編輯:關於Android編程

 

最近有一個需求就是往程序中加入大數據的采集點,但是因為我們的Android程序包含兩個進程,所以涉及到跨進程通信的問題。現將Android中的跨進程通信方式總結如下。

Android中有4種跨進程通信方式,分別是利用AIDL Service、ContentProvider、Broadcast、Activity實現。

1.利用AIDL Service實現跨進程通信

這是我個人比較推崇的方式,因為它相比Broadcast而言,雖然實現上稍微麻煩了一點,但是它的優勢就是不會像廣播那樣在手機中的廣播較多時會有明顯的時延,甚至有廣播發送不成功的情況出現。

注意普通的Service並不能實現跨進程操作,實際上普通的Service和它所在的應用處於同一個進程中,而且它也不會專門開一條新的線程,因此如果在普通的Service中實現在耗時的任務,需要新開線程。

要實現跨進程通信,需要借助AIDL(Android Interface Definition Language)。Android中的跨進程服務其實是采用C/S的架構,因而AIDL的目的就是實現通信接口。

首先舉一個簡單的栗子。

服務端代碼如下:

首先是aidl的代碼:

package com.android.service;

interface IData
{
    int getRoomNum();
}

 

RoomService的代碼如下:

 

package com.android.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class RoomService extends Service{

	private IData.Stub mBinder=new IData.Stub() {
		
		@Override
		public int getRoomNum() throws RemoteException {
			 return 3008;
		}
	};

	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

}
AndroidManifest如下:

 

 




    

          
        
            
                
            
        
    

然後運行該Service所在的Project即可。

 

客戶端代碼如下:

注意客戶端也要有aidl文件,所以最簡單的辦法就是將Service端中aidl所在的包直接復制過去。另外要注意的是在onDestroy中要解除和Service的綁定。

MainActivity.java的代碼如下:

 

package com.example.aidlsampleclient;

import com.android.service.IData;

import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.util.Log;
import android.view.Menu;
import android.widget.Button;
import android.widget.Toast;
import android.view.View;

public class MainActivity extends Activity implements View.OnClickListener{

	
	private static final String TAG=MainActivity;
	private static final String ROOM_SERVICE_ACTION=com.aidl.service.room;
	
	private Button bindServiceButton;
	private Button getServiceButton;
	
	
	IData mData;
    
	private ServiceConnection conn=new ServiceConnection()
	{

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
		
			Log.i(TAG,----------------onServiceConnected--------);
			mData=IData.Stub.asInterface(service);
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
		
			Log.i(TAG,----------------onServiceDisconnected-------------);
			mData=null;
			
		}
		
	};
	
	private void initView()
	{
		bindServiceButton=(Button)findViewById(R.id.bindServiceButton);
		getServiceButton=(Button)findViewById(R.id.getServiceButton);
		bindServiceButton.setOnClickListener(this);
		getServiceButton.setOnClickListener(this);
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
	}
	
	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch(v.getId())
		{
		case R.id.bindServiceButton:
			bindService();
			break;
		case R.id.getServiceButton:
			getService();	
		     break;
		default:
			 break;
		}
	}
	
	
	private void bindService()
	{
		Intent intent=new Intent();
		intent.setAction(ROOM_SERVICE_ACTION);
		bindService(intent,conn,BIND_AUTO_CREATE);
	}
	
	private void getService()
	{
		try
		{
			if(mData!=null)
			{
				int roomNum=mData.getRoomNum();
				showLongToast(RoomNum:+roomNum);
			}
			
		}
		catch(RemoteException ex)
		{
			ex.printStackTrace();
		}
		
	}
	
	private void showLongToast(String info)
	{
		Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		unbindService(conn);
	}
}
activity_main.xml的代碼如下:

 

 

運行結果如下:

 

\

 

然後舉一個稍微復雜一點的栗子,注意如果*.aidl文件中含有自定義的對象,那麼該對象的類要實現Parcelable接口,並且要新建一個該類的aidl文件,否則會出現could not find import for class com.android.service.XX的錯誤,其中XX為類名。還是上面的栗子,但是aidl文件中添加了一些新的方法。仍以上面的RoomService為例,

Service端的代碼如下:

Room類的代碼為:

 

package com.android.service;

import android.os.Parcel;
import android.os.Parcelable;

public class Room implements Parcelable{

	//房間號
	private int roomNum;
	//房間大小
	private float roomSpace;
	//是否有空調
	private boolean hasAirConditioner;
	//是否有Wifi
	private boolean hasWifi;
	//房間內的裝飾風格
	private String decorativeStyle;
	
	public static final Parcelable.CreatorCREATOR=new Parcelable.Creator() {

		@Override
		public Room createFromParcel(Parcel source) {
			return new Room(source);
		}

		@Override
		public Room[] newArray(int size) {
			// TODO Auto-generated method stub
			return null;
		}
		
	};
	
	public Room(int roomNum,float roomSpace,boolean hasAirConditioner,boolean hasWifi,String decorativeStyle)
	{
		this.roomNum=roomNum;
		this.roomSpace=roomSpace;
		this.hasAirConditioner=hasAirConditioner;
		this.hasWifi=hasWifi;
		this.decorativeStyle=decorativeStyle;
	}
	
	private Room(Parcel source)
	{
		roomNum=source.readInt();
		roomSpace=source.readFloat();
		boolean[]tempArray=new boolean[2];
		source.readBooleanArray(tempArray);
	   hasAirConditioner=tempArray[0];
	   hasWifi=tempArray[1];
	   decorativeStyle=source.readString();	
	}
	
	@Override
	public String toString()
	{
		StringBuilder sb=new StringBuilder();
		sb.append(Basic info of room is as follows:
);
		sb.append(RoomNum:+roomNum+
);
		sb.append(RoomSpace:+roomSpace+
);
		sb.append(HasAirConditioner:+hasAirConditioner+
);
		sb.append(HasWifi:+hasWifi+
);
		sb.append(Decorative Style:+decorativeStyle);
		return sb.toString();
		
	}
	
	@Override
	public int describeContents() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest,int flags) {
		dest.writeInt(roomNum);
		dest.writeFloat(roomSpace);
		dest.writeBooleanArray(new boolean[]{hasAirConditioner,hasWifi});
		dest.writeString(decorativeStyle);		
	}

}

Room的聲明為:

 

 

package com.android.service;
parcelable Room;

IRoom.aidl的代碼為:

 

 

package com.android.service;
import com.android.service.Room;

interface IRoom
{
  Room getRoom();
}
RoomService的代碼為:

 

 

package com.android.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

public class RoomService extends Service{

	private IRoom.Stub mBinder=new IRoom.Stub() {
		
		@Override
		public Room getRoom() throws RemoteException {
			Room room=new Room(3008,23.5f,true,true,IKEA);
			return room;		
		}
	};
	
	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

}
由於AndroidManifest.xml的代碼不變,因而此處不再貼出。下面是客戶端的代碼:

 

 

package com.example.aidlsampleclient;
import com.android.service.IRoom;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.util.Log;
import android.view.Menu;
import android.widget.Button;
import android.widget.Toast;
import android.view.View;

public class MainActivity extends Activity implements View.OnClickListener{

	
	private static final String TAG=MainActivity;
	//private static final String SERVICE_ACTION=com.aidl.service.data;
	private static final String ROOM_SERVICE_ACTION=com.aidl.service.room;
	
	private Button bindServiceButton;
	private Button getServiceButton;
	
	
	IRoom mRoom;
    
	private ServiceConnection conn=new ServiceConnection()
	{

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
		
			Log.i(TAG,----------------onServiceConnected--------);
			showLongToast(onServiceConnected);
			mRoom=IRoom.Stub.asInterface(service);
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
		
			Log.i(TAG,----------------onServiceDisconnected-------------);
			mRoom=null;
			
		}
		
	};
	
	private void initView()
	{
		bindServiceButton=(Button)findViewById(R.id.bindServiceButton);
		getServiceButton=(Button)findViewById(R.id.getServiceButton);
		bindServiceButton.setOnClickListener(this);
		getServiceButton.setOnClickListener(this);
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
	}
	
	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch(v.getId())
		{
		case R.id.bindServiceButton:
			bindService();
			break;
		case R.id.getServiceButton:
			getService();	
		     break;
		default:
			 break;
		}
	}
	
	
	private void bindService()
	{
		Intent intent=new Intent();
		intent.setAction(ROOM_SERVICE_ACTION);
		bindService(intent,conn,BIND_AUTO_CREATE);
	}
	
	private void getService()
	{
		if(mRoom!=null)
		{
			try
			{
				showLongToast(mRoom.getRoom().toString());
			}
			catch (RemoteException e) 
			{
				e.printStackTrace();
			}
		}

	}
	
	private void showLongToast(String info)
	{
		Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		unbindService(conn);
	}
}

注意首先仍然是要將Room,IRoom的代碼復制過去,否則會出錯。

 

運行結果如下:

\

顯然,客戶端已經成功讀取到服務信息。

注意,上面的所舉的栗子其實不只是跨進程,還是跨應用。要注意的是,跨應用一定跨進程,但是跨進程不一定是跨應用。對於跨應用的情況,利用AIDL基本上是較好的解決了問題,但也只是“較好”而已,實際上並不完美,比如,如果要增加一個服務,如果利用AIDL的話,那麼又要改寫aidl文件,如果是涉及自定義對象,則還要增加自定義對象的聲明,而且這種改變不只是Service端的改變,客戶端也要跟著改變,顯然這種解決方案不夠優雅。

那麼,有沒有更優雅的方法呢?

當然有,那就是利用Service的onStartCommand(Intent intent, int flags, int startId)方法。

服務端代碼如下:

 

package com.android.service;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

public class RoomService extends Service{

	private static final String TAG=RoomService;
	private static final int CLEAN_SERVICE=0x1;
	private static final int ORDER_SERVICE=0x2;
	private static final int PACKAGE_SERVICE=0x3;
	private static final String SERVICE_KEY=ServiceName;	
	@Override
	public void onStart(Intent intent, int startId) {
	   showLog(onStart);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		//String action=intent.getAction();
		Log.i(TAG,onStartCommand);
		
		int actionFlag=intent.getIntExtra(SERVICE_KEY, -1);
		switch(actionFlag)
		{
		case CLEAN_SERVICE:
			showShortToast(Start Clean Service Right Now);
			break;
		case ORDER_SERVICE:
			showShortToast(Start Order Service Right Now);
			break;
		case PACKAGE_SERVICE:
			showShortToast(Start Package Service Right Now);
			break;
		default:
			break;
		}
		return super.onStartCommand(intent, flags, startId);
	}

    private void showLog(String info)
    {
    	Log.i(TAG,info);
    }
    
    private void showShortToast(String info)
    {
    	Toast.makeText(getBaseContext(), info, Toast.LENGTH_SHORT).show();
    }
	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		showLog(onDestroy);
		super.onDestroy();
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		showLog(onCreate);
		super.onCreate();
	}


	@Override
	public IBinder onBind(Intent intent) {
		showLog(onBind);
		return null;
	}

	@Override
	public boolean onUnbind(Intent intent) {
		showLog(onUnbind);
		return super.onUnbind(intent);
	}
	
}
客戶端代碼如下:

 

 

package com.example.aidlsampleclient;
import com.android.service.IRoom;
import com.android.service.RoomService;

import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.util.Log;
import android.view.Menu;
import android.widget.Button;
import android.widget.Toast;
import android.view.View;

public class MainActivity extends Activity implements View.OnClickListener{

	private static final String TAG=MainActivity;
	private static final String ROOM_SERVICE_ACTION=com.aidl.service.room;
	
	private static final int CLEAN_SERVICE=0x1;
	private static final int ORDER_SERVICE=0x2;
	private static final int PACKAGE_SERVICE=0x3;
	
	private static final String SERVICE_KEY=ServiceName;
	
	private Button cleanButton;
	private Button orderButton;
	private Button packageButton;
	
	private void initView()
	{
		cleanButton=(Button)findViewById(R.id.cleanButton);
		orderButton=(Button)findViewById(R.id.orderButton);
		packageButton=(Button)findViewById(R.id.packageButton);
	
		cleanButton.setOnClickListener(this);
		orderButton.setOnClickListener(this);
		packageButton.setOnClickListener(this);
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
	}
	
	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch(v.getId())
		{
		case R.id.cleanButton:
		    cleanAction();
		    break;
		case R.id.orderButton:
			orderAction();
			break;
		case R.id.packageButton:
			packageAction();
			break;
		default:
			 break;
		}
	}
		
	private void cleanAction()
	{
		startAction(ROOM_SERVICE_ACTION,CLEAN_SERVICE);
	}
	
	private void orderAction()
	{
		startAction(ROOM_SERVICE_ACTION,ORDER_SERVICE);
	}
	
	private void packageAction()
	{
		startAction(ROOM_SERVICE_ACTION,PACKAGE_SERVICE);
	}
	
	private void startAction(String actionName,int serviceFlag)
	{
		//Intent intent=new Intent(this,RoomService.class);
		Intent intent=new Intent();
		intent.setAction(actionName);
		intent.putExtra(SERVICE_KEY, serviceFlag);
		this.startService(intent);
	}
	
	private void showLongToast(String info)
	{
		Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
	}
}
運行結果如下:

 

\
 

 

\


 

\

 

 

顯然,此時客戶端順利獲取了服務。

上面舉的是跨應用的例子,如果是在同一個應用的不同進程的話,則有更簡單的實現方法。

RoomService的代碼如下:

 

package com.android.service;

import com.android.actions.Actions;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

public class RoomService extends Service{

	private static final String TAG=RoomService;
	@Override
	public void onStart(Intent intent, int startId) {
	   showLog(onStart);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		//String action=intent.getAction();
		Log.i(TAG,onStartCommand);
		String action=intent.getAction();
		if(Actions.CLEAN_ACTION.equals(action))
		{    
			showShortToast(Start Clean Service Right Now);
		}
		else if(Actions.ORDER_ACTION.equals(action))
		{
			showShortToast(Start Order Service Right Now);
		}
		else if(Actions.PACKAGE_ACTION.equals(action))
		{
			showShortToast(Start Package Service Right Now);
		}
		else
		{
			showShortToast(Wrong action);
		}
		return super.onStartCommand(intent, flags, startId);
	}

    private void showLog(String info)
    {
    	Log.i(TAG,info);
    }
    
    private void showShortToast(String info)
    {
    	Toast.makeText(getBaseContext(), info, Toast.LENGTH_SHORT).show();
    }
	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		showLog(onDestroy);
		super.onDestroy();
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		showLog(onCreate);
		super.onCreate();
	}


	@Override
	public IBinder onBind(Intent intent) {
		showLog(onBind);
		return null;
	}

	@Override
	public boolean onUnbind(Intent intent) {
		showLog(onUnbind);
		return super.onUnbind(intent);
	}
	
}

 

MainActivity的代碼如下:

 

package com.android.activity;
import com.android.activity.R;
import com.android.service.RoomService;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.widget.Button;
import android.widget.Toast;
import android.view.View;
import com.android.actions.Actions;

public class MainActivity extends Activity implements View.OnClickListener{

	private static final String TAG=MainActivity;

	private static final String SERVICE_KEY=ServiceName;
	
	private Button cleanButton;
	private Button orderButton;
	private Button packageButton;
	
	private void initView()
	{
		cleanButton=(Button)findViewById(R.id.cleanButton);
		orderButton=(Button)findViewById(R.id.orderButton);
		packageButton=(Button)findViewById(R.id.packageButton);
	
		cleanButton.setOnClickListener(this);
		orderButton.setOnClickListener(this);
		packageButton.setOnClickListener(this);
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
	}
	
	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch(v.getId())
		{
		case R.id.cleanButton:
		    cleanAction();
		    break;
		case R.id.orderButton:
			orderAction();
			break;
		case R.id.packageButton:
			packageAction();
			break;
		default:
			 break;
		}
	}
		
	private void cleanAction()
	{
		startAction(Actions.CLEAN_ACTION);
	}
	
	private void orderAction()
	{
		startAction(Actions.ORDER_ACTION);
	}
	
	private void packageAction()
	{
		startAction(Actions.PACKAGE_ACTION);
	}
	
	private void startAction(String actionName)
	{
		Intent intent=new Intent(this,RoomService.class);
		intent.setAction(actionName);
		this.startService(intent);
	}
	
	private void showLongToast(String info)
	{
		Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
	}
}
運行結果同上,此處不再貼圖。

 

那AIDL還有存在的必要嗎?當然有!最重要的一點是代價問題,從打印的log可看出,客戶端每調用一次Context.startService(Intent),Service就會重新執行一次onStartCommand---->onStart,而使用AIDL的話,綁定服務之後,不會重復執行onStart,顯然後者的代價更小。

最後再講一個Service的重點應用:前台Service,像我們經常用的天氣、音樂其實都利用了前台Service來實現。 實例到明天再補上吧。

轉載請注明出處:http://blog.csdn.net/bettarwang/article/details/40947481

 

 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved