The development of App is more inclined to the user level, from UI display to business logic processing, processing user behavior in the whole process. SDK is for developers, development is more inclined to function, focus on the implementation of function development. In today’s post, FinCliip’s engineers talk to us about designing the SDK.

What is SDK?

SDK stands for Software Development Kit. In a broad sense, SDK is a collection of Development tools used to build applications for specific Software packages, Software frameworks, hardware platforms, operating systems, etc. (In iOS projects, SDK is also called library).

In iOS or Android development, it is inevitable to use third-party tools to improve product development efficiency, such as aurora for message push, Alipay for third-party payment and login, wechat and so on. However, most commercial products don’t give the source code directly (perhaps only open source projects for love power will do so selflessly), and we need to integrate these third-party SDKS into our projects when developing apps.

SDK stands for Software Development Kit, which translates as Software Development Kit. It is a specific Software package written to assist in the Development of certain kinds of Software.

Ii. Basic principles of SDK design

A good and well-designed SDK must follow the following four basic principles:

  1. SDK is secure and stable
  2. Uniform development specifications
  3. Library is small but good
  4. Do not rely on third-party SDKS
  • Security and stability: Considering that SDK needs to be embedded into App, the most important feature of SDK is security, which will not lead to App data leakage due to randomly open interfaces; The second important thing is the stability of SDK. If THE Crash of SDK is not captured and processed, the application will completely Crash (which will lead to the poor experience of third-party access App), and even directly lead to the loss of users of the access party.
  • Unified development specification: For SDK development specification, unified naming specification is very important, the best state is “access party can know which manufacturer’s SDK after seeing the interface naming”, in other words, the unified naming specification of SDK forms its own brand effect, in addition, it is also convenient for developers to access and use. In addition, you also need to have their own coding standards. You can find the standard templates of big factories on the Internet, and sort out their own specifications by referring to them, so as to unify the code style as soon as possible.
  • Library is small but fine: Small means to avoid a large increase in the number of apps of access parties, or it will cause dissatisfaction of access parties, or even the removal of the App. Fine means to focus on the function, such as aurora push, is focused on pushing related functions;
  • Not relying on third-party SDK: This is also easy to understand. If the SDK relies on other third-party SDKS, it will not only increase the size of SDK, but also affect the related costs of SDK integration for access party.

3. Develop SDK in iOS environment

1. SDK in iOS

As mentioned above, in iOS development, we refer to the SDK as a “library,” and here’s how we define it:

  1. A collection of independent programs that provide a general service to an application.
  2. Are generally compiled, easy to use.

We will divide the library into “static library” and “dynamic library” according to the call method:

  1. Static linking: Generally, libraries are integrated into an application when it is created. The advantage of this is that the application package itself can run independently, but the disadvantage is that the package is a bit bloated and libraries cannot be shared (static libraries often end in.a).
  2. Dynamic connection: The application is created with the convention of the call relationship with the library rather than the complete integration of the library package into the application. This requires libraries to be provided in the runtime environment and connected to load when the application runs. In contrast to static libraries, dynamically linked libraries require a library environment, but they are smaller because they do not integrate library content. At the same time, it is possible to share libraries with other applications (common dynamic libraries are.dll under Windows, so under Linux,.dylib/.tbd under Mac).

Note: The Framework (in Apple) is used in Cocoa/Cocoa Touch applications as a way of packaging resources. It can put code files, header files, resource files, documentation, etc., in one place for developers to use. In other words, our Framework is actually a way of packaging resources, which has nothing to do with the nature of static libraries and dynamic libraries.

2. The difference between static and dynamic libraries

The difference between a static library and a dynamic library can be explained in terms of file linking (each source code module is compiled independently and assembled as needed, the process of assembling modules is called linking) :

  • Static libraries: Links are copied in their entirety into executables, so if both programs use a static library, each binary executable will contain the code for that static library.
  • Dynamic library: Linking is not copied, but is dynamically loaded after the program is started, followed by symbolic resolution (symbolic binding). Theoretically, only one copy of the dynamic library exists. Other programs can dynamically link to the library to save memory (there is only one copy of the library in memory). Another benefit is that since a dynamic library is not tied to an executable, it is easy to upgrade the dynamic library, as is common with plug-in and module mechanisms on Windows and Linux.

The specific advantages and disadvantages can be seen in this table:

