Since I quit my job some time ago, I have not chosen to work again due to personal matters, which also led to the original article did not produce on time. Recently, my personal affairs have been sorted out, and there happened to be many friends who asked about SDK development. Here I will do a simple sorting, hoping to help you.

At present, more developers are keen on application development, and very few developers have the opportunity to engage in SDK development work, and there are few articles on the market about SDK development, so that we feel that SDK development is relatively difficult and very boring work, today we will simply talk about SDK development which things.

In addition, recently there seems to be a blog star selection, I also come to gather a lively, just for a vote, not other: point me, point me, vote, all for the girl point me, point me, vote, all for the girl

An explanation of the SDK

Before we get started, let’s talk about what the SDK is.

SDK is the abbreviation of Software Development Kit, which is translated as “Software Development Kit”. It is usually a specific Software package, framework collection, etc., written to assist the Development of a certain kind of Software. SDK generally contains related documents, examples, and tools.

SDK can be divided into system SDK and application SDK. The so-called system SDK is a collection of development tools used for specific software packages, software frameworks, hardware platforms, operating systems and other applications. The application SDK is a collection of specific functions developed based on the system SDK and independent of specific business.

For example, when developing Android applications, we use the system SDK provided by Google (Android SDK), while the Umeng SDK and Aurora SDK that we often use are developed based on the system SDK.

Now that the CONCEPT of the SDK is clear, let’s talk about the three concepts :Library,API, and Framework

What is the Library

A Library is usually a collection of classes or groups of classes. It is usually a concrete implementation of some function in an application or an enhancement or supplement to existing functions in the system. For Android developers, the most common is the Support Library, and we often use various Web request libraries (OkHttp,Volley), database manipulation, Glide,ImageLoader, etc.

What is a Framework

A Framework, as we call it, is usually the skeleton of a system or application. Most of the time, it is represented as a set of abstract ways to build and interact with instances of components. Therefore, the Framework can be thought of as specifying the application architecture, clarifying the overall design, dependencies between writing components, and control flow. Note that the Framework is not exactly the same as the Android Framework you are familiar with. It can be considered that the Android Framework embodies the ideas of the Framework and implements them.

What is the API

API is an Application Programming Interface, also known as an Application Programming Interface, which connects different components of a software system. More generally speaking, an API is a method or function that we commonly write.

summary

Having defined the concepts mentioned above, it is now possible to describe the relationship between the four: SDK mainly consists of three parts: Framework,API and Library. The Framework defines the reusable design of the SDK as a whole, and specifies the responsibilities and dependencies of each functional module of the SDK. One of the functional modules is Library. The internal communication between modules and SDK external communication (SDK external service interface) is through API.

Also, a full SDK should include plenty of examples and other tools. For example, the Tools directory of the Android SDK provides a number of auxiliary development tools.

For us, most of the time it’s about developing an SDK for a specific business requirement so that it can be used as a third party by other demanders. For example, the SDK pushed by Baidu mainly realizes the message push function, and the demander only needs to integrate the SDK pushed by Baidu to enable their application to have the push function.

Now that we’ve covered the main components of the SDK, let’s focus on the goals of the SDK and some of the core points in the SDK architecture.

A brief introduction to SDK goals

Now that we’ve introduced common concepts in development, let’s talk about what the SDK can accomplish. Any application should have: simple and easy to use, stable, efficient, lightweight,SDK as a specific application is certainly no exception.

Concise and easy to use

According to Occam’s razor theory, a good product should be simple and easy to use for third party users without requiring users to spend too much time learning. The same goes for SDKS — SDKS should not have too much code intrusion into the host application, nor should they have complex and frequent access work. For example, when a developer needs to use an SDK service, all it needs to do is add a new line to the source code. Common SDK initializations are as follows:

public class Ad{ @TargetApi(9) public synchronized static void init(Context context, SdkParams params) { }
}
Copy the code

Ad.init(this,params) is enabled in one line of code when we need to use the SDK’s services.

To ensure less code intrusion, it is important to design a good API that fully considers the user’s usage scenarios when providing services externally. A good API should be defined in the way most developers expect it to be – semantically easy to understand, and simple and reliable to use.

A good API is first and foremost simple and reliable. In the case of normal use, it is reflected in stable and reliable execution, and in abnormal cases, it is reflected in informing users of errors in time. Following consistent explicit rules and having all apis present a consistent style is good news for development beyond the first time.

