Android AIDL 与 Service

一.首先介绍常见的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应用中创建了AIDLService类,应用A,中只需拷贝B中的AIDL文件(包名保持一致)即可与应用B进行通信。原理类似于上述第3条。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值