Mac开发系列2-通过AXObserver对window进行监控

1,420 阅读3分钟

通过上一篇文章的介绍跳转,相信大家都会使用Accessibility APIAXUIElement。那今天我们继续第二遍文章,这次将介绍AXObserver,一个可以通过具体Key值进行监控。如window的size、position的变化。

AXObserver

从apple的官方文档可以找到,有两种方式可以创建AXObserver

1.public func AXObserverCreate(_ application: pid_t, _ callback: AXObserverCallback, _ outObserver: UnsafeMutablePointer<AXObserver?>) -> AXError

2.public func AXObserverCreateWithInfoCallback(_ application: pid_t, _ callback: AXObserverCallbackWithInfo, _ outObserver: UnsafeMutablePointer<AXObserver?>) -> AXError

通过这里,发现要成功创建一个AXObserver,需要以下几点要素

  1. pid:程序的进程ID
  2. callback:用于接收监听的回调。是一个闭包类型.而这个callback有两种类型,一种是AXObserverCallback,另外一种是AXObserverCallbackWithInfo
  3. outObserver:inout类型的对象,用于接收AXObserver
  4. AXError: 这个是返回值,用于判断你的AXObserver是否成功创建了。

那么对于callback的回调类型AXObserverCallbackAXObserverCallbackWithInfo有什么不同呢?我们先看看官方文档的定义

1. public typealias AXObserverCallback = @convention(c) (AXObserver, AXUIElement, CFString, UnsafeMutableRawPointer?) -> Void
2. public typealias AXObserverCallbackWithInfo = @convention(c) (AXObserver, AXUIElement, CFString, CFDictionary, UnsafeMutableRawPointer?) -> Void

从官方的文档中,我们可以发现这个闭包都是无返回,但参数基本一致,只是AXObserverCallbackWithInfoAXObserverCallback对了一个字典项CFDictionary

  1. AXObserver:这个就是正在监听的AXObserver对象
  2. AXUIElement:这个就是被监听的AXUIElement对象
  3. CFString:这个就是监听对象的键值,其实就是你要监听的对象的那一个行为的键值
  4. UnsafeMutableRawPointer?: 这个就是调用者,可以为nil
  5. CFDictionary: 只有AXObserverCallbackWithInfo这个才拥有,其实就和我们NSNotification中的userInfo一样。

了解完了,那就上代码

定义回调CallBack

AXObserverCallback

let _observerCallback:AXObserverCallback = { (observer, element, notification, refcon) in
    print("AXObserverCallback did called")
}  

AXObserverCallbackWithInfo

let _observerCallbackWithInfo: AXObserverCallbackWithInfo = {  (observer, element, notification, userInfo, refcon) 
	print("it will called when element did modify")
}

创建AXObserver对象

通过AXObserverCallback创建的AXObserver

var observer : AXObserver?
let error:AXError = AXObserverCreate(pid, _observerCallback, &observer)

通过AXObserverCallbackWithInfo创建的AXObserver

var observer : AXObserver?
let error:AXError = AXObserverCreateWithInfoCallback(pid, _observerCallbackWithInfo, &observer)

添加通知

let selfPtr = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
let error : AXError = AXObserverAddNotification(observer, element, kAXWindowMovedNotification, selfPtr)

kAXWindowMovedNotification这个就是Observer的key了。具体可以查阅AXNotificationConstants.h

添加完通知后,还需要把它添加到RunLoop才算完成:

 CFRunLoopAddSource(CFRunLoopGetCurrent(), observer.runLoopSource(), CFRunLoopMode.defaultMode)

移除通知

首先我们要移除RunLoop

CFRunLoopRemoveSource(CFRunLoopGetCurrent(), observer.runLoopSource(), CFRunLoopMode.defaultMode)

然后移除Key的通知

let error : AXError = AXObserverRemoveNotification(observer, element, kAXWindowMovedNotification)

关于通知和回调的refcon: UnsafeMutableRawPointer?的Get&Set

在添加通知和回调CallBack中,可以看到参数里面都有一个refcon: UnsafeMutableRawPointer?

1. public func AXObserverAddNotification(_ observer: AXObserver, _ element: AXUIElement, _ notification: CFString,_ refcon: UnsafeMutableRawPointer?) -> AXError
2. public typealias AXObserverCallback = @convention(c) (AXObserver, AXUIElement, CFString, UnsafeMutableRawPointer?) -> Void

那我们该如何传值和取值呢?

在需要传值的函数的时候,需要转换一下:

//self 就是你要传过去的对象,这里的self在我的代码其实对应的是ViewController
let selfPtr = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
let error : AXError = AXObserverAddNotification(observer, element, kAXWindowMovedNotification, selfPtr)

那么如何在回调中取值呢?同样也需要转换一下:

let _observerCallbackWithInfo: AXObserverCallbackWithInfo = {  (observer, element, notification, userInfo, refcon) in
    if let ref = refcon{
    	 //因为在传值的时候,我传了一个self(其实是ViewController的对象),所以在转换的时候就变成ViewController
         let obj : ViewController =  Unmanaged<ViewController>.fromOpaque(ref).takeUnretainedValue()
         obj.observerLogView.string += log
    }
}