Hello everyone, I am the Cang King. The series is now in its 30th episode and has been running for a year and a half. In a year and a half, we have established a QQ group of thousands of people. Many editors have asked me to edit books, and some colleagues have asked me to cooperate with the public account. But personal time is limited, it is impossible to achieve all wishes. So last year I chose a very meaningful thing in my life to publish a book about componentization technology with the Publishing House of Electronics Industry. Thank you very much to Editor Chen Xiaomeng for finding me to publish the book with him, and also to the friends in the technology group who keep exploring the componentized technology. The book focuses on the use of componentization to build an Android project, the idea of componentization, componentization programming technology, multi-person management componentization, componentization compilation optimization, and the thought of the evolution of the project. This book is not only about technology, but also about my understanding of life and technical thinking. Jingdong, Taobao and Dangdang can buy, you can click on the link to jump.

Android componentized architecture

The following is my series of related articles, you can refer to, you can give a like or follow my article.

[Android] How to make a chapter list of apps with a crash rate of less than three per thousand

The full name for spi is Service Provider Interfaces. ServiceLoder A loader used to dynamically load interface implementation classes. You can use @autoService 3 to dynamically load entity classes that inherit from an interface. You can use @autoService 3 to dynamically load entity classes that inherit from an interface. It doesn’t load singletons, and the constructor takes no arguments because the ServiceLoader is loaded using reflection. 4. The files should be loaded in the same order as the resources/ meta-INF /services directory, so @autoService is not controllable. 5.ServiceLoader inherits the Iterator interface and can iterate through entity classes like a List. 6. It also implements initialization via reflection, using interfaces to decouple modules, ServiceLoader loaders, and initiators. 7. It is suitable for the unified loading scenario of module entry initialization in componentization.

The principle diagram of the SPI

Using a Modular framework for loading as an example, 1. Declare the interface

public interface IModule {
    /** * module initialization, only when formed, used to open child thread training message */
    void init(a);

    /** * module ID ** @return Module ID */
    int getModuleId(a);

    /** * module registered and connected successfully, can do the following things: * 

* 1, register listening event * 2, send event * 3, register service * 4, call service */

void afterConnected(a); } Copy the code

2. Write the full pathname to the resources/ meta-INF /services directory using @autoService

@AutoService(IModule.class)
public class Module extends BaseModule {
    @Override
    public void afterConnected(a) {}@Override
    public int getModuleId(a) {
        returnConstants.MODULE_B; }}Copy the code

3. Use ServiceLoder to load the module

@Override// Only if the component is run separately will the onCreate class be executed when the Application is run
    public void onCreate(a) {
        super.onCreate(); ...// Automatically register the server (if there is only one IModule in a separate module)
        ServiceLoader<IModule> modules = ServiceLoader.load(IModule.class);
        mBaseModule = (BaseModule) modules.iterator().next();

        // The module is initializedmBaseModule.init(); ... }Copy the code
public void onCreate(a) {
        super.onCreate(); ...//SPI automatic registration service (when the main module was loaded, all META_INF files had been merged)
        ServiceLoader<IModule> modules = ServiceLoader.load(IModule.class);
        for (IModule module : modules) module.afterConnected();
    }
Copy the code

As simple as it seems to be to use, let’s take a look at the special features of the ServiceLoader source code.

    // Call the static load method to initialize XXXInterface information.
    public static <S> ServiceLoader<S> load(Class<S> service) {
        // Get the current thread ClassLoader
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

    // Build the ServiceLoader object
    public static <S> ServiceLoader<S> load(Class<S> service.ClassLoader loader)
    {
        return new ServiceLoader<>(service, loader);
    }

    private ServiceLoader(Class<S> svc.ClassLoader cl) {
        // Check whether the interface exists
        service = Objects.requireNonNull(svc, "Service interface cannot be null");
        // Check whether the classloader is empty. If it is empty, use the system classloader
        loader = (cl == null)? ClassLoader.getSystemClassLoader() : cl;// Android-changed: Do not use legacy security code.
        // On Android, System.getSecurityManager() is always null.
        // acc = (System.getSecurityManager() ! = null) ? AccessController.getContext() : null;
        reload();
    }
   
    public void reload() {
        // Clean up the provides configuration loader
        providers.clear();
        // Initializes lazy-loaded iterators
        lookupIterator = new LazyIterator(service, loader);
    }
Copy the code

You can see that lazy iterators are used, and only when the iterator is used will each entity class that inherits the interface actually be initialized.

   // Determine if there is a next object
   private boolean hasNextService(a) {
            if(nextName ! =null) {
                return true;
            }
            if (configs == null) {
                try {
                    //PREFIX = "META-INF/services/"
                    // Load the configuration address
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        // Load the configuration
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    fail(service, "Error locating configuration files", x); }}// Parse the configuration file. If you find an interface that needs to be parsed, jump out
            while ((pending == null) | |! pending.hasNext()) {if(! configs.hasMoreElements()) {return false;
                }
                // Parse the node of config
                pending = parse(service, configs.nextElement());
            }
           
            nextName = pending.next();
            return true;
        }
Copy the code

The interface class is initialized by reflection

        private S nextService(a) {
            if(! hasNextService())throw new NoSuchElementException();
            String cn = nextName;
            nextName = null; Class<? > c =null;
            try {
                // Load class information using the classpath name
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
                fail(service,
                     // Android-changed: Let the ServiceConfigurationError have a cause.
                     "Provider " + cn + " not found", x);
                     // "Provider " + cn + " not found");
            }
            if(! service.isAssignableFrom(c)) {// Android-changed: Let the ServiceConfigurationError have a cause.
                ClassCastException cce = new ClassCastException(
                        service.getCanonicalName() + " is not assignable from " + c.getCanonicalName());
                fail(service,
                     "Provider " + cn  + " not a subtype", cce);
                // fail(service,
                // "Provider " + cn + " not a subtype");
            }
            try {
               // reflection initializes the class and converts it to an interface
                S p = service.cast(c.newInstance());
                // Record the mapping
                providers.put(cn, p);
                // Return the interface entity
                return p;
            } catch (Throwable x) {
                fail(service,
                     "Provider " + cn + " could not be instantiated",
                     x);
            }
            throw new Error();          // This cannot happen
        }
Copy the code

ServiceLoader is actually done by pathname reflection, but it is decoupled by configuring directory files in META_INF. ServiceLoader is used when module loading order is not required. If there is a loading order, it needs to be reordered and then initialized.

The advantages and limitations of SPI have been introduced at the beginning of the program. By jumping out of SPI, a more flexible and controllable loading mechanism can still be created, such as dynamic update of JSON script and XML script.