Обмен данными с сервером 1С

Сегодня подробно рассмотрим возможности FBA  по организации процесса обмена данными с web-сервисом.

FbaDBExchangeActivity

1. Самый простой способ выполнить обмен, это в Activity, наследнике от FbaDBExchangeActivity вызвать функцию:

1
startExchange(ExchangeVariant.FULL, true);
startExchange(ExchangeVariant.FULL, true);

где первым параметром указывается вариант обмена по предопределёнными правилам (подробнее о существующих вариантах), а вторым признак возможной отмены процесса обмена пользователем интерактивно. Реализацию смотрите в ru.profi1c.engine.exchange.ExchangeTask.java

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

2. Здесь же обмен можно запустить и по своим правилам, указав вместо варианта свою задачу (наследник от  BaseExchangeTask или ExchangeTask):

1
startExchange(BaseExchangeTask exchangeTask, boolean cancelable);
startExchange(BaseExchangeTask exchangeTask, boolean cancelable);

Например, в статье «Рапорт руководителю» показан вариант с дополнением стандартных правил (см. MyExchangeTask.java).

3. Если требуется реализовать реакцию (выполнить какие то действия) в вашей Activity при начале или по окончании обмена, то это делается так:

а) создайте подписчика:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private class MyExchangeObserver extends WSSimpleExchangeObserver {
 
    public MyExchangeObserver(FbaActivity activity, Handler handler) {
            super(activity, handler);
    }
 
    @Override
    public void onFinish(boolean success) {
            super.onFinish(success);
            if (success) {
                    //сделать что-то полезное 
        }
    }
}
private class MyExchangeObserver extends WSSimpleExchangeObserver {

   	public MyExchangeObserver(FbaActivity activity, Handler handler) {
          	super(activity, handler);
   	}

   	@Override
   	public void onFinish(boolean success) {
          	super.onFinish(success);
          	if (success) {
                 	//сделать что-то полезное 
   	   	}
   	}
}

б) установите ваш подписчик как основного наблюдателя за процессом обмена для Activity:

1
2
3
4
@Override
protected ExchangeObserver getExchangeObserver() {
    return new MyExchangeObserver(this, new Handler());
}
@Override
protected ExchangeObserver getExchangeObserver() {
   	return new MyExchangeObserver(this, new Handler());
}

Пример смотрите в статье «Задачи сотруднику (часть 2)».

Если хотите реализовать свой вариант диалога-прогрессора, при этом не менять реакцию на окончание обмена, то подписчик делайте от SimpleExchangeObserver. Полностью свой вариант наблюдателя лучше делать от ExchangeObserver.

Обратите внимание, что если вы закроете Activity до окончания обмена, ваши обработчики не будут выполнены.

ExchangeService

Основная служба обмена (на базе IntentService т.е. выполняется в отдельном потоке в очередь) с доступом к базе данных. Эта служба вызывается при запуске обмена методом FbaDBExchangeActivity.startExchange(…), а также из планировщика, если в настройках программы установлен обмен по расписанию. Запуск службы должен быть обязательно задекларирован в AndroidManifest.xml вашего приложения (генератор FBA в 1С делает это автоматически).

Вручную службу (и естественно процесс обмена) можно запустить так:

1
2
3
4
5
6
//По фиксированным правилам (с проверка расписания т.е если расписание установлено,
//но сегодня не рабочий день – обмен запущен не будет)
ExchangeService.start(context, ExchangeVariant.ONLY_SAVE);
 
//По фиксированным правилам (игнорируя расписание планировщика)
ExchangeService.startForce(context, ExchangeVariant.INIT);
//По фиксированным правилам (с проверка расписания т.е если расписание установлено,
//но сегодня не рабочий день – обмен запущен не будет)
ExchangeService.start(context, ExchangeVariant.ONLY_SAVE);

//По фиксированным правилам (игнорируя расписание планировщика)
ExchangeService.startForce(context, ExchangeVariant.INIT);

Если обмен требуется реализовать по своим правилам, можно подключится к службе в Activity (как это делано в FbaDBExchangeActivity) и назначить свою задачу (наследник от  BaseExchangeTask или ExchangeTask). Но можно сделать проще, достаточно переопределить метод ExchangeSettings.getDefaultExchangeTask:

