aidl 在异步线程 bindService

aidl 在异步线程 bindService,第1张

Android的 IPC api 有很多,但是很多都是一过性的,并不会保存连接状态,比如 provider 的 call 方法就是一种非常直接的方法,但是被call的进程也容易挂掉。


bindService 是一种可以保证连接的跨进程通信机制,在Android系统设计中,它对后台进程具有很强的保活效应(假设前台进程bind了它),但是美中不足的是,调用方式就有些麻烦了。


因为不管客户端啥时候调用都要提前预留时间进行bind,而等待时间又不确定,此时我们有几种处理方法

1. 让调用线程等待,当bind成功后再放开

2. 将接口改为回调式的

第二种方式我们先不讨论,因为实现起来比较简单。


第一种方式,直接hold住调用线程,就有问题了,如果调用线程是主线程,而bindService 的ServiceConnection 的回调线程也是主线程,那么显然你就永远锁住了,主线程将永远不能被唤醒。


因此我们需要将 ServiceConnection 回调到其他线程。


怎样实现呢?

我们看了 Context 代码发现有这样一个方法可以被我们利用,这个方法在至少 Android 7 就已经存在了,所以可用性还是比较强的。


    /**
     * Same as {@link #bindService(Intent, ServiceConnection, int, UserHandle)}, but with an
     * explicit non-null Handler to run the ServiceConnection callbacks on.
     *
     * @hide
     */
    public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
            Handler handler, UserHandle user) {
        throw new RuntimeException("Not implemented. Must override in a subclass.");
    }

我们通过反射来调用它

        HandlerThread thread = new HandlerThread("my_thread");
        thread.start();
        Handler handler = new Handler(thread.getLooper());
        handler.post(() -> {
            aidlConnection = new ServiceConnection() {

                @Override
                public void onServiceConnected(ComponentName className, IBinder service) {
                    log("on aidl service connected!!, service = " + service + ", is main thread = " + (Looper.getMainLooper() == Looper.myLooper()));
                    caller = IpcCaller.Stub.asInterface(service);
                }

                @Override
                public void onServiceDisconnected(ComponentName className) {
                    caller = null;
                }
            };

            try {
                Intent intent = new Intent(Contract.ACTION_MSGR);
                intent.setPackage(getPackageName());
                intent.setAction(Contract.ACTION_AIDL);

                Method bindServiceAsUser = Context.class.getDeclaredMethod("bindServiceAsUser"
                        , Intent.class, ServiceConnection.class,
                        int.class, Handler.class, UserHandle.class);
                bindServiceAsUser.setAccessible(true);
                bindServiceAsUser.invoke(getApplicationContext(), intent, aidlConnection, Context.BIND_AUTO_CREATE,
                        handler, UserHandle.getUserHandleForUid(Process.myUid()));
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
                log(e);
            }
        });

注意bindService也必须在这个线程调用,否则会有问题

    Caused by: java.lang.RuntimeException: ServiceConnection com.xbb.ipcdemo.MainActivity@9fef978 registered with differing handler (was Handler (android.os.Handler) {c62f851} now Handler (android.app.ActivityThread$H) {d7298b6})
        at android.app.LoadedApk$ServiceDispatcher.validate(LoadedApk.java:1473)
        at android.app.LoadedApk.getServiceDispatcher(LoadedApk.java:1363)
        at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1624)
        at android.app.ContextImpl.bindService(ContextImpl.java:1596)
        at android.content.ContextWrapper.bindService(ContextWrapper.java:636)
        at com.xbb.ipcdemo.MainActivity.onStart(MainActivity.java:155)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1254)
        at android.app.Activity.performStart(Activity.java:6930)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2767)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2875) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1578) 
        at android.os.Handler.dispatchMessage(Handler.java:105) 
        at android.os.Looper.loop(Looper.java:156) 
        at android.app.ActivityThread.main(ActivityThread.java:6623) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832) 

这样就可以放心大胆地挂起任何调用,然后在 onServiceConnected 成功后再唤醒它。


如果帮助到了你,点个赞吧

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/625893.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-16
下一篇 2022-04-16

发表评论

登录后才能评论

评论列表(0条)

保存