stable

From the perspective of SDK users, we expect third-party SDK services to be stable and efficient, which is reflected in providing stable and reliable services and being efficient enough on the premise of not affecting host stability. This requires SDK designers to do the following as much as possible when designing and implementing SDK:

  • Once the API of SDK is determined, it cannot be changed unless there is a very serious situation. As a service provider, the change costs associated with API changes are significant.
  • To provide stable external business. After a stable API, there must be a stable business to support.
  • The stability of SDK runtime. As the service provider, we must ensure the stable operation of SDK itself, and ensure that the access party will not be unstable due to our SDK.
  • Version updates are steady. SDK versions iterate very slowly compared to applications intended for the average user. Iterative processes need to be shielded from developers as much as possible to avoid unnecessary adaptation costs.

efficient

Performance issues should be considered in both general application development and SDK development. SDK designers should focus on the following issues:

  • Less memory footprint. In the case of not using multiple processes, the SDK service and the host program run in the same process, in this case, it must be required to limit the OCCUPATION of SDK memory, not because our SDK occupies too much memory resources, resulting in a shorter application survival time.
  • Less memory jitter. SDK designers must deliberately reduce memory jitter caused by repeated GC in order to use less memory.
  • Less power consumption. Although most of the time it’s not possible to make a good trade-off, there are some things you can do, such as reducing the amount of time you spend using power-consuming modules. For example, when using location services, network location is preferred over GPS location without requiring very high accuracy.
  • Less traffic consumption.

SDK overall architecture design

The architectural implementation of THE SDK determines the subsequent maintenance difficulty of the SDK, so it is necessary to briefly explain some points in the overall architecture of the SDK here.

Modular development

Split the system into smaller modules based on a single responsibility, with each module remaining relatively independent.

Modules communicate with each other through protocols or interfaces to reduce dependent coupling between them. In order to ensure that the module itself can be flexibly implemented, the module is implemented according to several principles of design

For modern development, modularization is a common means. From a macroscopic point of view, module is the smallest component unit of the system.

Componentized development

Component development is also an old concept, but in my personal experience, components encapsulate logic and are individually portable. For example, logging can be made into a component that can then be easily applied to different projects. For Android developers, every UI control provided by Android is also a component, such as Button,TextView, etc.

After clear the concept of component, modular development also is easy to understand: the so-called modular is to the whole project is divided into multiple modules, several modules, or a single module as a component, we can in the development process to parallel development of each component, finally released by relying on the component merge into a complete application.

So why use componentization? With the gradual maturity of Android, the app business is becoming more and more complex. At the same time, the Android project is getting bigger and bigger,and tens of thousands of lines of code are the norm. At this time, several problems will be highlighted:

  1. Any change to the project will cause the entire project to be recompiled. The deepest memory is early in the absence of componentization, large projects often need 10 minutes of compilation time, came out of a cup of tea time, most of the time, had to eagerly waiting for, although you can now use facebook ‘buck as well as from ali feeline to speed up the compilation process, the sheet is still not enough.
  2. The whole project is full of a large number of repetitive or redundant sub-modules, the business coupling degree is very high, affecting the whole body. This results in “old people dare not change, new people can not change”, because no one can predict after making changes, what will happen.
  3. Collaborative development is basically impossible, god knows what each other is doing. Code merging is even more painful.
  4. Not easy to test. The highly coupled business and modules make it impossible to start testing, so it can only be done hastily.

By introducing componentization, the above problems can be easily solved. In THE SDK, we componentize it according to the actual situation. For example, we componentize the sharing function, which can easily support multiple channels of sharing. When the sharing function needs to be updated, it can be separately compiled and tested.

Componentalization also makes it easy to implement SDK customization. By writing and compiling scripts, we can determine which components to rely on and eventually incorporate into the full application. Such is the case with the customizable sharing component provided by Umeng (pictured below).

Plug-in development

What is plug-in development here will not be introduced, on the one hand, plug-in is not a new concept, the other is that plug-in has so far been very mature on the theoretical level, do not want to 15 read the beginning of the research when relatively little information.

