Подтвердить что ты не робот

Лучшая практика: расширение или переопределение класса проекта библиотеки Android

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

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

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

  • Запишите целевые функции в классах Core-библиотеки и используйте if/switch для выбора поведения на основе SKU продукта. Этот подход не очень модульный и раздувает базовую библиотечную базу.
  • Расширение конкретного класса Core в целевом проекте и переопределение базовых (основных) функций класса по мере необходимости. Затем сохраните ссылку на объект базового класса в библиотеке Core и создайте экземпляр объекта с расширенным объектом класса (из Как переопределить класс в проекте библиотеки Android?)

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

4b9b3361

Ответ 1

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

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

Из версии 17 Журнал изменений

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

Дополнительная информация http://tools.android.com/recent/dealingwithdependenciesinandroidprojects

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

EDIT:

Мое решение для перезагрузки/расширения Activity из библиотеки jar:

Я создал простой класс 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", это проект, который зависит от этой библиотеки, создайте новый класс с именем SampleActivityExtended в проекте в том же пакете и добавьте новое действие в ваш AndroidManifest.xml.

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

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

Ответ 2

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

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

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

Решение, данное Android, Проект библиотеки.
Решение, данное Java, Наследование и Polymorphism.
Приходите вместе, наилучшая практика IMO - это второй вариант, который вы упомянули в вопросе:

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

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

Ответ 3

Решение основано на решении 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;
    }
}

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

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

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

Ответ 4

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

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

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

Ответ 5

Не могли бы вы прояснить, что отличает 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.

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

Ответ 6

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

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)

Ответ 7

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