Android AIDL 与 Service
2016-07-23 18:59
483 查看
一.首先介绍常见的Activity与Service建立通信的方式:
1.创建MyService:
2.在AndroidManifest.xml中注册刚刚创建的Service.
3.在Client中进行绑定调用。
运行应用,我们可以在logcat中看到输出结果
二.指定Service进程
我们直接运行发现程序崩溃了
三.通过AIDL来实现跨进程通信
1.创建AIDL文件
我们输入文件名IDownload,会自动生成aidl文件,我们在其中添加 int getPid(); void basicType()是自动生成的,我们可以留着作为测试。
然后点击 Build-> Make Project 编译,在build generated中我们能看到自动生成的IDownload接口,这个文件中比较重要的是内部抽象类Stub,这个类继承自Binder并且实现了我们在aidl文件中定义的接口。
然后我们回到MyService中,修改MyBinder继承至IDownload.Stub
回到Activity中,修改红色部分
我们把AndroidManifest中 service 的 process属性去掉再试试。
根据以上的实验,我们可以得出以下结论:
在同一个应用内:
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条。
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条。
相关文章推荐
- Android中的长度单位
- android独特的天气预报
- Android最佳实践之触摸手势
- 调用相机拍照,处理图片旋转,保存图片
- android获取音量分贝值
- android如何给整个视图view圆角显示
- android 启动流程
- android中IO操作数据总结
- android-----我眼中的Binder
- Android中各类Dialog实例——交互
- 封装控件的原理
- windows下Qt5.1 for android开发环境配置
- android:shape属性详解
- 【Android】使用Notification实现状态通知栏的通知
- 控件封装的一点心得
- android中的ListView数据量大时如何提高效率。。。。
- jni ndk 学习笔记,带源码
- Button的三种监听方法
- Android LayoutParams修改的正确姿势
- Android Material Design 进度条 自定义进度条样式