Android跨进程通信-AIDL

来源:互联网 发布:淘宝的限时特价怎么做 编辑:程序博客网 时间:2024/05/04 01:44

转至:http://codingnow.cn/android/529.html


文章摘要: 上一篇复习了Android生命周期和本地Service的使用,这一篇继续总结一下Android远程Service的使用,远程Service就是在新的进程中开启service,这样会遇到一个问题,就是进程间通信的问题。Android系统的进程之间不能共享内存,那怎么传递对象呢,需要把对象弄成操作系统可以识别的形式,在Android中,可以采用AIDL…

上一篇复习了Android生命周期和本地Service的使用,这一篇继续总结一下Android远程Service的使用,远程Service就是在新的进程中开启service,这样会遇到一个问题,就是进程间通信的问题。Android系统的进程之间不能共享内存,那怎么传递对象呢,需要把对象弄成操作系统可以识别的形式,在Android中,可以采用AIDL来公开服务的接口,采用远程过程调用(Remote Procedure Call,RPC)和代理模式来实现跨进程通信。AIDL:Android Interface Definition Language,即Android接口描述语言,ADT会根据aidl文件在gen目录下生成对应的java接口文件。我们需要手工创建一个Service的子类并实现生成的java接口,然后在AndroidManifest.xml文件中进行配置。远程服务可以为多个客户端服务,由于涉及到数据通信,一般采用bindService的方式。
下面我们通过一个demo来看看AIDL是如何实现的。
首先创建服务端Android工程。目录结构如图

代码如下
User.java,为了实现跨进程数据传递,需要实现Parcelable 接口,是一种序列化方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
publicclass User implementsParcelable {
 
    privateint id;
    privateString name;
 
    publicUser() {
    }
 
    publicUser(Parcel parcel) {
        this.id = parcel.readInt();
        this.name = parcel.readString();
    }
 
    publicint getId() {
        returnid;
    }
 
    publicvoid setId(intid) {
        this.id = id;
    }
 
    publicString getName() {
        returnname;
    }
 
    publicvoid setName(String name) {
        this.name = name;
    }
 
    @Override
    publicint describeContents() {
        // TODO Auto-generated method stub
        return0;
    }
 
    @Override
    publicvoid writeToParcel(Parcel dest, intflags) {
        //顺序需与构造函数中read保持一致
        dest.writeInt(id);
        dest.writeString(name);
    }
 
    publicstatic final Parcelable.Creator<User> CREATOR = newCreator<User>() {
 
        @Override
        publicUser createFromParcel(Parcel source) {
            returnnew User(source);
        }
 
        @Override
        publicUser[] newArray(intsize) {
            returnnew User[size];
        }
    };
 
}

User.adil

1
parcelable User;

IRemoteService.aidl

1
2
3
4
5
6
7
8
9
10
11
12
/**
 
远程的服务
IRemoteService.aidl
*/
interfaceIRemoteService {
    //返回基本类型
    intgetId();
    //返回对象
    User getUser();
 
}

RemoteService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
publicclass RemoteService extendsService {
 
    @Override
    publicvoid onCreate() {
        Log.i(this.getClass().getName(),"onCreate");
    }
 
    @Override
    publicint onStartCommand(Intent intent, intflags, intstartId) {
        Log.i(this.getClass().getName(),"onStartCommand");
        returnsuper.onStartCommand(intent, flags, startId);
    }
 
    @Override
    publicvoid onDestroy() {
        Log.i(this.getClass().getName(),"onDestroy");
    }
 
    @Override
    publicIBinder onBind(Intent intent) {
        returnmRemoteServiceBinder;
    }
 
    @Override
    publicboolean onUnbind(Intent intent) {
        Log.i(this.getClass().getName(),"onUnbind");
        returnsuper.onUnbind(intent);
    }
 
    @Override
    publicvoid onRebind(Intent intent) {
        Log.i(this.getClass().getName(),"onRebind");
        super.onRebind(intent);
    }
 
    IRemoteService.Stub mRemoteServiceBinder = newIRemoteService.Stub() {
 
        @Override
        publicUser getUser() throwsRemoteException {
            User user = newUser();
            user.setId(123456);
            user.setName("alexzhou");
            returnuser;
        }
 
        @Override
        publicint getId() throwsRemoteException {
            return123456;
        }
 
    };
}

