More efficient event bus (BusUtils) than EventBus

background

Designing this BusUtils is actually doing ApiUtils Do it by hand, because they are basically the same way. I didn't want to compare them with EventBus of greenrobot before design, but there's always a need to compare them after design. So compare EventBus, the best EventBus in the industry, and you'll see that BusUtils that can't reach 300 in my area perform better than Ev.The entBus needs to be much higher, of course, all on the premise that BusUtils is practical and effective, and it's also a thread-safe event bus, which I've actually tested in a single test, don't boast, we talk about the data later, and you can download me if your little partner doesn't believe it.Source code can be compared, single test address: BusUtilsVsEventBusTest , Android test address: BusCompareActivity , BusUtils at AucFrame The role of the in is to pass in values from a module, which plays the following roles:

BusUtilsPlayer

Its use is described below:

Use

To configure

Add the bus plug-in to build.gradle in the project root directory:

buildscript {
    dependencies {
        ...
        classpath 'com.blankj:bus-gradle-plugin:2.0'
    }
}

Then use the plug-in in the application module:

apply plugin: "com.blankj.bus"

Add to your project AndroidUtilCode Dependency:

api "com.blankj:utilcode:1.25.0"

If you just want to import BusUtils, you need to copy the class yourself and put it in your project. Remember to copy ThreadUtils, and configure the SDL domain of bus in build.gradle under app as follows:

api {
    busUtilsClass "com.xxx.xxx.BusUtils"
}

android {
    ...
}

You can guess that the default busUtilsClass is com.blankj.utilcode.util.BusUtils Ha.

If you turn on confusion, you also need to configure your BusUtils annotation method to prevent confusion, if you use it directly AndroidUtilCode If you do not need to configure it, I have already done it for you. Configure your own BusUtils to prevent confusion as follows:

-keepattributes *Annotation*
-keepclassmembers class * {
    @com.xxx.xxx.BusUtils$Bus <methods>;
}

Of course, if your project is open to confusion, introduce it in full AndroidUtilCode Yes, confusion can help you remove unused classes and methods.

Okay, plug-ins and dependencies are all configured, and the basic usage is described below.

Basic Use

public static final String TAG_NO_PARAM  = "TagNoParam";
public static final String TAG_ONE_PARAM = "TagOneParam";

@BusUtils.Bus(tag = TAG_NO_PARAM)
public void noParamFun() {/* Do something */}

@BusUtils.Bus(tag = TAG_ONE_PARAM)
public void oneParamFun(String param) {/* Do something */}

@Override
public void onStart() {
    super.onStart();
    BusUtils.register(this);
}

@Override
public void onStop() {
    super.onStop();
    BusUtils.unregister(this);
}

BusUtils.post(TAG_NO_PARAM);// noParamFun() will receive
BusUtils.post(TAG_ONE_PARAM, "param");// oneParamFun() will receive

EventBus is sure to be understood in a flash.

Advanced Use

Sticky event

Supports sticky events, which are sent first, then received at subscription time, consumed, and used in the same way as EventBus, by setting sticky = true in the @BusUtils.Bus annotation, as shown in the following example:

public static final String TAG_NO_PARAM_STICKY  = "TagNoParamSticky";

@BusUtils.Bus(tag = TAG_NO_PARAM_STICKY, sticky = true)
public void noParamStickyFun() {/* Do something */}

BusUtils.postSticky(TAG_NO_PARAM_STICKY);

BusUtils.register(xxx);// will invoke noParamStickyFun

BusUtils.removeSticky(TAG_NO_PARAM_STICKY);// When u needn't use the sticky, remove it

BusUtils.unregister(xxx);

Thread Switching

Thread switching uses the thread pool in ThreadUtils, which has a secure Cached thread pool along with MAIN, IO, CPU, CACHED, SINGLE thread pool. By default, it is POSTING for submitted threads, using threadMode = BusUtils.ThreadMode.xx in the @BusUtils.Bus comment.

Standard

