AIDL

来源:互联网 发布:股票资产分析软件 编辑:程序博客网 时间:2024/05/02 03:52

AIDL

AIDL:Android Interface Definition Language

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

SsoAuth.aidl(远程调用端)

// SsoAuth.aidlpackage com.songzheng.cilent_server;// Declare any non-default types here with import statementsinterface 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的actionmIntent.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;    }}

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

1 0
原创粉丝点击