Отправка действий пользователя к действиям в backstack
Я разрабатываю социальное приложение. Предположим, что у меня есть стек действий A -> B -> C -> D
.
D находится на переднем плане, и пользователь нажимает кнопку "like" на что-то там (сообщение, комментарий, пользователь и т.д.). Каков наилучший способ уведомить обо всех других действиях об этом действии, чтобы обновить их данные? Здесь я вижу 3 варианта:
- Используйте локальную базу данных и некоторые загрузчики для автоматического обновления данных. Однако для этого требуется много кода, если у нас есть разные модели данных с общими данными (например,
BasicUserInfo
, UserInfo
, DetailedUserInfo
).
- Используйте EventBus с липкими событиями (продюсерами Отто). В этом случае я должен уведомлять ТОЛЬКО операции backstack и игнорировать те, которые будут созданы. Также мне нужно управлять переопределением событий.
- Используйте простой шаблон наблюдателя с помощью WeakReferences для операций backstack. Но тогда у меня есть проблема с убитыми действиями, которые будут повторно создаваться.
Реальный пример:
В Instagram: я открываю какой-то конкретный профиль пользователя (A), там я открываю какую-то конкретную запись (B), снова профиль (A) и т.д. A → B → A → B → A..., Поэтому он загружает данные из Интернета каждый раз. На шаге "n + 1" появляется новый комментарий к сообщению. Если я начну возвращаться через свою заднюю часть, я увижу, что instagram отправил этот "новый" комментарий ко всем видам B без перезагрузки любых данных из Интернета. Поэтому мне интересно, как они это делают.
Ответы
Ответ 1
для обновления данных?
Вот ответ. Действия не должны иметь собственные данные. Деятельность представляет данные и позволяет пользователю действовать на нее. Сами данные должны быть в отдельном объекте, с которым могут взаимодействовать экземпляры активности, Модель.
Кроме того, не следует предполагать, что в back-stack всегда есть экземпляр активности. Эти действия могут быть уничтожены, а затем повторно созданы как разные объекты по системе, когда пользователь перейдет обратно. Данные всегда обновляются, когда вся деятельность создается.
Разделите обработку данных на специализированные классы, к которым легко доступны операции, и могут предоставлять привязку данных/событий к действиям по требованию. Связанный Service
является хорошим кандидатом.
Если вы не загружаете данные из Интернета, вы можете настроить локальный кеш самых последних данных (кеш, поскольку у мобильных телефонов есть строгие ограничения на хранение, сервер и db не так). Таким образом, любое изменение с пользовательской стороны также привязано к этому кешу вдоль стороны, распространяющейся на сервер. Все это лучше инкапсулироваться специализированными классами данных, а не полагаться на задний стек или специальный код в классах действий.
В качестве шаблона вы можете построить классы Model
для участвующих сущностей данных. Напишите интерфейс API интерфейса API, чтобы поговорить с сервером. А затем поместите уровень кэша перед интерфейсом API. Кэш сохранит исходящие изменения и входящие обновления на/из уровня API и просто отразит запросы данных, когда вызов сервера не нужен.
Кэш должен делать 3 вещи главным образом:
- Вывод: по мере поступления новых данных отбрасывайте наименее важные данные, поэтому кеш остается фиксированным. Большинство реализаций кеша (как этот) делают это автоматически.
- Invalidate: несколько раз из-за действия пользователя или внешнего события на стороне сервера некоторые данные должны быть обновлены.
- Истекает: данные могут быть установлены с ограничением по времени и будут автоматически выселены. Это полезно при обеспечении периодического обновления данных.
Теперь большинство реализаций кеша имеют дело с необработанными байтами. Я бы рекомендовал использовать что-то вроде Realm, объект db и обернуть его в кеш, как функции. Следовательно, типичным потоком запросов пользовательских твитов будет:
- Отображается действие.
- Активность привязывается к службе данных и выражает свою заинтересованность в "твитах".
- Служба данных просматривает таблицу
Tweets
кэша db для последнего выбранного списка твитов и немедленно возвращает это.
- Но служба данных также вызывает сервер, чтобы дать чириканье после отметки времени последнего твита, который он локально находится в db.
- Сервер возвращает последний набор твитов.
- Служба данных обновляет все связанные действия, которые проявили интерес к твитам, с новыми входящими данными. Эти данные также реплицируются локально в кэше db. На этом этапе таблица твитов также оптимизируется путем удаления старых записей.
- Действия отключаются от службы данных, поскольку активность уходит, останавливается, уничтожается и т.д.
- Если никакой связанной деятельности не интересует "твиты", служба данных перестает загружать больше твитов.
Ваша реализация данных может идти еще дальше, поддерживая подключения сокетов к серверу и получая интересные изменения в режиме реального времени. Это шаг 5 выше будет вызываться сервером всякий раз, когда появляются новые данные.
TL;DR; Отделить управление данными от действий, а затем вы можете развить его независимо от проблем пользовательского интерфейса.
Ответ 2
Основной вариант использования системы уведомлений (событие, наблюдатель, BroadcastReceiver
,...) - это когда вы хотите, чтобы получатель действовал более или менее немедленно, когда что-то происходит.
Я думаю, что это не так: операции backstack не должны действовать немедленно, поскольку они не видны. Кроме того, они могут даже больше не существовать (убиты/заморожены). На самом деле им нужно получить последние данные, когда они вернутся на передний план (возможно, после воссоздания).
Почему бы просто не запустить обновление в onStart()
или onResume()
(используя Loader
или что-нибудь, что вы уже используете)?
Если статус "понравился" должен быть сохранен, вы можете сделать это в D
onPause()
.
Если нет, любимый объект может быть сохранен в глобальной переменной (что на самом деле является липким событием)
Ответ 3
Вы можете использовать LocalBroadcastManager
, чтобы уведомить о ваших уложенных действиях, которые произошли в вашем Liked-событии.
Предположим, что в вашей деятельности D:
private void liked() {
Log.d("liked", "Broadcasting message");
Intent intent = new Intent("like-event");
// You can also include some extra data.
intent.putExtra("message", "my like event occurs!");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
Теперь он будет уведомлять обо всех действиях, зарегистрированных в этом приемнике трансляции
например, в ваших действиях A, B, C:
@Override
public void onCreate(Bundle savedInstanceState) {
...
// Register to receive messages.
// We are registering an observer (mMessageReceiver) to receive Intents
// with actions named "custom-event-name".
LocalBroadcastManager.getInstance(this).registerReceiver(mLikeEventReceiver ,
new IntentFilter("like-event"));
}
// Our handler for received Intents. This will be called whenever an Intent
// with an action named "like-event" is broadcasted.
private BroadcastReceiver mLikeEventReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Get extra data included in the Intent
String message = intent.getStringExtra("message");
Log.d("receiver", "Got message: " + message);
}
};
@Override
protected void onDestroy() {
// Unregister since the activity is about to be closed.
LocalBroadcastManager.getInstance(this).unregisterReceiver(mLikeEventReceiver );
super.onDestroy();
}
ссылки:
[http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html][1]
[как использовать LocalBroadcastManager?
[https://androidcookbook.com/Recipe.seam?recipeId=4547][3]
Ответ 4
Классическим способом обработки этого типа является использование BroadcastReceivers.
Здесь пример получателя:
public class StuffHappenedBroadcastReciever extends BroadcastReceiver {
private static final String ACTION_STUFF_HAPPENED = "stuff happened";
private final StuffHappenedListener stuffHappenedListener;
public StuffHappenedBroadcastReciever(@NonNull Context context, @NonNull StuffHappenedListener stuffHappenedListener) {
this.stuffHappenedListener = stuffHappenedListener;
context.registerReceiver(this, new IntentFilter(ACTION_STUFF_HAPPENED));
}
public static void notifyStuffHappened(Context context, Bundle data) {
Intent intent = new Intent(ACTION_STUFF_HAPPENED);
intent.putExtras(data);
context.sendBroadcast(intent);
}
@Override
public void onReceive(Context context, Intent intent) {
stuffHappenedListener.onStuffHappened(intent.getExtras());
}
public interface StuffHappenedListener {
void onStuffHappened(Bundle extras);
}
}
И как прикрепить его к активности:
public class MainActivity extends AppCompatActivity implements StuffHappenedBroadcastReciever.StuffHappenedListener {
private StuffHappenedBroadcastReciever mStuffHappenedReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStuffHappenedReceiver = new StuffHappenedBroadcastReciever(this, this);
}
@Override
protected void onDestroy() {
unregisterReceiver(mStuffHappenedReceiver);
super.onDestroy();
}
@Override
public void onStuffHappened(Bundle extras) {
// do stuff here
}
}
"onStuffHappened" будет вызван до тех пор, пока активность активна.
Ответ 5
Я согласен с подходом @bwt. Эти системы уведомлений должны иметь значение, если вы хотите немедленно сообщить об этом.
Мой подход будет системой кеша. Вам не нужно иметь дело с "если активность снова сложена или вновь создана", вам всегда нужно запрашивать то, что вам нужно в onResume
активности. Таким образом, вы всегда будете получать самые последние данные.
При извлечении данных с вашего сервера вам также потребуется копия ваших моделей данных в локальной базе данных. Прежде чем вы выполните ping ваш сервер, когда, например. в записи пользователя есть что-то подобное, вам нужно установить это как Flag
в локальной базе данных и отправить свой запрос Http на ваш сервер, чтобы сказать "Эй, этот пост понравился". И позже, когда вы получите ответ от этого запроса, если он будет успешным или нет, попробуйте снова изменить этот флаг.
Итак, в ваших действиях вы получите самые последние данные, если вы запрашиваете локальную базу данных в onResume
. А для изменений в сообщении других пользователей вы можете иметь BroadcastReceiver
, чтобы отразить изменения в вашей видимой деятельности.
P.S: Вы можете проверить Realm.io для относительно более быстрых запросов, так как вам потребуется быстрее использовать вызовы локальной базы данных.
Ответ 6
Как и многие другие ответы, это был классический пример, когда BroadcastReceiver легко выполнил бы свою работу.
Я бы также рекомендовал использовать класс LocalBroadcastManager совместно с BroadcastReceiver. Из документации BroadcastReceiver:
Если вам не нужно отправлять трансляции по всем приложениям, подумайте об использовании этого класса с LocalBroadcastManager вместо более общих возможностей, описанных ниже. Это даст вам гораздо более эффективную реализацию (нет необходимости в перекрестной обработке) и позволит вам не думать о каких-либо проблемах безопасности, связанных с другими приложениями, которые могут принимать или отправлять ваши трансляции.
Ответ 7
Если вам нужно внести изменения во все действия в отношении некоторых данных, вы можете следовать шаблону интерфейса. Например, у вас есть собственный класс класса ActivityData, у которого есть контент, который необходимо обновить во всех действиях.
Шаг 1:
Создайте интерфейс следующим образом
public interface ActivityEventListener
{
ActivityData getData( Context context,
Activity activity );
}
Шаг 2:
Создайте BaseActivity для ссылки на все для u-действий, которые у вас есть в вашем приложении, и реализуйте интерфейс следующим образом
public class BaseActivity extends Activity
{
protected AcitivityData mData= null;
@Override
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
}
protected ActivityEventListener mListener = new ActivityEventListener()
{
@Override
public ActivityData getData( Context context,
Activity activity )
{
// TODO Auto-generated method stub
return mData;
}
};
}
Шаг 3:
Расширяет вашу собственную активность с помощью BaseActivity, например A или B или C........
открытый класс A расширяет BaseActivity
{
ActivityData mData;
@Override
protected void onCreate( Bundle savedInstanceState )
{
mData = mListener.getData(this,this);
updateWidgets(mData);
}
}
где updateWidgets - это функция, в которой вы можете определить элементы пользовательского интерфейса и использовать данные, которые у вас есть с интерфейсом
Как и все ваши действия, B/C/so может получить ссылку на ActivityData. Действия формы backstack начнут выполнение через onStart(), пользователь может обрабатывать действия в одном и том же виде с деталями, существующими в ActivityDatap >
В вашем случае, когда вам нравится в последнем действии, вы можете обновить объект ActivtyData, и когда возобновленная или запущенная активность стека вы можете получить обновленные данные, поскольку ваша операция backstack распространяется с использованием интерфейса BaseActiivty.