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

2016-02-19 09:25 3 1 收藏

人生本是一个不断学习的过程,在这个过程中,图老师就是你们的好帮手,下面分享的Android 进程间通信实现原理分析懂设计的网友们快点来了解吧!

【 tulaoshi.com - 编程语言 】

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

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

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的使用。

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

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

新建一个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

来源:http://www.tulaoshi.com/n/20160219/1590712.html

延伸阅读
以前也模仿者ireader实现了书架的效果,但是那种是使用listview实现的,并不好用。绝大多数都是用gridview实现的,网上这方面资料比较少,有些开源的电子书都是重点做了阅读,并没有像ireader和QQ阅读这样的书架效果。 书架这种效果我早就实现了,本来想做一个完美的电子书,但是因为自己的懒惰,仅仅持续了一两天,今天又找到了以前的代码分...
由于最近项目忙,博客一直没有时间更新,今天有点时间就连续更新两篇吧,过过瘾。 这篇图片拖拽缩放也是我在项目中用到的,今天整理一下,将源码奉献给大家,希望对大家以后碰到相似的问题有帮助。 这篇就不做过多介绍了,直接上源码: 代码如下: public class SpacePageActivity extends Activity { private LinearLayout linnerLayout_...
    很多网友可能发现Android中除了Service还有一个IntentService,他们之间到底有哪些区别呢? 在继承关系上而言IntentService是Service的子类,内部实现的代码中涉及到一些Android入门开发者不了解的Looper,Android123在早期的文章中已经说明他们的用法,这里不再赘述,有关原理大家可以看源码实现如下:     代...
创建 XML 配置文件 以下代码演示如何创建 XML 配置文件字符串。 HRESULT CSIPLogin::CreateXMLProvision(LPSTR szURI, LPSTR szSIPIP, LPSTR szTransport, BSTR *bstrBuf) { ... . // 生成 XML 装置文档 wsprintf(szBuf, "<provision key="AVDConf_2" name="AVDConf_2">" "<user uri="%s" account="" password="" realm="%s" /...
在MySQL 中,只有一种 Join 算法,就是大名鼎鼎的 Nested Loop Join,他没有其他很多数据库所提供的 Hash Join,也没有 Sort Merge Join。顾名思义,Nested Loop Join 实际上就是通过驱动表的结果集作为循环基础数据,然后一条一条的通过该结果集中的数据作为过滤条件到下一个表中查询数据,然后合并结果。如果还有第三个参与 Join,则再通...

经验教程

31

收藏

61
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部