Android 进程间通信实现原理分析

Android Service是分为两种:
  本地服务(Local Service): 同一个apk内被调用
  远程服务(Remote Service):被另一个apk调用
远程服务需要借助AIDL来完成。

AIDL 是什么
  AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
  AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

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

选择AIDL的使用场合
  官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。
  如果不需要进行不同应用程序间的并发通信(IPC),you should create your interface by implementing a Binder;或者你想进行IPC,但不需要处理多线程的,则implement your interface using a Messenger。无论如何,在使用AIDL前,必须要理解如何绑定service——bindService。

下面将要讲到的这个例子来自:http://www.cnblogs.com/lonkiss/archive/2012/10/23/2735548.html

下面用一个客户端Activity操作服务端Service播放音乐的实例演示AIDL的使用。

服务端代码结构(左)                                 客户端代码结构(右)
被标记的就是需要动手的。
服务端

新建一个android application project,命名为PlayerServer。 在res下的raw文件夹里面放入一个音乐文件,我这里放入的是Delta Goodrem的《Lost Without You》片段。如果不存在raw这个文件夹就自己新建一个,命名为raw。这个文件夹在raw文件夹下,与layout文件夹平级。raw中的文件遵守标识符的命名规则,不要出现中文和空格,多个单词可以用下划线连接。
  
新建一个IRemoteServiice.aidl 文件,加入如下代码


代码如下:

package pandafang.demo.playerserver;
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类。代码如下


代码如下:

/**
 * 播放音乐的服务
 */
public class PlayerService extends Service {

public static final String TAG = "PlayerService";

private MediaPlayer mplayer;

@Override
 public IBinder onBind(Intent intent) {
  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.lost);
   // 方法一结束

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

// 实现aidl文件中定义的接口
 private IBinder mBinder = new IRemoteService.Stub() {

@Override
  public void stop() throws RemoteException {
   try {
    if (mplayer.isPlaying()) {
     mplayer.stop();
    }
   } catch (Exception e) {
    // TODO: handle exception
    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) {
    // TODO: handle exception
    e.printStackTrace();
   }
  }
 };
 @Override
 public boolean onUnbind(Intent intent) {
  if (mplayer != null) {
   mplayer.release();
  }
  Log.i(TAG,"service onUnbind");
  return super.onUnbind(intent);
 }
}

服务编写好以后,按照惯例在AndroidManifest.xml中加入声明,代码如下


代码如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="pandafang.demo.playerserver"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    <application
        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.example.playerserver.PlayerService"/>
            </intent-filter>
         </service>
    </application>
</manifest>

需要加入的只是<service>...</service>那段,要注意的是 android:process=":remote" 和 intent-filter 。
  运行服务端到设备上,准备给客户端调用
客户端
  新建一个android application project,命名为PlayerClient。将服务端放有aidl文件的包直接copy到客户
端src目录下,保留包中的aidl文件,其他删除。
 编写MainActivity.java 代码如下


代码如下:

/**
 * 客户端控制界面
 */