Library type advantages disadvantages
Static library 1. The target program can run directly without external dependencies. 2. Higher efficiency than dynamic library. 1. Will use the target program’s size increases.
The dynamic library 1. It does not need to be copied to the target program and does not affect the volume of the target program. 2. The same library can be used by multiple programs (for this reason, dynamic libraries are also called shared libraries). 3. Features that are loaded at compile time also allow us to replace libraries at any time without recompiling the code. Implementing dynamic updates 2. Dynamic libraries also make programs dependent on the external environment. If the environment is missing dynamic libraries or the library version is not correct, the program will not run (Linux lib Not found error).

A static library can be understood simply as a package of object files (.o/.obj) (not a binary), while a dynamic library can be understood simply as an executable without the main function.

3. Understand iOS dynamic libraries (i.e., emasculated dynamic libraries)

As a bit of background note, iOS officially does not allow dynamic libraries, and all ipAs need to be encrypted with Apple’s private key before they can be used. Even if you use dynamic libraries, they will not load due to the wrong signature (jailbreak and non-App Store excepted). This makes it impossible for developers to develop their own dynamic libraries.

Before iOS8, iOS apps were running in a sandbox, different programs could not share code, and iOS is a single process (that is, only one process is running at a time), so even if you write a shared library can not be shared with others.

Dynamic downloading of code is officially prohibited by Apple, which means that the benefits of dynamic libraries are completely out of play, so there is no need for dynamic libraries.

But all of this has changed dramatically with the release of iOS8 with the Extesion feature for Swift.

Because the main iOS App needed to share code with Extension, and the Swift language mechanism also needed a dynamic library, Apple later proposed the Embedded Framework, This dynamic library allows the APP and APP Extension to share code (the life of the dynamic library is limited to one APP process).

A simpler explanation: dynamic libraries are provided, but they are neutered.

However, there is a big difference between Embedded Framework and THE SYSTEM’s UIKit.Framework. While traditional dynamic libraries are used by multiple processes, Embedded Framework is used by multiple executables in a single process.

The system Framework no longer needs to be copied to the target program. Even if the Embedded Framework is dynamic, it still needs to be copied to the App (the Bundle of App and Extension is shared). So Instead of calling it a dynamic library, Apple calls it Embedded Framework.

Swift is mentioned above for a reason. In a Swift project, if you want to use external code in a project, there are only two options, one is to copy the code into the project, the other is to use dynamic Framework. Using static libraries is not supported.

The root cause of this problem is that the Swift runtime is not included in the iOS system, but packaged into the App (which is also the reason for the large size of the Swift App), and the static library will result in the final target application containing duplicate runtime.

4. Using the dynamic library as an example, start making the SDK

Step 1: Create App project, named RealDemo

If you don’t know how to create it, you can click here, okay

Step 2: Close the RealDemo project, and create the Framework project in the RealDemo directory, named RealSDK

Select the Framework below to create

Remember not to choose the wrong directory

Step 3: Set the Build Settings for the Framework project

Select Dynamic Library to create Dynamic libraries and Static Library to create Static libraries

Step 4: Close the RealSDK project and create a WorkSpace named RealDemo

Click file-new-workspace one by one

The corresponding workspace file will appear after the creation

Step 5: Connect the Framework project and App project

We need to open up RealDemo.xcWorkspace, which you’ll find empty.

Then we directly need to connect to the Framework project (realsdK.xcodeProj) and App project (realDemo.xcodeProj) drag in!

Just drag it into Xcode

If you drag and drop, you’ll see that they have the same hierarchy

Step 6: Add the Framework to the App project

Click one by one, don’t point wrong

Select the framework you created earlier

Students who have SDK development experience should have seen it clearly here. The so-called real-time integration is to connect two projects with WorkSpace, which is somewhat similar to the principle of Pod.

Step 7: Add features to the Framework

We need to add a RealDog class that defines an eat method that prints the words “eat bones”. Then change the Target Membership of realdog. h to Public, meaning that it is a Public starting file.

Here I define the eat method RealDog as follows:

@implementation RealDog + (void)eat {NSLog(@" eat bone "); } @endCopy the code

Step 8: Call the SDK methods in the App ViewController

#import "ViewController.h"
#import <RealSDK/RealDog.h>

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [RealDog eat];
}

@end
Copy the code

Step 9: Run it to see that the App project successfully called the SDK’s methods

Okay, this is the end of the live feed

5. Use scripts to merge multiple architectures such as real machine and simulator

Step 1: Add an Aggregate Target

RealSDK Project -> TARGETS -> “+”(lower left) -> cross-platform-other -> Aggregate

Step 2: Name Aggregate Target “RealSDK-script”

After naming it, click Finish

Step 3: Rely on the RealSDK

Configure the Dependencies in Dependencies

Step 4: Add the script

Click on New Run Script Phase here

This script is universal, you can directly copy and paste:

# Type a script or drag a script file from your workspace to insert its path. UNIVERSAL_OUTPUTFOLDER=.. /Framework/ # create output directory, Mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" rm -rf "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}. Framework "# Build -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH= no-configuration ${configuration} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" BUILD_ROOT="${BUILD_ROOT} IPHONE_BUILD=${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework SIMULATOR_BUILD=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}. Framework # Copy framework to univer cp -r ${IPHONE_BUILD}" "${UNIVERSAL_OUTPUTFOLDER}/" #cp -r "${SIMULATOR_BUILD}" "${UNIVERSAL_OUTPUTFOLDER}/" # define the output path variable ${PROJECT_NAME}. Framework Output the final framework to the build directory lipo -create "${IPHONE_BUILD}/${PROJECT_NAME}" "${SIMULATOR_BUILD}/${PROJECT_NAME}" -output "${OUTPUT_PATH}/${PROJECT_NAME}"Copy the code

Step 5: Run the script

Again, click the play button on the left

Step 6: View the results

If you can see this folder, you have compiled successfully!

If your Mac is the latest M1 chip, you may get the following error:

fatal error: lipo: /Users/hujianhui/Library/Developer/Xcode/DerivedData/RealDemo-ckvcidkkuvgpadeiqrvgjdyikcdc/Build/Products/Debug-iphoneos /RealSDK.framework/RealSDK and /Users/hujianhui/Library/Developer/Xcode/DerivedData/RealDemo-ckvcidkkuvgpadeiqrvgjdyikcdc/Build/Products/Debug-iphonesi mulator/RealSDK.framework/RealSDK have the same architectures (arm64) and can't be in the same fat output fileCopy the code

You just need to remove the ARM64 architecture of the iOS emulator, and here’s how:

Click on the minus sign in front of arm64 to copy and paste this script:

# Type a script or drag a script file from your workspace to insert its path. UNIVERSAL_OUTPUTFOLDER=.. /Framework/ # create output directory, Mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" rm -rf "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}. Framework "# Build -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH= no-configuration ${configuration} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" BUILD_ROOT="${BUILD_ROOT} IPHONE_BUILD=${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework SIMULATOR_BUILD=${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}. Framework # Copy framework to univer cp -r ${IPHONE_BUILD}" "${UNIVERSAL_OUTPUTFOLDER}/" #cp -r "${SIMULATOR_BUILD}" "${UNIVERSAL_OUTPUTFOLDER}/" # define the output path variable ${PROJECT_NAME}. Framework Output the final framework to the build directory lipo -create "${IPHONE_BUILD}/${PROJECT_NAME}" "${SIMULATOR_BUILD}/${PROJECT_NAME}" -output "${OUTPUT_PATH}/${PROJECT_NAME}"Copy the code

Step 5: Run the script

Again, click the play button on the left

Step 6: View the results

If you can see this folder, you have compiled successfully!

If your Mac is the latest M1 chip, you may get the following error:

fatal error: lipo: /Users/hujianhui/Library/Developer/Xcode/DerivedData/RealDemo-ckvcidkkuvgpadeiqrvgjdyikcdc/Build/Products/Debug-iphoneos /RealSDK.framework/RealSDK and /Users/hujianhui/Library/Developer/Xcode/DerivedData/RealDemo-ckvcidkkuvgpadeiqrvgjdyikcdc/Build/Products/Debug-iphonesi mulator/RealSDK.framework/RealSDK have the same architectures (arm64) and can't be in the same fat output fileCopy the code

You just need to remove the ARM64 architecture of the iOS emulator, and here’s how:

Click on the minus sign in front of arm64

6. Tips:

  1. The Framework uses categories

Add -objc to the Framework project Build Setting. In addition, -objc should also be added to the Build Setting of the App using our SDK.

  1. The Framework supports bitcode

4. Develop SDK in Android environment

1. Introduction to Android SDK

There are three main file types for Android App to integrate third-party SDK. One is JAR package file and SO file, and the other is AAR file. JAR package is the SDK file type provided by Java, which contains pure Java compiled code. SO is generally C and C++ packaged as a library file.

AAR, named after Android Archive, is a binary Archive of Android library projects. Using Android Studio, you can easily generate an AAR file. AAR library file contains JAR, SO, resource Res and other files, the structure is equivalent to an App.