If you want to make your tools comfortable to use, the specifications must be followed. The so-called irregularities are not square, otherwise there will be a lot of problems. The following specifications are recommended here:

  • Classes and functions that hold events ensure that they are public.
  • Since BusUtils is used for in-module calls, you can write a BusConfig class to hold all the bus Tag s in a module for easy discovery of users and callers.
  • Tag s can also best have business module suffix names to prevent duplication, sticky for sticky types, and thread names for specific threads, such as update_avatar_sticky_main_info, which makes sense directly.
  • If you can combine AucFrame To use it would be more standard.
  • You need to keep the bean s for event transfers in BusUtils, otherwise you will fail to find the entity object after turning on confusion.

Now that you've finished using it, let's compare it to EventBus in terms of performance.

performance testing

First, define the events for both, because the comparison is how fast the events reach, so empty implementations can be done internally, as shown in the code below:

@Subscribe
public void eventBusFun(String param) {
}

@BusUtils.Bus(tag = "busUtilsFun")
public void busUtilsFun(String param) {
}

BusUtils generates a mapping table of record tag s and method signatures from the @BusUtils.Bus annotation at compile time, since this is done at compile time, and here we do it by reflection.

@Before
public void setUp() throws Exception {
    // This step was injected at AOP time, where the busUtilsFun event was injected by reflection with the same effect.
    ReflectUtils getInstance = ReflectUtils.reflect(BusUtils.class).method("getInstance");
    getInstance.method("registerBus", "busUtilsFun", BusUtilsVsEventBusTest.class.getName(), "busUtilsFun", String.class.getName(), "param", false, "POSTING");
}

Complete the comparison by comparing the following tests:

  • Register 10,000 subscribers and average 10 times
  • Send * 1000000 times to a subscriber, averaging 10 times
  • Send * 100,000 times to 100 subscribers, 10 times averaged
  • Log off 10,000 subscribers, averaging 10 times

The test machine is as follows:

macOS: 2.2GHz Intel Core i7 16GB
//One plus 6: Android 9 8GB

On Android, we've added EventBus's annotation processor to improve EventBus's efficiency and compare it to BusUtils in the best case.

Next, we write the template code for the test so that the code that compares the two can be directly inserted into the callback later, as follows:

/**
 * @param name       The test function name passed in
 * @param sampleSize Number of samples
 * @param times      Number of times per execution
 * @param callback   Callback function for comparison
 */
private void compareWithEventBus(String name, int sampleSize, int times, CompareCallback callback) {
    long[][] dur = new long[2][sampleSize];
    for (int i = 0; i < sampleSize; i++) {
        long cur = System.currentTimeMillis();
        for (int j = 0; j < times; j++) {
            callback.runEventBus();
        }
        dur[0][i] = System.currentTimeMillis() - cur;
        cur = System.currentTimeMillis();
        for (int j = 0; j < times; j++) {
            callback.runBusUtils();
        }
        dur[1][i] = System.currentTimeMillis() - cur;
        callback.restState();
    }
    long eventBusAverageTime = 0;
    long busUtilsAverageTime = 0;
    for (int i = 0; i < sampleSize; i++) {
        eventBusAverageTime += dur[0][i];
        busUtilsAverageTime += dur[1][i];
    }
    System.out.println(
            name +
            "\nEventBusCostTime: " + eventBusAverageTime / sampleSize +
            "\nBusUtilsCostTime: " + busUtilsAverageTime / sampleSize
    );
}

public interface CompareCallback {
    void runEventBus();
    void runBusUtils();
    void restState();
}

Let's do a one-to-one comparison test.

Register 10,000 subscribers and average 10 times

/**
 * Register 10,000 subscribers and average 10 times
 */
@Test
public void compareRegister10000Times() {
    final List<BusUtilsVsEventBusTest> eventBusTests = new ArrayList<>();
    final List<BusUtilsVsEventBusTest> busUtilsTests = new ArrayList<>();
    compareWithEventBus("Register 10000 times.", 10, 10000, new CompareCallback() {
        @Override
        public void runEventBus() {
            BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();
            EventBus.getDefault().register(test);
            eventBusTests.add(test);
        }
        @Override
        public void runBusUtils() {
            BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();
            BusUtils.register(test);
            busUtilsTests.add(test);
        }
        @Override
        public void restState() {
            for (BusUtilsVsEventBusTest test : eventBusTests) {
                EventBus.getDefault().unregister(test);
            }
            eventBusTests.clear();
            for (BusUtilsVsEventBusTest test : busUtilsTests) {
                BusUtils.unregister(test);
            }
            busUtilsTests.clear();
        }
    });
}
// MacOS Output:
// Register 10000 times.
// EventBusCostTime: 427
// BusUtilsCostTime: 41

// One plus 6 Output:
// Register 10000 times.
// EventBusCostTime: 1268
// BusUtilsCostTime: 399

Send * 1000000 times to a subscriber, averaging 10 times

/**
 * Send * 1000000 times to a subscriber, averaging 10 times
 */
@Test
public void comparePostTo1Subscriber1000000Times() {
    comparePostTemplate("Post to 1 subscriber 1000000 times.", 1, 1000000);
}
// MacOS Output:
// Post to 1 subscriber 1000000 times.
// EventBusCostTime: 145
// BusUtilsCostTime: 33

// One plus 6 Output:
// Post to 1 subscriber 1000000 times.
// EventBusCostTime: 1247
// BusUtilsCostTime: 696

private void comparePostTemplate(String name, int subscribeNum, int postTimes) {
    final List<BusUtilsVsEventBusTest> tests = new ArrayList<>();
    for (int i = 0; i < subscribeNum; i++) {
        BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();
        EventBus.getDefault().register(test);
        BusUtils.register(test);
        tests.add(test);
    }
    compareWithEventBus(name, 10, postTimes, new CompareCallback() {
        @Override
        public void runEventBus() {
            EventBus.getDefault().post("EventBus");
        }
        @Override
        public void runBusUtils() {
            BusUtils.post("busUtilsFun", "BusUtils");
        }
        @Override
        public void restState() {
        }
    });
    for (BusUtilsVsEventBusTest test : tests) {
        EventBus.getDefault().unregister(test);
        BusUtils.unregister(test);
    }
}

Send * 10,000 times to 100 subscribers, 10 times averaged

/**
 * Send * 100,000 times to 100 subscribers, 10 times averaged
 */
@Test
public void comparePostTo100Subscribers10000Times() {
    comparePostTemplate("Post to 100 subscribers 100000 times.", 100, 100000);
}
// MacOS Output:
// Post to 100 subscribers 100000 times.
// EventBusCostTime: 139
// BusUtilsCostTime: 79

// One plus 6 Output:
// Post to 100 subscribers 100000 times.
// EventBusCostTime: 3092
// BusUtilsCostTime: 2900

Log off 10,000 subscribers, averaging 10 times

/**
 * Log off 10,000 subscribers, averaging 10 times
 */
@Test
public void compareUnregister10000Times() {
    final List<BusUtilsVsEventBusTest> tests = new ArrayList<>();
    for (int i = 0; i < 10000; i++) {
        BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();
        EventBus.getDefault().register(test);
        BusUtils.register(test);
        tests.add(test);
    }
    compareWithEventBus("Unregister 10000 times.", 10, 1, new CompareCallback() {
        @Override
        public void runEventBus() {
            for (BusUtilsVsEventBusTest test : tests) {
                EventBus.getDefault().unregister(test);
            }
        }
        @Override
        public void runBusUtils() {
            for (BusUtilsVsEventBusTest test : tests) {
                BusUtils.unregister(test);
            }
        }
        @Override
        public void restState() {
            for (BusUtilsVsEventBusTest test : tests) {
                EventBus.getDefault().register(test);
                BusUtils.register(test);
            }
        }
    });
    for (BusUtilsVsEventBusTest test : tests) {
        EventBus.getDefault().unregister(test);
        BusUtils.unregister(test);
    }
}
// MacOS Output:
// Unregister 10000 times.
// EventBusCostTime: 231
// BusUtilsCostTime: 23

// One plus 6 Output:
// Unregister 10000 times.
// EventBusCostTime: 800
// BusUtilsCostTime: 199

conclusion

For easy observation, we generate a chart to compare the performance of the two:

BusUtilsVsEventBusChart

The performance of four functions in MacOS and OnePlus6 is counted in the chart. From left to right are "BusUtils for MacOS", "EventBus for MacOS", "BusUtils for OnePlus6", "EventBus for OnePlus6". It can be found that BusUtils are more important than EventBus in registration and cancellation.Several times faster, BusUtils is much faster to send multiple events to a small number of subscribers than EventBus, and a little faster to send multiple events to multiple subscribers than EventBus.

Based on all the above, if you use event buses more frequently in your project, try replacing EventBus with my BusUtils to improve performance, or in new projects, you can use BusUtils with better performance directly.

Here's a summary of the benefits of BusUtils:

  • BusUtils determines the only event through the event Tag, so the receive function supports no parameters or one parameter, whereas EventBus can only determine the specific recipient through the MessageEvent, and can only receive one parameter, even if it is just a notification, it also needs to define a MessageEvent, so BusUtils parameters are more flexible.
  • When BusUtils is applied to a project, a list of u bus_u.json events is generated in the application after compilation, as shown in the list of events generated above:
{
  "BusUtilsClass": "com.blankj.utilcode.util.BusUtils",
  "rightBus": {
    "noParamFun": "{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#noParamFun(), threadMode: POSTING }",
    "oneParamFun": "{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#oneParamFun(java.lang.String param), threadMode: POSTING }"
  },
  "wrongBus": {}
}

Modifying oneParamFun to two parameters ensures that the project does not crash because BusUtils is running, and that the api plug-in will make it compile later. The u bus_u.json file, shown below, prompts you that the number of parameters is incorrect:

{
  "BusUtilsClass": "com.blankj.utilcode.util.BusUtils",
  "rightBus": {
    "noParamFun": "{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#noParamFun(), threadMode: POSTING }",
  },
  "wrongBus": {
    "oneParamFun": "{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#oneParamFun(java.lang.String param, java.lang.String param1), threadMode: POSTING, paramSize: 2 }"
  }

Similarly, if two buses have the same Tag, they will also compile, but it will prompt you that there is a bus with the same Tag in your project.

So BusUtils is more friendly than EventBus.

  • BusUtils has far fewer codes than EventBus. BusUtils only have 300 lines of source code, and EventBus 3000 lines is definitely more than just Ha.
  • BusUtils perform better than EventBus.

principle

bus Plugin Principle Analysis

The source code for the bus plug-in is here: bus Plugin Source Port , the plug-in uses Gradle's transform ation to complete the injection of BusUtils.init(), which is analyzed in one step:

If you don't understand transforms, let's start with them. Simply put, transforms are designed for byte code insertion. The most common part is AOP (Face Oriented Programming). I won't be a popular science student, so I can search for them if I'm interested.

When it comes to byte code operations, there's something more knowledgeable. You can use javassist to get started quickly and easily. However, I chose a stronger and faster ASM. I won't go into details here. Interested ones can learn by themselves. ASM is also very simple. In fact, ASM ASM Bytecode Outline This plugin helps write quickly.

All functions annotated with @BusUtils.Bus are scanned by ASM, and the annotated values and function's parameter information are read and saved as follows:

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
    className = name.replace("/", ".");
    super.visit(version, access, name, signature, superName, interfaces);
}

@Override
public MethodVisitor visitMethod(int access, String funName, String desc, String signature, String[] exceptions) {
    if (cv == null) return null;
    MethodVisitor mv = cv.visitMethod(access, funName, desc, signature, exceptions);
    busInfo = null;
    mv = new AdviceAdapter(Opcodes.ASM5, mv, access, funName, desc) {
        @Override
        public AnnotationVisitor visitAnnotation(String desc1, boolean visible) {
            final AnnotationVisitor av = super.visitAnnotation(desc1, visible);
            if (("L" + mBusUtilsClass + "$Bus;").equals(desc1)) {
                busInfo = new BusInfo(className, funName);
                funParamDesc = desc.substring(1, desc.indexOf(")"));
                return new AnnotationVisitor(Opcodes.ASM5, av) {
                    @Override
                    public void visit(String name, Object value) {// Obtainable annotation values
                        super.visit(name, value);
                        if ("tag".equals(name)) {
                            tag = (String) value;
                        } else if ("sticky".equals(name) && (Boolean) value) {
                            busInfo.sticky = true;
                        }
                    }
                    @Override
                    public void visitEnum(String name, String desc, String value) {
                        super.visitEnum(name, desc, value);
                        if ("threadMode".equals(name)) {
                            busInfo.threadMode = value;
                        }
                    }
                };
            }
            return av;
        }
        @Override
        public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
            super.visitLocalVariable(name, desc, signature, start, end, index);// Getting method parameter information
            if (busInfo != null && !funParamDesc.equals("")) {
                if ("this".equals(name)) {
                    return;
                }
                funParamDesc = funParamDesc.substring(desc.length());// Every time a parameter is removed until it is "", then it is no longer a parameter
                busInfo.paramsInfo.add(new BusInfo.ParamsInfo(Type.getType(desc).getClassName(), name));
                if (busInfo.isParamSizeNoMoreThanOne && busInfo.paramsInfo.size() > 1) {
                    busInfo.isParamSizeNoMoreThanOne = false;
                }
            }
        }
        @Override
        public void visitEnd() {
            super.visitEnd();
            if (busInfo != null) {
                List<BusInfo> infoList = mBusMap.get(tag);
                if (infoList == null) {
                    infoList = new ArrayList<>();
                    mBusMap.put(tag, infoList);
                } else if (infoList.size() == 0) {
                    mBusMap.put(tag, infoList);
                } else if (infoList.size() == 1) {
                    BusInfo info0 = infoList.get(0);
                    info0.isTagRepeat = true;
                    busInfo.isTagRepeat = true;
                } else {
                    busInfo.isTagRepeat = true;
                }
                infoList.add(busInfo);
            }
        }
    };
    return mv;
}

Then insert the scanned content into BusUtils.init(), such as the oneParamFun function mentioned above, and the code it eventually inserts is as follows:


private void init() {
    this.registerBus("TagOneParam", "com.blankj.bus.BusTest", "oneParamFun", "java.lang.String", "param", false, "POSTING");
}

Its ASM inserts the following code:

@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
    if (!"init".equals(name)) {
        return super.visitMethod(access, name, descriptor, signature, exceptions);
    }
    // Write to init() function
    if (cv == null) return null;
    MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);
    mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, descriptor) {
        @Override
        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
            return super.visitAnnotation(desc, visible);
        }
        @Override
        protected void onMethodEnter() {
            super.onMethodEnter();
        }
        @Override
        protected void onMethodExit(int opcode) {
            super.onMethodExit(opcode);
            for (Map.Entry<String, List<BusInfo>> busEntry : mBusMap.entrySet()) {
                List<BusInfo> infoList = busEntry.getValue();
                if (infoList.size() != 1) continue;
                BusInfo busInfo = infoList.get(0);
                if (!busInfo.isParamSizeNoMoreThanOne) continue;
                mv.visitVarInsn(ALOAD, 0);
                mv.visitLdcInsn(busEntry.getKey());
                mv.visitLdcInsn(busInfo.className);
                mv.visitLdcInsn(busInfo.funName);
                if (busInfo.paramsInfo.size() == 1) {
                    mv.visitLdcInsn(busInfo.paramsInfo.get(0).className);
                    mv.visitLdcInsn(busInfo.paramsInfo.get(0).name);
                } else {
                    mv.visitLdcInsn("");
                    mv.visitLdcInsn("");
                }
                mv.visitInsn(busInfo.sticky ? ICONST_1 : ICONST_0);
                mv.visitLdcInsn(busInfo.threadMode);
                mv.visitMethodInsn(INVOKESPECIAL, mBusUtilsClass, "registerBus", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)V", false);
            }
        }
    };
    return mv;
}

Principle Analysis of BusUtils

Next, look at the implementation of BusUtils.registerBus:

private void registerBus(String tag,
                         String className, String funName, String paramType, String paramName,
                         boolean sticky, String threadMode) {
    mTag_BusInfoMap.put(tag, new BusInfo(className, funName, paramType, paramName, sticky, threadMode));
}

Simply insert key as tag and value as an instance of BusInfo into the mTag_BusInfoMap to keep an event.

The next step is to use it. At the beginning, we all register first. The source code is as follows:

public static void register(final Object bus) {
    getInstance().registerInner(bus);
}

private void registerInner(final Object bus) {
    if (bus == null) return;
    String className = bus.getClass().getName();
    synchronized (mClassName_BusesMap) {
        Set<Object> buses = mClassName_BusesMap.get(className);
        if (buses == null) {
            buses = new CopyOnWriteArraySet<>();
            mClassName_BusesMap.put(className, buses);
        }
        buses.add(bus);
    }
    processSticky(bus);
}

We get the class name of bus, then lock mClassName_BusesMap to insert it into the value set of mClassName_BusesMap. You can see that we have used the thread-safe CopyOnWriteArraySet set, and then we need to deal with whether we have subscribed to the sticky event processSticky before, so register hereThat's it.

Then post sends the event with the following source code:

public static void post(final String tag) {
    post(tag, NULL);
}

public static void post(final String tag, final Object arg) {
    getInstance().postInner(tag, arg);
}

private void postInner(final String tag, final Object arg) {
    postInner(tag, arg, false);
}

private void postInner(final String tag, final Object arg, final boolean sticky) {
    BusInfo busInfo = mTag_BusInfoMap.get(tag);
    if (busInfo == null) {
        Log.e(TAG, "The bus of tag <" + tag + "> is not exists.");
        return;
    }
    if (busInfo.method == null) {
        Method method = getMethodByBusInfo(busInfo);
        if (method == null) {
            return;
        }
        busInfo.method = method;
    }
    invokeMethod(tag, arg, busInfo, sticky);
}

private Method getMethodByBusInfo(BusInfo busInfo) {
    try {
        if ("".equals(busInfo.paramType)) {
            return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName);
        } else {
            return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName, Class.forName(busInfo.paramType));
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    return null;
}

private void invokeMethod(final String tag, final Object arg, final BusInfo busInfo, final boolean sticky) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            realInvokeMethod(tag, arg, busInfo, sticky);
        }
    };
    switch (busInfo.threadMode) {
        case "MAIN":
            Utils.runOnUiThread(runnable);
            return;
        case "IO":
            ThreadUtils.getIoPool().execute(runnable);
            return;
        case "CPU":
            ThreadUtils.getCpuPool().execute(runnable);
            return;
        case "CACHED":
            ThreadUtils.getCachedPool().execute(runnable);
            return;
        case "SINGLE":
            ThreadUtils.getSinglePool().execute(runnable);
            return;
        default:
            runnable.run();
    }
}

private void realInvokeMethod(final String tag, Object arg, BusInfo busInfo, boolean sticky) {
    Set<Object> buses = mClassName_BusesMap.get(busInfo.className);
    if (buses == null || buses.size() == 0) {
        if (!sticky) {
            Log.e(TAG, "The bus of tag <" + tag + "> was not registered before.");
            return;
        } else {
            return;
        }
    }
    try {
        if (arg == NULL) {
            for (Object bus : buses) {
                busInfo.method.invoke(bus);
            }
        } else {
            for (Object bus : buses) {
                busInfo.method.invoke(bus, arg);
            }
        }
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

You can see that there is still a lot of code, but don't worry, we're still simple. First, look for BusInfo with that tag in the mTag_BusInfoMap we injected before, and then output the error log and return it directly.

Then we find the method instance based on the obtained BusInfo. BusInfo will save the method in the instance for the first time, and then call it to take the method out of the instance directly.

Then we take out the thread information from BusInfo, and finally execute the reflection of the method in the thread, which is generally the case, and we need to analyze the source code for the details.

Finally unregister:

public static void unregister(final Object bus) {
    getInstance().unregisterInner(bus);
}

private void unregisterInner(final Object bus) {
    if (bus == null) return;
    String className = bus.getClass().getName();
    synchronized (mClassName_BusesMap) {
        Set<Object> buses = mClassName_BusesMap.get(className);
        if (buses == null || !buses.contains(bus)) {
            Log.e(TAG, "The bus of <" + bus + "> was not registered before.");
            return;
        }
        buses.remove(bus);
    }
}

unregister, in contrast to register, is removed from the value collection of mClassName_BusesMap, which also requires a lock on mClassName_BusesMap.

Keywords: Android Gradle Java JSON

Added by Nulletz on Mon, 22 Jul 2019 05:34:43 +0300