One, foreword

Android SDK development art exploration series is based on the practical experience of business SDK development in actual production, with a certain combat and technology, not only contains experience under a certain business background, but also systematically introduces the development process of a third party SDK and the selection of related technologies. In this series, you’ll learn not only how to develop a third-party SDK, but also general Android development, software engineering ideas, and even some of the weirdest things.

In this article you can learn how to design an SDK that defines data structures and data preprocessing for interaction with third parties. Understand the communication mechanism of SDK and APP interaction, including the interaction logic design of the whole process, to avoid trust problems. More from these designs to understand the corresponding general technical points and design ideas.

Series of articles:

Android SDK Development Art Exploration (1) Introduction and Design

(2) Exception or ErrorCode

Android SDK development art exploration (iii) initialization

Android SDK development art exploration (4) personalized configuration

Android SDK development art exploration (5) security and verification

Android SDK development art exploration (6) compression and optimization

Android SDK development art exploration (vii) Dependency principles and packaging methods

Ii. Definition and scenario of SDK

2.1 Definition of SDK

The full name of SDK is 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 the narrow sense, SDK is a set of independent tools that can complete specific functions and return relevant data, encapsulated by secondary development based on basic components of the system.

In human terms, SDK is generally the common JAR package, SO library, AAR package, large to a SET of JDK, small to only a method JAR package, can be called SDK. The essence of SDK is a collection of methods and logic, which encapsulates resources and apis.

2.2 SDK scenarios

Now that we know the definition of SDK, what are the scenarios of SDK in the industry? The SDK is widely available in 2B products. . Such as push SDK, payment SDK, map SDK, OCR-SDK, face recognition SDK, game SDK and so on. Standardized and reusable SDK products have widely improved the efficiency of APP development for small and medium-sized companies.

SDK interface design

There are two clear principles that govern the design of an SDK:

One is: the principle of minimum availability, that is, use the least code, if not necessary to add entities; The second is: the least dependence principle, that is, the minimum external dependence, if not necessary do not increase dependence.

First we need to clarify the SDK’s responsibilities and boundaries, and define the parameters for interacting with the host App. What does the SDK receive? Output what?

Here’s an example:

Key points:

  • Each entry and exit parameter has a token that is used for the association and credential of this call.
  • When the front-end SDK is designed for input, the interaction of front-end parameters should be minimized, and the relevant parameters should be passed into the back-end service when the token is obtained, so as to keep the simplicity and flexibility of the SDK interface invocation.

3.1. Entry and exit design

In most cases, entry and exit parameters need to interact with multiple fields. Then there is the problem of data assembly. Therefore, it is necessary to design a convenient and clear data structure.

SDK input and exit parameters essentially interact with a set of key-value pairs, so the following ways can be considered:

3.1.1. Pass a Map

Generate a Map and set the corresponding key-value. It is feasible, but the disadvantage is that too many details and internal logic of generating parameters are exposed. The generation of Map and the setting of KEY are relatively uncontrollable, and the hidden trouble of data inconsistency is also buried.

** When transferring a HashMap instance between activities, you need to use bundle.putSerialIZABLE (KEY_REQ, value);

3.1.2. Pass a String

Generate a STRING in JSON format, similar to the shortcomings of the Map scheme, the assembly process is also prone to JSON format problems.

3.1.3. Pass a custom entity class

Through the SDK module built-in instances of special entity classes, dynamic Settings of relevant parameters, interaction class instances. The program is simple and easy to use, shielding the details of generation and setting, directly through the simple set method interface for its assignment, and can be in the assignment of data verification, qualification.

Input parameter build example:

// Build the request entity
final ReqContent req = new ReqContent();
req.setToken("a_valid_token");
req.setSignature("a_valid_signature");
Copy the code

Return parameter example:

// Get the returned entity
ResultContent result = getResult(intent);
int code = result.getCode();
String message = result.getMessage();
String token = result.getToken();
Copy the code

**Tips1: ** When a custom entity class is used as an entry and exit parameter between activities, the entity cannot be passed directly and needs to be serialized. There are two ways to do this

1, The entity class needs to implement the Serializable interface in JDK, via bundle:

bundle.putSerializable(KEY_REQ, req);
Copy the code

Bundle. putParcelable(KEY_REQ, req);

Note that intEnts use Binder bundles to transmit data with limited size. The recommended maximum size for intEnts is 500K, and the smaller the better. (Different versions of the test have different restrictions, 500K is not a definite number, interested can test yourself)

3.2. Call packaging design

The INVOCATION of SDK must follow the principle of simplicity and encapsulation. There are two main types, one is pure, unbounded, do not interact with the user code logic, such as common encryption, summary generation SDK, and so on, directly through a simplest method call directly back. The second is a complex, interface, direct user interface SDK, which is also the focus of this article.

Background: There are a series of activities that carry different business logic inside SDK. The third party calls SDK and distributes services through the entry of launching SDK activities. Then how to make an elegant call encapsulation?

Call before encapsulation:

// Tedious call code
Bundle bundle = new Bundle();
// A static KEY is exposed or contracted
bundle.putParcelable(KEY_REQ, request);
Intent intent = new Intent(context, EntryActivity.class);
// Block the transition animation to make the screen jump more harmonious
intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
intent.putExtras(bundle);
context.startActivity(intent);
Copy the code

Wrapped invocation:

// Simple call code
MySDK.launchSdk(context,req);

// Build complex logic or details into the SDK and provide encapsulated static methods externally
public static void launchSdk(Context context, ReqContent request) {
    Bundle bundle = new Bundle();
    bundle.putParcelable(KEY_REQ, request);
    Intent intent = new Intent(context, EntryActivity.class);
    // Block the transition animation to make the screen jump more harmonious
    intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
    intent.putExtras(bundle);
    context.startActivity(intent);
}
Copy the code

Not only does this simplify the calls, but it also shields the template code for a number of intermediate processes. Why not? SDK to write a few more lines of code, can be convenient for developers. Here is just a simple idea, specific process specific analysis, diverging from here to open, but also to avoid excessive encapsulation, resulting in expansion, compatibility problems.

It is worth mentioning that using Activity to do SDK entry and business distribution is a “heavy” logic, which is also a performance dimension worth considering in SDK design.

3.3 data return design

Data return is an integral part of SDK design. Follow the principle of “call is response” **, response can be divided into return code, throw exceptions. Never design SDK logic that has no response, no callback. After the SDK internal process is completed, a return must be sent to the caller to continue the service. In particular, it’s part of good Java programming practice to focus on the else if and the default if case.

How do you design the data return mechanism for a business SDK? First, let’s take a look at the demand background:

  • You need to transfer data between the App and the SDK’s activities
  • After obtaining the SDK, you need to execute an asynchronous logic and close it in time.
  • The argument needs to be constructed and returned whenever and wherever the logical code is processed;
  • SDK code needs to be integrated into various third-party apps and a common interaction mechanism is required to avoid more compatibility problems.

Using the startActivityForResult ()? Theoretically feasible, but not flexible enough, abandoned.

Adopt the EventBus? Avoid introducing unnecessary third party dependencies and give them up for now.

The BroadcastReceiver? One of the four components built into Android, designed for interaction between components. It is also flexible and can be used to transfer data from an Intent in parallel with the incoming method.

Broadcast can be divided into global broadcast and in-app broadcast. Global broadcast can be delivered across apps with Binder mechanisms, which is too small for this scenario. Intra-application broadcast, based on the Handler mechanism, is limited to data interaction within the same application. Compared with global broadcast, it does not require inter-process communication, which is more efficient and does not need to worry about security problems caused by other applications receiving broadcast. Here’s an example:

To send data

// Static methods encapsulated within the SDK are provided for unified invocation by SDK internal modules
public static void sendResult(String token, int code, String message) {

    ResultContent result = new ResultContent();
    result.setToken(token);
    result.setCode(code);
    result.setMessage(message);

    Bundle bundle = new Bundle();
    bundle.putParcelable(KEY_RESULT, result);

    Intent intent = new Intent();
    intent.setAction(MY_SDK_RECEIVER_ACTION);
    intent.putExtras(bundle);
    LocalBroadcastManager.getInstance(MySDK.getContext()).sendBroadcast(intent);
}
Copy the code