1
2
3
4
5
6
7
8
9
@Override
public BaseExchangeTask getDefaultExchangeTask(ExchangeVariant variant,
                        DBOpenHelper dbHelper) {
 
        WSHelper wsHelper = new WSHelper(this);
        MyExchangeTask runTask = new MyExchangeTask(variant, wsHelper, dbHelper);
        runTask.setListener(new ExchangeListener(getContext(),variant,dbHelper));
        return runTask;
}
@Override
public BaseExchangeTask getDefaultExchangeTask(ExchangeVariant variant,
                    	DBOpenHelper dbHelper) {

    	WSHelper wsHelper = new WSHelper(this);
    	MyExchangeTask runTask = new MyExchangeTask(variant, wsHelper, dbHelper);
    	runTask.setListener(new ExchangeListener(getContext(),variant,dbHelper));
    	return runTask;
}

В этом случае, если при запуске задача явна не указана (а только вариант обмена), будет использована эта задача в т.ч и при запуске из планировщика.

Дополнительно служба содержит функции по обслуживанию планировщика. Создать (или отменить) задание автоматического обмена по расписанию можно с помощью статических методов:

1
2
3
4
public static final void createScheduleUpdate(Context ctx,
                        BaseExchangeSettings exSetting, ExchangeVariant variant);
 
public static final void cancelSchedule(Context ctx, ExchangeVariant variant);
public static final void createScheduleUpdate(Context ctx,
                    	BaseExchangeSettings exSetting, ExchangeVariant variant);

public static final void cancelSchedule(Context ctx, ExchangeVariant variant);

Восстановить задание (например, после перезагрузки устройства) можно с помощью метода:

1
2
public static final void restoreScheduleUpdate(Context ctx,
                        BaseExchangeSettings exSetting, ExchangeVariant variant);
public static final void restoreScheduleUpdate(Context ctx,
                    	BaseExchangeSettings exSetting, ExchangeVariant variant);

IExchangeCallbackListener

Как и в 1С, в мобильном приложении тоже можно установить дополнительные обработчики при загрузке/выгрузке данных. Например, можно исключить какой-либо объект из обмена или выполнить очистку таблицы перед загрузкой. Делается это через установку слушателя обмена реализующего интерфейс IExchangeCallbackListener.

Например, этот обновляет какие-то (не важно в данном случае) параметры приложения при успешном окончании обмена и уведомляет само приложение, что обмен завершился.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class ExchangeListener implements IExchangeCallbackListener {
 
        private final Context context;
        private final ExchangeVariant exchangeVariant;
        private final DBOpenHelper dbHelper;
 
        public ExchangeListener(Context ctx, ExchangeVariant variant, DBOpenHelper dbOpenHelper){
                context = ctx;
                exchangeVariant = variant;
                dbHelper = dbOpenHelper;
        }
 
        /**
         * Обмен завершен
         *
         * @param result флаг успешности
         */
        @Override
        public void onComplete(boolean result) {
 
                if(result && exchangeVariant !=ExchangeVariant.ONLY_SAVE){
 
                        try {
                                App app =  (App) context.getApplicationContext();
                                app.resreshAppParams(dbHelper);
 
                        } catch (SQLException e) {
                                        e.printStackTrace();
                        }
 
                }
 
                //Уведомить подписчиков
                Intent i = new Intent(Const.ACTION_EXCHANGE_COMPLETE);
                i.putExtra(Const.EXTRA_EXCHANGE_VARIANT, exchangeVariant.ordinal());
                i.putExtra(Const.EXTRA_EXCHANGE_RESULT, result);
 
                context.sendBroadcast(i);
        }
        //…обработчики других событий…
}
public class ExchangeListener implements IExchangeCallbackListener {

    	private final Context context;
    	private final ExchangeVariant exchangeVariant;
    	private final DBOpenHelper dbHelper;

    	public ExchangeListener(Context ctx, ExchangeVariant variant, DBOpenHelper dbOpenHelper){
            	context = ctx;
            	exchangeVariant = variant;
            	dbHelper = dbOpenHelper;
    	}

    	/**
    	 * Обмен завершен
    	 *
    	 * @param result флаг успешности
    	 */
    	@Override
    	public void onComplete(boolean result) {

            	if(result && exchangeVariant !=ExchangeVariant.ONLY_SAVE){

                    	try {
                            	App app =  (App) context.getApplicationContext();
                            	app.resreshAppParams(dbHelper);

                    	} catch (SQLException e) {
                                    	e.printStackTrace();
                    	}

            	}

            	//Уведомить подписчиков
            	Intent i = new Intent(Const.ACTION_EXCHANGE_COMPLETE);
            	i.putExtra(Const.EXTRA_EXCHANGE_VARIANT, exchangeVariant.ordinal());
            	i.putExtra(Const.EXTRA_EXCHANGE_RESULT, result);

            	context.sendBroadcast(i);
    	}
    	//…обработчики других событий…
}

