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

Android进程间通信——AIDL

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

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

SDKServer代码实现

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

// IAuth.aidl
package 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.aidl
package com.example.server.aidl;parcelable User;

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

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

在AndroidManifest中注册,

<serviceandroid:name=".aidl.AuthService"android:enabled="true"android:exported="true"android:process=":remote"><intent-filter><action android:name="auth_request_from_sdk" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</service>

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() {@Overridepublic void onReceive(Context context, Intent intent) {//notify()方法能够唤醒一个正在等待该对象的锁的线程,当有多个线程都在等待该对象的锁的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知//调用某个对象的notify()方法,当前线程也必须拥有这个对象的锁,因此调用notify()方法必须在同步块或者同步方法中进行synchronized (waitUserAuth) {isUserAuth = intent.getBooleanExtra("isUserAuth", false);waitUserAuth.notify();}}};public AuthImpl(Service service) {mService = service;}@Overridepublic String getAuthCode() throws RemoteException {authCode = UUID.randomUUID().toString();try {return RSATool.encode(authCode);} catch (Exception e) {e.printStackTrace();}return "";}@Overridepublic void checkAuthCode(String code) throws RemoteException {if (code.equals(authCode)) {isAuthed = true;}}@Overridepublic 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;@Overrideprotected 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);}@Overridepublic 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当前账户信息给SDKClientIntent 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() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {remoteAuth = IAuth.Stub.asInterface(iBinder);Log.e(TAG, "Service 连接成功");}@Overridepublic 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 {@Overridepublic 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;@Overrideprotected 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);}@Overridepublic void onClick(View view) {switch (view.getId()) {case R.id.btn_jump:jumpToSDKServer();break;default:break;}}private void jumpToSDKServer() {new Thread(new Runnable() {@Overridepublic 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() {@Overridepublic 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();

效果图如下:

Android Studio使用AIDL技术进行SDK开发相关推荐

  1. 在Android Studio上进行OpenCV 3.1开发

    在Android Studio上进行OpenCV 3.1开发 发布于 2016年1月27日 作者: John Hany 5,466次阅读 2016.07.08更新:增加Android Studio 2 ...

  2. Android Studio快速集成讯飞SDK实现文字朗读功能

    今天,我们来学习一下怎么在Android Studio快速集成讯飞SDK实现文字朗读功能,先看一下效果图: 第一步 :了解TTS语音服务 TTS的全称为Text To Speech,即"从文 ...

  3. Android电视切换回放,Android Studio V3.12环境下TV开发教程(五)建立电视回放应用...

    Android Studio V3.12环境下TV开发教程 文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619382 浏览和播 ...

  4. android studio3.12,Android Studio V3.12环境下TV开发教程(六)提供卡片视图

    Android Studio V3.12环境下TV开发教程 文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619656 在上一课 ...

  5. Android Studio 是谷歌基于IntelliJ IDEA开发的安卓开发工具,有点类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调

    Android Studio 是谷歌基于IntelliJ IDEA开发的安卓开发工具,有点类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调 ...

  6. Android Studio 实现地图定位(移动开发技术作业)

    文章目录 一.项目要求 二.项目功能展示 三.项目具体实现方法 1.创建项目及Android studio基础配置 2.将地图进行显示以及定位(代码实现) 四.遇到的问题解决方法及总结 五.项目源代码 ...

  7. 基于Android Studio的简易天气APP项目开发

    参考文献:第一行代码(第二版),郭霖 源码地址:https://github.com/2066972218/coolweather/commits/master 一.功能需求 1.罗列全国的省.市.县 ...

  8. NDK开发 从入门到放弃(七:Android Studio 2.2 CMAKE 高效NDK开发)

    原文地址:http://blog.csdn.net/cuiyufeng2/article/details/64125594 前言 之前,每次需要边写C++代码的时候,我的内心都是拒绝的.  1. 它没 ...

  9. Android Studio旧版(内含SDK)安装和环境变量配置 转自I-T枭

    win10下Android Studio和SDK下载.安装和环境变量配置 ------made by siwuxie095 转自I-T枭https://me.csdn.net/hahahhahahah ...

最新文章

  1. AdventureWorksBI.msi 和 AdventureWorksDB.msi 的官方下载地址及安装方法
  2. 快速记忆python函数-让Python程序快速提升30%的技巧
  3. Android Bitmap开发之旅--基本操作
  4. 为什么接吻需要闭眼睛?
  5. python调试_Python调试坑
  6. 你真的知道什么是多线程吗?为什么要学习多线程?
  7. Python之网络编程(基于tcp实现远程执行命令)
  8. pandas根据某列值为key整合其他列值,拆分某列值增加多行数据
  9. Java面试题总结(二)
  10. sql server 2008 新建服务器注册,SQL Server 2008中不能注册服务器怎么回事
  11. Linux日志管理工具 journalctl
  12. 关于天线信号测量方法的记录-确定天线质量好坏-记录
  13. codesys写文件到远程并在远程读取文件
  14. 如何用python编写财务记账软件_Python实现简单的记账本功能
  15. Xamarin Mono For Android
  16. android平板电脑怎么才能连接电脑,平板连接电脑没反应怎么办 平板怎样连接电脑...
  17. C++打印图片的方法
  18. 全志T7/T507 Qt5.12.5移植记录
  19. 可靠性试验类毕业论文文献都有哪些?
  20. 宇宙存在三级量子--超越爱因斯坦

热门文章

  1. python excel条件格式_关于python调用Excel“条件格式”——二探openpyxl
  2. 数据分析模型篇—安索夫矩阵
  3. cad怎么设置线的粗细_CAD图纸线条粗细如何修改?CAD图纸线宽如何调整?
  4. 阿里云MVP北京闭门会圆满落幕 多把“利剑”助力开发者破阵蜕变...
  5. 他向导师下跪,仍被强制退学!5年博士白读,双方各执一词,同门师兄也有回应……...
  6. 1007 Maximum Subsequence Sum (25 分) java 题解
  7. Information Communication Technology,简称ICT
  8. linux startx 后返回命令行,输入命令: startx 反过来
  9. 讲道理,只要你是一个爱折腾的程序员,毕业找工作真的不需要再花钱培训!
  10. android开发流程