• Best practice по клиент-серверным приложениям для Android?

    @ilitaexperta Автор вопроса
    Вообщем я нашел приемлимый подход, может кому пригодится:

    1. Делаем слой для запросов. Все поля статические. В нем делаем список листенеров и метод для создания запроса через AsynTask. В ответе асинк такски обходим список листенеров и вызываем нужный коллбек.
    2. Делаем слой для данных(тоже все статиком) и подписываем его на ответы(добавляем листенер) с наивысшим приоритетом(т.е. чтобы первым вызывался листенер в слое данных)
    3. Нужные активити подписываем на ответы в onResume и отписываемся в onPause.
    4. Данные всегда берем только из слоя данных, а активити обновляем по коллбекам из листенера и в onResume


    Таким образом:
    1. Не будет утечек т.к. AsyncTask содержит ссылки только на статики
    2. Ответ на запрос не придет в мертвую\запауженую активити,
    3. Ответ не потеряется и данные сохраняется в слое данных.
    4. Если активити была на паузе или умерла, она подсосет данные из дата слоя в onResume
    5. Если активити пересоздалась, то ответ придет уже в новую. А если ответ придет между убийством старой и созданием новой, то см. пункты 3 и 4
    6. Никаких сторонних костылей и оверинжиниринга, можно даже чуток усложнить в некоторых местах(Наример сделать отдельный листенер на изменение данных в дата слое и подписывать активити на него т.е. как писали выше "запрос меняет данные, дата слой оповещает об изменении, потребитель сам забирает данные")


    Накидал упрощенный пример кода, некоторые места опущены для простоты.

    Слой для запросов
    public class Requests
    {
    	public interface OnCompleteListener
    	{
    		default void onSomeMethod1(JsonElement answer) {}
    		default void onSomeMethod2(JsonElement answer) {}
    
    		default int getPriority() { return 0; }
    	}
    
    	private static class RequestTask extends AsyncTask<Void, Void, String>
    	{
    		public RequestTask(String method)
    		{
    			this.method = method;
    		}
    
    		private String method;
    
    		@Override
    		protected String doInBackground(Void ... params)
    		{
    			// Тут делаем запрос к серверу
    			// ...
    		}
    
    		@Override
    		protected void onPostExecute(String result)
    		{
    			handleAnswer(result);
    		}
    	}
    
    	private static List<OnCompleteListener> listeners = new ArrayList<>();
    
    	private static void handleAnswer(String answer)
    	{
    		for(OnCompleteListener listener : listeners)
    		{
    			if(methodName.equals("api/method1")) listener.onSomeMethod1(answer);
    			else if(methodName.equals("api/method2")) listener.onSomeMethod2(answer);
                    }
    	}
    
    	private static void makeRequest(String method)
    	{
    		new RequestTask(method).execute();
    	}
    
    	public static void registerListener(OnCompleteListener listener)
    	{
    		listeners.add(listener);
    
    		Collections.sort(listeners, (l1, l2) -> l2.getPriority() - l1.getPriority());
    	}
    
    	public static void unregisterListener(OnCompleteListener listener)
    	{
    		listeners.remove(listener);
    	}
    
    	public static void method1()
    	{
    		makeRequest("api/method1");
    	}
    
    	public static void method2()
    	{
    		makeRequest("api/method2");
    	}
    }


    Активити
    public class MainActivity extends AppCompatActivity implements Requests.OnCompleteListener
    {
    	// ...
    
    	@Override
    	protected void onPause()
    	{
    		super.onPause();
    
    		Requests.unregisterListener(this);
    	}
    
    	@Override
    	protected void onResume()
    	{
    		super.onResume();
    
    		Requests.registerListener(this);
    		updateView();
    	}
    
    	private void updateView()
    	{
    		String someData = Data.getSomeData();
    
    		// Тут обновляем вьюшки в активити
    		// ...
    	}
    
    	@Override
    	public void onMethod1(String answer)
    	{
    		updateView();
    	}
    }


    Слой данных
    public class MyApp extends Application
    {
    	@Override
    	public void onCreate()
    	{
    		super.onCreate();
    		
    		Requests.registerListener(Data.listener);
    	}
    }
    
    public class Data
    {
    	// Просто какие-то данные для примера
    	private static List<String> someDataStorage = new ArrayList<>();
    
    	public static String getSomeData()
    	{
    		return someDataStorage.last();
    	}
    
    	public static Requests.OnCompleteListener listener = new Api.OnCompleteListener()
    	{
    		@Override
    		public void onMethod1(String answer)
    		{
    			someDataStorage.add(answer);
    		}
    
    		public void onMethod2(String answer)
    		{
    			someDataStorage.add(answer);
    		}
    
    		@Override
    		public int getPriority()
    		{
    			return Integer.MAX_VALUE;
    		}
    	};
    }
    Ответ написан
    Комментировать
  • Какие книги по SOLID принципам стоит прочитать?

    @ilitaexperta
    Никакие. SOLID это мусор. Встречал много людей, любящих рассуждать об архитектуре. Их всех объединяет одна вещь - они не могут спроектиоровать и написать с нуля систему.

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

    Вам же советую больше заниматься практикой, а не мусорные книжки читать. У вас сама постановка вопроса неправильная. Какую нафиг книгу? Все что можно узнать о SOLID - гугится в википедии за 15 секунд.

    Вообще лучше не думайте об архитектуре, просто делайте проекты. Первые несколько раз получится дерьмо, зато потом заткнете за пояс любого знатока значения буковок из SOLID
    Ответ написан
    Комментировать