前面我们有介绍AIDL的基本用法:

Android进程间通信——AIDL

Android进程间通信——AIDL Binder连接池

现在我们来介绍利用AIDL来实现一个简陋的SDK,将获取用户信息的方法暴露给客户端,先放工程目录:

SDKServer代码实现

首先作为服务端,我们创建IAuth.aidl文件,声明IAuth接口

// IAuth.aidlpackage com.example.server.aidl;import com.example.server.aidl.User;// Declare any non-default types here with import statementsinterface IAuth {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */     String getAuthCode();     void checkAuthCode(String code);     User getCurrentUser();}
// User.aidlpackage com.example.server.aidl;parcelable User;

接下来我们创建一个Service监听客户端的连接

public class AuthService extends Service {    @Nullable    @Override    public IBinder onBind(Intent intent) {        return new AuthImpl(this);    }}

在AndroidManifest中注册,

                        

AuthService能响应action为auth_request_from_sdk的Intent

接口实现如下:

接口实现如下:public class AuthImpl extends IAuth.Stub {    private String authCode = null;    private boolean isAuthed = false;    private boolean isUserAuth = false;    private Service mService;    private Object waitUserAuth = new Object();    private BroadcastReceiver mReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            //notify()方法能够唤醒一个正在等待该对象的锁的线程,当有多个线程都在等待该对象的锁的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知            //调用某个对象的notify()方法,当前线程也必须拥有这个对象的锁,因此调用notify()方法必须在同步块或者同步方法中进行            synchronized (waitUserAuth) {                isUserAuth = intent.getBooleanExtra("isUserAuth", false);                waitUserAuth.notify();            }        }    };    public AuthImpl(Service service) {        mService = service;    }    @Override    public String getAuthCode() throws RemoteException {        authCode = UUID.randomUUID().toString();        try {            return RSATool.encode(authCode);        } catch (Exception e) {            e.printStackTrace();        }        return "";    }    @Override    public void checkAuthCode(String code) throws RemoteException {        if (code.equals(authCode)) {            isAuthed = true;        }    }    @Override    public User getCurrentUser() throws RemoteException {        Log.e("SDK_Server", "AuthImpl ___isAuthed--" + isAuthed + "--mService--" + mService + "--isUserAuth--" + isUserAuth);        if (!isAuthed) {            throw new RemoteException("Not Authed 1001");        }        if (mService == null) {            throw new RemoteException("Not Authed 1002");        }        //获取当前用户信息,跳转到AuthActivity界面        mService.registerReceiver(mReceiver, new IntentFilter("call_back_from_AuthActivity"));        Intent intent = new Intent(mService, AuthActivity.class);        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);        mService.startActivity(intent);        //如果调用某个对象的wait()方法,当前线程必须拥有这个对象的锁,因此调用wait()方法必须在同步块或者同步方法中进行        //调用某个对象的wait()方法,相当于让当前线程交出此对象的锁,然后进入等待状态,等待后续再次获得此对象的锁        synchronized (waitUserAuth) {            try {                waitUserAuth.wait();            } catch (Exception e) {                e.printStackTrace();            }        }        mService.unregisterReceiver(mReceiver);        if (isUserAuth) {            User user = new User(1, "ZOUJIN", "ZOUJIN6649", "https://dss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2075772401,2375569036&fm=179&app=42&f=JPEG?w=121&h=140&s=D7F5C46A051445C018C03E68030090F5");            return user;        } else {            return null;        }    }}

用户授权界面:

public class AuthActivity extends AppCompatActivity implements View.OnClickListener {    TextView tvUserName;    ImageView ivHeadPic;    Button btnConfirm;    ImageView ivBack;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_auth);        tvUserName = findViewById(R.id.tv_username);        ivHeadPic = findViewById(R.id.iv_headPic);        ivBack = findViewById(R.id.iv_back);        btnConfirm = findViewById(R.id.btn_confirm);        btnConfirm.setOnClickListener(this);        ivBack.setOnClickListener(this);        showCurrUserInfo();    }    private void showCurrUserInfo() {        //查询当前用户,将当前登录的账户信息显示再界面上        //前面我们在AuthImpl中实现getUserInfo,我们为了方便,直接new User对象        //User user = new User(1, "ZOUJIN", "ZOUJIN6649", "https://dss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2075772401,2375569036&fm=179&app=42&f=JPEG?w=121&h=140&s=D7F5C46A051445C018C03E68030090F5");        //所以这里也直接将前面的User信息和前面保持一致        tvUserName.setText("ZOUJIN");        Glide.with(this).load("https://dss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2075772401,2375569036&fm=179&app=42&f=JPEG?w=121&h=140&s=D7F5C46A051445C018C03E68030090F5").into(ivHeadPic);    }    @Override    public void onClick(View view) {        switch (view.getId()) {            case R.id.iv_back:                //取消使用SDK登录                Intent cancelAction = new Intent("call_back_from_AuthActivity");                cancelAction.putExtra("isUserAuth", false);                sendBroadcast(cancelAction);                finish();                break;            case R.id.btn_confirm:                //使用SDK登录,传递SDKServer当前账户信息给SDKClient                Intent confirmAction = new Intent("call_back_from_AuthActivity");                confirmAction.putExtra("isUserAuth", true);                sendBroadcast(confirmAction);                finish();                break;            default:                break;        }    }}

服务端我们使用CheckAuthCode()来进行SDK的认证。服务端getAuthCode()暴露给外部调用,内部使用的RSA非对象加密算法的公钥对Code进行加密。SDK供客户端集成,内部使用RSA的私钥对Code进行解密。客户端先通过AIDL调用服务端的getAuthCode获得加密后的Code,再调用SDK的解密方法对Code解密。解密之后的Code,再传递给服务端进行验证。SDK认证成功,isAuth为true,否则为false。如果验证失败,getCurrentUser直接返回并抛异常。验证成功之后,我们会先注册一个广播,监听用户是否授权,再跳转至AuthActivity界面供用户确认授权交互,并利用对象锁将线程阻塞,直到收到广播释放对象锁,线程获得对象锁被唤醒。这里我们为了方便,直接new了一个对象。

在用户授权界面,由于前面我们获取用户信息的时候是直接创建的一个User对象,因此,这里我们与前面保持一致即可。当用户点击确认授权,我们会发送一个广播,并携带确认授权的数据信息。

SDK开发

首先我们创建一个auth_sdk的Library。将SDKServer中的AIDL接口和User类复制过来,注意保持包名一致,然后build一下,我们就可以调用接口文件中的方法了。

首先我们来看一下SDK类和AuthService类

public class SDK {    public static void initSDK(Application application) {        AuthService.initApp(application);    }}
public class AuthService {    private static final String TAG = "AuthService";    private static Application mApplication;    private IAuth remoteAuth;    private ServiceConnection mConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {            remoteAuth = IAuth.Stub.asInterface(iBinder);            Log.e(TAG, "Service 连接成功");        }        @Override        public void onServiceDisconnected(ComponentName componentName) {            Log.e(TAG, "Service 连接失败");        }    };    private static AuthService sInstance;    private AuthService() {    }    public synchronized static AuthService getInstance() {        if (sInstance == null) {            sInstance = new AuthService();        }        return sInstance;    }    public static void initApp(Application application) {        Intent intent = new Intent("auth_request_from_sdk");        intent.setPackage("com.example.server");        mApplication = application;        mApplication.bindService(intent, getInstance().mConnection, Context.BIND_AUTO_CREATE);    }    public static void removeApp() {        mApplication.unbindService(getInstance().mConnection);        mApplication = null;        sInstance = null;    }    public String getUserInfo() throws Exception {        if (!CheckAppTool.isInstallSDKServer(mApplication)) {            try {                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.example.server"));                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                mApplication.startActivities(new Intent[]{intent});            } catch (Exception e) {                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.example.server"));                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                mApplication.startActivities(new Intent[]{intent});            }            return null;        }        String code = RSATool.decode(remoteAuth.getAuthCode());        Log.e(TAG, "getUserInfo--code:" + code);        remoteAuth.checkAuthCode(code);        return remoteAuth.getCurrentUser().toString();    }}

