Skip to content

Instantly share code, notes, and snippets.

@NikitiJ
Created May 3, 2018 15:50
Show Gist options
  • Save NikitiJ/110492083d15d18c62e32c9a622140d3 to your computer and use it in GitHub Desktop.
Save NikitiJ/110492083d15d18c62e32c9a622140d3 to your computer and use it in GitHub Desktop.
архитектура в стиле ООП для выполнения асинхронных запросов к серверу
*************** Архитектура асинхронные запросы в Андроид
//Имеются общие классы для запросов :
// обертка над любыми запросами
public class RequestWrapper <T> {
public String apiVersion = "2.0";
public String accessToken;
public T requestData;
public RequestWrapper(String accessToken, T requestData){
this.accessToken = accessToken;
this.requestData = requestData;
}
}
//конкретный класс-модель запроса на логин пользователя
public class LoginRequest {
String phone;
String password;
String requestId;
public LoginRequest(String phone, String password, String requestId){
this.phone = phone;
this.password = password;
this.requestId = requestId;
}
}
// Классы для ответа с сервера:
//общий базовый класс для всех запросов с клиента
public class BaseApiClient {
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
public <TRequest, TResponse> void makeRequestAsync(String urlAddress, TRequest request) {
OkHttpClient clientHttp = new OkHttpClient();
Gson gsonConverter = new Gson();
String jsonContentBody = gsonConverter.toJson(request);
RequestBody requestBody = RequestBody.create(JSON, jsonContentBody);
Request finalRequest = new Request.Builder()
.url(urlAddress)
.post(requestBody)
.build();
clientHttp.newCall(finalRequest).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.e("TestApp", e.getMessage());
call.cancel();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String rawResponseBody = response.body().toString();
Gson gsonConverter = new Gson();
TResponse responseObject;
// здесь также вопрос как конвертировать дженерики? и собственно вернуть данные от сервера?
//т.е ответ от сервера в конкретный класс модели-ответа
Type typeToken = new TypeToken<ResponseWrapper<LawyerLoginResponse>>(){}.getType();
responseObject = gsonConverter.fromJson(rawResponseBody, typeToken);
}
});
}
}
//конкретный класс для выполнения запроса на логин - клиента
//т.е в каждом конкретном запросе будет вызываться базовый метод, выполняющий асинхронный запрос на сервер
public class LawyersProfileServiceApiClient extends BaseApiClient {
public <TRequest> void loginAsync(TRequest request) throws IOException {
makeRequestAsync("http://127.0.0.1/Services/Lawyers/ProfileService.svc/Login", request);
}
}
//В активити вызов асинхронного запроса к серверу предполагается использовать конкретные классы и методы, основанные на базовом классе выше
LawyersProfileServiceApiClient apiLogin = new LawyersProfileServiceApiClient();
//оборачиваем данные с формы логина
LoginRequest formLoginData = new LoginRequest("+79999999999", "21813323", "vsQnjxifI3nNreFA");
// далее предполагается вызов метода - запроса у конкретного объекта клиентского API - что-то вроде этого
// реализация выполнения асинхронного запроса?
// как вернуть данные с сервера?
// как это делать асинхронно? сам процесс запроса в базовом классе, а как отловить ошибки и прочее здесь на UI ?
apiLogin.loginAsync(requestWrapper);
@AlexMisiulia
Copy link

//Самый простой способ (чтобы не лезть в дебри `Rx`).
// Добавил interface ResponseListener<TResponse> listener, в клиентском коде его везде прокидываю

public class BaseApiClient {
    public static final MediaType JSON
            = MediaType.parse("application/json; charset=utf-8");
    
    public static final Gson GSON = new Gson();