It provides everything you need to build your application, including source code, resource files, and Android listings. However, the Android library will be compiled as an Android ARchive (AAR) file that you can use as an Android application module dependency, rather than an APK that runs on the device.

Unlike JAR files, AAR files provide the following capabilities for Android applications:

  • An AAR file can contain multiple Android resources and a manifest file, allowing you to bundle in shared resources such as layouts and drawable objects in addition to Java classes and methods;
  • An AAR file can contain C/C++ libraries for use with the C/C++ code of a module.

2. Create an SDK project

Open the sample project we created in the previous section, create a Library Module on the project, named GPush, and let’s simulate implementing an interface that pushes short news items.

If you forget how to do it, you can click here, okay

Select Android Library and type GPush in the name

Once added, we’ll see the corresponding Libray file on the left

3. Add dependencies

To use the new Android library code in another application or library module in the same project, add a project-level dependency like this:

  1. Go to File > Project Structure > Dependencies;
  2. Select the module in which you want to use the library;
  3. On the Declared Dependencies TAB, click +, then select Module Dependency from the drop-down menu.

Click the plus sign and then the corresponding Module Dependency

Add it to the dependency

4. Interface design

Since it is an interface to push news, it must be divided into client side and push side, namely Client#onReceiveMessage and GPush#pushMessage.

As you can see from the UML diagram below, there is only one method to listen to the newsfeed, GPushImpl#start(Client Client).

Take a look at the UML diagrams I drew to help you understand more

GPush class

package com.myname.library;
interface GPush {
    void pushMessage(String msg);
}
Copy the code

GPushImpl class

package com.myname.library; import android.os.Handler; import android.os.HandlerThread; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; public class GPushImpl implements GPush { private List<Client> mClients; private HandlerThread mHandlerThread; private Handler mHandler; private Random mRandom = new Random(); Private List<String> MSGS = new ArrayList<String>() {{add("1, "); 2. Accelerate the reform of household registration along the Yangtze River to serve the high-quality development of the Yangtze Economic Belt. ); The websites of 10 illegal social organizations were shut down in the first batch this year, including the China Literary and Art Celebrities Association. ); Shanghai: From May 1, electric bike riders must wear helmets. ); 5, Guangzhou: Tomb-sweeping day to implement real-name appointment, advocating online worship, wrong peak delayed worship. ); 6. The accident of 6 deaths in Hebei Wuan Iron Mine was suspected of concealing the report, and the relevant personnel of the enterprise were controlled. ); 7. Huang Zheng resigned as chairman of PINDUoduo: He will give up super voting rights and devote himself to scientific research. ); 8. Breaking the 20-year monopoly of foreign countries, the domestic artificial heart has been successfully developed, but the time of commercial implementation is uncertain. ); 9, survey: 60% of young people fall asleep later than 23 o 'clock, dream more sleep light into young people sleep main problem. ); }}; GPushImpl() { mClients = new ArrayList<>(); mHandlerThread = new HandlerThread("Push-Thread"); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); } @Override public void pushMessage(String msg) { Iterator<Client> iterator = mClients.iterator(); while (iterator.hasNext()) { iterator.next().onReceiveMessage(msg); } } public static void start(Client client) { GPushImpl gPush = new GPushImpl(); gPush.mClients.add(client); gPush.mHandler.post(gPush.mRunnable); } private Runnable mRunnable = new Runnable() { @Override public void run() { mHandler.postDelayed(mRunnable, mRandom.nextInt(10000)); pushMessage(msgs.get(mRandom.nextInt(msgs.size()))); }}; }Copy the code

The Client class

public interface Client {
    void onReceiveMessage(String msg);
}
Copy the code

Start listening to your news feed

GPushImpl.start {
            Toast.makeText(MainActivity@this,it,Toast.LENGTH_LONG).show()
        }
Copy the code

The SDK package

 ./gradlew :GPush:assembleRelease 
Copy the code

When the package is complete, an AAR file is generated, which is the result of our package

5. Last note –confusion

For code protection purposes, Gradle packaging by default confuses code based on build. Gradle and proGuard-rules.pro obturation rules. If the library uses techniques such as GSON or reflection, some classes need to be kept.

buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
Copy the code
-keep class com.myname.library.** {*; }Copy the code

If you follow this tutorial correctly, you have successfully created your first iOS and Android SDK. This tutorial is still implemented on a MAC, but if you have a Windows or other operating system, you will need to make some flexible configurations.