Инъекция зависимости с помощью Dagger при десериализации Gson - PullRequest
0 голосов
/ 02 сентября 2018

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

Однако доступ к связанному компоненту в конструкторе по умолчанию, вызываемом Gson, выглядит следующим образом

DaggerExampleComponent.builder().build().inject(this)

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

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

РЕДАКТИРОВАТЬ Десериализация выполняется для данных, извлеченных из базы данных с использованием библиотеки Android Room Persistence . Преобразование данных в пользовательские объекты реализуется с помощью аннотации @TypeConverter для статических методов, которые неявно вызываются при извлечении элемента из базы данных. Это не позволяет мне внедрять созданные объекты прямо здесь - преобразователи являются статическими методами в статическом классе, который не создан, поэтому я не могу передать ему объект DaggerComponent, который будет использоваться для внедрения созданных экземпляров, как предложено Торбеном ниже.

1 Ответ

0 голосов
/ 02 сентября 2018

Отказ

Я давно не работал с Кинжалом. Примите следующие растворы с добавлением соли! Решение сработало для меня локально.

Без DaggerExampleComponent

Одним из возможных вариантов решения проблемы может быть использование пользовательской реализации интерфейса JsonDeserializer , которая принимает экземпляр объекта, который вы хотите внедрить, в качестве аргумента конструктора.

Вы можете написать свой собственный десериализатор, который внедряет экземпляр синглтона в десериализованный объект следующим образом:

class MyJsonDeserializer implements JsonDeserializer<MyObject> {

    private final MyComponent singleton;

    public MyJsonDeserializer(MyComponent component) {
        this.singleton = component;
    }

    public MyObject deserialize(JsonElement json, Type tye, JsonDeserializationContext context) throws JsonParseException {
        // you could here parse some arguments
        return new MyObject(singleton);
    }
}

Вы бы зарегистрировали это так:

MyComponent component = ...
Gson gson = new GsonBuilder().registerTypeAdapter(MyObject.class, new MyJsonDeserializer(component)).create();

Если вы хотите внедрить класс MyComponent, убедитесь, что каждый созданный объект имеет один и тот же экземпляр объекта MyComponent.

Лично я предпочел бы, чтобы это решение не смешивало Даггер и Гсон.

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

Вы также можете изменить код для использования DaggerAppComponent в указанном JsonDeserializer следующим образом:

class MyJsonDeserializer implements JsonDeserializer<MyObject> {

    private final DaggerAppComponent singletonProvider;

    public MyJsonDeserializer(DaggerAppComponent componentProvdider) {
        this.singletonProvider = componentProvider;
    }

    public MyObject deserialize(JsonElement json, Type tye, JsonDeserializationContext context) throws JsonParseException {
        // you could here parse some arguments
        MyObject object =  ...
        singletonProvider.inject(object);
        return object;
    }
}

и измените регистрацию следующим образом:

DaggerAppComponent componentBuilder = DaggerExampleComponent.builder().build();
Gson gson = new GsonBuilder().registerTypeAdapter(MyObject.class, new MyJsonDeserializer(componentBuilder)).create();

UPDATE

Из-за новой информации я бы предложил усовершенствовать существующий класс, который используется для библиотеки постоянства комнат Android (класс, содержащий аннотированный метод), например:

class Convert {
    static DaggerAppComponent singletonProvider;
    @TypeConverter
    public static MyObject convert(String arg) {
        Gson gson = new GsonBuilder().registerTypeAdapter(MyObject.class, new MyJsonDeserializer(componentBuilder)).create();
        return gson.fromJson(arg, MyObject.class);
    }

    @TypeConverter
    public static String fromArrayLisr(MyObject object) {
        Gson gson = new Gson();
        String json = gson.toJson(v);
        return json;
    }
}

Я получил вдохновение от thetechguru . Кроме того, это относится к тому же JsonDeserializer, который указан выше.

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

Чтобы использовать это эффективно, где-то в коде (до того, как будут сделаны какие-либо вещи, связанные с базой данных), это должно быть вызвано:

Convert.singletonProvider = DaggerExampleComponent.builder().build();

Это позволит классу Convert видеть правильный DaggerAppComponent.

Возможно, с этим еще есть проблема. Это состояние гонки , определяющее нулевое состояние статической переменной. Если база данных будет вызвана в ближайшее время, результатом будет NullpointerException, так как статическое поле еще не установлено. Чтобы противодействовать этому, вы можете использовать семафор (или что-нибудь подобное), чтобы создать своего рода барьер. В случае семафора это будет простой семафор с 0 разрешениями. Перед использованием вызывающей переменной приобретите и отпустите ее. После того, как переменная была установлена ​​(вне этого класса), вызовите release для нее один раз.

Это не очень хорошее решение (с точки зрения разработки программного обеспечения), но оно должно сработать.

...