Android开源项目之Music (二)--- AIDL实现IPC进程间通讯

根据上一篇  Android开源项目之Music (一)--- MusicBrowserActivity 提到的,要继续往下分析源代码的话,需要具备AIDL的知识,如果有这方面知识的大神们请自行跳过,废话不多说,现在就进行AIDL的简单学习


一.什么是AIDL,AIDL的作用

AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。

由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。
  通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。

  如果有做过C#方面开放的,我想做个比喻,个人觉得这个AIDL很像C#里面提到的dll, 在“服务器”端有一份,在“客户端”也有一份,然后就可以调用了

(不知道这种比喻对不对,勿喷!)


二.AIDL的工作原理解析(直接写个demo)

1.居然要写远程服务进行进程间通信的话,那我们先在workspace下面建立两个工程,分别为AIDLServer和AIDLClient

2.在AIDLServer的包com.example.aidlserver下new一个文件IMyMusicPlayerService.aidl,写入以下代码.

package com.example.aidlserver;//注意一定要加入这个包名
interface IMyMusicPlayerService {//这个aidl的写法就是写一个interface一样,但是不需要加入修饰符public什么的
 void play();//播放音乐
 void stop();}

 

Ctrl + S 保存后 ADT 会根据这个IMyMusicPlayerService.aidl文件自动生成IMyMusicPlayerService.java文件。如同R.java文件一样在“gen/包名”下,代码是自动生成的,不要手动修改这个文件

3.由于我们要分析的是Music项目源码,所以我们搞一个专门用于播放音乐的Service吧.

这里首先需要有点Service和Binder的知识,在生成的.java文件中有个静态类

/** Local-side IPC implementation stub class. */public static abstract class Stubextends android.os.Binderimplementscom.example.aidlserver.IMyMusicPlayerService

Sub类它继承于Binder,引用了IMyMusicPlayerService接口,一个类继承了Binder,如果远程进程获得了

这个类的对象,那么它的对象就可以被远程的进程使用。

4.在res文件夹下,建立一个raw文件夹,下面放一首.mp3歌曲。接下来让我们编写这个Service类,MyMusicPlayerService

package com.example.aidlserver;

import java.io.FileDescriptor;
import java.io.IOException;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/*
 * 播放音乐服务类
 * 2013年8月7日14:20:27
 * Author: 胖虎
 * http://blog.csdn.net/ljphhj
 * */
public class MyMusicPlayerService extends Service{

	//Logger TAG
	public static final String LogTAG = "com.example.aidlserver.MyMusicPlayerService";
	
	//android自带播放音乐类: android.media.MediaPlayer
	private MediaPlayer mPlayer = null;
	
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		if (mPlayer == null)
		{
			mPlayer = new MediaPlayer();
			//得到音乐资源
			FileDescriptor fildDescriptor = getResources().openRawResourceFd(R.raw.love).getFileDescriptor();
			
			try {
				//设置数据源
				mPlayer.setDataSource(fildDescriptor);
				//设置是否循环播放
				mPlayer.setLooping(true);
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalStateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return mBinder;
	}
	@Override
	public boolean onUnbind(Intent intent) {
		// TODO Auto-generated method stub
		if (mPlayer != null)
		{
			//释放资源
			mPlayer.release();
		}
		return super.onUnbind(intent);
	}
	/**
	 * 实现了接口中的两个函数
	 */
	private IBinder mBinder = new IMyMusicPlayerService.Stub() {
		
		@Override
		public void stop() throws RemoteException {
			// TODO Auto-generated method stub
			if (mPlayer.isPlaying())
			{
				//停止播放
				mPlayer.stop();
			}
		}
		
		@Override
		public void play() throws RemoteException {
			// TODO Auto-generated method stub
			try {
				if (!mPlayer.isPlaying())
				{
					mPlayer.prepare();
					mPlayer.start();
				}
			} catch (Exception e) {
				// TODO: handle exception
				Log.i(LogTAG, "play函数出现异常");
			}
		}
	};
	
}

并在AndroidManifest.xml文件中进行注册

