Pull to refresh

Comments 29

Подход здравый. Парочка замечаний:

1. Зачем нужно разделение на ActivityMediator и MyActivityMediator? Неужели нельзя было одним классом обойтись? И назвать его как-то получше, чтобы в имени не отражались детали реализации(паттерн), скажем ActivityLauncher?
2. Зачем вы специализируете ссылку на контекст классом Activity? А если вы захотите из сервиса активити вызывать? Менять же придется. У вас уже есть интерфейс Context, используйте его.
3. Чекстайл-онли: что за манера писать члены класса с префиксом m? Ну любая среда разработки подкрашивает члены класса, зачем это надо?
Нормальные вопросы, спасибо.

1) ActivityMediator я использую в нескольких проектах, он у меня вынесен в библиотеку. Отсюда и разделение. Название с упоминанием имени паттерна мне кажется логичнее, так как соответсвует принципу наименьшего удивления — видишь в названии слова Activity и Mediator и уже примерно понимаешь, что класс будет делать.
2) Да, можно было и просто Context использовать. Но у Activity есть метод startActivityForResult. Я здесь выложил не все исходники, только необходимый минимум что бы показать суть. В реальном ActivityMediator у меня около десятка методов. В данном случае да, вы правы, можно было обойтись Context
3) source.android.com/source/code-style.html «Non-public, non-static field names start with m.»
1) В том-то и дело, что класс сразу говорит: внутри я устроен так-то. А должен говорить — какая разница тебе, как я устроен, я просто запускаю нужные активити!
2) с этим согласен, да, startActivityForResult все портит.
Ага, я понял вашу мысль. Я привык к именно такому стилю именования классов, что бы понятно было не только что он делает, но и как. Например, всем известный StringBuilder — паттерн Builder, в Spring Framework есть ClientHttpRequestFactory — паттерн абстрактная фабрика, в MIDP реализации паттерна комманда назывались FooCommand и тд. Но синглтоны почему-то никогда постфиксом не обозначаются…
В общем, я привык к такому подходу, и пока проблем от этого не получил. Если проблемы возникнут, я поменяю подход.
про 2. возможно стоит подумать про IntentFactory всесто ActivityMediator'а? Решит вопрос и со startActivityForResult и с уведомлениями и др.
Можно взглянуть на полную верию ActivityMediator?
Был опыт использования подобного подхода. На практике оказалось довольно муторно если взять чуть влево/вправо (управление стеком активити, наследование только от супер-активити).
Наблюдал подобный подход в приложенииGoogle IO в классе ActivityHelper, только там этот класс еще и ActionBar устанавливал.
Собственно в своих проектах тоже этим подходом пользуюсь и для ActionBar и для взаимодействия между активити, очень удобно.
Только я не делал один базовый класс для активити, т.к. нужно было наследоваться от TabActivity и MapActivity.
Не совсем по теме, но интересно обсудить вопрос. Подход с MyActivity классом бывает полезным во многих ситуациях. Но он спотыкается об TabActivity, MapActivity… Интересно как народ выходит из положения? Придумал ли ктото чтото более умное чем использование MyActivity где можно и копирование его содержимого там где базовый класс другой?
можно решить эту проблему, если пользоваться фрагментами.
А так мне пару месяцев назад пришлось городить костыль в виде TabbedMapActivity класса, наследовавшегося от TabActivity и реализующего функционал MapActivity :)
А почему не MapActivity как одна из табов TabActivity?
А вообще Android SDK навевает устойчивую мысль о споре про множественное наследование.
в том проекте у меня был UI со вложенными табами. И вот во второе вложенное TabActivity необходимо было добавить MapView. Большой уровень вложенности активити время от времени вызывал стаковерфлов, поэтому пришлось городить подобный костыль.

Вроде как с переходом на фрагменты ситуация улучшается. MapFragment, TabFragment гораздо проще использовать.
Да это проблема, тут сразу всплыла мысль про trait из Scala/Kotlin. Просто расширяем trait'ом любой класс Activity и получаем функционал.

