gifts2017

Давайте подружим 1С и Android. Часть 2 - авторизуемся в 1С

Опубликовал Тимур Мансорунов (solarisman) в раздел Программирование - Мобильные приложения

Авторизуемся в 1С.

Вспомним основную задачу (часть 1):

Задача: Создать приложение, которое позволит осуществлять приемку товара в розничном магазине при перемещении со склада компании.


В этой части:

  • Создадим приложение, кастомизированое под 1С
  • Будем выполнять авторизацию в базе 1С.

Касательно веб-сервисов рассмотрим:

  • Вызов операции сервиса без параметров.
  • Вызов операции сервиса с параметрами примитивного типа.

Начнем

Для вызова операции веб-сервиса пользователю нужна роль, которой доступен вызов операций этого сервиса. Логично, что вызов операций проходит с теми же логином и паролем, что и вход в обыное приложение 1С. Если устройства персонализированы, то все достаточно просто - можно хранить в настройках тот самый пароль. Расммотим более тяжелый случай, когда с одного и того же устройства могут авторизоваться несколько человек.

Какие осложнения нам это дает:

1) Список пользователей должен быть динамическим

2) Пароли на устройстве хранить нельзя

3) Для обращению к списку пользователей ИБ нужны административные права

Тогда мы создадим отдельного пользователя с минимальным набором прав, который даст список пользователей, затем будем выбирать нужного, вводить пароль и начинать работу.

Создадим:

  • Пользователя "WSuser", который получит логины.
  • Регистр сведений "ПользователиМобильногоКлиента" с одним-единственным измерением "Пользователь", в котором будем хранить список пользователей для авторизации на мобильном клиенте
  • XDTO-пакет "AcceptingOrdersPackage" с URI "AcceptingOrdersService"
  • Веб-серис "AcceptingOrders", определим 2 операции "GetLoginList" и "Login" первая, соответственно, будет получать список пользователей, вторая проверять имя пользователя и пароль.
  • Роль "СлужебныйПользовательМобильногоКлиента", у которой есть доступ к обеим операциям сервиса, право чтения регистра "ПользователиМобильногоКлиента" и справочника "Пользователи", право администрирования.

Теперь наш пакет и веб-сверис имеют такой вид:

 

 

Операция GetLoginList:

Функция GetLoginList()

	Запрос = Новый Запрос("ВЫБРАТЬ РАЗЛИЧНЫЕ
	|	ПользователиМобильногоКлиента.Пользователь.ИдентификаторПользователяИБ КАК ИдентификаторПользователяИБ,
	|	ПользователиМобильногоКлиента.Пользователь.Наименование КАК Наименование
	|ИЗ
	|	РегистрСведений.ПользователиМобильногоКлиента КАК ПользователиМобильногоКлиента");
	Выборка = Запрос.Выполнить().Выбрать();
	
	xdtoТипОтвета = ФабрикаXDTO.Тип("AcceptingOrdersService", "LoginList");
	xdtoТипЛогин = ФабрикаXDTO.Тип("AcceptingOrdersService", "Login");
	xdtoОтвет = ФабрикаXDTO.Создать(xdtoТипОтвета);	
	
	Пока Выборка.Следующий() Цикл
		Если Не ЗначениеЗаполнено(Выборка.ИдентификаторПользователяИБ) Тогда
			Продолжить;
		КонецЕсли;	
		xdtoЛогин = ФабрикаXDTO.Создать(xdtoТипЛогин);
		xdtoЛогин.Description = Выборка.Наименование;
		xdtoЛогин.ID = Строка(Выборка.ИдентификаторПользователяИБ);
		xdtoОтвет.Login.Добавить(xdtoЛогин);
	КонецЦикла;	
	
	Возврат xdtoОтвет;
		
КонецФункции

Операция Login:

Функция Login(ID, Password)

	xdtoТипОтвета = ФабрикаXDTO.Тип("AcceptingOrdersService", "LoginResult");
	xdtoОтвет = ФабрикаXDTO.Создать(xdtoТипОтвета);	
	xdtoОтвет.Name = "";
	
	ИдентификаторПользователя = Новый УникальныйИдентификатор(ID);	
	ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(ИдентификаторПользователя);
	Если ПользовательИБ <> Неопределено
		И ПользовательИБ.СохраняемоеЗначениеПароля = СтрЗаменить(Password, Символы.ПС, "") Тогда
		
		xdtoОтвет.Name = ПользовательИБ.Имя;
		xdtoОтвет.Result = Истина;
		Возврат xdtoОтвет;
		
	КонецЕсли;
	
	xdtoОтвет.Result = Ложь;
	Возврат xdtoОтвет;
	
КонецФункции

Серверная часть готова, переходим к Android

В файлах есть архив с проектом для Android Studio, если необходимо. Весь код выкладывать будет излишним, буду разбирать только самое необходимое

Далее не претендую на грамотность объяснений и оптимальность кода, расскажу как понимаю сам, проводя иногда аналоги с 1С.

Что важно понимать при программировании обращения к сетевым ресурсам:

В андроид (Java) есть понятие потоков , параллельно работающих процессов. Основной поток программы управляет главной активностью (окно программы по-простому) и нельзя в основном потоке выполнять действия, которые будут "занимать" его. Таким образом, для обращения к веб-сервису мы должны создать дополнительный поток, Thread (я бы сказал, что это очень похоже на фоновое задание). Для того, чтобы получить ответ используется обработчик, Handler (как ОбработкаОповещения).

То есть мы создаем параллельный поток и просто ждем ответа от него. Это можно сравнить с подбором товара в табличную часть документа - при нажатии кнопки открывается общая форма, но при этом форма документа продолжает работать в обычном режиме. А при выборе строки в форме подбора срабатывает обработчик оповещения, в параметры которого передается строка.

Рассмотрим основной класс - MainActivity

Опустим описание переменных кроме трех:

public static final int ACTION_ConnectionError = 0;
public static final int ACTION_GetLoginList = 1;
public static final int ACTION_Login = 2;

Это вспомогательные переменные, имеющие фиксированное значение.

1) Обработчик создания

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Инициализируем вспомогательный класс
uiManager = new UIManager(this);
// Инициализируем менеджер настроек
preferences = PreferenceManager.getDefaultSharedPreferences(this);
// Читаем идентификатор последнего выбранного пользователя из настроек
wsParam_LoginID = preferences.getString("LoginID", "");
// Читаем настройки подключения
initiateConnectionSettings();
// Инициализируем обработчик ответа от сервиса
soapHandler = new incomingHandler(this);

if (soapParam_URL.equals(""))
// Первый запуск, открываем настройки
openSettings();
else {
// Выводим на экран форму авторизации
setContentView(R.layout.activity_main);
// Запрашиваем список пользователей
startExchange(ACTION_GetLoginList);
}
}

Думаю, комментарии излишни

2) Класс для обработки сообщений от сервиса (от параллельного потока)

private static class incomingHandler extends Handler {

private final WeakReference<MainActivity> mTarget;

// Конструктор
public incomingHandler(MainActivity context){
mTarget = new WeakReference<>(context);
}

@Override
public void handleMessage(Message msg) {

MainActivity target = mTarget.get();
switch (msg.what) {
case ACTION_ConnectionError:
uiManager.showToast("Ошибка" + getSoapErrorMessage());
break;
case ACTION_GetLoginList:
target.initiateLoginList();
break;
case ACTION_Login:
target.checkLoginResult();
break;
}

}
}

Метод "handleMessage" как раз выполняет обработку сообщения, тело сообщения - msg.what. В зависимости от значения выполняем обработку. Как мы видим, при получения сообщения об ошибке соединения, выводится сообщение. О том как оно формируется - ниже.

3) Обработчик заполнения списка пользователей после получения ответа от сервиса

protected void initiateLoginList(){

ArrayList<String> loginList = new ArrayList<>();
loginIDList = new ArrayList<>();

int count = soapParam_Response.getPropertyCount();
int position = 0;

for (int i = 0; i < count; i++) {
SoapObject login = (SoapObject) soapParam_Response.getProperty(i);
String name = login.getPropertyAsString("Description");
String id = login.getPropertyAsString("ID");
loginList.add(name);
loginIDList.add(id);

if (wsParam_LoginID.equals(id)){
position = i;
}

}

ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.spinner_user, loginList);

Spinner spinner = (Spinner) findViewById(R.id.spinner);
spinner.setPrompt("Выберите пользователя");
spinner.setAdapter(adapter);
spinner.setSelection(position);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
saveUserID(position);
}

@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});

}

Давайте обратим внимание на цикл:

 int count = soapParam_Response.getPropertyCount();
int position = 0;