这里的AuthService并不是真正的Service,在初始化的时候会绑定能响应auth_request_from_sdk意图的Service。其实这里才是客户端通过AIDL调用服务端的方法的真正实现,这一步我们封装在了SDK中方便客户端调用。

当客户端调用getUserInfo()时,SDK首先会系统是否安装了SDKServer的应用,如果已经安装了,客户端再调用服务端的getAuthCode,拿到的Code再进行验证,最后再调用getCurrentUser()获取用户信息。

最后是生成aar文件,Android studio最右侧,Gradle-->auth_sdk/Tasks/build/assemble,双击assemble就会在auth_sdk/build/outputs/aar目录下生成两个aar文件

客户端集成SDK

新建一个工程SDKClient,再新建一个module导入aar包,这里我们导入的是SDK的release包

然后我们在SDKClient中依赖auth_sdk-release包

依赖成功之后,我们新建一个Application,并修改AndroidMenifest中application的name值

public class MyApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        SDK.initSDK(this);    }}

我们将SDKClient的Application传递给SDK,SDK的initSDK()方法会调用AuthService的initApp(),initApp()会绑定服务端的Service,这就相当于在SDKClient的Application的onCreate()方法中绑定服务端的Service。

接下来看如何使用

public class MainActivity extends AppCompatActivity implements View.OnClickListener {    public static final String TAG = "MainActivity";    private static final int MSG_USER_INFO = 1;    Button btnJump;    TextView tvUserInfo;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btnJump = findViewById(R.id.btn_jump);        tvUserInfo = findViewById(R.id.tv_user_info);        btnJump.setOnClickListener(this);    }    @Override    public void onClick(View view) {        switch (view.getId()) {            case R.id.btn_jump:                jumpToSDKServer();                break;            default:                break;        }    }    private void jumpToSDKServer() {        new Thread(new Runnable() {            @Override            public void run() {                try {                    String userInfo = AuthService.getInstance().getUserInfo();                    Log.e("SDKClient", "@@@@@@@@" + userInfo);                    handler.obtainMessage(MSG_USER_INFO, userInfo).sendToTarget();                } catch (Exception e) {                    e.printStackTrace();                }            }        }).start();    }    private Handler handler = new Handler() {        @Override        public void handleMessage(@NonNull Message msg) {            switch (msg.what) {                case MSG_USER_INFO:                    tvUserInfo.setVisibility(View.VISIBLE);                    tvUserInfo.setText("get user info:" + msg.obj);                    break;                default:                    super.handleMessage(msg);                    break;            }        }    };}

SDKClient客户端只需要一句话就可以获取到SDKServer的用户信息:AuthService.getInstance().getUserInfo();

效果图如下:

更多相关文章

  1. 在Android(安卓)Studio中运行java main方法
  2. Android(安卓)Audio代码分析5 - 函数getAudioSessionId
  3. Android定时器Timer.schedule
  4. [Android(安卓)Studio] 关于*.SO压缩问题
  5. Android中不使用AIDL实现Service的远程调用
  6. Android-EventBus(手写简易版)
  7. Android(安卓)下分批加载数据以及listView使用过程中的优化
  8. AndroidStudio中的图片资源存放位置以及drawable文件夹的创建方
  9. Android四大组件生命周期,组件类的继承与实现,Context

随机推荐

  1. Coder Essential之客户端知识索引(iOS/An
  2. Android Socket 实现
  3. Android 中文 API (27) ―― SeekBar.OnSee
  4. android小知识点
  5. 为Android内核添加新驱动
  6. ChkBugReport工具 for Android 1
  7. Android之SharedPreferences简介及使用说
  8. 设置Android SDK tools工作路径环境变量
  9. [Android Samples视频系列之ApiDemos] Ap
  10. -Android各版本系统源代码下载