Не забудьте для методов onSerializeTable и onUpdateTable установить возвращаемое значение true, если вы не хотите совсем отключить обмен.Установка этого обработчика показана выше, см. getDefaultExchangeTask.

ExchangeManager

Менеджер обмена в web-сервисом, вспомогательный класс. Может использоваться как для запуска обмена  по фиксированным правилам (в рабочем или отдельном потоке):

1
2
3
public boolean startExchange(ExchangeVariant exchangeVariant);
public void startExchange(final ExchangeVariant exchangeVariant,
                          final IExchangeCallbackListener listener);
public boolean startExchange(ExchangeVariant exchangeVariant);
public void startExchange(final ExchangeVariant exchangeVariant,
                          final IExchangeCallbackListener listener);

так и для вызова отдельных функций web-сервиса, как правило, это доп. функции GetShortData, WriteShortData, GetLargeData и WriteLargeData. Подробнее о них читайте в статье «GPS-трекер, мониторинг местоположения сотрудника».

Вот несколько примеров. Передача произвольной структуры:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
App app = (App) getContext().getApplicationContext();
ExchangeManager exManager = new ExchangeManager(app.getExchangeSettings());
 
Gson gson = new Gson();
 
JSONObject jsonObject = new JSONObject();
jsonObject.put("test_key", true);
jsonObject.put("lat", 45.4788);
jsonObject.put("lng", 37.4568);
jsonObject.put("test_array", new String[]{"value1,value2"});
jsonObject.put("test_int", 100);
 
//Сериализовать в строку
String strJson = gson.toJson(jsonObject);
//передать в 1С
boolean result = exManager.getWSHelper().writeShortData("your_id", strJson, "");
App app = (App) getContext().getApplicationContext();
ExchangeManager exManager = new ExchangeManager(app.getExchangeSettings());

Gson gson = new Gson();

JSONObject jsonObject = new JSONObject();
jsonObject.put("test_key", true);
jsonObject.put("lat", 45.4788);
jsonObject.put("lng", 37.4568);
jsonObject.put("test_array", new String[]{"value1,value2"});
jsonObject.put("test_int", 100);

//Сериализовать в строку
String strJson = gson.toJson(jsonObject);
//передать в 1С
boolean result = exManager.getWSHelper().writeShortData("your_id", strJson, "");

Получить структуру произвольных данных:

1
2
3
4
5
String strJson = exManager.getWSHelper().getShortData("your_id", "");
JSONObject jsonObject = new JSONObject(strJson);
 
assertEquals(jsonObject.getInt("ЧисловойКлюч"), 100);
assertTrue(jsonObject.getString("СтроковыйКлюч").equals("Строка данных"));
String strJson = exManager.getWSHelper().getShortData("your_id", "");
JSONObject jsonObject = new JSONObject(strJson);

assertEquals(jsonObject.getInt("ЧисловойКлюч"), 100);
assertTrue(jsonObject.getString("СтроковыйКлюч").equals("Строка данных"));

Передать произвольный файл, может быть достаточно большого объема в несколько МБ:

1
2
boolean result = exManager.getWSHelper()
        .writeLargeData("test_id", "test_ref", "/mnt/sdcard/myFile.dat", "");
boolean result = exManager.getWSHelper()
        .writeLargeData("test_id", "test_ref", "/mnt/sdcard/myFile.dat", "");

Получить файл и сохранить его в кеш:

1
2
3
File fTmpPic = new File(app.getAppSettings().getCacheDir(),”myFile.dat);
File result = exManager.getWSHelper()
        .getLargeData(id, null, null, fTmpPic.getAbsolutePath());
File fTmpPic = new File(app.getAppSettings().getCacheDir(),”myFile.dat”);
File result = exManager.getWSHelper()
        .getLargeData(id, null, null, fTmpPic.getAbsolutePath());

Прочее

Если обмен с сервером занимает много времени или передается большой объём данных, рекомендуется увеличить таймаут. Это делается в ExchangeSettings, например:

1
2
3
4
@Override
protected int getConnectionTimeout() {
    return 180000;
}
@Override
protected int getConnectionTimeout() {
   	return 180000;
}

Для опытных разработчиков

WSHelper – помощник для работы с методами web-сервиса с учетом уставленных настроек. Перед вызовом любого метода, проверяет была ли выполнена авторизация и выполняет ее если требуется. Для реализации расширений и своих правил обмена рекомендуется использовать его.

WebService – основной класс, через который производится вызов функции web-сервиса.

На этом по организации обмена с сервером все, если у вас есть вопросы или комментарии, используйте форму «Контакты» для связи.

Похожие записи: