你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS ExternalAccessory框架初探

iOS ExternalAccessory框架初探

編輯:IOS開發基礎

這個框架能做什麼

顧名思義:External:外部的;Accessory:配件。應該是和外部設備相關的一個框架。

ExternalAccessory框架,就是可以用來和Lightning接口的硬件,或者藍牙(2.1)設備進行連接、通訊的這麼一個框架。(當然,也可以和30-pin接口的硬件連接、通訊——不過現在幾乎沒有這種接口的設備了吧~)

就是你現在有一個Lightning耳機(iPhone7, 7Plus的耳機~),或者有一個藍牙2.1的音箱,你要寫一個App去控制這些設備,你要選用的框架,就是ExternalAccessory。

比如我前公司,幫美國公司代工的一款藍牙2.1的音箱,寫了一個App進行控制(燈光、音效);還有現在公司,做Lightning設備的App,用來對耳機進行簡單的控制、固件升級。這都需要用到ExternalAccessory框架。

框架簡介

ExternalAccessory框架的主要功能,就是提供一個管道,讓外圍設備可以和基於iOS系統的設備進行通訊。

主要的幾個類:

  • EAAccessory:表示你連接的設備。

  • EAAccessoryManager:有一個重要的屬性connectedAccessories,用來獲取已經連接上手機的設備。

  • EASession:這個類主要用來建立通道,讓App和設備可以進行數據的傳輸(發送和接收)

設備的連接

其實設備的連接、斷開,都是系統自動完成的。

EAAccessoryManager類中有一個屬性connectedAccessories(一個array),裡面就已經包含了所有已經連接的外圍設備(EAAccessory對象)。像什麼設備名稱、制造廠商、硬件型號、固件型號等等信息,都可以在EAAccessory對象中拿得到。

但是,ExternalAccessory框架,並不會自動幫你監控設備的斷開、連接狀態。如果你想拿到設備連接、斷開的回調,則需要手動敲一些代碼了:

拿到連接、斷開的回調

需要注冊通告,即調用EAAccessoryManager的方法registerForLocalNotifications。

當有硬件連接,ExternalAccessory框架就會發送EAAccessoryDidConnectNotification這個通告,當有硬件斷開連接,就會發出EAAccessoryDidDisconnectNotification通告。所以,要監聽、接收這兩個通告。

// 注冊通告
[[EAAccessoryManager sharedAccessoryManager] registerForLocalNotifications];

// 監聽EAAccessoryDidConnectNotification通告(有硬件連接就會回調Block)
[[NSNotificationCenter defaultCenter] addObserverForName:EAAccessoryDidConnectNotification
                                                  object:nil
                                                   queue:nil
                                              usingBlock:^(NSNotification * _Nonnull note) {

                                                  // 從已經連接的外設中查找我們的設備(根據協議名稱來查找)
                                                  [self searchOurAccessory];
}];

[[NSNotificationCenter defaultCenter] addObserverForName:EAAccessoryDidDisconnectNotification
                                                  object:nil
                                                   queue:nil
                                              usingBlock:^(NSNotification * _Nonnull note) {
                                                  // Do something what you want
}];

此外,硬件斷開連接,除了通告回調,框架還提供了Delegate的回調方式,遵守EAAccessoryDelegate協議,並實現accessoryDidDisconnect:這個可選方法(這個協議中的唯一一個方法),也可以拿到硬件斷開連接的回調。(好奇怪,Apple為什麼單單只弄這麼一個方法?)

識別硬件

好了,我們知道硬件連接進行了,那怎麼知道是不是我們的硬件呢?

蘋果公司將這個能識別硬件身份的東東叫做「協議」。本質上就是一個字符串,一個由反向域名組成的字符串,例如om.apple.myProtocol。

而這個協議(字符串)的定義,是由硬件的生產廠商定義的,所以App開發人員,要和廠商溝通拿到這部分的資料。

所以我們要做幾件事件:

  • 導入框架(這個不用說了吧~)#import

  • 在Info.plist中,增加UISupportedExternalAccessoryProtocols這個key,然後值賦為協議名稱(就是那個反向域名字符串)。(其實是一個array,所以這裡可以支持多個協議,不分順序)

  • 在硬件已經連接的回調中,遍歷所有已經連接的設備,根據協議名稱找到自己的硬件(實現上述代碼的searchOurAccessory方法):

