📜 ⬆️ ⬇️

Several problems in the development of Android applications and their solutions

In this article I would like to give a few problems that I encountered while 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 playing with words).

Solution: I used the stackoverflow solution, which is to create a static Application'a class.
Here you need to be careful - and use it wisely. For example, to obtain resources - translations, pictures, styles.
Where it cannot be used: for working with GUI elements or, for example, with LayoutInflator (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:

 <manifest xmlns:a="http://schemas.android.com/apk/res/android"> <application a:name=".ApplicationContext"> // ... </application> </manifest> 


Using a higher level android api in an application with a lower level


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

Solution: Android - open platform, hence the source code available. Take and transfer the non-accessible classes to your library. Naturally, there are disadvantages in this approach: you need to check the performance of the classes transferred and edit the dependencies that are not supported at this level (here how lucky you are).

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 XML API


Problem: you often need 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 in such cases JAXB is used, but JAXB is not available on the android platform.

Solution: use another library. I, for example, used Simple (XML serialization) simple.sourceforge.net

How to use?

First, let's connect the library to the project:

 <dependency> <groupId>org.simpleframework</groupId> <artifactId>simple-xml</artifactId> <version>2.6.1</version> <exclusions> <exclusion> <artifactId>stax-api</artifactId> <groupId>stax</groupId> </exclusion> </exclusions> </dependency> 


Note: exclusion is set for stax-api - otherwise the project is not going to (not supported by android api)

Mark objects with 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 the 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 translations (departure from class R dependency)


Problem: In android, there is a mechanism for receiving translations by the field identifier R of a class and wherever you want to use this translation, you have to have a dependency on this class.

Solution: translation cache at the application level, which 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<String, Map<String, String>> captions = new HashMap<String, Map<String, String>>(); 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<String, String> captionsByLanguage = new HashMap<String, String>(); 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<String, String> 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 was applied in practice:

Source code is available at github.com .

Working application on android.market .

Thanks for attention!

Source: https://habr.com/ru/post/134006/


All Articles