Several problems 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.simpleframework simple-xml 2.6.1 stax-api stax 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!