Android 进程间通讯之AIDL

#前言 因为android系统中应用程序之间不能共享内存。所以,在不一样应用程序之间交互数据(跨进程通信)就稍微麻烦一些。html

在android SDK中提供了4种用于跨进程通信的方式。这4种方式正好对应于android系统中4种应用程序组件:Activity、Content Provider、Broadcast和Service。java

  • Activity能够跨进程调用其余应用程序的Activity;
  • Content Provider能够跨进程访问其余应用程序中的数据(以Cursor对象形式返回),固然,也能够对其余应用程序的数据进行增、删、改操 做;
  • Broadcast能够向android系统中全部应用程序发送广播,而须要跨进程通信的应用程序能够监听这些广播;
  • Service和Content Provider相似,也能够访问其余应用程序中的数据,但不一样的是,Content Provider返回的是Cursor对象,
  • Service返回的是Java对象,这种能够跨进程通信的服务叫AIDL服务。

#AIDL介绍android

##AIDL 是什么   AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成能够在Android设备上两个进程之间进行进程间通讯(interprocess communication, IPC)的代码。若是在一个进程中(例如Activity)要调用另外一个进程中(例如Service)对象的操做,就可使用AIDL生成可序列化的参数。   AIDL IPC机制是面向接口的,像COM或Corba同样,可是更加轻量级。它是使用代理类在客户端和实现端传递数据。 ##AIDL 的做用   因为每一个应用程序都运行在本身的进程空间,而且能够从应用程序UI运行另外一个服务进程,并且常常会在不一样的进程间传递对象。在Android平台,一个进程一般不能访问另外一个进程的内存空间,因此要想对话,须要将对象分解成操做系统能够理解的基本单元,而且有序的经过进程边界。   经过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工做。git

##选择AIDL的使用场合   github

官方文档特别提醒咱们什么时候使用AIDL是必要的:只有你容许客户端从不一样的应用程序为了进程间的通讯而去访问你的service,以及想在你的service处理多线程。   若是不须要进行不一样应用程序间的并发通讯(IPC),you should create your interface by implementing a Binder;或者你想进行IPC,但不须要处理多线程的,则implement your interface using a Messenger。不管如何,在使用AIDL前,必需要理解如何绑定service——bindService。多线程

#实践部分 Android Service是分为两种:并发

  本地服务(Local Service): 同一个apk内被调用   远程服务(Remote Service):被另外一个apk调用app

远程服务须要借助AIDL来完成。ide

下面学习一下 做者:Panda Fang 的例子 连接工具

这是篇不错的AIDL学习例子 下面用一个客户端Activity操做服务端Service播放音乐的实例演示AIDL的使用。

##服务端 新建一个Android工程,命名为AIDLPlayerserver。 包名为com.ylbf.aidlplayerserver在res下的raw文件夹里面放入一个音乐文件,我这里放入的是 TheFatRat - Monody (feat. Laura Brehm).mp3的一个片断。若是不存在raw这个文件夹就本身新建一个,命名为raw。这个文件夹在raw文件夹下,与layout文件夹平级。raw中的文件遵照标识符的命名规则,不要出现中文和空格,多个单词能够用下划线链接,我这里改成monody.mp3

###目录结构 这里写图片描述

###IRemoteServiice.aidl 文件 新建一个IRemoteServiice.aidl 文件,加入以下代码

package com.ylbf.aidlplayerserver;

interface IRemoteService {
 void play();
 void stop();
}

可见aidl文件的代码跟java的interface同样,可是aidl中不能加public等修饰符。Ctrl + S 保存后 ADT 会根据这个IRemoteService.aidl文件自动生成IRemoteService.java文件。如同R.java文件同样在gen/包名下,代码是自动生成的,不要手动修改。

接下来就是bound service的知识了。IRemoteService.java 中有一个Stub静态抽象类extends Binder implements IRemoteService。本身动手写一个PlayerService 用来播放音乐,播放音乐须要使用android.media.MediaPlayer类。

###PlayerService 代码以下

/**
 * @category 播放音乐的服务
 * @author ylbf
 * @version 2016-02-18 14:35:48
 */
