Android Studio使用AIDL技术进行SDK开发
前面我们有介绍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开发相关推荐
- 在Android Studio上进行OpenCV 3.1开发
在Android Studio上进行OpenCV 3.1开发 发布于 2016年1月27日 作者: John Hany 5,466次阅读 2016.07.08更新:增加Android Studio 2 ...
- Android Studio快速集成讯飞SDK实现文字朗读功能
今天,我们来学习一下怎么在Android Studio快速集成讯飞SDK实现文字朗读功能,先看一下效果图: 第一步 :了解TTS语音服务 TTS的全称为Text To Speech,即"从文 ...
- Android电视切换回放,Android Studio V3.12环境下TV开发教程(五)建立电视回放应用...
Android Studio V3.12环境下TV开发教程 文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619382 浏览和播 ...
- android studio3.12,Android Studio V3.12环境下TV开发教程(六)提供卡片视图
Android Studio V3.12环境下TV开发教程 文章源自:光谷佳武 https://blog.csdn.net/jiawuhan/article/details/80619656 在上一课 ...
- Android Studio 是谷歌基于IntelliJ IDEA开发的安卓开发工具,有点类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调
Android Studio 是谷歌基于IntelliJ IDEA开发的安卓开发工具,有点类似 Eclipse ADT,Android Studio 提供了集成的 Android 开发工具用于开发和调 ...
- Android Studio 实现地图定位(移动开发技术作业)
文章目录 一.项目要求 二.项目功能展示 三.项目具体实现方法 1.创建项目及Android studio基础配置 2.将地图进行显示以及定位(代码实现) 四.遇到的问题解决方法及总结 五.项目源代码 ...
- 基于Android Studio的简易天气APP项目开发
参考文献:第一行代码(第二版),郭霖 源码地址:https://github.com/2066972218/coolweather/commits/master 一.功能需求 1.罗列全国的省.市.县 ...
- NDK开发 从入门到放弃(七:Android Studio 2.2 CMAKE 高效NDK开发)
原文地址:http://blog.csdn.net/cuiyufeng2/article/details/64125594 前言 之前,每次需要边写C++代码的时候,我的内心都是拒绝的. 1. 它没 ...
- Android Studio旧版(内含SDK)安装和环境变量配置 转自I-T枭
win10下Android Studio和SDK下载.安装和环境变量配置 ------made by siwuxie095 转自I-T枭https://me.csdn.net/hahahhahahah ...
最新文章
- AdventureWorksBI.msi 和 AdventureWorksDB.msi 的官方下载地址及安装方法
- 快速记忆python函数-让Python程序快速提升30%的技巧
- Android Bitmap开发之旅--基本操作
- 为什么接吻需要闭眼睛?
- python调试_Python调试坑
- 你真的知道什么是多线程吗?为什么要学习多线程?
- Python之网络编程(基于tcp实现远程执行命令)
- pandas根据某列值为key整合其他列值,拆分某列值增加多行数据
- Java面试题总结(二)
- sql server 2008 新建服务器注册,SQL Server 2008中不能注册服务器怎么回事
- Linux日志管理工具 journalctl
- 关于天线信号测量方法的记录-确定天线质量好坏-记录
- codesys写文件到远程并在远程读取文件
- 如何用python编写财务记账软件_Python实现简单的记账本功能
- Xamarin Mono For Android
- android平板电脑怎么才能连接电脑,平板连接电脑没反应怎么办 平板怎样连接电脑...
- C++打印图片的方法
- 全志T7/T507 Qt5.12.5移植记录
- 可靠性试验类毕业论文文献都有哪些?
- 宇宙存在三级量子--超越爱因斯坦
热门文章
- python excel条件格式_关于python调用Excel“条件格式”——二探openpyxl
- 数据分析模型篇—安索夫矩阵
- cad怎么设置线的粗细_CAD图纸线条粗细如何修改?CAD图纸线宽如何调整?
- 阿里云MVP北京闭门会圆满落幕 多把“利剑”助力开发者破阵蜕变...
- 他向导师下跪,仍被强制退学!5年博士白读,双方各执一词,同门师兄也有回应……...
- 1007 Maximum Subsequence Sum (25 分) java 题解
- Information Communication Technology,简称ICT
- linux startx 后返回命令行,输入命令: startx 反过来
- 讲道理,只要你是一个爱折腾的程序员,毕业找工作真的不需要再花钱培训!
- android开发流程