AndroidManifest.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
    package="com.alexzhou.aidl.server"
    android:installLocation="internalOnly"
    android:versionCode="1"
    android:versionName="1.0">
 
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="10"/>
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:name=".RemoteServiceActivity"
            android:label="@string/app_name">
            <intent-filter>
                <actionandroid:name="android.intent.action.MAIN"/>
 
                <categoryandroid:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
 
        <!-- android:process="name" name的值是随便取的 ,android:exported:是否允许被其它程序调用-->
        <service
            android:name="com.alexzhou.aidl.server.RemoteService"
            android:exported="true"
            android:process=":remote">
            <intent-filter>
                <actionandroid:name="com.alexzhou.service.REMOTE_SERVICE"/>
            </intent-filter>
        </service>
 
    </application>
 
</manifest>

服务端的Activty是自动生成的。没写任何其他代码,这里就不贴出来了。
接着需要创建一个客户端Android工程,目录结构如下图:

代码如下:
先把User.java,User.aidl,IRemoteService.aidl三个文件复制到客户端,注意包名必须跟服务端所在的包名一致。
创建客户端主界面类ClientActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
publicclass ClientActivity extendsActivity implementsOnClickListener{
 
    privateTextView callbackView;
    privateButton bindButton;
    privateboolean isBind;
    privatefinal String REMOTE_SERVICE_ACTION = "com.alexzhou.service.REMOTE_SERVICE";
 
    privateIRemoteService mRemoteService;
 
    /** Called when the activity is first created. */
    @Override
    publicvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        findViews();
        setListeners();
        callbackView.setText("no callback");
    }
 
    privatevoid findViews() {
        callbackView = (TextView)this.findViewById(R.id.callback);
        bindButton = (Button)this.findViewById(R.id.bind);
 
    }
 
    privatevoid setListeners() {
        bindButton.setOnClickListener(this);
    }
 
    @Override
    publicvoid onClick(View view) {
        switch(view.getId()) {
            caseR.id.bind:
                this.bindService(newIntent(REMOTE_SERVICE_ACTION), mConntectin, Context.BIND_AUTO_CREATE);
                callbackView.setText("binding...");
                break;
        }
    }
 
    privateServiceConnection mConntectin = newServiceConnection() {
 
        @Override
        publicvoid onServiceDisconnected(ComponentName arg0) {
            callbackView.setText("Disconnected!");
        }
 
        @Override
        publicvoid onServiceConnected(ComponentName name, IBinder binder) {
            mRemoteService = IRemoteService.Stub.asInterface(binder);
            isBind = true;
            try{
                intid = mRemoteService.getId();
                User user = mRemoteService.getUser();
                StringBuffer buffer = newStringBuffer();
                buffer.append("id:");
                buffer.append(id);
                buffer.append("name");
                buffer.append(user.getName());
                callbackView.setText(buffer.toString());
            }catch(RemoteException e) {
                e.printStackTrace();
            }
        }
    };
 
    @Override
    protectedvoid onDestroy() {
        if(isBind) {
            this.unbindService(mConntectin);
            isBind = false;
        }
        super.onDestroy();
    }
}

布局文件main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
 
 <TextViewandroid:id="@+id/callback"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
 
    <Buttonandroid:id="@+id/bind"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/bind_remote_service_text"/>
 
</LinearLayout>

现在运行你的客户端程序,点击绑定远程服务按钮,如果一切顺利,将会看到服务端返回的信息。如图:

通过eclipse控制台Devices视图,可以看到远程服务进程已启动,如图:

可能会遇到的问题和解决办法:
(1)AIDL unable to start service not found
客户端和服务端activty包名相同了,改成不同就可以
(2)Not allowed to bind to service Intent
在服务端配置文件中把android:exported = false 改成 true,android:exported表示是否允许被其它程序调用
(3)Binder invocation to an incorrect interface
客户端aidl文件的包名跟service的包名不一样,改成一样的就ok


原创粉丝点击