public class PlayerService extends Service {
	public static final String TAG = "PlayerService";
	private MediaPlayer mPlayer;
	private IBinder mBinder = new IRemoteServiice.Stub() {

		@Override
		public void stop() throws RemoteException {
			try {
				if (mPlayer.isPlaying()) {
					mPlayer.stop();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		@Override
		public void play() throws RemoteException {
			try {
				if (mPlayer.isPlaying()) {
					return;
				}
				// start以前须要prepare。
				// 若是前面实例化mplayer时使用方法一,则第一次play的时候直接start,不用prepare。
				// 可是stop一次以后,再次play就须要在start以前prepare了。
				// 前面使用方法二 这里就简便了, 不用判断各类情况
				mPlayer.prepare();
				mPlayer.start();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	};

	@Override
	public IBinder onBind(Intent arg0) {
		Log.i(TAG, "service onbind");
		if (mPlayer == null) {
			// 方法一说明
			// 此方法实例化播放器的同时指定音乐数据源 ,若用此方法在,mPlayer.start()
			// 以前不需再调用mplayer.prepare()
			// 官方文档有说明 :On success, prepare() will already have been called and
			// must not be called again.
			// 译文:一旦create成功,prepare已被调用,勿再调用 。查看源代码可知create方法内部已经调用prepare方法。
			// 方法一开始
			// mPlayer = MediaPlayer.create(this, R.raw.monody);
			// 方法一结束

			// 方法二说明
			// 若用此方法,在mplayer.start() 以前须要调用mplayer.prepare()
			// 方法二开始
			mPlayer = new MediaPlayer();
			try {
				FileDescriptor fd = getResources().openRawResourceFd(R.raw.monody).getFileDescriptor(); // 获取音乐数据源
				mPlayer.setDataSource(fd); // 设置数据源
				mPlayer.setLooping(true); // 设为循环播放
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// 方法二结束
			Log.i(TAG, "player created");
		}
		return mBinder;
	}

	@Override
	public boolean onUnbind(Intent intent) {
		if (mPlayer != null) {
			mPlayer.release();
		}
		Log.i(TAG, "service onUnbind");
		return super.onUnbind(intent);
	}

}

###AndroidManifest文件 服务编写好之后,按照惯例在AndroidManifest.xml中加入声明,代码以下 须要加入的只是<service>...</service>那段,要注意的是 android:process=":remote"intent-filter

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ylbf.aidlplayerserver"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="21" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <service
            android:name=".PlayerService"
            android:process=":remote" >
            <intent-filter>
                <action android:name="com.ylbf.aidlplayerserver.PlayerService" />
            </intent-filter>
        </service>
    </application>

</manifest>

安装运行服务端到设备上,准备给客户端调用

安装运行日志

[2016-02-18 15:25:46 - AIDLPlayerserver] ------------------------------
[2016-02-18 15:25:46 - AIDLPlayerserver] Android Launch!
[2016-02-18 15:25:46 - AIDLPlayerserver] adb is running normally.
[2016-02-18 15:25:46 - AIDLPlayerserver] No Launcher activity found!
[2016-02-18 15:25:46 - AIDLPlayerserver] The launch will only sync the application package on the device!
[2016-02-18 15:25:46 - AIDLPlayerserver] Performing sync
[2016-02-18 15:25:46 - AIDLPlayerserver] Automatic Target Mode: Unable to detect device compatibility. Please select a target device.
[2016-02-18 15:25:50 - AIDLPlayerserver] Uploading AIDLPlayerserver.apk onto device 'xxxxxxxxxxxx'
[2016-02-18 15:25:50 - AIDLPlayerserver] Installing AIDLPlayerserver.apk...
[2016-02-18 15:25:53 - AIDLPlayerserver] Success!
[2016-02-18 15:25:53 - AIDLPlayerserver] \AIDLPlayerserver\bin\AIDLPlayerserver.apk installed on device
[2016-02-18 15:25:53 - AIDLPlayerserver] Done!

##客户端

新建一个Android工程,命名为AIDLPlayerClient,包名为com.ylbf.aidlplayerclient。将服务端放有aidl文件的包直接copy到客户端src目录下,保留包中的aidl文件,其余删除。 ###目录结构以下 这里写图片描述 ###MainActivity.java代码 编写MainActivity.java 代码以下

/**
 * 客户端控制界面
 * 
 * @author ylbf
 * @version 2016-02-18 15:44:45
 */
public class MainActivity extends Activity implements OnClickListener {
	public static final String TAG = "MainActivity";

	/**
	 * 服务端 AndroidManifest.xml中的intent-filter action声明的字符串
	 */
	public static final String ACTION = "com.ylbf.aidlplayerserver.PlayerService";
	private IRemoteServiice mService;
	private boolean isBinded = false;
	private Button playbtn, stopbtn;

	private ServiceConnection conn = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName name) {
			isBinded = false;
			mService = null;
		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			mService = IRemoteServiice.Stub.asInterface(service);
			isBinded = true;
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		doBind();
		initViews();
	}

	private void initViews() {
		playbtn = (Button) findViewById(R.id.button1);
		stopbtn = (Button) findViewById(R.id.button2);
		playbtn.setOnClickListener(this);
		stopbtn.setOnClickListener(this);
	}

	@Override
	protected void onDestroy() {
		doUnbind();
		super.onDestroy();
	}

	/**
	 * 绑定服务
	 */
	public void doBind() {
		Intent intent = new Intent(ACTION);
		bindService(intent, conn, Context.BIND_AUTO_CREATE);
	}

	/**
	 * 解绑服务
	 */
	public void doUnbind() {
		if (isBinded) {
			unbindService(conn);
			mService = null;
			isBinded = false;
		}
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == playbtn.getId()) {
			// play
			Log.i(TAG, "play button clicked");
			try {
				mService.play();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		} else {
			// stop
			Log.i(TAG, "stop button clicked");
			try {
				mService.stop();
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}
}

运行客户端到设备,按下按钮能够播放/中止 就能够了

#项目源代码 项目源代码

#参考连接 Android Service学习之AIDL, Parcelable和远程服务 2011-04-07;

相关文章
相关标签/搜索