for (int i = 0; i < count; i++) {
SoapObject login = (SoapObject) soapParam_Response.getProperty(i);
String name = login.getPropertyAsString("Description");
String id = login.getPropertyAsString("ID");
loginList.add(name);
loginIDList.add(id);

if (wsParam_LoginID.equals(id)){
position = i;
}

}

В 1С в xdto пакете мы определили тип "LoginList" со свойством типа "Login" и переменная "soapParam_Response" - ответ от сервиса типа "LoginList". Если в отладке 1С мы попытаемся посмотреть свойства пакета, то будет одно свойство типа "Login" и в нем только несколько экземпляров. Это несколько искажает представление о том, как работать с xdto в Android. Тут же у пакета "soapParam_Response" столько свойств с именем "Login", сколько экземпляров мы в него добавили в 1С. То есть у нас count логинов и мы столько же свойств и получаем - getProperty(i). Если бы в пакете были свойства другого типа, нам пришлось бы проверять имя свойства. В дальнейшем такие моменты тоже разберем.

Сам же экземпляр списка, полученный методом getProperty(i) имеет тип так же SoapObject. Его поля имеют строковый тип и получаются так - getPropertyAsString("Description").

В данном обработчике мы заполняем выпадающий список пользователей и в качестве текущего устанавливаем последний выбранный.

4) Обработчик авторизации

public void checkLoginResult(){

Boolean isLoginSuccess = Boolean.parseBoolean(soapParam_Response.getPropertyAsString("Result"));

if (isLoginSuccess){
soapParam_user = soapParam_Response.getPropertyAsString("Name");
EditText wsParam_Password = (EditText) findViewById(R.id.wsParam_Password);
soapParam_pass = wsParam_Password.getText().toString();
setActivityTaskList();
}
else
uiManager.showToast("Ошибка! Неверно введен пароль");

}

Обратите внимание на то, как мы получаем реквизит типа булево - приходится парсить его из строки. Так же можно воспользоваться методом "getPrimitiveProperty". Аналогично с данными типа число.

5) Обработчик разбора ошибок сервиса

private static String getSoapErrorMessage () {

String errorMessage;

if (responseFault == null)
errorMessage = "Отсутствует соединение с сервером.";
else{
try {
errorMessage = responseFault.faultstring;
}
catch (Exception e) {
e.printStackTrace();
errorMessage = "Неизвестная ошибка.";
}
}

return errorMessage;
}

В случае ошибки при вычислении на стороне 1С в responseFault.faultstring будет полное описание ошибки.

6) Вызов операции веб-сервера

protected void startExchange(int ACTION){

SOAP_Dispatcher dispatcher = new SOAP_Dispatcher(soapParam_timeout, soapParam_URL, soapParam_user, soapParam_pass, ACTION);
dispatcher.start();

}

В качестве параметра передается переменная ACTION - номер операции для вызова сервиса.

 

Рассмотрим класс SOAP_Dispatcher - это основной класс для работы с сервисом 1С

public class SOAP_Dispatcher extends Thread {

int timeout;
String URL;
String user;
String pass;
int ACTION;
SoapObject soap_Response;
final String NAMESPACE = "AcceptingOrdersService";

public SOAP_Dispatcher(int soapParam_timeout, String soapParam_URL, String soapParam_user, String soapParam_pass, int SOAP_ACTION){
timeout = soapParam_timeout;
URL = soapParam_URL;
user = soapParam_user;
pass = soapParam_pass;
ACTION = SOAP_ACTION;
}

@Override
public void run() {

switch (ACTION) {
case MainActivity.ACTION_GetLoginList:
GetLoginList();
break;
case MainActivity.ACTION_Login:
Login();
break;
}

if (soap_Response != null) {
MainActivity.soapParam_Response = soap_Response;
MainActivity.soapHandler.sendEmptyMessage(ACTION);
} else {
MainActivity.soapHandler.sendEmptyMessage(MainActivity.ACTION_ConnectionError);
}

}

void GetLoginList(){

String method = "GetLoginList";
String action = NAMESPACE + "#AcceptingOrders:" + method;
SoapObject request = new SoapObject(NAMESPACE, method);
soap_Response = callWebService(request, action);

}

void Login(){

String method = "Login";
String action = NAMESPACE + "#AcceptingOrders:" + method;
SoapObject request = new SoapObject(NAMESPACE, method);
request.addProperty("ID", MainActivity.wsParam_LoginID);
request.addProperty("Password", MainActivity.wsParam_PassHash);
soap_Response = callWebService(request, action);

}


private SoapObject callWebService(SoapObject request, String action){

SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.setOutputSoapObject(request);
envelope.dotNet = true;
envelope.implicitTypes = true;
HttpTransportSE androidHttpTransport = new HttpTransportBasicAuthSE(URL, user, pass, timeout);
androidHttpTransport.debug = true;

try {
androidHttpTransport.call(action, envelope);
return (SoapObject) envelope.getResponse();
} catch (Exception e) {
e.printStackTrace();
MainActivity.responseFault = (SoapFault) envelope.bodyIn;
}

return null;
}

}

Здесь:
NAMESPACE - пространство имен, заданное в 1С
timeout,
URL,user,pass - параметры подключения
public
SOAP_Dispatcher - конструктор класса
run() - обработчик, срабатываемый при вызове метода start() экземпляра класса (смотри 6) Вызов операции веб-сервера)

Далее следуют как раз вызовы операций веб-сервисов:

void GetLoginList(){

String method = "GetLoginList";
String action = NAMESPACE + "#AcceptingOrders:" + method;

SoapObject request = new SoapObject(NAMESPACE, method);

soap_Response = callWebService(request, action);

}

Здесь:

  • method - имя операции веб сервиса как в 1С
  • AcceptingOrders - имя сервиса
  • request - вспомогательная переменная
  • callWebService - служебный метод, его можно просто скопировать.
  • soap_Response - ответ от сервиса.


Обратите внимание: переменная soap_Response имеет тип SoapObject, как и метод callWebService. Если операция веб-сервиса будет возвращать ответ примитивного типа - string или boolean, то ответ будет типа SoapPrimitive и для таких операций нужны отдельные методы и переменные.

void Login(){

String method = "Login";
String action = NAMESPACE + "#AcceptingOrders:" + method;

SoapObject request = new SoapObject(NAMESPACE, method);
request.addProperty("ID", MainActivity.wsParam_LoginID);
request.addProperty("Password", MainActivity.wsParam_PassHash);

soap_Response = callWebService(request, action);

}

Это уже вызов операции с параметрами. Ничего сложного нет, параметр вставляется так:

request.addProperty("ID", MainActivity.wsParam_LoginID);

Таким образом, класс SOAP_Dispatcher получился довольно универсальным и добавление операции на сервисе не создаст нам трудностей.

Пароли

Теперь еще раз о паролях. Повторим то, с чего начали - мы имеем устройство не персональное, то есть любой пользователь должен иметь возможность авторизоваться с любого устройства. Понятно, что для удобства список пользователей должен быть доступен на устройстве, для этого мы придумали служебного пользователя. Как же поступаем дальше?

Вот мы ввели пароль, теперь выполним проверку. В 1С у пользователя ИБ есть свойство "СохраняемоеЗначениеПароля" которое составляется следующим образом: вычисляется хеш-функция SHA1 от пароля и через запятую хеш-функция от пароля в верхнем регистре. Из соображений безопасности нам этот хеш надо вычислять на мобильном устройстве. Для работы с текстом есть отдельный класс Parser_Text

Вот метод, вычисляющий хеш от пароля:

public static String getPassHash(String text){ 

return base64string(sha1(text)) + "," + base64string(sha1(text.toUpperCase()));

}

И уже далее если хеш пароля совпадает с хранимым в 1С, мы продолжаем работу с сервисом, но не с логином WSuser, а уже под выбранным пользователем. Для этого операция Login возвращает нам результат проверки пароля и имя пользователя для входа.

Кастомизация

Далее не относящееся к веб-серсиам. Сделаем наше приложение более-менее похожим на 1С:

 

 

 

Чтобы все элементы управления были одинаковыми без вмешательства разработчика, в файле Styles.xml описываем стили.

<style name="AppTheme" parent="android:Theme.NoTitleBar.Fullscreen">
<item name="android:textViewStyle">@style/textViewStyle</item>
<item name="android:editTextStyle">@style/editTextStyle</item>
<item name="android:spinnerStyle">@style/spinnerStyle</item>
<item name="android:windowBackground">@color/form</item>
</style>

<style name="textViewStyle" parent="android:Widget.TextView">

<item name="android:textColor">@color/text</item>
<item name="android:textSize">@dimen/textsize</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginTop">@dimen/layout_vertical_margin</item>
<item name="android:paddingBottom">@dimen/element_vertical_padding</item>
<item name="android:paddingTop">@dimen/element_vertical_padding</item>
<item name="android:paddingLeft">@dimen/element_horizontal_padding</item>
<item name="android:paddingRight">@dimen/element_horizontal_padding</item>

</style>

<style name="editTextStyle" parent="android:Widget.EditText">

<item name="android:textColor">@color/text</item>
<item name="android:textSize">@dimen/textsize</item>
<item name="android:background">@drawable/edit</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginTop">@dimen/layout_vertical_margin</item>
<item name="android:singleLine">true</item>

</style>

<style name="spinnerStyle" parent="android:Widget.Spinner">

<item name="android:textColor">@color/text</item>
<item name="android:textSize">@dimen/textsize</item>
<item name="android:background">@drawable/spinner</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:dropDownWidth">wrap_content</item>

</style>

Все необходимые ресурсы есть в архиве с проектом.

 

В следующей части:

  • Поработаем с последовательностью xdto (ПоследовательностьXDTO в 1С)
  • Посканируем при помощи камеры устройства
  • Вызовем операцию сервиса с параметром типа xdtoОбъект.

Каждый раз я буду выкладывать архив проекта, если нет необходимости - не качайте

Скачать файлы

Наименование Файл Версия Размер
Архив проекта AndroidStudio 21
.7z 4,73Mb
08.03.16
21
.7z 4,73Mb Скачать

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Евгений Плешивцев (infosoft-v) 08.03.16 19:24
Отличная статья. Спасибо.
2. Анна Бондаренко (Ann.prog1C) 08.03.16 21:48
3. Ruslan (flyer) 09.03.16 07:33
продолжайте слежу за темой
4. gudun_ku (gudun_ku) 09.03.16 09:28
Отличная статья. Код под Андроид хорош, оценил, что легко переложить на net cf, чем я и займусь как-нибудь. Жду продолжения, про сканирование.
5. Иван Титов (Ibrogim) 09.03.16 09:49
Вот теперь ставлю плюс.

Вы кстати вдохновили меня тоже статью написать)
6. Ярослав Радкевич (WKBAPKA) 09.03.16 10:30
о, отличная статья. спасибо
7. Яков Коган (Yashazz) 09.03.16 16:14
Вообще не очень понимаю, нафига это всё... Мобильное приложение - кривоватое побочное дитя попытки 1С угнаться за модой, которое однажды канет в прошлое, как веб-расширения и тому подобная мутота... Охота вам силы тратить.
8. Тимур Мансорунов (solarisman) 09.03.16 20:31
(7) Yashazz, Не понял, при чем тут мобильное приложение 1С? Приложение нативное, написанное на Java. По поводу
канет в прошлое
- дык все в прошлое канет, что же теперь, ничего не делать? Google задумывается переходить с Java и заглядывается на другие языки. Никто же разработку не бросает.
9. Сергей Галюк (dj_serega) 10.03.16 08:15
10. Тимур Мансорунов (solarisman) 10.03.16 09:00
(9) dj_serega, Вообще не понял, о чем речь здесь? HTTP - протокол передачи данных, SOAP - протокол доступа к объектам, который использует HTTP.
11. Тимур Мансорунов (solarisman) 10.03.16 09:36
Коллеги, давайте по существу, хочется конструктивной критики.
12. Александр Антонов (Nenaviju1C) 10.03.16 11:05
Нечто подобное тоже себе организовал:
База (если так можно сказать) 1с - самописная конфиг-я для формирования ежемесячных отчетов по счетчикам гор/хол воды для ЖКХ с рассылкой определенную дату по регламенту от своего имени (*@mail.ru) на почту ЖКХ.

Мобильное приложение написано на java (Android Studio). Транспорт использую тот же - WS (soap).
Писал ЭТО для себя от нечего делать :) ... да и надоело счета собирать в папке на компе вручную ))

По потоку:
Из базы передается список XDTO состоящий из GUID "документа-отчета" и даты отчета.
Каждая строка содержит список XDTO (номер счетчика + замеры до и после)

На приложении:
Всего 3 layout:настройки, список документов, детализация по документу (она же регистрация).
Можно было и динамически клепать их, но лениво все описывать было да и отлавливать баги потом :)

При открытии layouta документа (в т.ч. и нового) уже установлены номера счетчиков - только надо внести показания и все.
Как то так ...

Мобильное приложение - кривоватое побочное дитя попытки 1С угнаться за модой, которое однажды канет в прошлое, как веб-расширения и тому подобная мутота...

Что за консерватизм?? Мобильные устройства в бизнесе используются все больше и больше.
"Мутота" - это для тех кто не знает КАК.
13. Дмитрий Шерстобитов (DitriX) 11.03.16 09:54
(11) тут говорить особо не очем, то о чем вы пишете - говорили еще в 2012 году :)
Лучше вы не написали, чем было, новые механизмы (http реквесты, odata, json) - не использовали. И я в упор не пойму почему.
Как учебный материал - ваши статьи вобщем то тоже бесполезны (ИМХО). Это скорее смахивает на записки самому себе. А люди ставят плюсы просто потому что тут 1С+Андроид :)

Вобщем вы бы скилы вначале подкачали по статьям, начали бы с азов, а там глядишь - и народ подтянется, не все еще потеряно.
Но если вы думаете, что описывая технологии 5 летней давности - люди проявят интерес, то вы глубоко ошибаетесь, максимум можно сориентироваться на новеньких, или людей не умеющих пользоваться поиском.

P.S. Без обид, просто от таких статей все 1С ники и думают, что другие языки и направления - очень сложны в освоении.
14. Тимур Мансорунов (solarisman) 11.03.16 10:41
(13) DitriX, А на что обижаться, все правильно написано.
Конечно, мне до ваших статей далеко, но и на фурор я не рассчитывал.
15. Яков Коган (Yashazz) 11.03.16 17:19
(13) DitriX, а ещё от таких статей на ровном месте растёт "рейтинг"))) И потом начинаются распальцовки)
16. Яков Коган (Yashazz) 11.03.16 17:21
(12) Nenaviju1C,
Что за консерватизм?? Мобильные устройства в бизнесе используются все больше и больше.
"Мутота" - это для тех кто не знает КАК.

Ясно. Внимательно читать не умеете. Я же не про мобильные устройства "вообще" написал. Я про мобильное приложение 1С. И кое-что о перспективах оного знаю.
17. Тимур Мансорунов (solarisman) 11.03.16 22:14
(15) Yashazz, Ладно, ладно, еще одну статью напишу в завершение и все, честно-честно. А то ишь, покусился на святой рейтинг топ-авторов.
Вот честно, как дети. Напишите то же самое приложение с использованием
(http реквесты, odata, json)
покажите что оно лучше во всем и тогда я скажу что я опозорился и статьи удалю вообще
18. Тимур Мансорунов (solarisman) 11.03.16 22:34
(16) Yashazz,
И кое-что о перспективах оного знаю.

Поделитесь секретом-то. А то я понять не могу, почему у нас региональный пищевой холдинг автоматизируется на мобильной платформе.
19. Дмитрий Шерстобитов (DitriX) 12.03.16 06:15
(14) Ни в коем случае я не пытаюсь тут устроить дедовщину.
Наоборот, если вы таки хотите сделать качественные статьи и для себя новое узнать, то я готов вам даже помочь с написанием, т.е. подсказать что лучше писать, в каком виде подавать и т.д.
Просто обидно смотреть когда люди тратят свое время на написание сомнительных вещей.
Поэтому если вы заинтересованы написать такой цикл статей - то добро пожаловать ко мне в личку.

(17) (http реквесты, odata, json) оно лучше, но у каждого своя задача.

Вобщем если решите таки что то сделать из реально актуальных вещей - я готов вам помочь, я не жадный, у меня уже и так пол инфостарта в скайпе :)
20. Сергей (Che) Коцюра (CheBurator) 12.03.16 06:54
(18) как заавтоматизируется и выйдет в плановую работу - отпишитесь. порадуемся или поплачем вместе...
21. Ярослав Радкевич (WKBAPKA) 15.03.16 10:01
(13) DitriX, я поддержу автора. Для меня, как человека, который изучает Андроид, эта статья очень полезна. Меня больше интересует не подача текста, а примеры, которые автор очень любезно выложил.
22. Максим Костиков (mkostya) 24.03.16 18:24
Все отлично, заработало. Жду следующей статьи.
Там будет и критика и предложения))
23. Тимур Мансорунов (solarisman) 30.03.16 15:29
(22) Значит буду делать работу над ошибками :)

Постараюсь написать статью 2-3 числа, был в командировке и все не разберусь с текущими задачами