最近想總結一下關於應用如何封裝自己的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 版本之後必須添加
<service
android:name="com.example.androidsdkdemo.service.MySDKService"
android:enabled="true"
android:exported="true">
</service>
到此處服務端添加完成,此時可以運行一下服務端,先安裝到手機上方便後期直接調用.
第三步:客戶端 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("我是客戶端");
}
}
注意先運服務端再運行客戶端
運行結果如下: