Android с NDK поддерживает код C / C ++, а iOS с Objective-C ++ также поддерживает, так как я могу писать приложения с собственным кодом C / C ++, совместно используемым Android и iOS?
java
c++
java-native-interface
cross-platform
objective-c++
ademar111190
источник
источник
Ответы:
Обновить.
Этот ответ довольно популярен даже через четыре года после того, как я его написал, за эти четыре года многое изменилось, поэтому я решил обновить свой ответ, чтобы он лучше соответствовал нашей нынешней реальности. Идея ответа не меняется; реализация немного изменилась. Мой английский тоже изменился, он сильно улучшился, поэтому теперь ответ стал более понятным для всех.
Взгляните на репозиторий, чтобы вы могли загрузить и запустить код, который я покажу ниже.
Ответ
Прежде чем я покажу код, обратите внимание на следующую диаграмму.
Каждая ОС имеет свой пользовательский интерфейс и особенности, поэтому мы намерены написать специальный код для каждой платформы в этом отношении. С другой стороны, весь логический код, бизнес-правила и вещи, которыми можно поделиться, мы намерены написать с использованием C ++, чтобы мы могли скомпилировать один и тот же код для каждой платформы.
На схеме вы можете увидеть слой C ++ на самом нижнем уровне. Весь общий код находится в этом сегменте. Самый высокий уровень - это обычный код Obj-C / Java / Kotlin, здесь нет новостей, сложная часть - это средний уровень.
Средний уровень со стороны iOS прост; вам нужно только настроить свой проект для сборки с использованием варианта Obj-c, известного как Objective-C ++, и все, у вас есть доступ к коду C ++.
Со стороны Android дело стало сложнее: оба языка, Java и Kotlin, на Android работали под виртуальной машиной Java. Таким образом, единственный способ получить доступ к коду C ++ - использовать JNI , пожалуйста, найдите время, чтобы прочитать основы JNI. К счастью, сегодняшняя Android Studio IDE имеет значительные улучшения на стороне JNI, и при редактировании кода вам будет показано множество проблем.
Код по шагам
Наш образец - это простое приложение, в котором вы отправляете текст в CPP, оно преобразует этот текст во что-то еще и возвращает его. Идея состоит в том, что iOS отправит «Obj-C», а Android отправит «Java» со своих соответствующих языков, а код CPP создаст текст в следующем виде: «cpp передает привет << полученному тексту >> ».
Общий код CPP
Прежде всего, мы собираемся создать общий код CPP, сделав это, у нас есть простой файл заголовка с объявлением метода, который получает желаемый текст:
И реализация CPP:
Юникс
Интересным бонусом является то, что мы можем использовать один и тот же код для Linux и Mac, а также для других систем Unix. Эта возможность особенно полезна, потому что мы можем быстрее протестировать наш общий код, поэтому мы собираемся создать Main.cpp, как показано ниже, чтобы выполнить его с нашей машины и посмотреть, работает ли общий код.
Для сборки кода необходимо выполнить:
IOS
Пора реализовать на мобильной стороне. Поскольку iOS имеет простую интеграцию, мы начинаем с нее. Наше приложение для iOS представляет собой типичное приложение Obj-c с одним отличием; файлы есть
.mm
и нет.m
. т.е. это приложение Obj-C ++, а не приложение Obj-C.Для лучшей организации мы создаем CoreWrapper.mm следующим образом:
Этот класс отвечает за преобразование типов и вызовов CPP в типы и вызовы Obj-C. Это не обязательно, если вы можете вызвать код CPP для любого файла, который хотите на Obj-C, но он помогает сохранить организацию, и за пределами ваших файлов-оберток вы поддерживаете полный код в стиле Obj-C, только файл оберток становится в стиле CPP ,
Как только ваша оболочка подключена к коду CPP, вы можете использовать ее как стандартный код Obj-C, например ViewController "
Посмотрите, как выглядит приложение:
Android
Пришло время интеграции с Android. Android использует Gradle в качестве системы сборки, а для кода C / C ++ использует CMake. Итак, первое, что нам нужно сделать, это настроить CMake в файле gradle:
И второй шаг - добавить файл CMakeLists.txt:
Файл CMake - это то место, где вам нужно добавить файлы CPP и папки заголовков, которые вы будете использовать в проекте, в нашем примере мы добавляем
CPP
папку и файлы Core.h / .cpp. Чтобы узнать больше о конфигурации C / C ++, прочтите ее.Теперь основной код является частью нашего приложения, пришло время создать мост, чтобы упростить и упорядочить вещи, мы создаем определенный класс с именем CoreWrapper, который будет нашей оболочкой между JVM и CPP:
Обратите внимание, что этот класс имеет
native
метод и загружает собственную библиотеку с именемnative-lib
. Эта библиотека - та, которую мы создаем, в конце концов, код CPP станет.so
файлом общего объекта, встроенным в наш APK, иloadLibrary
он загрузит его. Наконец, когда вы вызываете собственный метод, JVM делегирует вызов загруженной библиотеке.Теперь самая странная часть интеграции Android - это JNI; Нам нужен файл cpp, как показано ниже, в нашем случае "native-lib.cpp":
Первое, что вы заметите, это то, что
extern "C"
эта часть необходима для правильной работы JNI с нашим кодом CPP и связями методов. Вы также увидите некоторые символы, которые JNI использует для работы с JVM, например,JNIEXPORT
иJNICALL
. Чтобы вы поняли значение этих вещей, необходимо уделить время и прочитать их , для целей данного руководства просто рассматривайте эти вещи как шаблон.Одна важная вещь, которая обычно является корнем многих проблем, - это название метода; он должен соответствовать шаблону «Java_package_class_method». В настоящее время студия Android имеет отличную поддержку для этого, поэтому она может автоматически сгенерировать этот шаблон и показать вам, правильно он или не назван. В нашем примере наш метод называется «Java_ademar_androidioscppexample_CoreWrapper_concatenateMyStringWithCppString», потому что «ademar.androidioscppexample» - это наш пакет, поэтому мы заменяем «.» по «_» CoreWrapper - это класс, в котором мы связываем собственный метод, а «concatenateMyStringWithCppString» - это само имя метода.
Поскольку у нас правильно объявлен метод, пришло время проанализировать аргументы, первый параметр - это указатель на
JNIEnv
то, как мы имеем доступ к материалам JNI, очень важно, чтобы мы сделали наши преобразования, как вы скоро увидите. Второй -jobject
это экземпляр объекта, который вы использовали для вызова этого метода. Вы можете думать, что это java " this ", в нашем примере нам не нужно его использовать, но мы все равно должны его объявить. После этого задания мы получим аргументы метода. Поскольку наш метод имеет только один аргумент - String «myString», у нас есть только «jstring» с тем же именем. Также обратите внимание, что наш возвращаемый тип также является jstring. Это связано с тем, что наш метод Java возвращает String, для получения дополнительной информации о типах Java / JNI прочтите его.Последний шаг - преобразовать типы JNI в типы, которые мы используем на стороне CPP. В нашем примере мы преобразуем
jstring
его вconst char *
отправку, конвертируем в CPP, получаем результат и конвертируем обратно вjstring
. Как и все другие шаги по JNI, это не сложно; это всего лишь шаблон, вся работа выполняетсяJNIEnv*
аргументом, который мы получаем, когда вызываемGetStringUTFChars
иNewStringUTF
. После этого наш код готов к запуску на устройствах Android, давайте посмотрим.источник
Подход, описанный в превосходном ответе выше, может быть полностью автоматизирован с помощью Scapix Language Bridge, который генерирует код оболочки на лету непосредственно из заголовков C ++. Вот пример :
Определите свой класс на C ++:
И назовите его из Swift:
И с Java:
источник