Receive data

// The registration method encapsulated within the SDK is provided for the APP module to call uniformly
public static void registerReceiver(BroadcastReceiver receiver) {
    IntentFilter filter = new IntentFilter();
    filter.addAction(MY_SDK_RECEIVER_ACTION);
    LocalBroadcastManager.getInstance(getContext()).registerReceiver(receiver, filter);
}

If registered in onCreate(), then unregistered in onDestroy().
public static void unregisterReceiver(BroadcastReceiver receiver) {
    LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(receiver);
}


// Broadcast receiver defined in APP to receive return parameters
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ResultContent result = MySDKHelper.getResult(intent);
        if (result == null) {
            return;
        }
        int code = result.getCode();
        String message = result.getMessage();
        String token = result.getToken();
        String log = String.format(Locale.CHINA,"Return code: %d Return message: %s Token: %s", code, message, token); Log.d(TAG, log); }}Copy the code

Supplement: Entry and exit should be designed flexibly according to the specific needs of the project, if not necessary, do not add entity, simple is beautiful. If the SDK business is simple enough, just use the common method call with the parameter return; More complex callbacks are handled in the form of Interface callbacks and BroadcastReceiver callbacks.

4. Parameter filtering design

Parameter filtering refers to filtering the input parameter data during the input process to avoid meaningless subsequent business processes and provide call feedback in time.

Common filtering methods include non-null detection, data type detection, and data format detection. Parameters can also be marked with custom annotations that allow the compiler to check for timely correction of data types.

A simple example of custom annotations

public class SourceAnnotationDemo {

    public static final String TYPE_A = "A";
    public static final String TYPE_B = "B";

    private String type;

    @StringDef({TYPE_A, TYPE_B})
    @Retention(RetentionPolicy.SOURCE)
    public @interface MyType {
    }

    public String getType(a) {
        return type;
    }

    // The parameter to this method must be TYPE_A or TYPE_B, or the compiler will error
    public void setType(@MyType String type) {
        this.type = type; }}Copy the code

5. Interactive process design

“There is no absolute security on the front end!” Lu xun,

As Lu Xun said, there is no absolute security in the front end, so we must consider the legitimacy of the returned results when designing the overall interaction logic of the SDK.

5.1. Error returns

In the case of an error return, the wrong result theoretically does not cause any loss to the business, such as payment failure. The abnormal result is not generated in the business process and can be treated as a failure.

5.2. Successful return

In a successful return, because the successful outcome is theoretically the basis for continuing the business process, such as the behavior of shipping after successful payment. Therefore, the reliability of the return must be strictly judged.

In the front-end code, it is easy to be tampered with or forged by abnormal means such as Hook, so the front-end return code cannot be trusted directly. The general payment process is similar in design.

5.3. Reference process

Key points of process:

  • The interaction Token is returned from the interaction of the back-end interface and used for the call. If more parameters need to be obtained, they should also be passed from the interface to keep the simplicity of the front-end interface and the flexibility of data interaction.
  • After the SDK processes the internal logic, the front-end return code is returned to inform the App that the process is over. The App determines whether further confirmation is needed according to the return code type. In this step, you can simplify the front-end return and return more service fields from the back-end query confirmation interface, keeping the simplicity of the front-end interface and the flexibility of data interaction.

Five, the conclusion

Starting from the SDK definition and scene at the beginning, this paper introduces the INTERFACE design of SDK, parameter filtering, and the process design under the timely trust chain. Next, we will continue the discussion of exceptions and error returns in SDK development. Exception or ErrorCode? It’s a question.

If this document is helpful or inspiring to your development, like or share it is the best incentive for the author to continue to create, thank you for your support!

Copyright Notice:

This article first appeared in my column AndDev Android development has been authorized to hongyang public account exclusive release.