Сегодня мы разберем внешнюю компоненту для «1С Мобильное приложение». Эта статья появилась по двум причинам. Разрабатывать будем все под тот же АТОЛ Smart.Lite
- Разработка нативного приложения оказалась куда сложней, чем я думал изначально.
- Поступило несколько запросов именно на внешнюю компоненту для 1С
До этого у меня уже был опыт написания c++
внешних компонент. И даже есть шаблон под x86 платформу. Писалось вообще без понимания c++
. Но тем не менее, в двух проектах работает, и не падает. Переходим к сути проблемы. Нужна нативная компонента для получения Broadcast сообщений в 1С. Пробежимся по интернету и поймем, что готовые решения есть. Но все они находятся на мной не очень любимом сайте, за черезмерную жадность. А платить за черный ящик я не хотел. Тем не менее там попалась отличная статья «Внешние компоненты мобильной платформы 1С для ОС Андроид». В ней описывается как слепить мобильную версию компоненты, и что надо установить. Как я понимаю вот здесь лежат исходники по мотивам которой и написана та самая статья. Огромное спасибо доброму человеку за труды. Очень помогло на живом примере понять что и как работает. Далее пришлось немного расширить свой кругозор как же работает JNI. Просто и понятно здесь и здесь. Рекомендую ознакомиться с ними. Уверен, что настоящим программистам c++ мой код не понравится. Прошу отнестись снисходительно и ткнуть, что можно улучшить и написать правильней.
Приступим. Я взял исходный код с репозитория который указывал раньше, и почти полностью его переделал под свои нужды. Взять можно здесь. Пробежимся по основным моментам. Главное процедурой у нас является startEventsWatch
В ней мы проверяем, что у нас не подключен BroadcastReceiver
и переопределяем функцию onReceive
Там мы смотрим какое событие к нам пришло, заполняем поля, и вызываем функцию OnBroadcastReceive
и вот она то и является связующей функцией между java и С++ и переносит нас из мира Android в мир 1С. Об этом чуть позже. Заветные строчки, что же мы хотим получать в 1С выглядят вот так.
filter.addAction("com.xcheng.scanner.action.BARCODE_DECODING_BROADCAST"); filter.addAction(NEW_KEY_UP);
Здесь описывается, что мы ожидаем событие(action) от сканера. В моем случае это com.xcheng.scanner...
. В вашем случае в зависимости от сканера, будет другая строка. Соответственно и данные внутри сообщения, тоже будут другими. Как правило эти данные можно получить у производителя ТСД. Ну или посмотреть в logcat. Еще я хотел получать коды нажатия аппаратных кнопок. Но проблема в лоб не решилась. Простое добавления onKeyUP
в код и отправка этого в sendBroadcast
успехом не увенчались. Оно и не удивительно, наша Activity не на переднем плане. По этой причине пришлось быстренько накидать AccessibilityService
public void startEventsWatch() { if (m_Receiver==null) { m_Receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent != null) { String event, type, data; switch (intent.getAction()) { case "com.xcheng.scanner.action.BARCODE_DECODING_BROADCAST": event = "NewBarcode"; type = intent.getStringExtra("EXTRA_BARCODE_DECODING_SYMBOLE"); data = intent.getStringExtra("EXTRA_BARCODE_DECODING_DATA"); OnBroadcastReceive(m_V8Object, event, type, data); break; case NEW_KEY_UP: event = "NewKeyUP"; type = "key"; data = intent.getStringExtra(KEY_CODE); OnBroadcastReceive(m_V8Object, event, type, data); } } } }; IntentFilter filter = new IntentFilter(); filter.addAction("com.xcheng.scanner.action.BARCODE_DECODING_BROADCAST"); filter.addAction(NEW_KEY_UP); m_Activity.registerReceiver(m_Receiver, filter); } }
Теперь вернемся к нашей отправке данных в 1С. Наша OnBroadcastReceive
вызывает процедуру extern "C" JNIEXPORT void JNICALL Java_org_innovait_atolsmartliteutils_MainApp_OnBroadcastReceive(JNIEnv* env, jclass jClass, jlong pObject, jstring j_event, jstring j_type, jstring j_data)
Вот здесь мы можем добавить переменные, с которыми хотим работать со стороны Java. jstring j_event, jstring j_type, jstring j_data
Это переменные в которых я передаю, событие, тип ШК, и сам ШК. Могут быть и другие данные.
extern "C" JNIEXPORT void JNICALL Java_org_innovait_atolsmartliteutils_MainApp_OnBroadcastReceive(JNIEnv* env, jclass jClass, jlong pObject, jstring j_event, jstring j_type, jstring j_data) { IAddInDefBaseEx *pAddIn = (IAddInDefBaseEx *) pObject; if (pAddIn != nullptr) { std::wstring ws_event =ToWStringJni(j_event); std::wstring ws_type = ToWStringJni(j_type); std::wstring ws_data = ToWStringJni(j_data); std::wstring obj_data{}; obj_data = L"{\"type\": \"" + ws_type + L"\", \"data\": \"" + ws_data + L"\"}"; WcharWrapper wdata((wchar_t*)obj_data.c_str()); WcharWrapper wmsg((wchar_t*)ws_event.c_str()); pAddIn->ExternalEvent(s_EventSource, wmsg, wdata); } }
std::wstring ws_event =ToWStringJni(j_event);
Этим мы переводим строку из jstring
в std::wstring
, и потом это все запаковываем для 1С WcharWrapper wmsg((wchar_t*)ws_event.c_str());
Спасибо умному человеку за функцию конвертации. Вторая функция идет из коробки в примере от 1С.
std::wstring ToWStringJni(jstring jstr) { std::wstring ret; if (jstr) { JNIEnv* env = getJniEnv(); const jchar* jChars = env->GetStringChars(jstr, NULL); jsize jLen = env->GetStringLength(jstr); ret.assign(jChars, jChars + jLen); env->ReleaseStringChars(jstr, jChars); } return ret; }
Тем кто не хочет все устанавливать и сам компилировать. Вот готовые релизы.
На этом все. Жду комментариев.
ссылка на оригинал статьи https://habr.com/ru/post/479132/