    public <TRequest, TResponse> void makeRequestAsync(String urlAddress,
                                                       TRequest request,
                                                       ResponseListener<TResponse> listener
    ) {
        OkHttpClient clientHttp = new OkHttpClient();

        String jsonContentBody = GSON.toJson(request);

        RequestBody requestBody = RequestBody.create(JSON, jsonContentBody);
        Request finalRequest = new Request.Builder()
                .url(urlAddress)
                .post(requestBody)
                .build();

        clientHttp.newCall(finalRequest).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                System.out.println("TestApp" + e.getMessage());
                call.cancel();
                listener.onFailure(e);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String rawResponseBody = response.body().toString();

				// здесь также вопрос как конвертировать дженерики? и собственно вернуть данные от сервера?
        //т.е ответ от сервера в конкретный класс модели-ответа
                Type typeToken = new TypeToken<TResponse>(){}.getType();

                // вынес GSON в переменную, чтобы не плодить объекты. По производительности бъёт прилично
                TResponse responseObject = GSON.fromJson(rawResponseBody, typeToken);
                listener.onSuccess(responseObject);
            }
        });


    }

    public interface ResponseListener<T> {
        void onSuccess(T response);
        void onFailure(Throwable error);
    }
}

public class LawyersProfileServiceApiClient extends BaseApiClient {

    public <TRequest, TResponse> void loginAsync(TRequest request, ResponseListener<TResponse> listener) {

        makeRequestAsync("http://127.0.0.1/Services/Lawyers/ProfileService.svc/Login", request, listener);

    }
}        

//В активити вызов асинхронного запроса к серверу предполагается использовать конкретные классы и методы, основанные на базовом классе выше
        LawyersProfileServiceApiClient apiLogin = new LawyersProfileServiceApiClient();

        //оборачиваем данные с формы логина
        LoginRequest formLoginData = new LoginRequest("+79999999999", "21813323", "vsQnjxifI3nNreFA");

        // ответ приходил в соотвествующий метод у `Listener`
        // это выполняется асинхронно на уровне makeRequestAsync (BaseApiClient:28 enqueue),
        // ошибки пробрасываются в onFailure?
        apiLogin.loginAsync(formLoginData, new BaseApiClient.ResponseListener<String>() {
            @Override
            public void onSuccess(String response) {
                
            }

            @Override
            public void onFailure(Throwable error) {

            }
        });

@mtvspec
Copy link

mtvspec commented Aug 15, 2021

Возможно ли использовать ООП в JS и асинхронные вызовы? Например, использовать async/await. При таком подходе клиентский код получается как цепочка await...await...await и т.д. Насколько такой подход применим?

@NikitiJ
Copy link
Author

NikitiJ commented Oct 5, 2021

Возможно ли использовать ООП в JS и асинхронные вызовы? Например, использовать async/await. При таком подходе клиентский код получается как цепочка await...await...await и т.д. Насколько такой подход применим?

В JS давно уже не погружался, там для ООП используют TypeScript насколько помню, но есть и нативные реализации ООП подхода в JS, сейчас уже все стараются переходить на реактивный подход (в Android сфере уже давно) но думаю и на JS дела обстоят примерно похожим образом. Посмотрите в сторону RX-js.

@mtvspec
Copy link

mtvspec commented Oct 5, 2021

Возможно ли использовать ООП в JS и асинхронные вызовы? Например, использовать async/await. При таком подходе клиентский код получается как цепочка await...await...await и т.д. Насколько такой подход применим?

В JS давно уже не погружался, там для ООП используют TypeScript насколько помню, но есть и нативные реализации ООП подхода в JS, сейчас уже все стараются переходить на реактивный подход (в Android сфере уже давно) но думаю и на JS дела обстоят примерно похожим образом. Посмотрите в сторону RX-js.

Я смотрел код на Java, например в Camunda BPM Platform. Часто встречаются такие вызовы String processDefinitionName = task.getProcessInstance().getProcessDefinition().getName(). Судя по структуре БД это хранится в разных, но связанных таблицах (task, process_instance и process_defenition). Как происходит, что эти вызовы выглядят как синхронные? Можно писать асинхронный код как синхронный или они для task поднимают полностью все данные из всех связанных таблиц (process_instance и process_definition), загружают в память и уже из памяти можно получить данные синхронно (без выполнения дополнительных запросов в связанные таблицы) - напрямую из памяти?

В примере этого гиста я вижу что асинхронный код отличается от синхронного, так же как в JS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment