您的位置:首页 > 大数据 > 人工智能

AIDL

2015-12-28 18:09 351 查看
AIDL介绍

AIDL表示“Android接口定义语言”:Android Interface Definition Language.

使用AIDL定义的接口,会被开发工具生成为可实现远程访问的接口。

进程间通信模式是组件间通信的“高级”应用方式,与一般组件间通信的区别在于:接口需要使用AIDL定义。

由于存在2个应用程序进行通信,因此,提供服务的应用程序通常被称之为“服务端”,而调用它的

接口的方法的应用程序则被称之为“客户端”。

AIDL接口:

使用java中创建接口的方式即可创建AIDL的接口,区别在于:

AIDL的接口的原文件扩展名为.aidl,一般的接口的源文件的扩展名为java。

AIDL的接口不允许使用public或任何访问权限修饰符去修饰该接口,它

默认即被修饰为public。

在创建了AIDL接口后,开发工具会在gen下生成匹配的J***A源文件。

使用AIDL绑定远程Service

在开发工具生成的AIDL接口的J***A源文件中,定义了名为stub的内部类,

继承了android.os.Binder,并实现了AIDL的接口,因此,开发人员在创建Service中的IBinder

的实现类时,仅需继承该Stub类即可。

由开发人员定义的AIDL接口中的抽象方法,都被定义为抛出远程异常(android.os.RemoteException),

在实现这些抽象方法时需要处理。

在AIDL接口的Stub内部类中,存在asInterface(IBinder obj)方法,用于将IBinder对象

转换为AIDL接口的实现类对象,因此,在绑定Service的组件中,可通过该方法得到AIDL接口的实现类对象。

使用AIDL与一般绑定服务的区别

使用AIDL与一般绑定服务的区别:

【服务端】使用AIDL接口

【服务端】创建AIDL接口的内部类Stub的子类对象,并将其作为Service中

onBind()的返回值;

【服务端】必须配置Service是可以隐式激活的

【客户端】将服务端的AIDL文件复制到客户端

【客户端】在ServiceConnection的onServiceConnected()中,使用AIDL接口的

Stub的asinterface()方法,将参数IBinder转换为AIDL接口的对象。

服务端开发步骤:

开发AIDL接口源文件,并确保扩展名是.aidl(如果先创建的*.java源文件后改名,则需刷新)

在Service中开发AIDL接口的实现类,并实现AIDL接口中定义的抽象方法;

实现Service类中的onBind()方法,并将AIDL的实现类的对象作为该方法的返回值

注册Service类,并配置<intent-filter>

部署服务端应用程序到设备上

客户端开发步骤

1、将服务端的AIDL相关文件复制到客户端中,注意:相关文件的包名、类名是不允许修改的

2、开发ServiceConnection接口中的实现类

3、使用隐式意图绑定服务端的Service

4、在ServiceConnection接口中的实现类的onServiceConnected()方法中,通过AIDL的内部类Stub的asInterface()方法

,将IBinder对象转换为AIDL接口的对象。

一个简单的小案例:

服务端:

MainActivity:

package com.edu.hpu.service;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class MainActivity extends Activity {

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

package com.edu.hpu.service;

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

public class PlayerService extends Service{

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		Log.d("AIDL","服务端:PlayerService onCreate");
		super.onCreate();
	}
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		Log.d("AIDL","服务端:PlayerService onBind");
		return new InnerBinder();
	}

	 private class InnerBinder extends PlayerAIDL.Stub{

		@Override
		public void playMusic() throws RemoteException {
			// TODO Auto-generated method stub
			Log.d("AIDL","服务端:PlayerService playMusic");
		}

		@Override
		public void pauseMusic() throws RemoteException {
			// TODO Auto-generated method stub
			Log.d("AIDL","服务端:PlayerService pauseMusic");
		}
		 
	 }
}


PlayerAIDL.aidl

package com.edu.hpu.service;

interface PlayerAIDL {
     void playMusic();
     void pauseMusic();
}


布局没有任何改变,初始的即可,

需要在AndroidMainfest.xml中配置信息:

<service
         android:name="com.edu.hpu.service.PlayerService">
        <intent-filter>
            <action android:name="com.edu.hpu.service.intent.action.REMOTE_PLAYER_SERVICE"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
    </service>
