AIDL

AIDL

AIDL:Android Interface Definition Language

AIDL是一种接口描述语言,常用于进程间通信(IPC,Inter-Process Communication),编译器会根据定义的aidl文件自动生成同名的java文件,通过预先定义的接口以及Binder机制进行进程间通信。客户端通过bindService与远程服务器建立连接,远程服务器返回一个IBinder对象,客户端通过asInterface方法获取这个IBinder对象,进而可以通过IBinder对象调用远程服务器的方法。

SsoAuth.aidl(远程调用端)

// SsoAuth.aidl
package com.songzheng.cilent_server;

// Declare any non-default types here with import statements

interface SsoAuth {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    /**
     * 实现Sso授权
     */
     void ssoAuth(String userName, String pwd);
}

创建一个AIDL文件并且声明远程要调用的方法后Build->Make Project后会自动生成一个同名的.java文件。服务端和客户端都必须要有相同的AIDL文件,并且AIDL文件包名必须是相同的

服务端中完成AIDL文件的编码和.Java文件的生成之后,要写一个服务,当客户端绑定服务时服务的onBind方法返回一个IBinder对象,这样服务端就可以通过这个IBinder对象远程访问服务中的方法。注意返回的IBinder对象继承自AIDL文件生成的java文件的Stub静态抽象类。

v
SinaSsoAuthService.java(远程调用端)

package com.songzheng.cilent_server;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.Toast;

/**
 * Created by make on 2016/11/20.
 */

public class SinaSsoAuthService extends Service{

    private SinaSsoImpl mBinder = new SinaSsoImpl();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("SinaSsoAuthService", "Service onCreate!!!");
    }

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

    private class SinaSsoImpl extends SsoAuth.Stub{

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public void ssoAuth(String userName, String pwd) throws RemoteException {
            //Toast.makeText(getApplicationContext(), "执行Sso登录:用户名:" + userName + "  密码:" + pwd, Toast.LENGTH_SHORT).show();
            Log.e("SinaSsoAuthServiec", "执行Sso登录:用户名:" + userName + "  密码:" + pwd);
        }
    }
}

在ssoAuth方法中我试图使用Toast显示一段提示,但是报错:java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare(),原因是非主线程中默认没有创建Looper对象,需要调用Looper的prepare方法启动Looper。

这样远程调用端的逻辑就完成了,接下来是Client。

Client有和服务端相同的AIDL文件和.java文件,并且AIDL文件包名相同。在MainActivity中设置一个Button的点击监听事件,点击按钮绑定服务并且连接成功时在ServiceConnection中的onServiceConnected方法获取远程服务器端返回的IBinder对象,通过SsoAuth.Stub.asInterface方法将IBinder对象转换成SsoAuth对象,进而调用远程服务器端的方法。这里由于Android 5.0以上版本Google出于安全角度不允许隐式绑定Service,阅读源码后发现在validateServiceIntent中会判断Component和Package是否为null,只要有一个不为null就不会进而判断SDK版本了。

官方推荐解决方案:

Intent mIntent = new Intent();

mIntent.setAction("XXX.XXX.XXX");//你定义的service的action

mIntent.setPackage(getPackageName());//这里你需要设置你应用的包名

context.startService(mIntent); 

我没试,而是直接在网上找了一段代码。

MainActivity.java:(客户端)

package com.songzheng.cilent;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.songzheng.cilent_server.SsoAuth;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private Button btSsoAuth;
    private SsoAuth mBinder;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btSsoAuth = (Button) findViewById(R.id.bt_ssoauth);
        btSsoAuth.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mBinder == null) {
                    bindSsoAuthService();
                } else {
                    doSsoAuth();
                }
            }
        });
    }

    private void bindSsoAuthService() {
        final Intent intent = new Intent();
        intent.setAction("com.songzheng.aidl.SinaSsoAuthService");
        final Intent eIntent = new Intent(createExplicitFromImplicitIntent(this, intent));
        bindService(eIntent, mConnection, BIND_AUTO_CREATE);
        Log.e("MainActivity", "bindSsoAuthService");
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.e("onServiceConnected", "onServiceConnected");
            mBinder = SsoAuth.Stub.asInterface(iBinder);
            doSsoAuth();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBinder = null;
        }
    };

    private void doSsoAuth() {
        Log.e("doSsoAuth", "doSsoAuth");
        try {
            Log.e("doSsoAuth", "mBinder.ssoAuth()");
            Log.e("doSsoAuth", mBinder.toString());
            mBinder.ssoAuth("TestCount", "TestPwd");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

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

    /***
     * Android L (lollipop, API 21) introduced a new problem when trying to invoke implicit intent,
     * "java.lang.IllegalArgumentException: Service Intent must be explicit"
     *
     * If you are using an implicit intent, and know only 1 target would answer this intent,
     * This method will help you turn the implicit intent into the explicit form.
     *
     * Inspired from SO answer: http://stackoverflow.com/a/26318757/1446466
     * @param context
     * @param implicitIntent - The original implicit intent
     * @return Explicit Intent created from the implicit original intent
     */
    public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }

        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);

        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);

        // Set the component to be explicit
        explicitIntent.setComponent(component);

        return explicitIntent;
    }

}

这样就完成了进程间通信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值