HarmonyOS Aspect-Oriented Programming (AOP) Practical Guide: Unveiling Hidden Treasure Features!
Hello everyone! Today, while browsing the HarmonyOS developer documentation, I accidentally discovered an official "aspect-oriented programming" (AOP) treasure case! These techniques can greatly improve efficiency in real development, but are rarely mentioned. Below, I'll use the simplest language and code to help you master HarmonyOS's AOP black technology!
1. What is HarmonyOS Aspect-Oriented Programming?
Core Idea: Patch methods without modifying the source code
Three Magic Tools:
-
addBefore
: Insert logic before method execution (e.g., parameter validation) -
addAfter
: Insert logic after method execution (e.g., time consumption statistics) -
replace
: Directly replace method logic (emergency fix magic tool)
Underlying principle: Dynamic proxy is implemented by modifying the class's prototype
(JS prototype chain mechanism)
2. Practical Case Studies
Scenario 1: Emergency Parameter Validation Fix (addBefore)
Pain Point: Online array out-of-bounds crash, business team can't modify the source code in time
Solution: Use AOP to add a "protective shield" to the method
// Original class
export class ArrayUtils {
getElementByIndex<T>(arr: T[], idx: number): T {
return arr[idx]; // Dangerous! May be out of bounds
}
}
// Emergency fix (entry file)
import { util } from '@kit.ArkTS';
util.Aspect.addBefore(ArrayUtils, 'getElementByIndex', false,
(_, arr, idx) => {
if (idx >= arr.length) throw Error("Index out of bounds!"); // Aspect logic
}
);
// Test
new ArrayUtils().getElementByIndex([1,2,3], 10); // Trigger error!
Key Points:
-
false
means instance method (usetrue
for static methods) -
_
represents the object the method belongs to (not needed here)
Scenario 2: Performance Monitoring (addBefore + addAfter Combination)
Requirement: Count method execution time without intruding on business code
let tStart = 0;
util.Aspect.addBefore(NetworkService, 'fetchData', false,
() => tStart = Date.now()
);
util.Aspect.addAfter(NetworkService, 'fetchData', false,
() => console.log(`Time taken: ${Date.now() - tStart}ms`)
);
Execution Effect:
>> new NetworkService().fetchData();
<< [LOG] Time taken: 248ms
Scenario 3: Modifying Third-Party Libraries (replace)
Scenario: The URL protocol returned by a third-party library is incorrect and needs to be forcibly changed to https
// Original class (third-party library)
class WebHandler {
getUrl(): string { return "http://riskysite.com"; }
}
// Security enhancement
util.Aspect.replace(WebHandler, 'getUrl', false,
() => "https://safesite.com" // Directly replace logic
);
// Test
console.log(new WebHandler().getUrl()); // Output https
Scenario 4: Subclass Customization (replace inherited methods)
Pain Point: Parent class methods do not meet the special needs of subclasses
class Base {
fetchData() { return "Base data"; }
}
class ChildA extends Base {}
class ChildB extends Base {}
// Only modify ChildA's logic
util.Aspect.replace(ChildA, 'fetchData', false,
() => "ChildA customized data"
);
new Base().fetchData(); // "Base data"
new ChildA().fetchData(); // "ChildA customized data"
new ChildB().fetchData(); // "Base data" (unaffected)
Advantage: Precise control, does not affect other subclasses
Scenario 5: Jump Interception (System API Aspect)
Requirement: Monitor all app jump behaviors
// EntryAbility.ets
export default class EntryAbility extends UIAbility {
onCreate() {
const contextClass = this.context.constructor;
util.Aspect.addBefore(contextClass, 'startAbility', false,
(_, want) => console.log(`Jump target: ${want.bundleName}`)
);
}
}
Output:
Jump target: com.example.shopping
Obtain the undeclared system class through constructor
3. Pitfalls to Avoid
- Recursive Trap Incorrect example:
util.Aspect.addBefore(Test, 'foo', false,
(obj) => obj.foo() // Infinite recursion!
);
Correct approach:
const originFoo = Test.prototype.foo; // Save the original method
util.Aspect.replace(Test, 'foo', false,
function(...args) {
console.log("Pre-operation");
return originFoo.apply(this, args); // Safe call
}
);
- Do not aspect struct components ⚠️ The following code may cause strange bugs:
@Component struct MyComp {
build() {...}
}
// Dangerous operation!
util.Aspect.replace(MyComp, 'build', false, ...);
- Multithreading Counting Issue When counting method execution times, avoid closure variables across threads:
// Wrong: count may be messed up in multithreading
let count = 0;
util.Aspect.addBefore(Service, 'request', false, () => count++);
// Recommended: use thread-safe storage
import { ConcurrentHashMap } from '...';
const countMap = new ConcurrentHashMap();
4. Summary
HarmonyOS's AOP capability is like a code scalpel, enabling:
- ✅ Emergency hotfixes (no need to release a new version)
- ✅ Non-intrusive monitoring
- ✅ Third-party library security enhancement
- ✅ Differentiated subclass customization
The official documentation hides it deep, but it's really useful in practice! I recommend everyone to bookmark these cases—they can save you 80% of your overtime in critical moments~
If you have any questions, feel free to discuss in the comments. Let's explore HarmonyOS black technology together! 🚀
Top comments (0)