2012-03-30 2 views
32

Мы используем Android Library Project для совместного использования основных классов и ресурсов в разных целях (целях) нашего приложения для Android. Android-проекты для каждой конкретной цели reference the Core library project (за кулисами Eclipse создает и ссылается на банку из проекта библиотеки ссылок).Лучшая практика: расширение или переопределение класса проекта библиотеки Android

Переопределение ресурсов, таких как изображения и макеты XML, очень просто. Файлы ресурсов, размещенные в целевом проекте, такие как значок приложения или макет XML, автоматически переопределяют ресурсы основной библиотеки с тем же именем при создании приложения. Однако иногда класс должен быть переопределен, чтобы включить поведение, специфичное для конкретного объекта. Например, экран настроек целевых настроек Amazon не может содержать ссылку на страницу приложения Google Play, требующую изменения в предпочтениях проекта Amazon.xml и предпочтения класса Activity.

Цель состоит в том, чтобы уменьшить количество дублирующихся кодов среди целевых проектов, удалив как можно больше целевого кода из библиотеки Core. Мы придумали пару подходов к реализации логики, специфичной для разных целей:

  1. Напишите функции для конкретных целей в классах классов ядра и используйте, если/переключаете блоки, чтобы выбрать поведение на основе SKU продукта. Этот подход не очень модульный и раздувает базовую библиотечную базу.
  2. Расширение конкретного класса ядра в целевом проекте и переопределение базовых (основных) функций класса по мере необходимости. Затем сохранить ссылку на объект базового класса в библиотеке ядра и создать его экземпляр с протяженным объектом класса (от How to override a class within an Android library project?)

Существуют ли другие стратегии переопределить или расширить Android класса проекта библиотеки? Каковы некоторые из лучших практик для совместного использования и распространения общих классов среди целевых приложений для Android?

+0

_ (за кадром, Eclipse создает и ссылается на баночку из ссылочного проекта библиотеки) _ - Можете ли вы объяснить, как вам удалось получить эту работу, это официально не поддерживается SDK еще, в соответствии с [этот блог] (http://android-developers.blogspot.co.nz/2011/10/changes-to-library-projects-in-android.html), опубликованный ранее. И в последней заметке о выпуске SDK r17 я не вижу в какой-либо главе упоминания о том, что это отсутствие функциональных возможностей было устранено. – yorkw

ответ

16

Проект библиотеки упоминается как необработанная зависимость проекта (исходный механизм), а не как скомпилированная jar-зависимость (механизм библиотеки, основанный на компилированном коде).

@yorkw это не верно для последних версий ADT плагин для Eclipse, http://developer.android.com/sdk/eclipse-adt.html

Начиная с версии 17 Изменить журнал

вбизи особенности Добавлена ​​возможность автоматической настройки JAR зависимостей. Любые файлы .jar в папке/libs добавляются в конфигурацию сборки (подобно тому, как работает система сборки Ant). Кроме того, файлы .jar, необходимые для проектов библиотек, также автоматически добавляются к проектам, которые зависят от этих проектов библиотеки. (Подробнее)

Подробнее http://tools.android.com/recent/dealingwithdependenciesinandroidprojects

До этого обновления перезапись деятельности из проекта библиотеки было легко, просто исключить класс. Теперь библиотека включена как файл jar, и нет способа исключить файл класса из jar-зависимости.

EDIT:

Моего решения overwrete/продлить активность из библиотеки баночки:

Я создал простой UTIL класса:

public class ActivityUtil { 

private static Class getActivityClass(Class clazz) { 

    // Check for extended activity 
    String extClassName = clazz.getName() + "Extended"; 
    try { 
     Class extClass = Class.forName(extClassName); 
     return extClass; 
    } catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
     // Extended class is not found return base 
     return clazz; 
    } 
} 

public static Intent createIntent(Context context, Class clazz) { 
    Class activityClass = getActivityClass(clazz); 
    return new Intent(context, activityClass); 
} 
} 

Для того, чтобы перезаписать класс "SampleActivity" А библиотеки это a проект, который зависит от этой библиотеки, создайте новый класс с именем SampleActivityExtended в проекте в том же пакете и добавьте новое действие в ваш AndroidManifest.xml.

ВАЖНО: все намерения ссылающихся перезаписаны деятельности должны быть созданы с помощью класса Util следующим образом:

Intent intent = ActivityUtil.createIntent(MainActivity.this, SampleActivity.class); 
... 
startActivity(intent); 
+0

Элегантное решение - мне оно нравится - благодаря –

+3

это отличное решение. Я внесла некоторые изменения, чтобы позволить расширенному классу находиться в пакете 'Application Project' вместо пакета' Library Project'. Если вы считаете, что это улучшение, вы можете включить редактирование. Я изменил способ, которым вы получили свое 'extClassName':' String pkgName = context.getPackageName(); String extClassName = pkgName + "." + clazz.getSimpleName() + "Extended"; 'Вам также необходимо передать контекст до' getActivityClass() ' –

+1

, к сожалению, это не сработает с proguard.Возможно, эта система может быть использована для разработки, тогда сценарий поиска/замены может быть запущен поверх источника до сборки релиза. – Sam

1

Как насчет использования подхода callback? (Хорошо, обратный вызов немного вводит в заблуждение, но в настоящее время у меня нет другого слова для этого:

Вы можете объявить интерфейс в каждой Деятельности, которая должна/может быть расширена пользователем. Этот интерфейс будет иметь методы, такие как List<Preference> getPreferences(Activity activity) (pass любые параметры, которые вам нужны здесь, я бы использовал Activity или, по крайней мере, Context, чтобы быть будущим.)

Этот подход может дать вам то, что вы хотите, когда я его правильно понял. Хотя я этого не делал раньше и не делал Не знаю, как другие люди справятся с этим, я бы попробовал и посмотрел, работает ли это.

4

за кулисами, Eclipse создает и ссылается на банку из проекта библиотеки ссылок.

Это не совсем точная информация. Проект библиотеки упоминается как необработанная зависимость проекта (исходный механизм), а не как скомпилированная jar-зависимость (механизм библиотеки, основанный на компилированном коде). В настоящее время Android SDK не поддерживает экспорт проекта библиотеки в автономный файл JAR. Проект библиотеки всегда должен быть скомпилирован/построен косвенно, путем ссылки на библиотеку в зависимом приложении и создания этого приложения. Когда проект, зависящий от сборки, скомпилированный источник и необработанные ресурсы, которые необходимо фильтровать/объединить из проекта библиотеки, копируются и правильно включаются в окончательный файл apk. Обратите внимание, что команда Android начала обновлять весь проект Библиотечного проекта (переместить его из механизма, основанного на нашем языке, на механизм библиотеки на основе компилированного кода) с r14, как указано в this earlier blog post.

Каковы некоторые из лучших практик для совместного использования и расширения общих классов среди целевых объектов Android?

Решение от Android: Library Project.
Решение, данное Java, - Inheritance и Polymorphism.
Соберитесь, лучшая практика ИМО является второй вариант, вы упомянули в вопросе:

2.Extend конкретный основной класс в целевом проекте и переопределить функции базовой (основной) класса по мере необходимости. Затем сохранить ссылку на объект базового класса в библиотеке ядра и создать его экземпляр с протяженным объектом класса (из проекта Android библиотеки? - Как переписать класс)

Из моего личного опыта, я всегда использую Android Библиотечный проект (иногда с Regular Java Project для реализации/создания common-lib.jar, который содержит только POJO) управляет общим кодом, например SuperActivity или SuperService, и расширяет/реализует соответствующие классы/интерфейсы в зависимом проекте для полиморфизма.

1

Не могли бы вы прояснить, что отличает Kindle и обычный Android? Я думаю - они такие же. Для вас нужны разные ресурсы для Kindle и других устройств. Затем используйте соответствующий ресурс. Например, я использую 2 ссылки для хранения:

<string name="appStore">&lt;a href=http://market.android.com/details?id=com.puzzle.jigsaw>Android Market&lt;/a> or &lt;a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore&lt;/a> &lt;br>http://market.android.com/details?id=com.puzzle.jigsaw &lt;br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string> 
<string name="appStore_amazon">&lt;a href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw>Amazon Appstore&lt;/a> &lt;br>href=http://www.amazon.com/gp/mas/dl/android?p=com.puzzle.jigsaw</string> 

и использовать AppStore для всех ни один Amazone продукта и appStore_amazon для Kindle.

Как определить, где вы находитесь во время выполнения - это будет еще один вопрос, на который был дан ответ здесь много раз.

1

Я был вдохновлен ответом PoinsoneR, чтобы создать класс Utility, чтобы сделать то же самое для фрагментов - переопределение фрагмент в андроидной библиотеке. Шаги похожи на его ответ, так что я не буду вдаваться в подробности, но вот класс:

package com.mysweetapp.utilities; 

import android.support.v4.app.Fragment; 

public class FragmentUtilities 
{ 
    private static Class getFragmentClass(Class clazz) 
    { 
     // Check for extended fragment 
     String extClassName = clazz.getName() + "Extended"; 
     try 
     { 
      Class extClass = Class.forName(extClassName); 
      return extClass; 
     } 
     catch (ClassNotFoundException e) 
     { 
      e.printStackTrace(); 
      // Extended class is not found return base 
      return clazz; 
     } 
    } 

    public static Fragment getFragment(Class clazz) 
    { 
     Class fragmentClass = getFragmentClass(clazz); 

     Fragment toRet = null; 

     try 
     { 
      toRet = (Fragment)fragmentClass.newInstance(); 

      return toRet; 
     } 
     catch (InstantiationException e) 
     { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     catch (IllegalAccessException e) 
     { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     return toRet; 
    } 
} 

Использование:

FragmentUtilities.getFragment(MySpecialFragment.class) 
+0

как передать параметр экземпляра фрагменту? – Killer

1

Вы можете также использовать деятельность завода, если вам нужно предоставлять расширенные действия для вариантов вариантов differnt и иметь вашу библиотеку только с абстрактной фабрикой. Это можно задать в ваших вариантах исполнения.

2

Решение на основе решения PoisoneR и решения Turbo.

public static Class<?> getExtendedClass(Context context, String clsName) { 

    // Check for extended activity 
    String pkgName = context.getPackageName(); 
    Logger.log("pkgName", pkgName); 
    String extClassName = pkgName + "." + clsName + "Extended"; 
    Logger.log("extClassName", extClassName); 

    try { 
     Class<?> extClass = Class.forName(extClassName); 
     return extClass; 
    } catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
     // Extended class is not found return base 
     return null; 
    } 
} 

Преимущества этого является то, что

  1. Расширенный класс может быть в пакете проекта, а не пакет библиотеки. Благодаря Turbo для этой части.

  2. Принимая в качестве аргумента String вместо объекта Class, этот метод можно использовать даже с ProGuard. getName() где проблема с ProGuard, так как это вернет нечто вроде «a» вместо имени исходного класса. Поэтому в исходном решении вместо поиска ClassExtended он будет искать aExtended вместо того, чего не существует.

Смежные вопросы