Why use plugins in the SDK? Unlike regular apps, SDKS cannot be updated frequently, lest developers feel that the SDK is unstable or make developers integrate frequently. The SDK appears to change slowly, but it changes frequently. As far as the previous advertising SDK is concerned, sometimes it is often necessary to collect data for certain models or update the anti-cheating module in time. It is very troublesome to solve this problem before using the plug-in. But after we took advantage of plugins, it became very easy to solve this problem: we divided the SDK into two parts: the host and the plug-in. The host provides only the necessary service interfaces to the developer and provides a custom plug-in loader. The core logic is in the plug-in. When data collection is needed, the developer only needs to develop a data collection plug-in and deliver it to the specified device. When you need to fix SDK defects, you just need to deliver a new plug-in package.

By using a plug-in solution in the SDK, you can effectively shield developers from the manual update process. The host is relatively stable and, once identified, generally does not change, while subsequent business changes are supported only by plug-in updates.

In addition to the plug-in solution to dynamic update mentioned above, by dividing the whole project into hosts and plug-ins, parallel development and separate compilation of hosts can be realized, and the limit of 65535 can be effectively solved. Before the use of plug-ins, our entire project was a massive project of many components dependent on each other that had to be passed

The SDK initialization

Unlike application development, in many cases the SDK does not have its own Context and must rely on the application to provide it. Common practice for SDK initialization: ad.init (Context Context,AdParams Params), we often recommend that developers remove this method from onCreate() in the Application component. This means that the initialization process is synchronous, and if the SDK itself takes a long time to initialize, it will affect the startup speed of the Application.

In this case, the SDK designer must address the problem. Generally, SDK services are further divided into core services and auxiliary services, and then the time of SDK initialization is reduced by means of parallel initialization and delayed initialization. In the ADVERTISING SDK that I was in charge of, some developers reported that our SDK started slowly. After analyzing the whole SDK startup process, we initialized plug-in loading service and cloud control service in parallel, and adopted appearance level initialization for log service, which effectively reduced the initialization time

Cloud update control

Cloud services as a server control control means the client is very important in the SDK development, now the SDK development can not support plug-in, but must provide cloud service, so that the server can control the SDK, such as without the need for data acquisition, can be closed by a cloud services control SDK acquisition function, in need of the open it.

The cloud control service is indispensable to the SDK, which is based on plug-in development.

From the perspective of implementation, cloud control services can be divided into server initiative and client initiative. The server initiative means that the server will push the information of the latest cloud control switch to the SDK, while the client initiative means that the SDK will request the cloud control information before performing operations. For students with push development and management, this is very easy to understand, just like in order to achieve the message push function, we can carry out message push through client rotation training or through the server to maintain a long connection.

security

SDK Security

In order to distinguish the access users and improve the security of SDK itself, we usually allocate API key and API Secret to developers. SDK will read the API key and API Secret configured by developers and use them in subsequent network communication. This is very common practice, for example, when you integrate aurora push SDK, it may require you to provide API key and API Secret, if not, you need to go to the official website for application.

The core logic is C/C++

For the sake of security, data encryption class and module algorithm class should be developed by NDK and encapsulated in so file. There are a lot of developers who don’t understand why this would enhance security. So let’s make a brief statement here. Since. So files are compiled by C/C++ files, they are less readable than Java decompiled files, and most Android developers do not have deep C/C++ capabilities, thus increasing the ability to be cracked to some extent.

Encrypted communication

To encrypt communication protocols, choose whether to use symmetric encryption or asymmetric encryption based on actual conditions. Also, use HTTPS instead of HTTP whenever possible.

Safety equipment

In many cases, such as with the AD SDK, some developers will be grinding ads from virtual machines, so it’s worth making a judgment call. Once the SDK detects an illegal request, it can adopt two schemes: one is SDK denial of service, the other is normal service. The SDK will upload the cheating information to the server, so that the back-end service can direct to exclude the data.

Reduce the data transfer size

When designing the data protocol between the SDK and server communication, you need to consider the actual situation, but the following suggestions are worth accepting:

  • If you have requirements on the size of data to be transmitted, compress the data.
  • Protocols such as JSON/XML /Protobuf can be used, and if they are still not available custom binary protocols can be considered.

Select support for the lowest system version

As designers of the SDK, facing a big problem is that we have to consider the developer application system supported by minimum version, but before the SDK release, we don’t know what kind of developers to use our services, so in order to get the SDK to support a wider range of equipment, we need to reduce the minimum support system version. For example, the mainstream system version of Insomnia is Android 5.0, so for the SDK, at least to support Android 4.0, or even Android 2.3.

Lowering the minimum supported version seems easy, but we have to do more work to ensure that the SDK behaves in a consistent way (often, we examine the current system version within the SDK to determine which methods can be called). The harder truth is that we spent a lot of effort supporting 2.3, but less than 1% of requests came from the 2.3 version.

Rights management

Any development in Android is not immune to permission requests. As SDK designers, we follow the “don’t add anything if you don’t need it” rule for permissions. In other words, if you don’t need it, don’t add it. This is what we call the minimum permissions rule, and it also applies to general application development.

When I first started SDK development, some early features required certain permissions, but later the features were cut, but the permissions were forgotten, which led to the situation that unnecessary permissions still exist.

In addition, too many permissions will make developers doubt your purpose. For example, what do you want to do when you apply for camera permission? Well, I suspect you’re taking pictures of me… .Ok, I’m just kidding here.

In addition, from Android 6.0 onwards, Google has changed its permission application policy, so it needs to be adapted separately.

The log service

Regardless of the size of the system, the logging service is a basic service. A good log service can help us quickly find problems, locate defects, and obtain solutions to problems.

The SDK’s logging service is not that different from other common logging services, but make sure that:

  1. The logging service can record valid information, which can be accessed at key locations in the SDK.
  2. When the log service uploads logs to the server, ensure maximum reliability and prevent logs from being discarded after the upload fails.
  3. The log service does not affect the performance of normal operations. The log information generated by the SDK is often very large, so you must consider the overhead of log I/O operations.

Delve into API design

API design is very important in any development, and many times the quality of the software is reflected in API design. In normal application development, the API is only circulated among application developers and not exposed to other people who are not working on the application, but the SDK as a service needs to expose some of the API to developers. We usually refer to apis that are circulated internally as internal apis, and apis that are open to developers as SDK apis.

Although they are used in different scenarios, there are a few common design rules that I can’t go into here, but there are only one that I think should be focused on:

The method name indicates its purpose

The method name is the first channel to understand the meaning of the method. A good method name is the ability to show off your functionality to others in the first place. The benefit of this is that it reduces unnecessary communication costs, and there is nothing more intuitive for developers than reading code directly.

Validation of parameters

Validation of parameters is very important, so don’t assume that you can substitute runtime exceptions. When the validity check fails, different processing policies are used for different method permissions:

  • Expose methods by showing how to check for exceptions, and use Javadoc’s @throw to explain why an exception was thrown
  • For private methods, the validation of parameters is checked by assertions
  • Checking the validity of the constructor’s arguments to keep the object in a uniform state requires consideration if the check is too expensive, such as if you are accepting a large List

Methods should be clear about their single function

A method should have a single function and do as few, but more specialized, things as possible. This is what we call the single responsibility principle. It is also important to keep in mind that it is better to provide small but beautiful methods than big and complete methods. The experience of big and complete methods often changes, and the risk is higher, so it is better to provide smaller methods to be used in combination

Method exception problem

Methods that need to be exposed to the developer need to throw traceable exceptions in a timely manner to help the developer find problems at compile time, and for runtime exceptions, the SDK designer must ensure that such exceptions do not cause problems with the host program and need to inform the developer.

Method permission control

Method permissions are also important, and SDK designers must consider which methods are public, which are non-public, and which are static from both a security and business perspective.

Avoid too long parameters

Too long parameters can cause memory difficulties and need to be treated with caution. In the case that too long parameters cannot be avoided, other solutions need to be considered:

A. By using the Builder pattern B. by using helper classes, usually in the form of static inner classes, see the use of static inner classes C. By encapsulating multiple parameters as class objects

D. By disassembling parameters into parameters of multiple methods

Be careful with method overloading

Overloading should not confuse the user, that is, it should not be the case that the same parameters are used, but the developer cannot be sure which method will be executed. In other words, don’t create ambiguity.

It is also important to note that there is no case where the argument type can be automatically converted to run in another method. I have seen such code in code Review: remove(Object) and remove(int) in list. Please make sure that you do not make similar errors. Although overloading is possible in Java, I do not recommend it, especially not overloading variable-length arguments, and it is better to use a different method name instead when overloading is required. The ObjectOutputStream class provided in Java provides a good example of this: its write has a variant for each primitive type, such as write character, write Boolean, etc. We find that the designers did not use overloading to design it as write(Long) L),write(Boolean b), instead design it as writeLong(l),writeBoolean().

