天天看点

android之Service介绍之二 AIDL

首先描述下我们想要实现的内容,我们希望在一个应用中通过点击按钮,去操作另一个进程中应用的音乐播放功能。

android之Service介绍之二 AIDL

如图,我们点击“播放”时,系统就会去远程调用我们提供的一个service(与当前service不是同一个应用哦),然后操作service中的音乐播放,点击“停止”则会终止播放。想要重新播放的话,必须先点“销毁service”,再点播放按钮哦。(至于这里为什么要先点销毁按钮才能播放,完全是为了给大家展示下,远程调用service时,怎么去解绑service)。

       在这个例子中,我们用到了一个非常重要的概念,AIDL。

AIDL(android interface definition language)android接口描述语言,其主要的作用就是允许我们在不同的进程间进行通信。

      我们都知道每个应用程序都运行在各自的进程中,并且android平台是不允许不同进程间进行直接的对象数据等传递的。如果我们非要进行进程间的通讯,那么我们就必须将我们的数据对象分解成一个个微小的、可以被操作系统理解的数据单元,然后有序的通过进程边界的检查。

      如果让我们自己实现,那么这个过程都愁死一批批的程序人了。幸好android,为我们解决了这个问题,这就是AIDL出现的原因了。

      AIDL (Android Interface Definition Language)是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。

AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

     注意:AIDL只支持方法,不能定义静态成员,并且方法也不能有类似public等的修饰符;AIDL运行方法有任何类型的参数和返回值,在java的类型中,以下的类型使用时不需要导入包(import),基本数据类型、String、Map、List.当然为了避免出错,建议只要使用了,就导入包。

-------------------------------------------------------------------------------

使用AIDL的步骤:

服务端(提供服务):

第一步:定义一个*.aidl文件,该文件里是符合aidl语言规范的接口定义,里面定义了外部应用可以访问的方法。当我们保存该文件的时候,eclipse会自动为我们在gen文件夹下生成一个相应的java接口文件。例如RemoteServiceInterface.java

 第二步:定义一个自己的service,    并将其注册到androidManifest.xml文件中,例如:

<service android:name="MyRemoteService">

  <intent-filter>

  <action  android:name="cn.com.chenzheng_java.remote"/>

  </intent-filter>

 </service>

注意这里一定要提供一个intent-filter,我们的客户端进程就是通过该action访问到服务端进程的哦。

我们都知道,在实现自己的service时,为了其他应用可以通过bindService来和我们的service进行交互,我们都要实现service中的onBind()方法,并且返回一个继承了Binder的内部类;在这里,eclipse自动为我们生成的RemoteServiceInterface.java中有一个实现了Binder的内部类,RemoteServiceInterface.Stub。AIDL要求我们,在这里不能再直接去实现Binder类了,而是去实现,AIDL提供给我们的Stub类。 实现stub类的同时,AIDL还要求我们同时实现我们在接口中定义的各种服务的具体实现。至此为止,我们的服务端已经和我们的aidl文件绑定到一起了哦。

  客户端:

第一步:客户端要想使用该服务,肯定要先知道我们的服务在aidl文件中到底对外提供了什么服务,对吧?所以,第一步,我们要做的就是,将aidl文件拷贝一份到客户端的程序中(这里一定要注意,包路径要和服务端的保持一致哦,例如服务端为cn.com.chenzheng_java.remote.a.aidl,那么在客户端这边也应该是这个路径)。

第二步:我们都知道,想要和service交互,我们要通过bindService方法,该方法中有一个ServiceConnection类型的参数。而我们的主要代码便是在该接口的实现中。

第三步:在ServiceConnection实现类的onServiceConnected(ComponentName name, IBinder service)方法中通过类似remoteServiceInterface = RemoteServiceInterface.Stub.asInterface(service);方式就可以获得远程服务端提供的服务的实例,然后我们就可以通过remoteServiceInterface 对象调用接口中提供的方法进行交互了。(这里的关键是通过*.Stub.asInterface(service);方法获取一个aidl接口的实例哦)

我们前面在服务端中说过了,必须提供一个intent-filter来匹配请求是否合法,所以我们在客户端访问服务的时候,还必须传递包含了匹配action的Intent哦。

--------------------------------------------------------------------------------------

下边整体是代码:

 远程服务端:

android之Service介绍之二 AIDL

RemoteServiceInterface.aidl

package cn.com.chenzheng_java.remote;

/**AIDL的语法和Interface的语法稍微有些不同,

*它里面只能有方法,并且java中的那些修饰词如public等,在这里是不支持的.

*当我们在eclipse中添加一个以.aidl结尾的AIDL文件时,如果你的格式正确,那么

*在gen目录下,你就会看到系统根据你提供AIDL文件自动为你生成的相应的java类

*@author chenzheng_java

*/

interface RemoteServiceInterface {

void startMusic();

void stopMusic();

}

MyRemoteService.java

package cn.com.chenzheng_java.remote;

import java.io.IOException;

import android.app.Service;

import android.content.Intent;

