I am trying to convert string-array from strings.xml to array in Java class
(the model part of MVC), there-for I cannot use getResources().getStringArray(R.array.array_name);
(it works only in Android components such as activity, fragment and so on).
So I can use only Resources.getSystem().getStringArray(R.array.array_name);
but When I try to run it in the emulator, I get an exception.
I found similar questions that referred that problem here. but I didn't understand the solution.
Here my exception:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.danirg10000gmail.therpiststep1/com.danirg10000gmail.therpiststep1.MainActivity}: android.content.res.Resources$NotFoundException: String array resource ID #0x7f0b0000
(My exception is same as in the link above.)
In my code I have two classes one class represents questions, another class have a list of questions objects.
Here is my code:
public class QuestionM {
private String mQuestion;
private String mAnswer;
private String mExplanation;
//constructor
public QuestionM(String question,String explanation) {
mQuestion = question;
mExplanation = explanation;
}
public class QuestionnaireM {
private List<QuestionM> mQuestionsList;
//constructor
public QuestionnaireM(){
mQuestionsList = new ArrayList<>();
Resources resources = Resources.getSystem();
//when i creating object of that class android points the crush here
String [] questions = resources.getStringArray(R.array.test);
String [] questionExplanations = resources.getStringArray(R.array.test_a);
for (int i=0; i<questions.length; i++){
QuestionM question = new QuestionM(questions[i],questionExplanations[i]);
mQuestionsList.add(question);
}
}
also I didn't quite understand the difference between system-level resources, and application-level resources, I search it in androidDevelopers and in Google but not found any good explanation. can somebody please explain that?
One suggestion, not sure if it will work. But you can try and let me know. Why not get the context in QuestionM constructor and initialize your class level context variable with the received context. Now use this context to
mContext.getResources().getStringArray(R.array.array_name);
public class QuestionM {
private String mQuestion;
private String mAnswer;
private String mExplanation;
private Context mContext;
//constructor
public QuestionM(String question,String explanation, Context context) {
mQuestion = question;
mExplanation = explanation;
mContext = context;
}
public class QuestionnaireM {
private List<QuestionM> mQuestionsList;
//constructor
public QuestionnaireM(){
mQuestionsList = new ArrayList<>();
//when i creating object of that class android points the crush here
String [] questions = mContext.getResources().getStringArray(R.array.test);
String [] questionExplanations = mContext.getResources().getStringArray(R.array.test_a);
for (int i=0; i<questions.length; i++){
QuestionM question = new QuestionM(questions[i],questionExplanations[i]);
mQuestionsList.add(question);
}
}
According to the docs getSystem() does this:
Return a global shared Resources object that provides access to only
system resources (no application resources), and is not configured for
the current screen (can not use dimension units, does not change based
on orientation, etc).
Therefore calling getStringArray() with the resource Id R.array.test is totally useless since the id referenced is that of an Application resource.
If you want to load the contents of R.array.test, use getStringArray() from getResources().
You can pass a parameter of type Resources to the constructor or String[]. i.e. :
public QuestionnaireM(Resources resource) {
// stuffs
}
Related
I am new to android studio but I am getting better at it as I program more and more. I have a MainActivity.java and the .xml file. And a friend provided me some code that it suppose to work with the input areas. The problem is I do not know how to access that regular java file. So that I can use it the way it is intended. He was using eclipse to build everything while I use android studio. I have the buttons all good to go and areas of input good to go but I just dont know how to implement his code. Any guidance will be greatly appreciated.
See examples to understand what I am trying to do.
"In android studio" a class is created called WaterDetails.java with a .xml file called activity_water_details.xml. There are calculations that were made for the duration that I need to be able to use or access from a java file created in eclipse called DurationCalculations.java. I have tried importing. I have tried opening the folder in explorer and putting the class in the same project. But, nothing seems to work.
Code:
public class WaterDetails extends AppCompatActivity {
Button continueWaterDetailsPart2;
EditText duration;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_water_details);
duration = (EditText)findViewById(R.id.enter_duration);
duration.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String user = duration.getText().toString();
if(duration.equals(" "))// if user inputs information
//Then get calculations from other java file.
}
});
Sample Code:
Second Java fie. The file I need to access.
package ScubanauiTables;
import java.util.Arrays;
public class DurationCalculations {
private int duration;
//Constructor
DurationCalculations(int duration, int maxDepth, int avgDepth, int temp, int visibility, int pressureStart,
int pressureEnd, String[] diveConditions, String[] diveActivities) {
setDuration(duration);
setMaxDepth(maxDepth);
setAvgDepth(avgDepth);
setTemp(temp);
setVisibility(visibility);
setPressureStart(pressureStart);
setPressureEnd(pressureEnd);
setAirType(21);
setDiveConditions(diveConditions);
setDiveActivities(diveActivities);
setPressureGroup();
public int getDuration() {
int temp = duration;
return temp;
}
private void setDuration(int duration) {
this.duration = duration;
}
I hope this sample code makes sense. Thank you all for your help in advance.
You want to use methods of your DurationCalculation class, and for that, you've to create an instance of that class.
You can instantiate and use your class like this
DurationCalculations durationCalculation = new DurationCalculations(
/*enter your constructor values*/);
Now you can call all public methods of your DurationCalculations class using durationCalculation variable like this
durationCalculation.getDuration();
You cannot call any private methods from outside of the class, like your setDuration() whose scope is set to private. For it be accessed outside of DurationCalculations class. You need to set it to public
I guess this question is more about understanding context and how to use it properly.
After having googled and "stackoverflowed" a lot I could not find the answer.
Problem:
when using DateUtils.formatDateTime I cannot use "this" as a context. The error message is as described in the title.
Application Info:
This is a simple weather app retrieving weather information via JSON and displaying it on the screen.
Activities:
- MainActivity.java
- FetchData.java
MainActivity: displaying the info
FetchData: getting JSON info from the API, formatting it and sending it back to MainActivity
I am using DateUtils.formatDateTime in the FetchData.java activity and using "this" as a context does not work.
As from my understanding Context provided the "environment" (?) of where the method is being called.
Why is the "environment" of FetchData not valid?
What content should be provided instead?
Help is much appreciated.
Thank you :)
Code:
private ArrayList<String> getWeatherDataFromJson(String forecastJsontStr) throws JSONException {
ArrayList<String> dailyWeatherInfo = new ArrayList<>();
int dataCount;
DateUtils tempDate = new DateUtils();
JSONObject weatherData = new JSONObject(forecastJsontStr);
JSONArray threeHourWeatherData = weatherData.getJSONArray(JSON_LIST);
dataCount = weatherData.getInt("cnt");
JSONObject tempJSONWeatherData;
for (int i = 0; i < dataCount; i++) {
tempJSONWeatherData = threeHourWeatherData.getJSONObject(i);
tempDate.formatDateTime(this,tempJSONWeatherData.getLong("dt"),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY |
DateUtils.FORMAT_ABBREV_ALL);
[more code here]
return dailyWeatherInfo;
}
Edit: I just realized I left out an important detail, namely this activity extends AsyncTask. After some further research apparently you provide the context bei adding WeakReference and then adding context in the constructor.
I added the following code:
private WeakReference<Context> contextWeakReference;
public FetchData (Content context) {
contextWeakReference = new WeakReference<>();
}
tempDate.formatDateTime(contextWeakReference.get(),tempJSONWeatherData.getLong("dt"),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY |
DateUtils.FORMAT_ABBREV_ALL);
This made the error disappear but I still don't understand why "this" doesn't work.
I am using DateUtils.formatDateTime in the FetchData.java activity and
using "this" as a context does not work. As from my understanding
Context provided the "environment" (?) of where the method is being
called.
You're incorrect, Context is Android context which is (from documentation):
Interface to global information about an application environment. This
is an abstract class whose implementation is provided by the Android
system. It allows access to application-specific resources and
classes, as well as up-calls for application-level operations such as
launching activities, broadcasting and receiving intents, etc.
DateUtils.formatDateTime() needs Context as one of its parameter. So, you need to pass a context.
Android Activity is sub class of Context, so you can use this (which refer to itself) as the context like the following:
public class MyActivity extends Activity {
...
protected void doSomething() {
// this refer to the MyActivity instance which is a Context.
DateUtils.formatDateTime(this, ...);
}
...
}
You need to pass the Context for every class that is not a Context subclass.
You can't use this in AsyncTask because it's not a Context subclass. So, you need to pass the Context using WeakReference to avoid Context leaking, like the following:
private class AsyncTaskRunner extends AsyncTask<String, String, String> {
private WeakReference<Context> contextWeakReference;
public FetchData (Content context) {
contextWeakReference = new WeakReference<>();
}
private void doSomething() {
// We have the context from the WeakReference
Context context = contextWeakReference.get();
DateUtils.formatDateTime(context, ...);
}
}
Last, you don't need to create a DateUtils object when calling DateUtils.formatDateTime(), so this isn't necessary:
DateUtils tempDate = new DateUtils();
tempDate.formatDateTime(...);
You can directly call it because it's a static method:
DateUtils.formatDateTime(...);
tempDate.formatDateTime(this,tempJSONWeatherData.getLong("dt"), instead of this you can pass context of application, this refers on class FetchData
I'm doing some big refactoring operations relative to some performance improvements in an android app which is using a class with lot of static variables and even static activity references which are then use through the app ! So I was looking for some best practices in Android to store data and give to these data a global access in my app.
First I removed all the activity references to avoid any memory leak, but I'm still looking to know what is the best practice regarding static variables which need to be used anywhere in the android app.
I read many times (example1, exemple2) : using static variables is not necessary a good practices and it's better/cleaner to use one singleton class with getter and setter to have access to my global variables whatever the activity where I am. So what I've started to think is a class which could looks like this one :
public class AppSingleton extends Application {
private static AppSingleton appInstance;
// different stored data, which could be relative to some settings ..
private String setting1;
private String setting2;
private AppSingleton() {
super();
appInstance = new AppSingleton();
}
public static AppSingleton getAppInstance() {
if (appInstance == null) {
appInstance = new AppSingleton();
}
return appInstance;
}
// Getter and Setter for global access
public String getSetting1() {return setting1;}
public void setSetting1(String setting1) {this.setting1 = setting1;}
public String getSetting2() {return setting2;}
public void setSetting2(String setting2) {this.setting2 = setting2;}
}
Then I can use for example :
// Get the application instance
AppSingleton appS = (App) getApplication();
// Call a custom application method
appS.customAppMethod();
// Call a custom method in my App singleton
AppSingleton.getInstance().customAppSingletonMethod();
// Read the value of a variable in my App singleton
String var = AppSingleton.getInstance().getCustomVariable;
For me AppSingleton sounds good because this singleton which restrics ths instantiation of this class to one object, also this class is not destroyed until there are any undestroyed Activity in the application so it means I can keep my global data in the current lifecycle of my app for example from a 'Log in'. But also I can maintain the state of my global variables from my getters/setters.
But then I also had a look on the official android documentation about Performance Tips which say it's good to use static variable it's faster and don't forget to avoid internal getter and setter it's too expansive !
I'm a bit confused about all of these and I'm really keen to learn more about that topic. What is the best practices about using one class to provide an access to some variables which are needed in different part of my code ? Is the class above AppSingeleton is something which could be interesting to use in terms of architecture and performance ?
Is it a good idea to use a singleton pattern for managing global variables in android ?
those lines are completely wrong on your code:
private AppSingleton() {
super();
appInstance = new AppSingleton();
}
public static AppSingleton getAppInstance() {
if (appInstance == null) {
appInstance = new AppSingleton();
}
return appInstance;
}
you cannot instantiate new Application, the Android framework instantiates it. Change to this:
private AppSingleton() {
super();
appInstance = this; // keep ref to this application instance
}
public static AppSingleton getAppInstance() {
return appInstance;
}
Regarding the accessing of global variables. I believe it's more organized to have those singletons somewhere else on your application. The application class have different responsibilities you should not overload it with different tasks. That's OO clean coding.
Also, sometimes there's not that much reason in an Android app to have getters/setters for everything, because u don't need as much access control as in bigger projects. But this should be considered case-by-case about the necessity and not be used a general rule.
So you could for example have it like:
public class Globals {
private static final Globals instance = new Globals();
public static Globals get() { return instance; }
public String value1 = "Hello"
public int value2 = 42;
}
then on your code call as needed:
Log.d(TAG, Globals.get().value1);
Globals.get().value1 = "World";
Log.d(TAG, Globals.get().value1);
Log.d(TAG, "Value2 = " + Globals.get().value2);
I've got an Android app with custom objects which implement the Parcelable interface. They way I have it set it up is that my program initially creates an ArrayList of a custom class Products from a file in the bundle. I can see and confirm that the arraylist and it's instance variabels are populated appropriately. This class has several instance variables along with one being another ArrayList but with the String class. Remember that fact.
I am trying to pass the ArrayList<Product> into a new activity like so:
try {
Intent i = new Intent(RootActivity.this, ProductsActivity.class); //Intent from this activity to the next
i.putParcelableArrayListExtra("Products", app_products); //Puts my ArrayList<Class A> as an extra
startActivity(i); //Launch the activity
}
catch(Exception e){
Log.d("Activity Error", "Error Here:" + e.getMessage());
}
I am collecting the information back from the intent in my new activity by pulling the ArrayList out by using
app_products = getIntent().getParcelableArrayListExtra("Products");
For my custom class, it looks something like this, along with the implemented Parcelable methods.
public class Product implements Parcelable{
private String name;
private String cost;
private ArrayList<String> similarItems;
public Product{
name = null;
cost = null;
similarItems = new ArrayList<String>();
}
public Product(String name, String cost){
this();
this.name = name;
this.cost = cost;
}
public addSimilarItem(String item){
similarItems.add(item);
}
public static final Parcelable.Creator<Product> CREATOR
= new Parcelable.Creator<Product>()
{
public Product createFromParcel(Parcel in) {
return new Product(in);
}
public Product[] newArray(int size) {
return new Product[size];
}
};
public int describeContents(){
return 0;
}
private Product(Parcel in){
name = in.readString();
cost = in.readString();
similarItems = in.readArrayList(String.class.getClassLoader());
}
public void writeToParcel(Parcel out, int flags){
out.writeString(name);
out.writeString(cost);
out.writeList(similarItems);
}
}
So this works well WITHOUT my String arraylist being added in the class
Comment out out.writeList(similarItems); and also similarItems = in.readArrayList(String.class.getClassLoader());
but once you add them back in into the class, the app crashes but it doesn't even throw a message for debugging. I've wrapped everything around try-catch statements and android doesn't even report the app crashed with the normal dialog on the springboard. I am truly at a loss.
It is worth mentioning that I've used some log statements to understand where the program is crashing despite the fact that android wont throw an exception. I can see that all of the items in my ArrayList undergoes the writeToParcelMethod and completes writing. The Product(Parcel in) method is never called. Lastly, I can also see the class I am launching the new activity from enters the Pause State and my new Activity is never created.
Let me know if I can provide any additional information.
Fairly certain your problem is the use of writeList(). writeList() seems to indicate that it follows the same contract as writeValue() for the items contained in the list. readList() however, seems to indicate that the values must be Parcelable (which String is not).
Either way, typically these calls have to be very specifically linked to their inverse (e.g. writeString() must be read in with readString(), not readValue()) so you should instead use the provided methods for reading/writing String Lists:
// Takes in a List<String> which may be null
out.writeStringList(similarItems);
// Returns either null or a new ArrayList<String> with the contents
similarItems = in.createStringArrayList();
These seemed to be due to some malformed XML which my app uses as a resource. Not sure why this was this issue but after many hours of hunting it down, I was able to remove the bad XML and will revisit this issue at a later date towards when I need to release the app.
Right now, I'm just gonna worry about continuing to develop it. I'll try to remember to check back here if I find anything interesting about my XML.
I found many simple solutions to this (such as Intent.putExtra(String, String) and Bundle.putString(String, String)), but this is not helpful for my situation.
I have a class called MyMP3 which contains non-primitive types. I need to pass the following for MyMP3...
private AudioFile audioFile;
private Tag tag;
private int index;
private boolean saved, startedWithLyrics;
private String id3lyrics;
AudioFile and Tag are both classes that I imported from a .jar file. How can I go about passing these to another Activity via Intents? I tried messing with implementing Parcelable for my "MyMP3" class, but I am not sure how to correctly use these methods when not passing primitive types.
Could you help me out and look at my code below and try to tell me how to correctly use Parcelable with a custom class like mine? How do I set the Parcel in the writeToParcel function and how do I correctly retrieve the class in another Activity?
Below is my code (the part that is important, at least). I've been trying different things for a couple of days now, but I cannot get it to work. Please help me out!
public class MyMP3 extends AudioFile implements Parcelable
{
private AudioFile audioFile;
private Tag tag;
private int index;
private boolean saved, startedWithLyrics;
private String id3lyrics;
public MyMP3(File f, int index)
{
this.audioFile = AudioFileIO.read(f);
this.tag = this.audioFile.getTag();
this.index = index;
this.saved = false;
this.id3lyrics = getLyrics();
}
#Override
public int describeContents()
{
return 0;
}
#Override
public void writeToParcel(Parcel out, int flats)
{
/* This method does not work, but I do not know how else to implement it */
Object objects[] = {this.audioFile, this.tag, this.index, this.saved, this.startedWithLyrics, this.id3lyrics};
out.writeArray(objects);
}
public static final Parcelable.Creator<MyMP3> CREATOR = new Parcelable.Creator<MyMP3>()
{
public MyMP3 createFromParcel(Parcel in)
{
/* Taken from the Android Developer website */
return new MyMP3(in);
}
public MyMP3[] newArray(int size)
{
/* Taken from the Android Developer website */
return new MyMP3[size];
}
};
private MyMP3(Parcel in)
{
/* This method probable needs changed as well */
Object objects[] = in.readArray(MyMP3.class.getClassLoader());
}
}
You can make your MyMP3 class Parcelable like that. Make sure you get the read/write order correct. The non-primitives must also be Parcelable, so you might not have control over that unfortunately. Alternatively, you could come up with your own serialization/deserialization. You could use a text format, like JSON or XML. Another alternative is to use subclass Application (make sure you declare it in your manifest) and use it is as a place to hang objects that span Activities. This keeps the object in memory for the lifecycle of your app, so be careful with doing this.