DEV Community

陈杨
陈杨

Posted on

Hong Mong 5 development treasure case sharing cutting-edge programming real war revealed

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!
Enter fullscreen mode Exit fullscreen mode

Key Points:

  • false means instance method (use true 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`)
);
Enter fullscreen mode Exit fullscreen mode

Execution Effect:

>> new NetworkService().fetchData();
<< [LOG] Time taken: 248ms
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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}`)
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Output:

Jump target: com.example.shopping
Enter fullscreen mode Exit fullscreen mode

Obtain the undeclared system class through constructor


3. Pitfalls to Avoid

  1. Recursive Trap Incorrect example:
util.Aspect.addBefore(Test, 'foo', false, 
  (obj) => obj.foo() // Infinite recursion!
);
Enter fullscreen mode Exit fullscreen mode

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
  }
);
Enter fullscreen mode Exit fullscreen mode
  1. Do not aspect struct components ⚠️ The following code may cause strange bugs:
@Component struct MyComp {
  build() {...}
}

// Dangerous operation!
util.Aspect.replace(MyComp, 'build', false, ...);
Enter fullscreen mode Exit fullscreen mode
  1. 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();
Enter fullscreen mode Exit fullscreen mode

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)