应用端:

需要将相对应的aidl包跟文件都复制到应用端,

MainActivity:

package com.example.receiveservice;

import com.edu.hpu.service.PlayerAIDL;

import android.app.Activity;
import android.content.ComponentName;
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.Menu;
import android.view.MenuItem;

public class MainActivity extends Activity {

	private ServiceConnection conn = null;
	PlayerAIDL player;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		Log.i("AIDL","客户端:MainActivity.onCreate");
		conn = new InnerServiceConnection();
		Intent service = new Intent("com.edu.hpu.service.intent.action.REMOTE_PLAYER_SERVICE");
		bindService(service,conn,BIND_AUTO_CREATE);
	}
	private class InnerServiceConnection implements ServiceConnection{

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			Log.i("AIDL","客户端:MainActivity.onServiceConnected");
			
			player = PlayerAIDL.Stub.asInterface(service);
			try {
				player.playMusic();
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				player.pauseMusic();
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub
			
		}
		
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		unbindService(conn);
		super.onDestroy();
	}
}


以上的就是这个简单的小项目,先将服务端配置到虚拟器上,然后将客户端配置到虚拟器上,运行成功,就会在客户端打印出服务端的信息。

注意:

绑定远程Service时,组件的相关方法执行顺序:

1、【客户端】绑定远程Service,即调用bindService()方法;

2、【服务端】Service的生命周期开始,即onCreate()->onBind();

3、【客户端】ServiceConnection中onServiceConnected()方法被回调。

在客户端执行bindService()时,由于服务端的Service尚未启动,更没有返回IBinder

对象,则客户端中onServiceConnected()方法还没有被回调,因此,直接在客户端bindService()后马上

调用远程服务可能导致NullPointerException.

使用AIDL远程播放音乐

服务端:

MainActivity:

package com.example.service1;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends Activity {

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


Service:

package com.example.service1;

import java.io.IOException;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;

public class PlayerService extends Service {
	private MediaPlayer player;
	private int Current;
	private String[] musics = new String[] {
			Environment.getExternalStorageDirectory().getAbsolutePath()
					+ "/Music/number1.mp3",
			Environment.getExternalStorageDirectory().getAbsolutePath()
					+ "/Music/number2.mp3",
			Environment.getExternalStorageDirectory().getAbsolutePath()
					+ "/Music/number3.mp3" };
	private int currentMusicIndex = 0;

	@Override
	public void onCreate() {

		// TODO Auto-generated method stub
		player = new MediaPlayer();
		player.setOnCompletionListener(new OnCompletionListener() {
			
			@Override
			public void onCompletion(MediaPlayer mp) {
				// TODO Auto-generated method stub
				doNext();
			}
		});
		super.onCreate();
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return new InnerBinder();
	}

	private void doPlay() {
		player.reset();
		try {
			player.setDataSource(musics[currentMusicIndex]);
			player.prepare();
			player.seekTo(Current);
			player.start();
			Current = 0;
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException 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();
		}
	}

	private void doPause() {
		if (player.isPlaying()) {
			Current = player.getCurrentPosition();
			player.pause();
		}
	}

	private void doPrevious() {
		currentMusicIndex--;
		if (currentMusicIndex < 0) {
			currentMusicIndex = musics.length - 1;
		}
		Current = 0;
		doPlay();
	}

	private void doNext() {
		currentMusicIndex++;
		if (currentMusicIndex >= musics.length) {
			currentMusicIndex = 0;
		}
		Current = 0;
		doPlay();
	}

	private class InnerBinder extends PlayerAIDL.Stub {

		@Override
		public void play() throws RemoteException {
			// TODO Auto-generated method stub
               doPlay();
		}

		@Override
		public void pause() throws RemoteException {
			// TODO Auto-generated method stub
              doPause();
		}

		@Override
		public void previous() throws RemoteException {
			// TODO Auto-generated method stub
              doPrevious();
		}

		@Override
		public void next() throws RemoteException {
			// TODO Auto-generated method stub
             doNext();
		}

	}
	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		player.release();
		player = null;
		super.onDestroy();
	}

}


需要提前将音乐添加到虚拟机中,我在这里边添加了三首歌曲,

PlayerAIDL.aidl

package com.example.service1;

