Javascript - это мощный язык сценариев, который также прост в изучении и очень популярен. Существует множество возможностей для использования его в приложении или игре, от разработки сценариев до создания полноценного движка.
Одна из замечательных особенностей этого заключается в том, что вы можете обновлять свое приложение или игру в горячем режиме без необходимости повторной отправки в магазины. Однако это может привести к бану, так что не вините меня за то, что я не сказал вам заранее.
У меня всегда было желание создать свой собственный кроссплатформенный SDK для приложений, похожий на React-native или Flutter с Javascript в качестве языка программирования. Разница будет в дизайне и архитектуре пользовательского интерфейса, но это уже другая история. Таким образом, моим первым камнем преткновения было бы запускать исходный код Javascript с собственным кодом.
Вы можете спросить, почему я хочу заново изобрести колесо, но, черт возьми, я этого не делаю. Дерьмо застряло на колесах. Дизайн пользовательского интерфейса с поддержкой React слишком утомителен, и я ненавижу флюс. Чтобы еще больше разозлить нас, программистов, Flutter использует Dart. При всем уважении к людям, создавшим эти хорошие SDK, я не могу понять, почему они не сделали вещи похожими на Microsoft WPF.
Во всяком случае, вернемся к основной теме.
Любопытно, что я не смог найти ни одного документа, который рассказывал бы мне, как выполнять эту работу поэтапно. После некоторой борьбы и усилий, чтобы сделать это самому, я решаю поделиться своим опытом здесь.
Если вы дочитали до этого места, держу пари, вы знаете, что хотите получить из этого рассказа, который я пишу. Вы, наверное, тоже страдали от отсутствия документов для этого задания.
Цель этой истории - запустить простую строку Javascript и показать результат на экране.
Давай начнем!
Почему именно JavaScriptCore?
- React-native использует его. Большая часть тяжелой работы была сделана сообществом React-native.
- Он встроен в iOS, половина работы уже выполнена.
Обзор
- Первое, что нужно сделать, это скомпилировать JavaScriptCore в общую библиотеку Android.
- Свяжите общую библиотеку с проектом Android.
- Запустите простой сценарий, используя эту библиотеку.
Шаг 1. Скомпилируйте JavaScriptCore
Это самая сложная и длительная задача, однако она была сделана очень простой, потому что сообщество, использующее реагирование, уже сделало все здесь: https://github.com/react-community/jsc-android-buildscripts.
Итак, для начала, клонируйте это репо где-нибудь и делайте точно так, как их инструкции в файле README, пока не получите папку с именем compiled в / build. Этот процесс может занять более 2 часов.
Вы можете многому научиться, пытаясь скомпилировать разделяемую библиотеку самостоятельно, не прибегая к тому, что уже было сделано сообществом react-native. Но поверьте мне, эти знания будут очень тяжелыми, и вы будете много ругаться в процессе.
В этой папке вы можете увидеть файлы общей библиотеки (файлы .so) JavaScriptCore:
Нам потребуются все созданные выше so файлы. Вы можете попробовать удалить некоторые из них позже и посмотреть, работает ли приложение по-прежнему.
Шаг 2 - Создайте новый проект Android
В этой истории для простоты я использую Android Studio.
При создании нового проекта убедитесь, что включена поддержка C ++:
И минимальный целевой API - 21, потому что он нужен JavaScriptCore:
Попробуйте запустить пустой проект приложения.
Часть нашего приложения с собственным кодом начинается в app / src / main / cpp / native-lib.cpp, мы изменим его для запуска этой простой строки Javascript.
Строка вычисляет простую математическую операцию с использованием определенной функции, а затем возвращает простое сообщение в контекст.
Но перед этим нам нужно связать разделяемую библиотеку с нашим проектом.
Шаг 3. Свяжите общую библиотеку с проектом Android
Вам необходимо создать каталог с именем jniLibs в app / src / main и скопировать сюда все so файлы, созданные на шаге 1. Таким образом, вы получите следующее:
Все, что находится в app / src / main / jniLibs, будет автоматически скопировано в сборку. Нам не нужно указывать Android Studio делать что-либо еще.
Теперь, если мы вызовем функции JavaScriptCore в файле native-lib.cpp, мы обязательно получим ошибки компиляции. Однако просто продолжайте, измените этот файл cpp следующим образом. Я хочу, чтобы вы получили ошибку при обучении.
#include <jni.h> #include <string> #include "JavaScriptCore/JavaScriptCore.h" std::string JSStringToStdString(JSStringRef jsString) { size_t maxBufferSize = JSStringGetMaximumUTF8CStringSize(jsString); char* utf8Buffer = new char[maxBufferSize]; size_t bytesWritten = JSStringGetUTF8CString(jsString, utf8Buffer, maxBufferSize); std::string utf_string = std::string(utf8Buffer, bytesWritten -1); delete [] utf8Buffer; return utf_string; } extern "C" JNIEXPORT jstring JNICALL Java_com_example_chungnguyen_testjsc_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { JSContextGroupRef contextGroup = JSContextGroupCreate(); JSGlobalContextRef globalContext = JSGlobalContextCreateInGroup(contextGroup, nullptr); JSStringRef statement = JSStringCreateWithUTF8CString("function degToRad(value) { return (value * Math.PI) / 180; } ('90deg = ' + degToRad(90))"); JSValueRef retValue = JSEvaluateScript(globalContext, statement, nullptr, nullptr, 1,nullptr); JSStringRef retString = JSValueToStringCopy(globalContext, retValue, nullptr); std::string hello = JSStringToStdString(retString); JSGlobalContextRelease(globalContext); JSContextGroupRelease(contextGroup); JSStringRelease(statement); JSStringRelease(retString); return env->NewStringUTF(hello.c_str()); }
Самая первая ошибка будет отсутствовать в файле заголовка JavaScriptCore / JavaScriptCore.h. Моя реакция, когда я получил эту ошибку компиляции, была: «Где, черт возьми, я могу найти этот файл заголовка?».
Вернитесь в папку репозитория jsc-android-buildscripts, которую вы клонировали ранее, вы можете найти исходный код JavaScriptCore в build / target / webkit / Source. Что нужно сделать, так это добавить путь поиска файлов заголовков в наш Android-проект.
Для этого в файле CMakeLists.txt по адресу app / src добавьте следующие строки:
include_directories(../../jsc-android-buildscripts/build/target/webkit/Source/JavaScriptCore) include_directories(../../jsc-android-buildscripts/build/target/webkit/Source/JavaScriptCore/ForwardingHeaders)
В моем случае папка репозитория jsc-android-buildscripts находится в том же месте в папке проекта Android, поэтому я помещаю пути поиска заголовков, как указано выше.
Теперь пути к файлам заголовков разрешены, но после повторной компиляции у нас есть ошибки ссылок о неопределенных ссылках.
Не нужно паниковать, нам просто нужно связать разделяемую библиотеку с нашим собственным кодом. Для этого сначала нам нужно определить свойство библиотеки, добавив эти строки в файл CMakeLists.txt.
add_library(jsc SHARED IMPORTED) set_target_properties( # Specifies the target library. jsc # Specifies the parameter you want to define. PROPERTIES IMPORTED_LOCATION # Provides the path to the library you want to import. src/main/jniLibs/${ANDROID_ABI}/libjsc.so )
Эти строки определяют общую библиотеку с именем jsc. Настоящее имя файла - libjsc.so, но расположение файла основано на архитектуре ABI при компиляции собственного кода.
Затем нам нужно фактически связать библиотеку, добавив jsc эту строку:
target_link_libraries( # Specifies the target library. native-lib jsc # Links the target library to the log library # included in the NDK. ${log-lib} )
Вот и все! Запустите приложение, чтобы увидеть результат.