Все сказанное в статье будет интересно исключительно разработчикам 1С.
Сегодня мы разберем внешнюю компоненту для «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;
}
Тем кто не хочет все устанавливать и сам компилировать. Вот готовые релизы.
На этом все. Жду комментариев.
Автор: innovaIT