// 從已經連接的外設中查找我們的設備(根據協議名稱來查找)
- (void)searchOurAccessory {
    NSMutableString *info = [[NSMutableString alloc] init];

    // search our device
    for (EAAccessory *accessory in [EAAccessoryManager sharedAccessoryManager].connectedAccessories) {

        if ([kSPKLightingHeadphoneProtocolString isEqualToString:[accessory.protocolStrings firstObject]] == YES) {

            // 硬件的協議字符串和硬件廠商提供的一致,這個就是我們要找的設備了!

            // log:可以打印一下該硬件的相關資訊
            for (NSString *proStr in accessory.protocolStrings) {
                [info appendFormat:@"protocolString = %@\n", proStr];
            }
            [info appendFormat:@"\n"];
            [info appendFormat:@"manufacturer = %@\n", accessory.manufacturer];
            [info appendFormat:@"name = %@\n", accessory.name];
            [info appendFormat:@"modelNumber = %@\n", accessory.modelNumber];
            [info appendFormat:@"serialNumber = %@\n", accessory.serialNumber];
            [info appendFormat:@"firmwareRevision = %@\n", accessory.firmwareRevision];
            [info appendFormat:@"hardwareRevision = %@\n", accessory.hardwareRevision];

            // Log...
        }
    }
}

另外,監視硬件連接的通告Block回調,NSNotification * _Nonnull note這個參數,其實是包含了EAAccessory對象,我們也可以直接通過EAAccessoryKey這個key拿到EAAccessory對象,再對比協議字符串是否相同,從而直接拿到已經連接的硬件,無須遍歷connectedAccessories數組。

傳輸數據(指令)

創建EASession、打開輸入、輸出通道

App和外圍設備通訊、數據傳輸,靠的是NSInputStream和NSOutputStream對象,而這兩個對象是EASession的兩個屬性。所以我們要創建EASession對象,謂曰:打開傳輸通道()。

  • 遵守NSStreamDelegate協議,類似:@interface YourClassName(),用於後面拿到相關回調。

  • 創建EASession並打開輸入、輸出通道,類似如下代碼:

- (BOOL)openSession {
    // 根據已經連接的EAAccessory對象和這個協議(反向域名字符串)來創建EASession對象,並打開輸入、輸出通道 
    self.session = [[EASession alloc] initWithAccessory:self.accessory forProtocol: kSPKLightingHeadphoneProtocolString];
    if(self.session != nil) {
        // open input stream
        self.session.inputStream.delegate = self;
        [self.session.inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        [self.session.inputStream open];

        // open output stream
        self.session.outputStream.delegate = self;
        [self.session.outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
        [self.session.outputStream open];
    }
    else {
        NSLog(@"Failed to create session");
    }

    return (nil != self.session);
}

到此為止,就完整創建了一個包含accessory對象、並已經可以進行數據發送和接收的EASession對象了。

stream:handleEvent:回調:

不過,雖然數據傳輸通道已經打開了,但是怎麼發送、接收數據呢?或者說,怎麼知道什麼時候可以發送數據,什麼時候要接收數據?

注意我們剛剛遵守了NSStreamDelegate協議,這裡就是利用delegate回調來監聽input stream和output stream的數據。

// delegate回調的方法
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
    switch (eventCode) {
        case NSStreamEventNone:
            break;
        case NSStreamEventOpenCompleted:
            break;
        case NSStreamEventHasBytesAvailable:
            //NSLog(@"Input stream is ready");
            // 接收到硬件數據了,根據指令定義對數據進行解析。
            [self readFromDevice];
            break;
        case NSStreamEventHasSpaceAvailable:
            //NSLog(@"Output stream is ready");
            // 可以發送數據給硬件了
            [self writeToDevice];
            break;
        case NSStreamEventErrorOccurred:
            break;
        case NSStreamEventEndEncountered:
            break;
        default:
            break;
    }
}
  • HasBytesAvailable:表示stream中有數據需要讀取(硬件發送了數據給App)

  • HasSpaceAvailable:表示stream中可以接收數據的寫入(App發送了數據給硬件)——當然,不是每次都需要等到這個回調執行,App才能發送數據給硬件,你可以判斷stream的hasBytesAvailable屬性,如果為Yes,照樣可以直接發送數據給硬件。類似如下:

BOOL isAvailable = self.session.outputStream.hasSpaceAvailable;
if (isAvailable == YES) {
    [self writeToDevice];
}

發送數據、接收數據的具體方法:

  • 發送數據:

outputStream的write:maxLength:方法,類似如下:

[self.session.outputStream write:[self.writeData bytes] maxLength:self.writeDataLen];
  • 接收數據:

inputStream的read:maxLength:方法,類似如下:

[self.session.inputStream read:buffer maxLength:SPK_INPUT_DATA_BUFFER_LEN];

到此,我們用ExternalAccessory框架,進行了從識別硬件連接、獲取硬件、打開傳輸通道、發送數據、接收數據的完整過程。

調試、Debug

我們開發的是一個Lightning接口設備的App,當手機連接硬件時,就沒辦法連接電腦進行調試,當手機連接電腦時,就沒辦法連接硬件進行測試。所以整個開發調試、Debug無從下手。網站上咨詢了蘋果,也在StackOverflow上提問,都沒有得到解決方案。

後來我就腦洞大開,把需要打印的日志收集起來,通過一個TextView,顯示到App上做調試用(如下圖)。也算是一個權宜之計,誰有更好的辦法麼~

Log.JPG

將Log轉移到App界面上進行Debug

如有謬誤,敬請斧正。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved