Back to Home

Several problems when developing android applications and how to solve them

android · development · android · development · xml · java · java · mmm coffee

Several problems when developing android applications and how to solve them

In this article, I would like to give a few problems that I encountered when developing android applications and how to solve them.

Static context


Problem: I want to be able to call some Context methods from a static context (sorry for the pun).

Solution: I took advantage of the solution with stackoverflow , which was to create a static Application class.
Here you need to be careful - and use it wisely. For example, to get resources - translations, pictures, styles.
Where it cannot be used: for working with GUI elements or, for example, with LayoutInflator (an Exception will be thrown).

In practice, it will look like this:

public class ApplicationContext extends android.app.Application {
    @NotNull
    private static ApplicationContext instance;
    public ApplicationContext() {
        instance = this;
    }
    @NotNull
    public static ApplicationContext getInstance() {
        return instance;
    }
}


and

AndroidManifest.xml:


        // ...
     


Using a higher level android api in a lower level application


Problem: I want to use features available in higher-level api (for example, GUI elements - Views).

Solution: android is an open platform, therefore source codes are available. Take yes and transfer the unavailable classes to our library. Naturally, there are disadvantages in this approach: you need to check the operability of the transferred classes and edit the dependencies that are not supported at this level api (it’s so lucky).

Example: NumberPicker is available only from level 11, but I transferred it to my project and use it with level 4: look at github

Using the XML API


Problem: it is often required to convert a java object to an xml representation (for example, to transfer an object between services or to save it in a persistance state).
Usually, JAXB is used in such cases, but JAXB is not available on the android platform.

Solution: use a different library. For example, I used Simple (XML serialization) simple.sourceforge.net

How to use?

First, connect the library to the project:

org.simpleframeworksimple-xml2.6.1stax-apistax


Note: exclusion is set for stax-api - otherwise the project will not be built (android api is not supported).

We mark objects with the necessary annotations (@Root, Transient , Element ):

@Root
public class Var implements IConstant {
    @Transient
    private Integer id;
    @Element
    @NotNull
    private String name;
    @Element(required = false)
    @Nullable
    private String value;
    //...
    private Var() {
    }
}


Save the object in xml:

final StringWriter sw = new StringWriter();
final Serializer serializer = new Persister();
try {
   serializer.write(vars, sw);
} catch (Exception e) {
   throw new RuntimeException(e);
}


Get the object from xml:

final String value = getVarString(); 
final Serializer serializer = new Persister();
try {
   final Vars vars = serializer.read(Vars.class, value);
   // ...
} catch (Exception e) {
   throw new RuntimeException(e);
}


Storage of transfers (leaving R class dependency)


Problem: Android has a mechanism for receiving translations by the class R field identifier, and wherever you want to use this translation, you have to have a dependency on this class.

Solution: an application-level translation cache that stores translations by name and language. The translation is taken from a static context.

Code example:

public enum TranslationsCache {
    instance;
    // first map: key: language id, value: map of captions and translations
    // second mal: key: caption id, value: translation
    private final Map> captions = new HashMap>();
    private Class resourceClass;
    private Context context;
    /**
     * Method load captions for default locale using android R class
     * @param context STATIC CONTEXT
     * @param resourceClass class of captions in android (SUBCLASS of R class)
     */
    public void initCaptions(@NotNull Context context, @NotNull Class resourceClass) {
        initCaptions(context, resourceClass, Locale.getDefault());
    }
    /**
     * Method load captions for specified locale using android R class
     * @param context STATIC CONTEXT
     * @param resourceClass class of captions in android (SUBCLASS of R class)
     * @param locale language to be used for translation
     */
    public void initCaptions(@NotNull Context context, @NotNull Class resourceClass, @NotNull Locale locale) {
        assert this.resourceClass == null || this.resourceClass.equals(resourceClass);
        this.context = context;
        this.resourceClass = resourceClass;
        if (!initialized(locale)) {
            final Map captionsByLanguage = new HashMap();
            for (Field field : resourceClass.getDeclaredFields()) {
                int modifiers = field.getModifiers();
                if (Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers)) {
                    try {
                        int captionId = field.getInt(resourceClass);
                        captionsByLanguage.put(field.getName(), context.getString(captionId));
                    } catch (IllegalAccessException e) {
                        Log.e(ResourceCache.class.getName(), e.getMessage());
                    }
                }
            }
            captions.put(locale.getLanguage(), captionsByLanguage);
        }
    }
    private boolean initialized(@NotNull Locale locale) {
        return captions.containsKey(locale.getLanguage());
    }
    /**
     * @param captionId id of caption to be translated
     * @return translation by caption id in default language, null if no translation in default language present
     */
    @Nullable
    public String getCaption(@NotNull String captionId) {
        return getCaption(captionId, Locale.getDefault());
    }
    /**
     * @param captionId  id of caption to be translated
     * @param locale language to be used for translation
     * @return translation by caption id in specified language, null if no translation in specified language present
     */
    @Nullable
    public String getCaption(@NotNull String captionId, @NotNull final Locale locale) {
        Map captionsByLanguage = captions.get(locale.getLanguage());
        if (captionsByLanguage != null) {
            return captionsByLanguage.get(captionId);
        } else {
            assert resourceClass != null && context != null;
            initCaptions(context, resourceClass, locale);
            captionsByLanguage = captions.get(locale.getLanguage());
            if (captionsByLanguage != null) {
                return captionsByLanguage.get(captionId);
            }
        }
        return null;
    }
}


After that, you can use, for example, as follows:

try{
    //...
} catch ( SomeException e ) {
    TranslationsCache.instance.getCaption(e.getMesageId());
}


Conclusion


Everything described above has been put into practice:

Source code is available on github.com .

A working application on android.market .

Thanks for attention!

Read Next