Страницы

Поиск по вопросам

вторник, 30 апреля 2019 г.

Как правильно реализовать callback в java?

Я недавно начал для себя изучать Android и собственно java, до этого всегда писал на nodeJS, и за долгое время очень привык к js.
И вот, в процессе изучения, настал момент, когда я решил написать один единственный класс который будет отправлять запросы, и в результате ошибки или успеха будет выполнять соответствующие методы класса из которого мы и шлем этот запрос.
Есть класс MainActivity в котором по клику на кнопку шлем запрос на сервер и ответ вставляем в TextView. Сейчас я реализовал коллбэки таким образом:
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
protected void onCreate(Bundle savedInstanceState) {
sendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DoRequestClass doRequestClass = new DoRequestClass(){
@Override public void onOK(JSONObject response) { successReq(response); }
@Override public void onError(VolleyError error) { errorReq(error); } }; doRequestClass.execute("http://myserver.local", MainActivity.this); } });
public void successReq(JSONObject response){ txtDisplay.setText("Response => "+response.toString()); }
public void errorReq(VolleyError error){ txtDisplay.setText("Error => "+error.toString()); }
// other methods
Такой вот для теста абстрактный класс DoRequestClass
public abstract class DoRequestClass implements DoRequestInterface {
@Override public void execute(String url, Context context) { RequestQueue queue = Volley.newRequestQueue(context);
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener() {
@Override public void onResponse(JSONObject response) { DoRequestClass.this.onOK(response); }
}, new Response.ErrorListener() {
@Override public void onErrorResponse(VolleyError error) { DoRequestClass.this.onError(error); }
});
queue.add(jsObjRequest); } }
С интерфейсом DoRequestInterface
interface DoRequestInterface { void execute(String url, Context context);
void onOK(JSONObject response);
void onError(VolleyError error); }
Все описанное выше работает, но можно ли как-то передать successReq и errorReq в DoRequestClass таким образом, как бы мы это делали это на javascript, о то что я имею ввиду, о желанном результате, смотрите как бы я хотел примерно чтоб они передавались:
sendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DoRequestClass doRequestClass = new DoRequestClass(); doRequestClass.setSuccessCallback(successReq); doRequestClass.setErrorCallback(errorReq); doRequestClass.execute("http://lessons.flexiblejs.ru/test.php", MainActivity.this); } });
Как вы поняли из кода выше, setSuccessCallback и setErrorCallback это методы которые будут говорить классу DoRequestClass какие методы класса MainActivity нужно выполнить после успешного или не успешного запроса.
Такое или подобное прекрасно работало бы в javascript'e, но такой java-код работать не будет. Вопрос в том, как создать что-то такое или приближенное к этому? И возможно ли вообще? Суть в том чтобы не писать лишний код и создавать лишнее методы типа как в моем рабочем примере onOK и onError
Прошу вас по возможности дать как можно более развернутый ответ с пояснениями, т.к. мои познания в java совсем не велики.


Ответ

Рефакторинг:
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Response.Listener, Response.ErrorListener {
protected void onCreate(Bundle savedInstanceState) { ((Button)findViewById(R.id.send)).setOnClickListener(this); }
@Override public void onClick(View v){ switch(v.getId()){ case R.id.send: doRequestClass.execute("http://myserver.local", this, this, this); break; } }
@Override public void onResponse(JSONObject response) { txtDisplay.setText("Response => "+response.toString()); }
@Override public void onErrorResponse(VolleyError error) { txtDisplay.setText("Error => "+error.toString()); } }
DoRequestClass.java
public class DoRequestClass {
@Override public void execute(String url, Context context, Response.Listener listener, Response.ErrorListener listenerError) { RequestQueue queue = Volley.newRequestQueue(context); JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, listener, listenerError); queue.add(jsObjRequest); } }
На самом деле вы продублировали интерфейсы Response.Listener и Response.ErrorListener, просто раскрыв generic и объединив их в один. В приведенном примере это просто не нужно. Впрочем вы можете реализовать как у вас и было этот интерфейс, например обработать response и прокинуть его дальше:
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener(){ @Override public void onResponse(JSONObject response) { Log.d(TAG, "JSON:"+ response.toString()); listener.onResponse(response);// listener будет final } },listenerError);
Для колбэков используется свой интерфейс (или несколько) и вносить в него метод execute смысла не очень много. В частности execute может быть в одном классе, а колбэк - в другом.
ЗЫ В приведенном коде есть один недочет. Вы каждый раз создаете новую очередь запросов. Так делать не стоит, для всего приложения достаточно одной очереди. Вариантов когда нужно несколько очередей, притом фиксированное количество не так много, например когда реализуется работа с несколькими акаунтами, на разные сервера слать запросы в отдельных очередях... Поэтому в классе приложения (например) стоит создать синглтон для очереди и брать ее оттуда. Либо сделать DoRequestClass (назвав его RequestManager'ом, например) синглтоном с инициализированной один раз очередью...
ЗЗЫ Еще перенес обработчик клика в активити, это по желанию "на вкус и цвет".

Комментариев нет:

Отправить комментарий