For constructors, you can replace overloading by using static factories.

Use variable-length parameters with caution

In most cases, it is not necessary to use variable length parameters. In general, it is recommended to use variable length parameters only when there are more than five parameters. In cases where there are other non-variable-length arguments, I think variable-length arguments are at the end of the parameter list.

Avoid methods that return NUll directly

Do not return null for methods that need to return an array or collection. For example, if we go to the pastry shop to buy bread, and run out of bread is a normal state, we should not return NULL. Instead, we should return an array or collection of length 0.

Make protective copies if necessary

When a class accepts an object from a client or needs to return an object to a client, it is necessary to make a protective copy of the object if the class cannot tolerate further changes to the incoming object. Also note that the validation of parameters occurs after the protected copy. It should be noted that if the objects to be protected copy are very large, for example, there are more than 100,000 objects in the list, it is necessary to weigh the processing.

I promoted these principles in the team and required them to be strictly observed. The following ten principles will be explained respectively.

SDK Development Process

As for the SDK development process, I will write from the following three aspects: firstly, how to cooperate in the team development; secondly, continuous integration of SDK; thirdly, SDK multi-warehouse separation and management.

These three aspects will be covered in another chapter (when it will be finished is not yet confirmed).

SDK version management policies

The SDK version name is not much different from our previous naming rules. It consists of four parts in the following format: V Major version number Sub version number Phase version number _ Date version number plus Greek letter version number. Such as V1_1_2_161209_beta.

Greek letter version number description

  • Alpha: Internal beta version. This version indicates that the software is mainly implemented at this stage, with relatively many bugs and needs to be modified. It is usually only circulated internally and not open to the public.
  • Beta: The external Beta version, which has been greatly improved over Alpha and has no serious bugs, but still has some defects that need further testing to check and eliminate bugs.
  • RC: This version is quite mature and has no bugs that cause errors. It is almost the same as the official version.
  • Release: This version means “final Release” and is the final user or public Release, also known as the standard Release. Note that this version is released with a conformance R instead of the Release word.

Version number Change rule

  1. Major version number change: when there is a major change in a functional module or an overall architecture change
  2. Subversion number change: When there is a change in functionality
  3. Phase version number changes: Usually Bug fixes or minor changes that require frequent revisions based on feedback.
  4. Date version number (161209): Used to record the current date of the modified item. The date version number is changed every time the item is modified.
  5. Greek version number: This version number is used to indicate which stage of development the software is in and which needs to be changed as it moves to another stage.

API Version Management

Unlike normal application API version management,SDK designers need to focus on SDK API management. In principle, the state (signature and implementation) of an SDK API should be immutable once it is published publicly.

For specific API changes, the “open closed rule” should be followed, meaning that a class, module, or method should be closed for extension development and modification. This requires us to do the following:

  1. When the SDK API needs to be adjusted, it is preferred to add new methods instead of making changes to the original ones. For new methods that implement the same function, be as compatible with the original method as possible.
  2. When you need to deprecate a method, use the @deprecated flag before the official release, with an alternative and a deprecated date (usually the SDK version number).

Access document and API document version management

Access documentation is used to tell SDK users how to use the SDK, the detailed steps to use it, and the problems that can occur. Each company has its own set of rules, which need not be explained too much.

In addition, access documentation is typically divided into two versions: internal and public. The internal version is usually more detailed for internal developers and testers, while the public version is for developers and leaves out more information than the internal version.

The API documentation is a more detailed description of the SDK API. Like the API Doc in Java, it can be generated directly with the help of the JDK’s built-in Javadoc. Of course, android Studio also provides a convenient way to generate.

Changes to either access documentation or API documentation generally occur when the SDK version changes. When the SDK changes, the documentation must be updated accordingly. The documentation does not match the updated SDK.

Integrates Demo version management

An integration Demo is usually a simple app that shows how to quickly access the SDK. The version change policy is consistent with the SDK version change.

conclusion

SDK development needs to pay attention to a lot of points, each point can not be completed in a few words, the later will be slowly added on this basis.

Click on me. Click on me for soy sauce