import android.media.MediaPlayer;

import android.os.IBinder;

import android.os.RemoteException;

/**

* @description 远程service

* @author chenzheng_java

*

*/

public class MyRemoteService extends Service {

MediaPlayer mediaPlayer;

@Override

public IBinder onBind(Intent intent) {

return new MyBinder();

}

@Override

public void onCreate() {

/*

* MediaPlayer.create方法第一个参数实际上为context对象,这里我们直接传递service给它,

* 是因为service本身也是继承了context的。

*/

mediaPlayer = MediaPlayer.create(MyRemoteService.this, R.raw.aiweier);

super.onCreate();

}

/**

* @description 这里一定要注意,继承的不再是Binder,而是系统自动为我们生成的

* Binder的一个内部类,叫做Stub。我们通过继承Stub类,然后实现AIDL

* 中定义的方法,便等于对接口的方法进行了具体的实现。

* @author chenzheng_java

*

*/

private class MyBinder extends RemoteServiceInterface.Stub{

public void startMusic() throws RemoteException {

mediaPlayer.start();

}

public void stopMusic() throws RemoteException {

mediaPlayer.stop();

try {

mediaPlayer.prepare();

} catch (IllegalStateException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

RemoteServiceActivity.java

package cn.com.chenzheng_java.remote;

import android.app.Activity;

import android.os.Bundle;

/**

* 很多人不理解,这里面基本上什么也没实现,为什么还要提供呢?

* 其实原因是这样的,service代码我们写好了,也在配置文件中注册了,

* 这样,手机系统就会识别了吗?不是的哦,你至少得将该service所在的

* 应用运行一次才可以哦。要不手机怎么知道你添加了一个service啊,对吧!

* @author chenzheng_Java

*

*/

public class RemoteServiceActivity extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

}

androidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="cn.com.chenzheng_java.remote"

android:versionCode="1"

android:versionName="1.0">

<uses-sdk android:minSdkVersion="8" />

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name=".RemoteServiceActivity"

android:label="@string/app_name">

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

</activity>

<service android:name="MyRemoteService">

<intent-filter>

<action android:name="cn.com.chenzheng_java.remote"/>

</intent-filter>

</service>

</application>

</manifest>

客户端:

android之Service介绍之二 AIDL

activity代码:

package cn.com.chenzheng_java;

import android.app.Activity;

import android.content.ComponentName;

import android.content.Context;

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.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import cn.com.chenzheng_java.remote.RemoteServiceInterface;

/***

* @author chenzheng_java

* @description 通过当前activity去调用不同进程中的远程service

*/

public class LocalServiceActivity extends Activity implements OnClickListener {

String ACTION_NAME = "cn.com.chenzheng_java.remote";

boolean flag = false;

Button button_start ;

Button button_stop ;

Button button_destroy ;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.music);

button_start = (Button) findViewById(R.id.button1);

button_stop = (Button) findViewById(R.id.button2);

button_destroy = (Button) findViewById(R.id.button3);

button_start.setOnClickListener(this);

button_stop.setOnClickListener(this);

button_destroy.setOnClickListener(this);

}

RemoteServiceInterface remoteServiceInterface ;

private class MyServiceConnection implements ServiceConnection{

public void onServiceConnected(ComponentName name, IBinder service) {

remoteServiceInterface = RemoteServiceInterface.Stub.asInterface(service);

try {

Log.i("flag", flag+"");

if(flag){

Log.i("通知", "已经开始唱歌");

remoteServiceInterface.startMusic();

}else{

Log.i("通知", "已经停止唱歌");

remoteServiceInterface.stopMusic();

}

} catch (RemoteException e) {

e.printStackTrace();

}

}

public void onServiceDisconnected(ComponentName name) {

}

}

private MyServiceConnection serviceConnection = new MyServiceConnection();

public void onClick(View v) {

if(v == button_start){

flag = true;

Intent intent = new Intent(ACTION_NAME);

/**

* Context.BIND_AUTO_CREATE 当绑定service时,如果发现尚未create,那么就先create一个,然后绑定

*/

bindService(intent, serviceConnection ,Context.BIND_AUTO_CREATE);

}

if(v == button_stop){

Log.i("通知", "已经点击了停止按钮");

flag = false;

Intent intent = new Intent(ACTION_NAME);

bindService(intent, serviceConnection ,Context.BIND_AUTO_CREATE);

try {

remoteServiceInterface.stopMusic();

} catch (RemoteException e) {

e.printStackTrace();

}

}

if(v == button_destroy){

flag = false;

Intent intent = new Intent(ACTION_NAME);

bindService(intent, serviceConnection ,Context.BIND_AUTO_CREATE);

unbindService(serviceConnection);

}

}

}

music.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent" android:layout_height="match_parent">

<Button android:text="播放" android:id="@+id/button1"

android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>

<Button android:text="停止" android:id="@+id/button2"

android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>

<Button android:text="销毁service" android:id="@+id/button3"

android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>

</LinearLayout>

其他没有粘贴出来的代码都是由系统默认生成的。