public class MainActivity extends Activity {

public static final String TAG = "MainActivity";

// 服务端 AndroidManifest.xml中的intent-filter action声明的字符串
 public static final String ACTION = "com.example.playerserver.PlayerService";

private Button playbtn, stopbtn;

private IRemoteService mService;

private boolean isBinded = false;

private ServiceConnection conn = new ServiceConnection() {

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

@Override
  public void onServiceConnected(ComponentName name, IBinder service) {
   mService = IRemoteService.Stub.asInterface(service);
   isBinded = true;
  }
 };
    @Override
    public 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(clickListener);
        stopbtn.setOnClickListener(clickListener);
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
    @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;
     }

}
    private OnClickListener clickListener = new OnClickListener() {

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

MainActivity是根据向导自动生成的,不需要在AndroidManifest.xml中注册声明了
运行客户端到设备,按下按钮可以播放/停止 效果如图

如果想对更加详细的实现原理进行研究,可以参见这篇文章:
http://www.cnblogs.com/over140/archive/2011/03/08/1976890.html

(0)

相关推荐

  • Android结束进程的方法详解

    本文实例讲述了Android结束进程的方法.分享给大家供大家参考,具体如下: 最近在做一个类似与任务管理器的东西,里面有个功能,可以通过这个管理器结束掉其他的进程. 在Android平台下,结束进程的方法还是比较多的.首先指明,此处的"结束进程",包含了结束自身进程和结束其他进程两个方面.通过查阅SDK文档和网上的一些资料,自己找到一些结束进程的方法.在这里做一些归纳和总结,文章的部分信息有可能来自网上已有的文章和帖子,由于过了比较长时间,所以若发现本文与其他文章雷同,请谅解. 一.结

  • Android开发中多进程共享数据简析

    背景 最近在工作中遇到一个需求,需要在接收到推送的时候将推送获得的数据存起来,以供app启动时使用.我们会认为这不是So easy吗?只要把数据存到SharedPreferences中,然后让app打开同一个SharedPreferences读取数据就可以了.但是在实际的测试中,我们发现推送进程存入的数据,并不能在app进程中获得.所以这是为什么呢,也许聪明的读者从我们上面的陈述中已经发现了原因,因为我们有两个进程,推送进程负责将推送数据存入,而app进程负责读取,但是正是由于是两个进程,如果它

  • Android 动画之ScaleAnimation应用详解

    android中提供了4中动画: AlphaAnimation 透明度动画效果 ScaleAnimation 缩放动画效果 TranslateAnimation 位移动画效果 RotateAnimation 旋转动画效果 本节讲解ScaleAnimation 动画, ScaleAnimation(float fromX, float toX, float fromY, float toY,int pivotXType, float pivotXValue, int pivotYType, flo

  • Android 动画之TranslateAnimation应用详解

    android中提供了4中动画: AlphaAnimation 透明度动画效果 ScaleAnimation 缩放动画效果 TranslateAnimation 位移动画效果 RotateAnimation 旋转动画效果 本节讲解TranslateAnimation动画,TranslateAnimation比较常用,比如QQ,网易新闻菜单条的动画,就可以用TranslateAnimation实现, 通过TranslateAnimation(float fromXDelta, float toXD

  • Android中应用多进程的整理总结

    前言 在计算机操作系统中,进程是进行资源分配和调度的基本单位.这对于基于Linux内核的Android系统也不例外.在Android的设计中,一个应用默认有一个(主)进程.但是我们通过配置可以实现一个应用对应多个进程. 本文将试图对于Android中应用多进程做一些整理总结. android:process 应用实现多进程需要依赖于android:process这个属性 适用元素:Application, Activity, BroadcastReceiver, Service, Content

  • Android 多进程资料总结

    温故而知新 网上说多进程的文章很多,不过基本都是在讨论很深的东西,这是需要去专研的,而我是来这篇是用来偷懒的(应用层次),记录的都是自己对多进程的理解,方便以后用到的时候来偷懒,如果有错,请指教,小小程序员万分感谢. 讲进程,先来回答下面几个问题: 1.什么是线程? 线程就是程序中单独执行的流控制. 2.什么是多线程? 多线程就是单个程序中执行多个流控制.作用就是最大限度的使用CPU资源. 3.什么是进程? 一般指的是一个执行单元,即一个应用程序(Android中就是一个应用). 4.线程和进程

  • Android应用程序四大组件之使用AIDL如何实现跨进程调用Service

    一.问题描述 Android应用程序的四大组件中Activity.BroadcastReceiver.ContentProvider.Service都可以进行跨进程.在上一篇我们通过ContentProvider实现了不同应用之间的跨进程调用,但ContentProvider主要是提供数据的共享(如sqlite数据库),那么我们希望跨进程调用服务(Service)呢?Android系统采用了远程过程调用(RPC)方式来实现.与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言

  • Android应用开发SharedPreferences存储数据的使用方法

    SharedPreferences是Android中最容易理解的数据存储技术,实际上SharedPreferences处理的就是一个key-value(键值对).SharedPreferences常用来存储一些轻量级的数据. 复制代码 代码如下: //实例化SharedPreferences对象(第一步) SharedPreferences mySharedPreferences= getSharedPreferences("test", Activity.MODE_PRIVATE);

  • Android编程防止进程被第三方软件杀死的方法

    本文实例讲述了Android编程防止进程被第三方软件杀死的方法.分享给大家供大家参考,具体如下: 项目测试的时候发现,按home键回到桌面,再用360清理内存,软件被结束,再次进入的时候报错,看了下log,以为是有的地方没有控制好,但是又不知道360结束的是什么(这个现在还没弄明白).使用小米系统的进程管理优化内存就不报错. 后来想到用Service防止软件被kill掉,查了下资料,发现google 管方就有,ForegroundService 前台服务,让服务一直以前台任务的方式运行,可以在s

  • 解析后台进程对Android性能影响的详解

    Android现在这么火,各种的设备也是琳琅满目,高中低等,大小屏幕都有,但是它始终未能达到iOS那样的令人称赞的卓越体验和性能,其操作的流畅度,性能和安全性方面总是略输iOS一筹.据说iPhone4虽然是单核512M内存,但是比Android的双核1G内存的操作起来更流畅,iPad2虽然是也只有512M的内存但是操作起来比Android四核1G内存还要流畅.另外在安全性方面也不如iOS. 造成Android性能,待机时间,操作流畅和安全性不好的原因是Android后台进程的管理. Androi

随机推荐