注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

民主与科学

独立之人格,自由之思想

 
 
 

日志

 
 

AIDL和Service实现两进程通信  

2010-06-15 20:00:48|  分类: Android基础 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
AIDL和Service实现两进程通信
AIDL (Android Interface Definition Language) 是一种接口定义语言,用于生成代码允许Android设备上的两个进程间进程通信(IPC).
如果你需要编写一个进程(比如Activity) 访问另一个进程(比如Services)的对象的方法代码,你可以使用AIDL自动生成代码而不用自己配置大量的参数.
AIDL IPC基于接口机制,类似COM,Corba,且更加轻量化.它使用一个代理来在客户和实现间传递值.

使用AIDL 实现进程通信可分为五个步骤:
一.创建 ISimpleRemoteService.aidl 文件
package com.teleca.ServiceSample;
interface ISimpleRemoteService
{
    void addCommands(String cmd);
    boolean isBusy();
}

利用aidl.exe生成接口文件.若你的IDE安装了ADT,将会在gen目录或src相应包中自动根据描述文件生成同名接口文件.否则请手动:
命令行:

adil path\SomeService.adil <CR>
注意:
1.自定义类在aidl描述文件中,即便在同一个包中,也要显式import.
2.在aidl文件中所有非Java原始类型参数必须加上标记:in, out, inout.
3.Java 原始类型默认为in,且不能为其它.
4.Java 原始类型包括为java.lang, java,util包中包括的类.
5.接口名同aidl文件名.
6.接口前不用加访问权限修饰符public ,private, protected等,也不能用final ,static.

接口文件分析:
接口中生成一个Stub的抽象类,里面包括aidl定义的方法.还包括一些其它辅助方法.值得关注的是asInterface(IBinder iBinder),它返回接口的实例. 
二.实现接口
接口的实现需继承接口.Stub.并实现Stub类的方法.
下面给出一个使用匿名方式实现的例子.

private final ISimpleRemoteService.Stub binder=new ISimpleRemoteService.Stub()
{
    public void addCommands(String cmd)
    {
        SimpleRemoteService.this.addCmd(cmd);
    }
    public boolean isBusy()
    {
        return SimpleRemoteService.this.isBusy();
    }
};

注意:
1. 没有异常会正常返回
2.RPC通常比较耗时且是异步的,因此应该在线程中调用RPC服务.
3.只支持方法,不支持静态字段. 

三.暴露接口给客户
客户要服务,当然要知道在哪有服务.通常一台服务器可能提供不止一个服务.我们这里只有一个服务.
暴露服务必须继承Service.并实现onBind()方法. 
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return binder;
    }

注意我们这里可以根据intent来返回不同的服务。
四.使用打包传送参数 
如果在接口定义文件中使用了默认允许的类型(基本类型,String,CharSequence,List和Map),这些类型都将被自动处理。如果使用了其他的类型,那么该类型必须实现打包功能(Parcelable接口),而且需要创建一个aidl文件声明它是可打包类
可分为如下5个步骤:
4.1 实现 Parcelable接口
4.2 实现 public void writeToParcel(Parcel out) 方法
4.3 实现 public void readFromParcel(Parcel in) 方法
4.4 添加一个静态字段 CREATOR 到实现 Parcelable.Creator 接口的类中
4.5 创建一个aidl文件声明你的可打包的类

示例:
Rect.java文件
import android.os.Parcel;
import android.os.Parcelable;
public final class Rect implements Parcelable {
    public int left;
    public int top;
    public int right;
    public int bottom;

    public static final Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
        public Rect createFromParcel(Parcel in) {
            return new Rect(in);
        }
        public Rect[] newArray(int size) {
            return new Rect[size];
        }

    };
    public Rect() {
    }
    private Rect(Parcel in) {
        readFromParcel(in);
    }
    public void writeToParcel(Parcel out) {
        out.writeInt(left);
        out.writeInt(top);
        out.writeInt(right);
        out.writeInt(bottom);
    }
    public void readFromParcel(Parcel in) {
        left = in.readInt();
        top = in.readInt();
        right = in.readInt();
        bottom = in.readInt();
    }
}
Rect.aidl文件
package android.graphics;
parcelable Rect
;
五.调用IPC方法
调用IPC方法还有6个步骤:
5.1 声明aidl定义的接口类型引用
5.2 实现 ServiceConnection
5.3 调用 Context.bindService(),传入 ServiceConnection 的实现
5.4 在你的 ServiceConnection.onServiceConnected(),你将得到一个 IBinder 实例(service).
 调用YourInterfaceName.Stub.asInterface((IBinder)service)强制转换 YourInterface 类型.
5.5 调用接口定义的方法.你应该始终小心 DeadObjectException 异常,当连接不成功或中断它就会抛出,这也是远程对象唯一的一个异常.
5.6 断开连接,调用 Context.unbindService().
 
注意现在关于如何让AIDL定义生成的接口在客服端可见并没有发现完美的方法。
现在我的做法是把AIDL定义生成的接口,拷贝到客服端的工程中。并且让客服端打包时把AIDL定义生成的接口也打包进去。
我尝试过只用AIDL定义生成的接口来编译客户端程序,并不它打包进去。但是客服端根本无法找到服务器端AIDL文件定义生成的接口。

实例1
服务器端
文件ISimpleRemoteService.aidl
package com.teleca.ServiceSample;
interface ISimpleRemoteService
{
    void addCommands(String cmd);
    boolean isBusy();

}
文件SimpleRemoteService.java
package com.teleca.ServiceSample;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class SimpleRemoteService extends Service implements Runnable{
private final ISimpleRemoteService.Stub binder=new ISimpleRemoteService.Stub()
{
    public void addCommands(String cmd)
    {
        SimpleRemoteService.this.addCmd(cmd);
    }
    public boolean isBusy()
    {
        return SimpleRemoteService.this.isBusy();
    }
};
String tag="hubin";
@Override
public void onCreate() {
    Log.i(tag,"oncreate");
    StartThread();
}
@Override
public void onDestroy() {
    blRun=false;
    Log.i(tag,"OnDestory");
}
public void StartThread()
{
    if(blRun==false)
    {
        Thread t=new Thread(this);
        t.start();
    }      
}
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return binder;
    }
    boolean blRun=false;
    final static int kSleepTime=5;
    final String cmdPool[]=new String[10];
    int cmdStartCursor=-1;
    int cmdEndCursor=-1;
    public void run()
    {
        blRun=true;
        while(blRun)
        {
            
            if(cmdStartCursor!=cmdEndCursor)
            {
                cmdStartCursor=(cmdStartCursor+1)%cmdPool.length;
                Log.i(tag, "run:"+cmdPool[cmdStartCursor]);
            }
            try{
            Thread.sleep(kSleepTime);
            }catch(InterruptedException e)
            {
                Log.e(tag, "InterruptedException", e);
            }
        }
    }
    void addCmd(String cmd)
    {
        if((cmdEndCursor+1)%cmdPool.length!=cmdStartCursor)
        {
            cmdEndCursor++;
            cmdEndCursor=cmdEndCursor%cmdPool.length;
            cmdPool[cmdEndCursor]=cmd;
        }        
    }
    boolean isBusy()
    {
        return cmdEndCursor!=cmdStartCursor;
    }
}
文件AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.teleca.ServiceSample"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
    <service android:name="SimpleRemoteService" android:process=":remote">
    <intent-filter>
    <action android:name="com.teleca.action.STARTSERVICE"/>
    </intent-filter>
    </service>
</application>
    <uses-sdk android:minSdkVersion="7" />
</manifest>

客服端Hello.java
package com.teleca;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri;
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 com.teleca.ServiceSample.*;
public class Hello extends Activity {
    final static String tag="hubin";
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button button1 = (Button) findViewById(R.id.Button01);
        OnClickListener listener1 = new OnClickListener() {
            @Override
            public void onClick(View v) {
                doBindService();
            }
        };
        button1.setOnClickListener(listener1);
        Button button2 = (Button) findViewById(R.id.Button02);
        OnClickListener listener2 = new OnClickListener() {
            @Override
            public void onClick(View v) {
                doUnbindService();
            }
        };
        button2.setOnClickListener(listener2);
        Button button3 = (Button) findViewById(R.id.Button03);
        OnClickListener listener3 = new OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mBoundService==null)
                {
                    Log.i(tag,"the Service has not bind!Please bind the service first");
                    return;
                }
                String cmd="Hello:"+System.currentTimeMillis()%100;
                try {
                    mBoundService.addCommands(cmd);
                    boolean res=mBoundService.isBusy();

                    Log.i(tag,"isBusy:"+res);
                } catch (RemoteException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        button3.setOnClickListener(listener3);
    }
    private ISimpleRemoteService mBoundService;
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder iservice) {
            mBoundService = ISimpleRemoteService.Stub.asInterface(iservice);
        }
        
public void onServiceDisconnected(ComponentName className) {
            mBoundService = null;
        }

    };
    boolean mIsBound=true;
    void doBindService() {
        Intent intent=new Intent("com.teleca.action.STARTSERVICE");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }
    void doUnbindService() {
        if (mIsBound) {
            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        doUnbindService();
    }
}
  评论这张
 
阅读(2376)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017