p.s. потихоньку, но уверенно отхожу от Java для Android. Правда никакого production, увы.
очень интересно. а вы не могли бы вы поподробнее, как вы на scala под андроид пишите?
Ну Scala, крута, только время сборки очень медленное. Там в принципе нужно в Eclipse скачать версию ниже 2.9 Scala + TreeShacker для отрезания кусков ненужных из Scala runtime. Да собственно и все, все делаеться в Eclipse кликами. Сейчас смотрю на Kotlin, его можно установить в IDEA и там же сразу без всяких проблем запускать на Android. Что интересно, как и предъявлено в документации языка, сборка практически не заметна, как и Java, что дает огромный плюс в сторону Kotlin, а не Scala. Ради этого так же стартанул проект github.com/vladlichonos/kotlinAndroidLib чтобы обвернуть все Android и сделать чтобы было легко использовать :)
А почему ActivityMediator не сделан синглтоном? В представленной реализации, имхо, медиатор слишком сильно привязывается к Activity и к MyActivity в частности. Можно было бы преспокойно сделать его синглтоном, с конструктором без параметров, а каждый вызов showDocumentViewer() и др. дополнительно параметризовать Context'ом. На один параметр больше, зато — полная независимость от MyActivity, TabActivity и проч…
ну вообще я уверен, что если синглтон с конекстом, то это явный признак того, что-то мы неправильно сделали с точки зрения архитектуры
1) если хранить постоянную ссылку на контекст, то получим утечку памяти
2) если сувать контекст параметром к каждому методу, то мы сделаем маленький шажок к говнокоду. Роберт Мартин писал, что такого рода параметры — плохо. а у меня нет оснований ему не верить))
В своем проекте с небольшим числом Активити использовал простой статический класс.
По сути, использование в коде ничем не отличается от предложенного в статье — только первый параметр будет this. Ну и намного проще в реализации :) Наверно фанаты паттернов иронично ухмыльнутся, а фанаты KISS согласятся :)

Пример:
Director.goTrainList(this, 100);

public class Director {
public static void goHome(Activity activity) {
activity.startActivity(new Intent(activity, MainActivity.class));
}

public static void goWordList(Activity activity) {
activity.startActivity(new Intent(activity, WordListActivity.class));
}

public static void goTrainList(Activity activity, int dicId) {
Intent intent = new Intent(activity, TrainListActivity.class);
intent.putExtra(TrainListActivity.TrainDicId, dicId);
activity.startActivity(intent);
}
}
использование паттернов не противоречит kiss
я уже сто раз убеждался, что понятия «просто», «правильно» и «работает» чаще всего появляются вместе.
Это верно.

Скажите, в чем преимущество вашего метода над моим, в данном контексте?

(Вопрос не ради поспорить а ради опыта)
Существенных преимуществ ни в том, ни в другом я на вскидку не вижу. По сути, у вас тот же медиатор, только статичный.
Давайте опишу несущественные. У вас:
— на 1 параметр больше в каждом методе
— дублируется код создания интента
У меня:
— нужно все активити от одного наследовать
— непонятно (как писали выше) как вызывать активити из сервиса (возможно, писать свой абстрактный класс от Service)
Существенных преимуществ ни в том, ни в другом я на вскидку не вижу. По сути, у вас тот же медиатор, только статичный.
Давайте опишу несущественные. У вас:
— на 1 параметр больше в каждом методе
— дублируется код создания интента
У меня:
— нужно все активити от одного наследовать
— непонятно (как писали выше) как вызывать активити из сервиса (возможно, писать свой абстрактный класс от Service)
Почему бы для вызова активити из сервиса не расширить реализацию медиаторов следующим образом:

public class ContextMediator
{
// -- construction

public ContextMediator(Context context) {
mContext = context;
}

// -- properties

protected Context getContext() {
return mContext;
}

// -- functions

protected void startActivity(Class<?> cls)
{
Intent intent = new Intent(mContext, cls);
mContext.startActivity(intent);
}

protected void startActivity(Class<?> cls, Bundle extras)
{
Intent intent = new Intent(mContext, cls);
intent.replaceExtras(extras);

mContext.startActivity(intent);
}

// -- variables

private final Context mContext;

}

public class ActivityMediator extends ContextMediator
{
// -- construction

public ActivityMediator(Activity activity) {
super(activity);
}

// -- properties

protected Activity getActivity() {
return (Activity) getContext();
}

// -- functions

protected void startActivityForResult(Class<?> cls, int requestCode)
{
Intent intent = new Intent(getActivity(), cls);
getActivity().startActivityForResult(intent, requestCode);
}

}


Дальше, соответственно:

public abstract class MyActivity extends Activity {

private ActivityMediator mActivityMediator = new ActivityMediator(this);

public ActivityMediator getActivityMediator(){
return mActivityMediator;
}

}

public abstract class MyService extends Service {

private ContextMediator mContextMediator = new ContextMediator(this);

public ContextMediator getActivityMediator() {
return mContextMediator;
}

}
С своем коде взял за правило в каждом активити, которое можно можно запустить с параметрами, иметь статический метод который и создает интент для запуска этого активити.
1. Каждый далее пользуется интентом как хочет
2. У каждого активити знаешь, где искать лаунчер.
Медиатор при количестве активити в проекте более 10 (каждое в несколькими вариантами запуска), как мне кажется, будет перегружен.
Все параметры активити известны только ему (private).
Sign up to leave a comment.

Articles