mac 自制app 打包安裝工具(2)

    上篇文章介紹了xcodebuild的命令使用。

1.連接設備

這裏本篇介紹如果連接手機, 安裝app.

因爲製作工具, 新建了一個mac app項目,導入需要使用的私有API,  MobileDevice.framework.

路徑: /System/Library/PrivateFrameworks/MobileDevice.framework

這個文件是沒有頭文件,自己導入一下頭文件MobileDevice.h  , 可以自己晚上找找,傳送門

這裏主要記錄一下常用的幾個接口, 註釋也是比較詳細。

mach_error_t AMDeviceNotificationSubscribe(am_device_notification_callback
    callback, unsigned int unused0, unsigned int unused1, void* //unsigned int
    dn_unknown3, struct am_device_notification **notification);

在當前線程註冊一個函數, 有設備連接時, 會進行通知,

        struct am_device_notification *notify;
        AMDeviceNotificationSubscribe(&device_callback, 0, 0, NULL, ¬ify);		

回調方法:

void device_callback(struct am_device_notification_callback_info *info, void *arg) {
    
    switch (info->msg) {
        case ADNCI_MSG_CONNECTED:
        {
		//device connect    info->dev
            break;
        }
        case ADNCI_MSG_DISCONNECTED:{
		//device disconnect
            break;
        }
        default:
            break;
    }
}

設備連接和設備斷開, info->dev獲取設備的信息。 調用

CFStringRef AMDeviceCopyValue(struct am_device *device, uint32_t, CFStringRef cfstring);
獲取設備信息, 參數分別爲, 設備, 0, 關鍵字。

關鍵字在頭文件裏面有詳細記錄, 

例如獲取設備名稱, 在獲取之前必須連接設備, 要不無法獲取。網上的很多資料都沒有查到, 坑了我不少時間。

AMDeviceConnect(dev);  //nessesary
AMDeviceCopyValue(dev, 0, CFSTR("DeviceName"));
AMDeviceDisconnect(dev); //nessesary


2.安裝

這裏就直接貼代碼了, CFStringRef的釋放。容易內存泄露。

void handle_device(AMDeviceRef device) {
    if (found_device) return; // handle one device only

    CFStringRef found_device_id = AMDeviceCopyDeviceIdentifier(device);

    if (device_id != NULL) {
        if(strcmp(device_id, CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding())) == 0) {
            found_device = true;
        } else {
            return;
        }
    } else {
        found_device = true;
    }

    CFRetain(device); // don't know if this is necessary?

    printf("[  0%%] Found device (%s), beginning install\n", CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding()));

    AMDeviceConnect(device);
    assert(AMDeviceIsPaired(device));
    assert(AMDeviceValidatePairing(device) == 0);
    assert(AMDeviceStartSession(device) == 0);

    CFStringRef path = CFStringCreateWithCString(NULL, app_path, kCFStringEncodingASCII);
    CFURLRef relative_url = CFURLCreateWithFileSystemPath(NULL, path, kCFURLPOSIXPathStyle, false);
    CFURLRef url = CFURLCopyAbsoluteURL(relative_url);

    CFRelease(relative_url);

    int afcFd;
    assert(AMDeviceStartService(device, CFSTR("com.apple.afc"), &afcFd, NULL) == 0);
    assert(AMDeviceStopSession(device) == 0);
    assert(AMDeviceDisconnect(device) == 0);
    assert(AMDeviceTransferApplication(afcFd, path, NULL, transfer_callback, NULL) == 0);      //安裝操作回調函數

    close(afcFd);

    CFStringRef keys[] = { CFSTR("PackageType") };
    CFStringRef values[] = { CFSTR("Developer") };
    CFDictionaryRef options = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    AMDeviceConnect(device);
    assert(AMDeviceIsPaired(device));
    assert(AMDeviceValidatePairing(device) == 0);
    assert(AMDeviceStartSession(device) == 0);

    int installFd;
    assert(AMDeviceStartService(device, CFSTR("com.apple.mobile.installation_proxy"), &installFd, NULL) == 0);

    assert(AMDeviceStopSession(device) == 0);
    assert(AMDeviceDisconnect(device) == 0);

    mach_error_t result = AMDeviceInstallApplication(installFd, path, options, install_callback, NULL);
    if (result != 0)
    {
       printf("AMDeviceInstallApplication failed: %d\n", result);
        exit(1);
    }

    close(installFd);

    CFRelease(path);
    CFRelease(options);

    printf("[100%%] Installed package %s\n", app_path);

    if (!debug) exit(0); // no debug phase

    AMDeviceConnect(device);
    assert(AMDeviceIsPaired(device));
    assert(AMDeviceValidatePairing(device) == 0);
    assert(AMDeviceStartSession(device) == 0);

    printf("------ Debug phase ------\n");

    mount_developer_image(device);      // put debugserver on the device
    start_remote_debug_server(device);  // start debugserver
    write_gdb_prep_cmds(device, url);   // dump the necessary gdb commands into a file

    CFRelease(url);

    printf("[100%%] Connecting to remote debug server\n");
    printf("-------------------------\n");

    signal(SIGHUP, gdb_ready_handler);

    pid_t parent = getpid();
    int pid = fork();
    if (pid == 0) {
        system(GDB_SHELL);      // launch gdb
        kill(parent, SIGHUP);  // "No. I am your father."
        _exit(0);
    }
}

//回調函數, 當前操作的狀態,
void transfer_callback(CFDictionaryRef dict, int arg) {
    int percent;
    CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
    CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);

    if (CFEqual(status, CFSTR("CopyingFile"))) {
        CFStringRef path = CFDictionaryGetValue(dict, CFSTR("Path"));

        if ((last_path == NULL || !CFEqual(path, last_path)) && !CFStringHasSuffix(path, CFSTR(".ipa"))) {
            printf("[%3d%%] Copying %s to device\n", percent / 2, CFStringGetCStringPtr(path, kCFStringEncodingMacRoman));
        }

        if (last_path != NULL) {
            CFRelease(last_path);
        }
        last_path = CFStringCreateCopy(NULL, path);
        if (last_path != NULL) {
            CFRelease(last_path);
        }
    }
    CFRelease(dict);
    CFRelease(status);
}

void install_callback(CFDictionaryRef dict, int arg) {
    int percent;
    CFStringRef status = CFDictionaryGetValue(dict, CFSTR("Status"));
    CFNumberGetValue(CFDictionaryGetValue(dict, CFSTR("PercentComplete")), kCFNumberSInt32Type, &percent);

    printf("[%3d%%] %s\n", (percent / 2) + 50, CFStringGetCStringPtr(status, kCFStringEncodingMacRoman));
    CFRelease(dict);
}

2個關鍵點,設備和安裝的app。
github上面有一個開源相關代碼, 可以借鑑一下。 

歡迎討論生氣


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章