R class on android has it's limitations. You can't use the resources dynamically for loading audio, pictures or whatever. If you wan't for example, load a set of audio files for a choosen object you can't do something like:
R.raw."string-upon-choosen-object"
I'm new to android and at least I didn't find how you could do that, depending on what objects are choosen or something more dynamic than that. So, I thought about making it dynamic with a little of memory overhead. But, I'm in doubt if it's worth it or just working different with external resources.
The idea is this:
Modify the ant build xml to execute my own task. This task, is a java program that parses the R.java file building a set of HashMaps with it's pair (key, value). I have done this manually and It's working good. So I need some experts voice about it.
This is how I will manage the whole thing:
Generate a base Application class, e.g. MainApplicationResources that builds up all the require methods and attributes. Then, you can access those methods invoking getApplication() and then the desired method.
Something like this:
package [packageName]
import android.app.Application;
import java.util.HashMap;
public class MainActivityResources extends Application {
private HashMap<String,Integer> [resNameObj1];
private HashMap<String,Integer> [resNameObj2];
...
private HashMap<String,Integer> [resNameObjN];
public MainActivityResources() {
super();
[resNameObj1] = new HashMap<String,Integer>();
[resNameObj1].put("[resNameObj1_Key1]", new Integer([resNameObj1_Value1]));
[resNameObj1].put("[resNameObj1_Key2]", new Integer([resNameObj1_Value2]));
[resNameObj2] = new HashMap<String,Integer>();
[resNameObj2].put("[resNameObj2_Key1]", new Integer([resNameObj2_Value1]));
[resNameObj2].put("[resNameObj2_Key2]", new Integer([resNameObj2_Value2]));
...
[resNameObjN] = new HashMap<String,Integer>();
[resNameObjN].put("[resNameObjN_Key1]", new Integer([resNameObjN_Value1]));
[resNameObjN].put("[resNameObjN_Key2]", new Integer([resNameObjN_Value2]));
}
public int get[ResNameObj1](String resourceName) {
return [resNameObj1].get(resourceName).intValue();
}
public int get[ResNameObj2](String resourceName) {
return [resNameObj2].get(resourceName).intValue();
}
...
public int get[ResNameObjN](String resourceName) {
return [resNameObjN].get(resourceName).intValue();
}
}
The question is:
Will I add too much memory use of the device? Is it worth it?
Regards,
I'm new to android and at least I
didn't find how you could do that,
depending on what objects are choosen
or something more dynamic than that.
The Resources class has a getIdentifier() method that will give you the resource ID given the name as a string. This uses reflection, so you would want to cache the results, perhaps using a LinkedHashMap as an LRU cache.
Is it worth it?
IMHO, not really. I would just use getIdentifer() or directly use reflection myself. In fact, I have directly used reflection myself (with the LRU cache) to address this issue.
Related
I'm kinda new to Android development so my question might be weird or not even possible. I wouldn't know!
Anyway, I'm building multiple apps that will have a lot of shared elements, so I decided to build a library with those components and use it in all of the apps, rather than stupid copying and pasting code.
For example, the library handles the welcome screen and login/signup flow activities, among other things. So here are the problems this approach might cause:
While the behavior is the same across the apps, but the logo that I show at the welcome screen is different. Right now I populate it with an image resource from the library resources (R class) which will be the same for all apps and is obviously not correct.
The login/signup process is based on Firebase, which will require the app to have a key to be able to use them. Right now I also populate it with a dummy string resource from the library resources.
So my question really boils down to 3 parts:
Is there anyway I could pass this info from the app to the library? can I somehow modify the R class of the library? Or can I use the app's R class from the library? I can also call this part of the library as a function passing the parameters I need. But the first solution looks maybe more clean to me?
Whatever the answer to Q1 is. Where would I do this and how? The library has the welcome activity itself which is supposed to be the first activity in the app. How and where do I do this once the app starts and before the first activity starts?
If what I'm doing is wrong or impossible, is there any other way to achieve it?
Is there anyway I could pass this info from the app to the library?
can I somehow modify the R class of the library? Or can I use the
app's R class from the library? I can also call this part of the
library as a function passing the parameters I need. But the first
solution looks maybe more clean to me?
You don't need to modify the R class because you can override the resource file by creating a file with the same name. But it's not a clean solution because you constantly need to ensure your project and library resources name are the same.
Whatever the answer to Q1 is. Where would I do this and how? The
library has the welcome activity itself which is supposed to be the
first activity in the app. How and where do I do this once the app
starts and before the first activity starts?
Instead of overriding the resources name, you're better to modify your library to receive a configuration as a contract to use the library. Here the sample:
First, create the class for holding the configuration:
public class Configuration {
private int welcomeImageDrawableId;
private int logoDrawableId;
// constructor
public Configuration(int welcomeImageDrawableId, int logoDrawableId) {
this.welcomeImageDrawableId = welcomeImageDrawableId;
this.logoDrawableId = logoDrawableId;
}
// setter and getter.
public int getLogoDrawableId() {
return logoDrawableId;
}
}
Second, use the configuration class for the library by creating a Singleton class which will be used internally by the library:
public class MyLibrary {
private static MyLibrary myLibrary;
private Configuration configuration;
private MyLibrary(){}
private MyLibrary(Configuration configuration) {
this.configuration = configuration;
}
public static MyLibrary getInstance() {
if(myLibrary == null) {
throw new RuntimeException("Need call createInstanceWith method first!!");
}
return myLibrary;
}
public static MyLibrary createInstanceWith(Configuration configuration) {
if(myLibrary == null) {
synchronized(MyLibrary.class) {
if (myLibrary == null) {
myLibrary = new MyLibrary(configuration);
}
}
}
return test;
}
public Configuration getConfiguration() {
return configuration;
}
}
Third, use the configuration class in your library via the singleton class. something like this:
// assume imvLogo is an existing ImageView
Configuration configuration = MyLibrary.getInstance().getConfiguration();
imvLogo.setImageResource(configuration.getLogoDrawableId());
Last, register the contract when the library is used with:
Configuration configuration = new Configuration(R.drawable.welcome, R.drawable.logo);
MyLibrary.createInstanceWith(configuration);
Note: all the code isn't tested yet, error is to be expected.
Apart from the solution above, I also found another way to achieve this whole thing without having to initialize libraries and whatnot.
I think the correct way to do this is to use productFlavors in the library. This allows the library to share the one main set of source code, one main set of resources, then an extra set of resource per app/flavors. This is very sufficient for my purposes.
For more info about build variants and flavors:
https://developer.android.com/studio/build/build-variants
First some background (I always like when people asking questions give that): I am writing a library project. It is a simple OpenGL 'graphics distortions' library. Users are supposed to be able to create certain objects called 'Regions' with a simple call to
myRegion = new DistortedRegion(w,h);
then define various distortions on them and draw them:
myRegion.addTwistDistortion(...)
myRegion.draw(x,y);
That's all there is to it. The library uses OpenGL to draw the Region (a rectangular bitmap really) distorted in various user-defined ways.
Now, the problem: the library needs to access its vertex and fragment shaders, which are .glsl files stored with its resources, in the 'raw' folder. In order to do that, we have to access the Resources object, and in order to do that we AFAIK have to have the Context object. So currently as it stands the library has to first be initialized with a call
Distorted.init(Context)
This is a giant problem for me, because this IMHO makes the whole API clumsy. If not for this damn Context, there would be no need to initialize anything, and the user would simply be able to create new objects straight away whenever he wants, which is elegant and simple to explain in the docs. A separate init() call , which is only really needed to pass the Context object to the library, which is only needed to retrieve the Resources object, which is only needed to open up two files containing shader code, makes all of that very clumsy.
I was thinking to simply move the shader code into statically initialized Strings inside my library instead, but this is cumbersome for development (the shaders are several hundred lines long of quite complicated code, and if I keep them in a separate .glsl files, I can have syntax highliting among other things)
Any advice?
So yes, I hopefully figured it out. Here's a function that returns the Context of the current Application by directly calling the underlying Android code by reflection. It can be called from anywhere, not only from classes which extend Application or Activity:
import android.content.Context;
import android.app.Application;
import java.lang.reflect.Method;
private Context getCurrentContext()
{
Class<?> cl =null;
Method method =null;
Application app =null;
try
{
cl = Class.forName("android.app.AppGlobals");
}
catch(ClassNotFoundException ex)
{
Log.e(TAG_DISTORTED_REGION, "Failure to retrieve the AppGlobals class: "+ex.toString() );
return null;
}
try
{
method = cl.getMethod("getInitialApplication", (Class [])(null) );
}
catch(NoSuchMethodException nm)
{
Log.e(TAG_DISTORTED_REGION, "No such method: "+nm.toString() );
return null;
}
try
{
app = (Application)method.invoke(null, (Object [])(null) );
}
catch(Exception e)
{
Log.e(TAG_DISTORTED_REGION, "failure calling method getInitialApplication: "+e.toString() );
return null;
}
return app.getApplicationContext();
}
I am making an application presenting a showroom and at this points I have created way too much classes.
The main view is a GridView containing all the series of cars.(Each GridView Item opens a new class, so there are 9 classes with very similar code)
How can I structure it?
To put a bit more flesh on #g00dy, start by creating a class
class BMW {
// Reference codes for every series
public final static int SERIES_1 = 0;
public final static int SERIES_2 = 1;
// etc
public final static int NUMBER_SERIES = 9;
// All the code needed for every car
// eg.
public String giveManufacturuer() {
return "BMW"; // But see #g00dy - use string resources
}
public String giveSeries() {
return XXXXX; // Depends on which approach you choose, see below
}
public String giveModelName() {
return XXXXX; // Depends on which approach you choose, see below
}
}
You can either load all the variations into this class (add in references codes for every car and set up some tables to make indexing easy).
Or you could extend the class using inheritance for each class:
class Series1 extends BMW {
#Override
public String giveSeries {
return "Series 1";
}
}
class Series1M3Door extends Series1 {
#Override
public String giveModelName {
return "3 Door";
}
}
When you then instantiate the final class it will have all three functions working correctly.
This approach is neat, but will still give you a lot of classes. I suspect that for what you are doing, some well thought out information tables (accessed by series and model code) may work better inside a hidden class.
A different, perhaps better approach, might be to structure the code using the information that you are returning as the core classes.
I do not actually have the time to write all this down, mean a unifying class, but here's hint for you. Use a flag, which will indicate the model of the car (Z4,M6 for example), then use it inside the class to determine the tree on which the code should run. Replace the hardcoded values with string resources (just do it, no other remarks are necessary). When instantiating the class and using it's functions, take into account the flag and put it inside an if() condition or inside a switch. If some models require more code than the others, you can always encapsulate it in the part of the code which is responsible for the model. But avoid nesting too much ifs, because it will get messy, like having 100 classes defined which do 99% the same thing as the others. Always try to re-use your code as much as possible. It will reduce the writing (copy/pasting) repetitive stuff, also the size of the application, the memory it will need etc. Conclusion: try combining the common parts of the classes into one class ( to RULE THEM ALL :-) ) and use flags, to let the program knwo what to do there.
I'm trying to persist data objects throughout my Android app. I want to be able to access an object in one activity, modify it, save it, navigate to a new activity, and access the same object with the updated value.
What I'm essentially talking about is a cache, but my data objects are complex. For example, ObjectA contains ObjectB which contains ObjectC. Does anyone know if a good method, tool, or framework for persisting complex objects in Sql?
Put a static field in a subclassed Application. Also inside your manifest, put:
android:name="MyApp" inside your application tags.
Also to access from other files, simply use:
MyApp myApp = (MyApp)getApplicationContext();
See here How to declare global variables in Android?:
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
#Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
You could use an ORM framework, like OrmLite for mapping objects into sql, but it may be an overkill for you situation.
You could also make these shared object Parcelable and pass them between the Activities thru the Intents.
You could also save these objects into the SharedPreferences, so each Activity can access them whenever they feel the need to it, and the objects are also persisted this way. This may mean more IO access though, so take that into consideration as well. You could use e.g. Gson to serialize the objects more painlessly for this.
These are the solutions I'd consider. But whatever you do, don't put this common object into some kind of "standard" global static variable, like using a custom Application class, static field or any implementation of the Singleton pattern, these are really fragile constructs on Android.
Why don't you use a JSON serialization mechanism ?
In association with a static access to your objects you can easily build a lite-weight database with some basic functionnalities:
loadObjectsFromCache
saveObjectsInCache
getObjects
You can also store your objects in differents files, and use a streaming json parser like this one: http://code.google.com/p/google-gson/
It's the same that this one: http://developer.android.com/reference/android/util/JsonReader.html
but can be used even if your application api level is inferior to 11.
It use less memory than the basic DOM parser:
http://developer.android.com/reference/org/json/JSONObject.html,
but with the same speed.
I am currently developing a mobile application using the latest version of Flash Builder and I need to create a Global ArrayCollection to store information in that is pulled from a local DB. I can pull back the data from the DB fine however I cannot seem to access the Global variable when I try. I have the follng .as file called "Model.as" which is located in a folder called valueObjects and that file contains the following code:
package valueObjects
{
import flash.data.SQLConnection;
import mx.collections.ArrayCollection;
public class Model
{
public var ids:ArrayCollection = new ArrayCollection();
public function Model()
{
}
}
}
Now I want to start populating this ArrayCollection with the info from the database so i import the class into the Default Package mxml doc which will be loaded up first when the app starts by using:
import valueObjects.Model;
Then in a private function I try to access the ids ArrayCollection and populate it however I get the following error:
-1120: Access of undefined property ids.
-Access of undefined property ids
Can anyone please help with this?? Thanks
Have you created an instance of your Model class?
In the classes you want to access it, you should do something like this:
var myModel : Model = new Model();
To share the model instance between classes, you're going to have to do slightly more work. Either by passing around a reference to the same object, or using an alternate method such as creating a Singleton. Many Flex frameworks, such as Cairngorm or RobotLegs, use Singletons if you need an example.
Or easiest way is make array static.
public static var ids:ArrayCollection = new ArrayCollection();
but for this case is Singleton way better.