您的位置:首页 > 移动开发 > Android开发

Android AIDL 与 Service

2016-07-23 18:59 483 查看
一.首先介绍常见的Activity与Service建立通信的方式:

1.创建MyService:

public class MyService extends Service {

MyBinder mMyBinder = new MyBinder();

@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMyBinder;
}
//Binder是IBinder的实现类,通过简单继承Binder,再添加自定义的方法,可用于与Client(大多数场景是Activity)进行通信。
public class MyBinder extends Binder {
//获取当前的进程id,每个进程有唯一的pid值,用于区分不同进程
public int getPid(){
return Process.myPid();
}
}

}


2.在AndroidManifest.xml中注册刚刚创建的Service.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="netease.com.canvasdemo"
android:versionCode="100">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".ClientActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

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

<span style="color:#ff0000;"> <service android:name=".MyService">
<intent-filter>
<action android:name= "netease.com.aidldemo.MyService"/>
</intent-filter>
</service></span>

</application>

</manifest>


3.在Client中进行绑定调用。

public class ClientActivity extends Activity {

private MyService.MyBinder mBinder;

private ServiceConnection con = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("ClientActivity", "onServiceConnected");
try {
mBinder = (MyService.MyBinder) service;
int pid = mBinder.getPid();
Log.d("ClientActivity", "currentPid:" + Process.myPid() + ",ServicePid:" + pid);
}catch (Exception e){
e.printStackTrace();
}

}

@Override
public void onServiceDisconnected(ComponentName name) {
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent("netease.com.aidldemo.MyService");
bindService(intent, con, BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(con);
}
}


运行应用,我们可以在logcat中看到输出结果

D/ClientActivity: onServiceConnected
D/ClientActivity: currentPid:25788,ServicePid:25788
这一结果证明,Service与Activity运行在相同的进程中。

二.指定Service进程

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="netease.com.canvasdemo"
android:versionCode="100">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".ClientActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

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

<service android:name=".MyService" <span style="color:#ff0000;">android:process="com.netease.service"</span>>
<intent-filter>
<action android:name= "netease.com.aidldemo.MyService"/>
</intent-filter>
</service>

</application>

</manifest>
红色为新增的属性,通过指定process属性即可让Service控件运行在不同的进程中。

我们直接运行发现程序崩溃了

W/System.err: java.lang.ClassCastException: <span style="color:#ff0000;">android.os.BinderProxy cannot be cast to netease.com.canvasdemo.MyService$MyBinder</span>
at netease.com.canvasdemo.ClientActivity$1.onServiceConnected(ClientActivity.java:24)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1101)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1118)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
我们根据错误的行数定于到出错的地点,发现是在Activity中的红色这一行:

private ServiceConnection con = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("ClientActivity", "onServiceConnected");
try {
<span style="color:#ff0000;">mBinder = (MyService.MyBinder) service;</span>
int pid = mBinder.getPid();
Log.d("ClientActivity", "currentPid:" + Process.myPid() + ",ServicePid:" + pid);
}catch (Exception e){
e.printStackTrace();
}

}

@Override
public void onServiceDisconnected(ComponentName name) {
}
};
说明在我们指定process时,传回来的IBinder实例对象发生了改变。现在传回来的实例是 android.os.BinderProxy,而不是原来的MyService.MyBinder实例了.  (笔者现在只了解,这应该是Android底层实现的代理类,用来实现跨进程通信,具体的实现方式,在下一篇博客中进行分析)。那么跨进程通信即IPC的正确打开方式是什么呢?我们来马上介绍。

三.通过AIDL来实现跨进程通信

1.创建AIDL文件



我们输入文件名IDownload,会自动生成aidl文件,我们在其中添加 int getPid(); void basicType()是自动生成的,我们可以留着作为测试。



然后点击 Build-> Make Project 编译,在build generated中我们能看到自动生成的IDownload接口,这个文件中比较重要的是内部抽象类Stub,这个类继承自Binder并且实现了我们在aidl文件中定义的接口。



然后我们回到MyService中,修改MyBinder继承至IDownload.Stub
public class MyService extends Service {

MyBinder mMyBinder = new MyBinder();

@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMyBinder;

}

public class MyBinder extends IDownload.Stub {

@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
// 后续再使用
}

public int getPid(){
return Process.myPid();
}
}

}


回到Activity中,修改红色部分
public class ClientActivity extends Activity {

private <span style="color:#ff0000;">IDownload</span> mBinder;

private ServiceConnection con = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("ClientActivity", "onServiceConnected");
try {
mBinder =  <span style="color:#ff0000;">IDownload.Stub.asInterface(service);</span>
int pid = mBinder.getPid();
Log.d("ClientActivity", "currentPid:" + Process.myPid() + ",ServicePid:" + pid);
}catch (Exception e){
e.printStackTrace();
}

}

@Override
public void onServiceDisconnected(ComponentName name) {
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent("netease.com.aidldemo.MyService");
bindService(intent, con, BIND_AUTO_CREATE);
}

@Override
protected void onDestroy() {
super.onDestroy();
unbindService(con);
}
}
这个时候,我们再次启动应用,发现不再报错了。

D/ClientActivity: onServiceConnected
D/ClientActivity: currentPid:21429,ServicePid:21442
根据pid不同,也确实验证了,此时的Service,运行在了与Activity不同的线程。

我们把AndroidManifest中 service 的 process属性去掉再试试。

D/ClientActivity: onServiceConnected
D/ClientActivity: currentPid:21999,ServicePid:21999
此时pid又相同了。

根据以上的实验,我们可以得出以下结论:

在同一个应用内:
1.不单独指定Service进程(AndroidManifest中指定Service的process属性),Activity中可通过ServiceConnect回调中 IBinder强转为自定义的MyBinder进行回调。
2.若单独指定Service进程,将ServiceConnect回调中 IBinder强转为实现类MyBinder时,会报错。(因为此时是通过代理Binder来实现跨进程通信,而不是自己定义的MyBinder,所以强转会报类型转换错误)
3.第2点中的错误,可通过定义AIDL接口的方式解决(假如定义了IDownload.aidl),此时想拿到IBinder的实现类,需要通过调用IDownload.Stub.asInstance(IBinder),这样就能实现跨进程通信。
最后AIDL既然能实现跨进程通信,其实也是能够实现不同应用之间的通信的。但是笔者实验过程中,发现会报错,目前仍在调试,先把使用方式总结下。
不同应用:
不同应用,可以使用相同的AIDL定义的接口,来进行通信。假如B应用中创建了AIDL及Service类,应用A,中只需拷贝B中的AIDL文件(包名保持一致)即可与应用B进行通信。原理类似于上述第3条。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: