The introduction

All sercive files in the project are now managed in the same Kotlin file. Because the service is a pure business layer and does not contain any user data, it is replaceable, as follows:

Kotlin version:

// Declare volatile and var for substitution
@Volatile
var testService = TestService()
Copy the code

Java version:

private static volatile TestService testService = new TestService();

public static final TestService getTestService(a) {
    return testService;
}

public static final void setTestService(TestService newTestService) {
    testService = newTestService;
}
Copy the code

Code the heat more

First, the original Service class and Runnable are inherited. For the implementation of the parent class, I believe you can understand. Implementing the Runnable interface is a project convention. When the main process is started, a separate hot-loaded daemon thread is started for class loading and instantiating the loaded class, and then the run method is called after being forced to Runnable.

class TestServiceBugFix: TestService(), Runnable {
    override fun run(a) {
        testService = this
    }
    // omit the fixed code
}
Copy the code

A potential problem

There is no problem with this process. However, if testService has references in other code, this can be a problem. As follows:

@Volatile
var routeMap:<RouteType, Service> = mapOf(
    RouteType.TEST to testService,
    ...
    RouteType.TEST_N to testServiceN
)
Copy the code

It is not enough to simply execute testService = this in the run method, because simply switching references in the service file, routeMap still holds the old reference. The correct code is as follows:

  • Solution 1: When testService points to the new object, change all the places that hold testService references
class TestServiceBugFix: TestService(), Runnable {
    override fun run(a) {
        testService = this
        // routeMap points to a new object
        routeMap = mapOf(
        RouteType.TEST to this. RouteType.TEST_N to testServiceN ) }// omit the fixed code
}
Copy the code
  • Option 2: Or change the routeMap value to a function (function reference), the getter, which is the latest object each time it is routed. As follows:
@Volatile
val routeMap:<RouteType, ()->Service> = mapOf(
     RouteType.TEST to ::testService,
     ...
     RouteType.TEST_N to ::testServiceN
)
Copy the code
  • Scheme 3: Manipulate the heap memory of the original object
Unsafe unsafe = Unsafe.getUnsafe();
long offset = unsafe.objectFieldOffset(Services.class, "testService");
unsafe.compareAndSetObject(Services.class, offset, oldService, newService);
Copy the code

conclusion

When doing hotfixes, it is important to check whether the object to be hotfixes has references in other code. To be supplemented…