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…