interface PlayerAIDL {
      void play();
      void pause();
      void previous();
      void next();
}


布局里边不用修改,就按照默认的就可以,

重要的一步,在AndroidMainfest.xml中配置Service:

<service android:name="com.example.service1.PlayerService">
            <intent-filter>
                <action android:name="com.example.service1.REMOTE_PLAYER_SERVICE_1"/>
                 <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>


应用端:

相应的,需要将服务端的相对应的.aidl所在的包名以及文件都拷贝到应用端,这里不再做介绍,

MainActivity:

package com.example.service2;

import com.example.service1.PlayerAIDL;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class MainActivity extends Activity {

	private InnerServiceConnection conn;
	private PlayerAIDL player;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		conn = new InnerServiceConnection();
		Intent service = new Intent(
				"com.example.service1.REMOTE_PLAYER_SERVICE_1");
		bindService(service, conn, BIND_AUTO_CREATE);
	}

	public void doPlay(View view) {
            try {
				player.play();
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}

	public void doPause(View view) {
       try {
		player.pause();
	} catch (RemoteException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	}

	public void doPrevious(View view) {
                try {
					player.previous();
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
	}

	public void doNext(View view) {
           try {
			player.next();
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private class InnerServiceConnection implements ServiceConnection {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			player = PlayerAIDL.Stub.asInterface(service);

		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub

		}

	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		unbindService(conn);
		super.onDestroy();
	}
}


布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
     >

    <ImageButton
        android:id="@+id/pause"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/play"
        android:layout_marginLeft="58dp"
        android:onClick="doPause"
        android:layout_toRightOf="@+id/play"
        android:src="@android:drawable/ic_media_pause" />

    <ImageButton
        android:id="@+id/play"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:onClick="doPlay"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="87dp"
        android:layout_marginTop="36dp"
        android:src="@android:drawable/ic_media_play" />

    <ImageButton
        android:id="@+id/previous"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/play"
        android:onClick="doPrevious"
        android:layout_below="@+id/play"
        android:layout_marginTop="49dp"
        android:src="@android:drawable/ic_media_previous" />

    <ImageButton
        android:id="@+id/next"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/pause"
        android:onClick="doNext"
        android:layout_alignTop="@+id/previous"
        android:src="@android:drawable/ic_media_next" />

</RelativeLayout>
AndroidMainfest.xml中的相关信息无需配置,直接将服务端的应用部署到虚拟机上边,然后将应用端的应用部署到虚拟机上边,运行应用端的应用,就会实现一个跨应用程序获取Service的一个能够进行歌曲播放、暂停、上一曲、下一曲的功能。

AIDL中的数据类型

在AIDL接口中,如需使用以下数据类型,无需导包语句即可直接使用:

J***A的基本数据类型,包括:byte,short,int,long,float,double,char,boolean.

String与CharSequence。

List与Map。

如需使用自定义的数据类型,或List,Map中封装自定义数据类型的数据,则要求:

1、自定义的数据类型实现Parcelable接口i;

2、编写自定义数据类型的*.aidl文件;

3、无论自定义数据类型与AIDL接口文件是否在同一个包中,在使用自定义数据类型时,都需在AIDL接口文件中

显式的使用import语句导入;

4、将自定义数据类型的*.java源文件与*.aidl源文件复制到客户端。

自定义数据类型的*.aidl文件:

需存在与*.java源文件相同的package语句;

添加parcelable类名;语句。

Parcelable接口:基于任何复合数据类型都是基本数据类型封装后的集合,因此,各种复合数据类型都可以被拆分为

基本数据类型进行传输。

parcelable接口约定了数据被序列化传输的方式。

在parcelable接口中,定义了一下抽象方法:

public int describeContents()(用于内容接口的描述)

public void writeToParcel(Parcel dest,int flags)(writeToParcel()方法用于将当前类的成员写入到Parcel

容器中,重写该方法时,调用Parcel类的write系列方法即可,例如writeInt(),writeString()等。)

通常开发人员仍自定义readFromParcel(Parcel parcel)方法,以对应必须重写的writeToParcel()方法,用于从Parcel容器中

读取数据。

读取数据时,顺序必须与写入数据完全相同!

public void writeToParcel(Parcel parcel,int flags){
	       parcel.writeInt(age);
	       parcel.writeString(name);
	       parcel.writeString(from);

	   }
	   public void readFromParcel(Parcel parcel){
	       this.age = parcel.readInt();
	       this.name = parcel.readString();
	       this.from = parcel.readString();
	   }


必须创建静态常量CREATOR,实现Parcelable.Creator<T>接口:

该常量必须被修饰为public static final ,且常量名必须为

CREATOR;

重写接口中的抽象方法。

使用AIDL访问自定义类型数据

功能:服务端提供getStudentList()方法,返回List<Student>类型数据。

Student类型中至少封装3个属性,且至少其中2个属性的数据类型是相同的,

例如2个String类型和1个int类型。

客户端可获取服务端提供的数据

下面的是相对应的案例:

服务端:

MainActivity:

package com.example.servicemen;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends Activity {

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


Student.java

package com.example.servicemen;

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

public class Student implements Parcelable{
    private String name;
    private String from;
    private int age;
    
    
    public Student(String name, String from, int age) {
		this.name = name;
		this.from = from;
		this.age = age;
	}
    
    

	@Override
	public String toString() {
		return "Student [name=" + name + ", from=" + from + ", age=" + age
				+ "]";
	}

	public Student(Parcel source) {
    	// TODO Auto-generated constructor stub
    	readFromParcel(source);
    }
	
	
    public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getFrom() {
		return from;
	}

	public void setFrom(String from) {
		this.from = from;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>(){

		@Override
		public Student createFromParcel(Parcel source) {
			// TODO Auto-generated method stub
			return new Student(source);
		}

		@Override
		public Student[] newArray(int size) {
			// TODO Auto-generated method stub
			return new Student[size];
		}
    	
	};

	@Override
	public int describeContents() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		// TODO Auto-generated method stub
		dest.writeString(name);
		dest.writeString(from);dest.writeInt(age);
		
	}
	
	private void readFromParcel(Parcel from){
		this.name = from.readString();
		this.from = from.readString();
		this.age = from.readInt();
	}

}


Service:

package com.example.servicemen;

import java.util.ArrayList;
import java.util.List;

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

public class StudentService extends Service{

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return new InnerBinder();
	}
	
	private class InnerBinder extends StudentAIDL.Stub{

		@Override
		public List<Student> getStudentList() throws RemoteException {
			// TODO Auto-generated method stub
			List<Student> students = new ArrayList<Student>();
			students.add(new Student("asjdasd","fdghfg",30));
			students.add(new Student("ssasd","saafg",60));
			students.add(new Student("sliuliud","ililfg",90));
			return students;
		}
		
	}

}


Student.aidl:

package com.example.servicemen;

parcelable Student;


StudentAIDL.aidl:

package com.example.servicemen;

import com.example.servicemen.Student;
interface StudentAIDL {
     List<Student> getStudentList();
     
}
相应的需要在AndroidMainfest.xml中配置相关的信息:

<service 
            android:name="com.example.servicemen.StudentService">
            <intent-filter>
                <action android:name="com.example.servicemen.intent.action.STUDENT_ACTION"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>


服务端的布局使用默认的即可,不用做更改,

应用端:相应的需要将Student.sidl,StudentAIDL.aidl,Student.java及他们所在的包名都复制到客户端,

MainActivity:

package com.example.service4;

import java.util.List;

import com.example.servicemen.Student;
import com.example.servicemen.StudentAIDL;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends Activity {
  
	private ServiceConnection conn;
    private StudentAIDL service;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.activity_main);
		conn = new InnerServiceConnection();
		Intent service = new Intent("com.example.servicemen.intent.action.STUDENT_ACTION");
		bindService(service, conn, BIND_AUTO_CREATE);
		
	}
	
	private class InnerServiceConnection implements ServiceConnection{

		@Override
		public void onServiceConnected(ComponentName name, IBinder arg0) {
			// TODO Auto-generated method stub
			service = StudentAIDL.Stub.asInterface(arg0);
			try {
				List<Student> students = service.getStudentList();
			    for (Student student : students) {
					System.out.println("student" + student);
				}
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub
			
		}
		
	}
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
	    unbindService(conn);
		super.onDestroy();
	}
}


布局不用做任何的修改,先将服务端部署到虚拟机上,然后运行应用端,查看logcat,就会看到服务端的打印的信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: