Android 应用提供SDK Jar包给第三方使用 (设计思路 以及实现步骤)

Ady ·
更新时间:2024-11-15
· 618 次阅读

最近想总结一下关于应用如何封装自己的SDK给第三方应用使用,提供jar包给第三方使用是现在比较常见的方式,方式有很多种,但是具体的大体思路都是一样的,今天写了一个SDK封装的大体框架Demo,方便后期查查阅:

工具基于AndroidStudio 3.6.3版本

AndroidSDKDemo

大体的设计思路分为三个步骤

第一: 服务端 作为服务端 我们需要建立自己的aidl 以及实现类 方便第三方调用的时候 将服务端代理的句柄传给SDK

第二: SDK作为服务端和客户端的中间代理,可以直接拿到服务代理对象进行操作,并暴露给客户端可见的实用类和接口回调类

第三: 客户端只能使用SDK暴露出来的接口进行操作,不持有服务端代理.

具体的逻辑可以下图:

下面是对Demo的详解,个人建议 直接将Demo下载下来阅读即可,后面文章过于繁琐,时间充足的话建议阅读.

如图本项目分为三个 :

第一个是 客户端 app是个应用.

第二个是 SDK mylibrary是个lib库.

第三个是 clienapp 是个应用.

整体的架构设计如下图:

 第一步先编写SDK(mylibrary)  

mylibrary是一个lib项目不是可运行项目 

首先需要确定的是需要AIDL做什么, 只有确定了具体需要做的功能才能确定mylibrary项目中新建aidl文件之中的方法,本案例的设计一个是数据传递 一个是 数据回调.

在 MyRemoteCtrl.aidl中做了如下操作

package com.example.mylibrary; import com.example.mylibrary.MySDKStatusCallBack; interface MyRemoteCtrl { //传递数据 void sendMessage(String msg); //建立死亡链接 void linkToDeath(IBinder binder); //断开死亡链接 void unlinkToDeath(IBinder binder); //注册callback void registerMySDKStatusCallBack(MySDKStatusCallBack mySDKStatusCallBack); void unregisterMySDKStatusCallBack(MySDKStatusCallBack mySDKStatusCallBack); }

MySDKStatusCallBack.aidl中做了三个回调函数

// MySDKStatusCallBack.aidl package com.example.mylibrary; // Declare any non-default types here with import statements interface MySDKStatusCallBack { void statusCallBackVisible(); void statusCallBackInvisible(); void sendMessage(String message); }

创建和aidl用户具体的交互基类Controller.java基本 确定SDK的基本功能 因为需要和 service进行绑定所以继承于 ServiceConnection 实现 ServiceConnection的方法.

public interface Controller extends ServiceConnection { /** * init sdk * * @param context context * @return result */ int init(Context context); /** * setStateCallback * * @param remoteSDKStatusCallBack */ void setStateCallback(RemoteSDKStatusCallBack remoteSDKStatusCallBack); void setMessage(String msg); /*** * release sdk */ void release(); }

有了上面的AIDL文件之后开始确定 SDK的入口类MyLibSDK.java 这个类是需要暴露给第三方的使用的入口类,别人使用jar包的时候 只能通过 MyLibSDK.java类进行操作.

package com.example.mylibrary; import android.content.Context; import com.example.mylibrary.base.Controller; import com.example.mylibrary.callback.RemoteSDKStatusCallBack; import com.example.mylibrary.imp.MyController; public class MyLibSDK { private volatile static MyLibSDK mInstance; private Controller mController; private MyLibSDK() { } public static MyLibSDK getInstance() { if (mInstance == null) { synchronized (MyLibSDK.class) { if (mInstance == null) { mInstance = new MyLibSDK(); } } } return mInstance; } /** * 初始化SDK * * @param context context * @return result */ public int init(Context context) { mController = MyController.getInstance(); return mController.init(context); } /*** * 发送消息 */ public void setMessage(String msg) { if (mController != null) { mController.setMessage(msg); } } /*** * 释放SDK */ public void release() { if (mController != null) { mController.release(); } } /*** * 设置监听 */ public void setStateCallback(RemoteSDKStatusCallBack remoteSDKStatusCallBack) { if (mController != null) { mController.setStateCallback(remoteSDKStatusCallBack); } } }

具体的业务实现类是 MyController.java 它持有 MySDKStatusCallBack和 MyRemoteCtrl 对象 ,MySDKStatusCallBack MyRemoteCtrl 是aidl的类系统对创建对应的java文件如果没有创建 就使用编译器clean 一下项目 然后再build一下 项目

其实MyController.java ;类中的的 MyRemoteCtrl 就是绑定服务端之后服务端返回的代理对象, MyController.java 持有代理对象进行和服务端的交互操作.

package com.example.mylibrary.imp; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import com.example.mylibrary.MyRemoteCtrl; import com.example.mylibrary.MySDKStatusCallBack; import com.example.mylibrary.base.Controller; import com.example.mylibrary.callback.RemoteSDKStatusCallBack; public class MyController implements Controller { private MyRemoteCtrl mMyRemoteCtrl; private RemoteSDKStatusCallBack mRemoteSDKStatusCallBack; private boolean isbind; private volatile static Controller mInstance; protected Context mContext; //死亡链接 private MySDKStatusCallBack mMySDKStatusCallBack = new MySDKStatusCallBack.Stub() { @Override public void statusCallBackVisible() throws RemoteException { mRemoteSDKStatusCallBack.statusCallBackVisible(); } @Override public void statusCallBackInvisible() throws RemoteException { mRemoteSDKStatusCallBack.statusCallBackInvisible(); } @Override public void sendMessage(String message) throws RemoteException { mRemoteSDKStatusCallBack.sendMessage(message); } }; private MyController() { } @Override public int init(Context context) { //初始化服务 Log.d("mysdk", " sdk 初始化服务 "); mContext = context; return initService(); } @Override public void setStateCallback(RemoteSDKStatusCallBack remoteSDKStatusCallBack) { Log.d("mysdk", " sdk setStateCallback "); this.mRemoteSDKStatusCallBack = remoteSDKStatusCallBack; } private int initService() { Log.d("mysdk", " sdk initService "); Intent intent = new Intent("com.example.androidsdkdemo.service.MySDKService"); intent.setClassName("com.example.androidsdkdemo", "com.example.androidsdkdemo.service.MySDKService"); isbind = mContext.bindService(intent, this, Service.BIND_AUTO_CREATE); return 0; } @Override public void setMessage(String msg) { try { Log.d("mysdk", " sdk setMessage "); mMyRemoteCtrl.sendMessage(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void release() { Log.d("mysdk", " sdk release "); //SDK内部做释放操作 } @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d("mysdk", " sdk onServiceConnected "); if (service == null) { if (mMyRemoteCtrl != null) { try { mMyRemoteCtrl.unlinkToDeath(mMySDKStatusCallBack.asBinder()); } catch (RemoteException e) { e.printStackTrace(); } } mMyRemoteCtrl = null; } else { mMyRemoteCtrl = MyRemoteCtrl.Stub.asInterface(service); if (mMyRemoteCtrl != null) { try { mMyRemoteCtrl.linkToDeath(mMySDKStatusCallBack.asBinder()); } catch (RemoteException e) { e.printStackTrace(); } try { mMyRemoteCtrl.registerMySDKStatusCallBack(mMySDKStatusCallBack); } catch (RemoteException e) { e.printStackTrace(); } } } } @Override public void onServiceDisconnected(ComponentName name) { Log.d("mysdk", " sdk onServiceDisconnected "); if (mMyRemoteCtrl != null) { try { mMyRemoteCtrl.unregisterMySDKStatusCallBack(mMySDKStatusCallBack); mMyRemoteCtrl.unlinkToDeath(mMySDKStatusCallBack.asBinder()); } catch (RemoteException e) { e.printStackTrace(); } } mMyRemoteCtrl = null; } public static Controller getInstance() { if (mInstance == null) { synchronized (MyController.class) { if (mInstance == null) { mInstance = new MyController(); } } } return mInstance; } }

MyController.java 里有一个 RemoteSDKStatusCallBack对象,这个对象是暴露给客户 让客户进行注册的类对象属于SDK自己的CallBack类,当客户端设置这个Callback之后 SDK中就持有了客户端实现的Callback对象实例, SDK在接收到 服务端的回调之后 可以通过RemoteSDKStatusCallBack 实例回调给客户.这样做有个好处,客户端不会持有服务端的代理对象和不会持有SDK中的AIDL的回调实例,SDK中实现的AIDL具体的Callback就对客户是不可见的,此处RemoteSDKStatusCallBack.java 内部的名字和MySDKStatusCallBack.aidl的相同,也可以根据自己的需求添加接口,或者修改名称.

package com.example.mylibrary.callback; public interface RemoteSDKStatusCallBack { void statusCallBackVisible(); void statusCallBackInvisible(); void sendMessage(String message); }

之后进行进行生成jar包的配置,在mylibrary 的build.gradle 里面添加如下代码

注意 from('build/intermediates/aar_main_jar/debug/') 可能路径不同的版本不同需要搜索一下 classes.jar 路径 进行替换即可

task makeJar(type:Copy){ //如果之前存在,则先删除 delete 'build/libs/mysdklib.jar' //设置拷贝的文件 from('build/intermediates/aar_main_jar/debug/') //生成jar包后的文件目录位置 into('build/libs/') //include,exclude参数来设置过滤 include('classes.jar') //重命名 rename('classes.jar','mysdklib.jar') } makeJar.dependsOn(build)

生成jar包的两种方式

  1,可以在根目录执行  ./gradlew makeJar

2.或者在右上角的 Gradle --> mylibrary-->Tasks --> other --> makeJar 双击makeJar 生成

到此 jar包的生成已经做完了.

第二步 : 写服务端的类 服务端app 

首先需要将 mylibrary 导入到app中,因为需要要到SDK中的aidl,在APP项目中的build.gradle 中的 dependencies 添加如下代码不然服务端使用不了mylibrary中的aidl文件.

implementation project(':mylibrary')

MySDKService.java 中的内部类 MyRemoteCtrlImpl 是服务的代理类 也是SDK端拿到的 MyRemoteCtrl代理对象类,服务中我们可以控制代理对象具体功能,那么也就间接的控制了第三方可以使用服务端app的那些功能,我们这里这里没有做具体的控制逻辑,只是将接收到的数据进行返回操作即可. 

package com.example.androidsdkdemo.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import androidx.annotation.Nullable; import com.example.mylibrary.MySDKStatusCallBack; import com.example.mylibrary.MyRemoteCtrl; public class MySDKService extends Service { private MyRemoteCtrlImpl mCarcorderRemoteCtrl = new MyRemoteCtrlImpl(); private MySDKStatusCallBack mMySDKStatusCallBack = null; @Nullable @Override public IBinder onBind(Intent intent) { //返回内部代理对象给调用端 return mCarcorderRemoteCtrl.asBinder(); } public class MyRemoteCtrlImpl extends MyRemoteCtrl.Stub { private IBinder mBinder = null; private Object deathRecipient; @Override public void sendMessage(String msg) throws RemoteException { if (mMySDKStatusCallBack != null) { mMySDKStatusCallBack.sendMessage("我已经接收到你的数据返回给你 = " + msg); } } @Override public void linkToDeath(IBinder binder) throws RemoteException { Log.d("mysdk"," 服务端 建立死亡链接 "); mBinder = binder; binder.linkToDeath(mDeathRecipient, 0); } @Override public void unlinkToDeath(IBinder binder) throws RemoteException { Log.d("mysdk"," 服务端 断开客户端死亡链接 "); binder.unlinkToDeath(mDeathRecipient, 0); mBinder = null; } @Override public void registerMySDKStatusCallBack(MySDKStatusCallBack mySDKStatusCallBack) throws RemoteException { Log.d("mysdk"," 服务端 接收到 registerMySDKStatusCallBack "); mMySDKStatusCallBack = mySDKStatusCallBack; // mMySDKStatusCallBack 为第三方通过SDK传递过来的 对象 调用 mySDKStatusCallBack.statusCallBackInvisible() // 相当于持有 MySDKStatusCallBack mMySDKStatusCallBack //这里不做操作直接返回即可 if (mMySDKStatusCallBack != null) { mySDKStatusCallBack.statusCallBackInvisible(); } } @Override public void unregisterMySDKStatusCallBack(MySDKStatusCallBack mySDKStatusCallBack) throws RemoteException { Log.d("mysdk"," 服务端 接收到 unregisterMySDKStatusCallBack "); mMySDKStatusCallBack = null; } } IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { /* if (mMySDKStatusCallBack != null) { try { mMySDKStatusCallBack.statusCallBackInvisible(); } catch (RemoteException e) { e.printStackTrace(); } }*/ //客户端可以执行释放操作 Log.d("mysdk"," 调用端已经死亡"); } }; }

在AndroidManifest.xml中天机如下代码       android:enabled="true"   android:exported="true" 4.4 版本之后必须添加

到此处服务端添加完成,此时可以运行一下服务端,先安装到手机上方便后期直接调用.

第三步:客户端 clienapp实现 

将mysdklib.jar 拷贝到 AndroidSDKDemo/clienapp/libs 下面 并且在 clienapp 项目 中 build.gradle的dependencies中

添加如下:

implementation files('libs/mysdklib.jar')

在 MainActivity 使用我们的sdk jar包中的类 MyLibSDK 进行服务端交互

package com.example.clienapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import com.example.mylibrary.MyLibSDK; import com.example.mylibrary.callback.RemoteSDKStatusCallBack; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyLibSDK.getInstance().init(this); MyLibSDK.getInstance().setStateCallback(new RemoteSDKStatusCallBack() { @Override public void statusCallBackVisible() { Log.d("mysdk"," 客户端 statusCallBackVisible "); } @Override public void statusCallBackInvisible() { Log.d("mysdk"," 客户端 statusCallBackInvisible "); } @Override public void sendMessage(String s) { Log.d("mysdk"," 客户端 sendMessage " + s); } }); } public void sedmessage(View view) { Log.d("mysdk"," 客户端 sendMessage 我是客户端 " ); MyLibSDK.getInstance().setMessage("我是客户端"); } } 注意先运服务端再运行客户端 


运行结果如下:

ChaoLi_Chen 原创文章 32获赞 36访问量 2万+ 关注 私信 展开阅读全文
作者:ChaoLi_Chen



jar jar包 设计思路 sdk Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号