        <service 
            android:name="com.example.aidlserver.MyMusicPlayerService"
            android:process=":remote"
            >
            <intent-filter >
                <action android:name="com.example.aidlserver.MyMusicPlayerService"/>
            </intent-filter>
        </service>

android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。(详细: http://galin.blog.sohu.com/170489657.html)
而intent-filter中这个action属性不可少,原因大家可以去看我之前的一篇博文:

浅谈Activity之启动方式(5种启动方式和隐式启动)


5.这样服务器端所要做的就做完了,现在编写客户端,客户端的话,需要得到服务器端的aidl文件,当然这个aidl所在的包是不能被改变的,也就是说我们需要把整个aidl放在一个包名依旧为com.example.aidlserver的包下才可以.


布局文件就写两个“Button”按钮,我就不发上来了,下面看看这个AIDLClientActivity.java文件

package com.example.aidlclient;

import com.example.aidlserver.IMyMusicPlayerService;

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.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
/**
 * AIDL demo 客户端
 * @author 胖虎
 *
 */
public class AIDLClientActivity extends Activity {

	//Logger TAG
	public static final String LogTAG = "com.example.aidlclient.AIDLClientActivity";
	// 服务端 AndroidManifest.xml中的service中intent-filter的action声明的字符串
	public static final String ACTION = "com.example.aidlserver.MyMusicPlayerService";
	
	private Button playButton = null;
	private Button stopButton = null;
	
	private IMyMusicPlayerService mService = null;
	private boolean isBinded = false;
	private ServiceConnection serviceConnection = new ServiceConnection() {
		
		/*Service断开连接时,调用*/
		@Override
		public void onServiceDisconnected(ComponentName name) {
		
			isBinded = false;
			mService = null;
		}
		/*Service连接时,调用*/
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			
			mService = IMyMusicPlayerService.Stub.asInterface(service);
			isBinded = true;
		}
	};
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_aidlclient);
		
		/*service绑定的知识,不了解的可以网上找找*/
		Intent intent = new Intent(ACTION);
		bindService(intent, serviceConnection, BIND_AUTO_CREATE);
		/*初始化View*/
		initComponent();
		
	}
	/*解除Service绑定(此demo中没调用)*/
	public void unBind()
	{
		if (isBinded)
		{
		unbindService(serviceConnection);
		mService = null;
		isBinded = false;
		}
	}
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.aidlclient, menu);
		return true;
	}
	
	private void initComponent()
	{
		playButton = (Button) findViewById(R.id.play);
		stopButton = (Button) findViewById(R.id.stop);
		playButton.setOnClickListener(buttonClickListener);
		stopButton.setOnClickListener(buttonClickListener);
	}
	private OnClickListener buttonClickListener = new OnClickListener() {
		
		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			switch (v.getId()) {
			case R.id.play:
			{
				if (mService != null)
				{
					try {
						mService.play();
					} catch (RemoteException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
				break;
			case R.id.stop:
			{
				if (mService != null)
				{
					try {
						mService.stop();
					} catch (RemoteException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
				break;
			default:
				break;
			}
		}
	};
}


总结一下所有的步骤:

  建立AIDL服务要比建立普通的服务复杂一些,具体步骤如下:
(1)在Eclipse Android工程的Java包目录中建立一个扩展名为aidl的文件。该文件的语法类似于Java代码,但会稍有不同。
(2)如果aidl文件的内容是正确的,ADT会自动生成一个Java接口文件(*.java)。
(3)建立一个服务类(Service的子类)。
(4)实现由aidl文件生成的Java接口。
(5)在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,<action>标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。
注意:Client调用的Server里面的类,因此这两个程序都需要在你的手机中安装,才可以实现调用



demo源码下载地址:

http://download.csdn.net/detail/u011133213/5885661


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值