Parcleable extra not there after starting activity - java

I am trying to implement Parcleable so that I can add it as an extra (this is a requirement).
Here is a slimmed down version of the class SwinImage which only shows relevant details, namely the parts which are derived from Parcleable:
package com.example.kevin.imagemetadata;
import android.os.Parcel;
import android.os.Parcelable;
public class SwinImage implements Parcelable
{
public SwinImage(String imageName, String location, String[] keywords, String imageDate, boolean share, String email, int rating)
{
update(imageName, location, keywords, imageDate, share, email, rating);
}
//A constructor for when we havent assigned any metadata.
public SwinImage(String imageName)
{
}
public void update(String imageName, String location, String[] keywords, String imageDate, boolean share, String email, int rating)
{
}
#Override
public String toString()
{
}
private void storeImageDetails() {
}
#Override
//We don't need it - but we are forced to due to interface.
public int describeContents()
{
return 0;
}
#Override
public void writeToParcel(Parcel out, int flags)
{
out.writeString(imageName);
out.writeString(location);
out.writeStringArray(keywords);
out.writeString(imageDate);
//Can write boolean array but not boolean...
boolean[] temp = {share};
out.writeBooleanArray(temp);
out.writeString(email);
out.writeInt(rating);
}
public static final Parcelable.Creator<SwinImage> CREATOR = new Parcelable.Creator<SwinImage>()
{
#Override
public SwinImage createFromParcel(Parcel parcel)
{
return new SwinImage(parcel);
}
#Override
public SwinImage[] newArray(int i)
{
return new SwinImage[i];
}
};
//THE PRIVATE CONSTRUCTOR - FOR INTERNAL USE ONLY.
private SwinImage(Parcel parcel)
{
imageName = parcel.readString();
location = parcel.readString();
parcel.readStringArray(keywords);
imageDate = parcel.readString();
boolean[] tempArr = new boolean[1];
parcel.readBooleanArray(tempArr);
share = tempArr[0];
email = parcel.readString();
rating = parcel.readInt();
}
}
Ok... So, in the calling class, I do this:
public void ClickedImage(View v)
{
Intent i = new Intent(this, MetaDataActivity.class);
switch (getResources().getResourceEntryName(v.getId()))
{
case "burgerView":
i.putExtra("SENT_IMAGE", burger); //IT IMPLEMENTS PARCLEABLE THEREFORE WE CAN PASS IT WHERE IT LOOKS FOR A PARCEL
break;
case "pitaView":
i.putExtra("SENT_IMAGE", pita);
break;
case "pizzaView":
i.putExtra("SENT_IMAGE", pizza);
break;
case "steakView":
i.putExtra("SENT_IMAGE", steak);
break;
}
startActivityForResult(i, GET_AND_SET_METADATA_REQUEST);
}
I set a breakpoint at the end, and can indeed see that it is in the intent correctly:
The Intent showing an Extra
However, when it comes to getting it:
#Override
protected void onCreate(Bundle savedInstanceState)
{
Log.d("Does", "Does this work");
Intent i;
i = getIntent();
image = i.getExtras().getParcelable("SENT_IMAGE");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_meta_data);
}
It is never there. The assignment to image crashes the program, and I cannot see through the debugger my SwinImage even existing inside the intent.
What am I doing wrong? Why is it crashing? I have tried for hours solutions (mainly changing how I'm assigning - and some "solutions" have stopped it from crashing, but image is assigned a null)
Can anyone provide some insight? Thanks.
Edit:
I tried moving the getIntent to after onCreate to no avail.
Edit #2: Stack trace:
http://pastebin.com/raw/XJbPxHRv
and types of steak etc:
public class MainActivity extends AppCompatActivity {
static final int GET_AND_SET_METADATA_REQUEST = 1; // The request code
SwinImage burger, pita, pizza, steak;

In your SwinImage constructor that takes a Parcel as argument, you do this:
parcel.readStringArray(keywords);
This fails because it attempts to read an array of String into keywords, which is null because you did not initialize it first.
When you call readStringArray(), you pass it a reference to an existing array of String, into which it will copy the array of String from the Parcel.
Note the readStringArray() and writeStringArray() will only work correctly if the array reference that you pass to writeStringArray() is not null and if the number of elements in the array you pass to readStringArray() is exactly the same as the number of elements that you have in the array you pass to writeStringArray().
Instead of readStringArray() you should probably use createStringArray() which doesn't have these restrictions. Use it like this:
keywords = parcel.createStringArray();

You can't call getIntent() before super.onCreate() - no Intent is available at that point.
Move your super call before the getIntent call.
UPDATE:
After the above fix, your stack trace shows the issue:
NullPointerException: Attempt to get length of null array
at android.os.Parcel.readStringArray(Parcel.java:1026)
at com.example.kevin.imagemetadata.SwinImage.<init>(SwinImage.java:108)
It seems keywords is null, so attempting to read it from the parcel crashes.
Either make sure keywords is never null, or put a boolean that says if keywords can be read or not.
Also, per this: How to use writeStringArray() and readStringArray() in a Parcel you should probably use createStringArray instead of readStringArray, a lot simpler.
A general note: you should always read your crashes stack traces carefully to better understand why your code isn't working as expected, usually it's the best way to fix broken code.

Related

How onLoadFinished() accept data from loadInBackground() when return Object?

i have this code :
public class WeatherLoader extends AsyncTaskLoader {
/** Tag for log messages */
private static final String LOG_TAG = WeatherLoader.class.getName();
private String mUrl;
private int mDataWeatherType;
public WeatherLoader(Context context, String url , int dataWeatherType) {
super(context);
mUrl = url;
mDataWeatherType = dataWeatherType;
}
#Override
public Object loadInBackground() {
Log.i(LOG_TAG , "TEST : loadInBackground() called ...");
if(mUrl == null){
return null;
}
if( mDataWeatherType == 1) {
CurrentWeather currentWeather = QueryUtils.fetchCurrentWeatherData(mUrl);
return currentWeather;
}else if(mDataWeatherType == 2) {
List<HourForecast> hourlyForecastsList = QueryUtils.fetchHourlyForecastsData(mUrl);
return hourlyForecastsList;
}else {
List<DayForecast> dailyForecastsList= QueryUtils.fetchDailyForecastsData(mUrl);
return dailyForecastsList;
}
}
}
in the main activity :
#Override
public Loader<List<HourForecast>> onCreateLoader(int id, Bundle args) {
return new WeatherLoader(this,mUrl,HOURLY_FORECASTS);
}
#Override
public void onLoadFinished(Loader<List<HourForecast>> loader, List<HourForecast> data) {
mHourForecastAdapter.clear();
mHourForecastAdapter.addAll(data);
}
#Override
public void onLoaderReset(Loader<List<HourForecast>> loader) {
mHourForecastAdapter.clear();
}
in the AsyncTaskLoader i do not specify generic type, and in the LoaderManager.LoaderCallbacks<List<HourForecast>> i specify generic type,
the code work correctly.
Could someone explain me how the result of loadInBackground gets passed on to onLoadFinished? I'm asking this as loadInBackground returns an Object and onLoadFinished accepts a List<HourForecast> and not an Object.
In java using generics removes the need for cast by the programmer and object in java can be anything, since its OOP every class extends Object by default.
In you case AsyncTaskLoader has a generic that extends Loader. If you do not specify the object with generic, the return object is Object.
Which means in the method
Loader<List<HourForecast>> onCreateLoader(int id, Bundle args) {
return new WeatherLoader(this,mUrl,HOURLY_FORECASTS);
You already are creating WeatherLoader you cast it to Loader (which is superclass of AsyncTaskLoader) And you cast it to Loader<List<HourForecast>> there for you get you list when you call
#Override
public Object loadInBackground()
However, this is a very bad example of generics you have there. Generics are made to eliminate runtime errors, and your example just makes more places to have a runtime error. And Please don't use AsyncTasks :) They are the evil. Read some basic books on android programming, it teaches you to use handlers. The ultimate solution to your threading would be RxJava, but its more for advanced programmers.

Android java app breaks if I attempt to initialise item in object array

I have declared an array of objects in the main activity of my Android app. I initialise the array (so far no problem) but as soon as I attempt to initialise one of the elements, my app breaks.
Here is the code from the main activity:
private cClubMemberDetails[] PlayerNames;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PlayerNames = new cClubMemberDetails[4];
PlayerNames[0] = new cClubMemberDetails();
It's that last line, setting PlayerNames[0], that causes the problem. If I comment out this line, the app works. If I leave it in, the app crashes before showing its opening screen (and long before I make any further use of the PlayerNames array). It's clearly something specific to the cClubMemberDetails class. I can initialise arrays of other types of object no problem. But the class is as simple as can be. Here it is in its entirety:
package uk.org.writerman.scoresheet;
/**
* Created by keith on 18/07/2016.
*/
public class cClubMemberDetails {
private int m_ClubNumber;
private String m_MemberName;
private int m_EBUNumber;
public cClubMemberDetails() {
m_ClubNumber = -1;
m_MemberName = "";
m_EBUNumber = 0;
}
public cClubMemberDetails(int ClubNumber, String MemberName, int EBUNo) {
setTo(ClubNumber, MemberName, EBUNo);
}
public int getClubNumber() { return m_ClubNumber; }
public String getMemberName() { return m_MemberName; }
public int getEBUNumber() { return m_EBUNumber; }
// public void setClubNumber(int ClubNumber) { m_ClubNumber = ClubNumber; }
// public void setMemberName(String MemberName) { m_MemberName = MemberName; }
public void setTo(int ClubNumber, String MemberName, int EBUNumber) {
m_ClubNumber = ClubNumber;
m_MemberName = MemberName;
m_EBUNumber = EBUNumber;
}
public boolean collatesAfter(cClubMemberDetails Another) {
return (m_MemberName.compareToIgnoreCase(Another.getMemberName()) > 0);
}
public boolean collatesBefore(cClubMemberDetails Another) {
return (m_MemberName.compareToIgnoreCase(Another.getMemberName()) < 0);
}
}
Any suggestions what could be going on? I'm at my wits' end (but maybe my wits don't amount to much)
Keith
Thanks for your input. Logcat revealed the problem and it was nothing to do with the code as posted. The program crashed in onSaveInstanceState when writing the contents of my array to the output bundle. Of course, no entries in the array = no opportunity for onSaveInstanceState to go wrong saving them. Doh!
Thanks again. I am slowly getting the hang of this Android stuff and the dev environment.
Keith

How can I add a value to an ArrayList from an inner method?

I am currently trying to add a value to an ArrayList object from a method inside of another class.
Here is the class I have created for the ArrayList Object:
public class ArrayClass {
public static ArrayList<String> array = new ArrayList<>();
public static void add_val(String s){
array.add(s);
}
public static int get_size(){
return array.size();
}
public static String get_val(int i){
return array.get(i);
}
}
And the other class where I attempt to edit the ArrayList object:
ArrayClass fill = new ArrayClass();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_explore);
Response.Listener<String> responseListener4 = new Response.Listener<String>(){
#Override
public void onResponse(String response) {
try {
JSONObject jsonResponse4 = new JSONObject(response);
boolean success = jsonResponse4.getBoolean("success");
if (success){
int l;
String filled;
int length4 = jsonResponse4.length();
for (l=0;l<length4;l++){
filled = jsonResponse4.getString(l+"");
fill.add_val(filled);
}
}else{
AlertDialog.Builder builder = new AlertDialog.Builder(ExploreActivity.this);
builder.setMessage("Could not retrieve restaurant tables filled")
.setNegativeButton("Retry", null)
.create()
.show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
FilledRequest filledRequest = new FilledRequest(responseListener4);
RequestQueue queue4 = Volley.newRequestQueue(ExploreActivity.this);
queue4.add(filledRequest);
If you look in the onResponse method, you can see the attempt to add a value from the jsonResponse into the ArrayClass object. However, when I launch my app, it does not add the value into the object. I'm used to python global variables and not having to deal with the semantics of java, so if you could shed some light on what changes need to be made, I would greatly appreciate it.
Apart from other given answers/solutions to the issue you are facing, let me share a best and optimized way to implement JSON parsing in Android.
I would suggest you to check GSON or Jackson libraries which provides Java serialization/deserialization that can convert Java Objects into JSON and back.
There are some benefits it does provide, one of the main benefits is you do not need to implement parsing manually and less chances of mistakes in implementing parsing, like you may make a mistake in mentioning key "Success" or "success" or any such silly mistakes!
Firstly, since your variable is static, and the methods are static too, you don't have to instantiate the object. You could do something like this:
ArrayClass.add_val("Hello");
But if you want to instantiate then you can do this:
public class ArrayClass {
private ArrayList<String> array;
public ArrayClass() {
array = new ArrayList<>();
}
public void add_val(String s){
array.add(s);
}
public int get_size(){
return array.size();
}
public String get_val(int i){
return array.get(i);
}
}
To make sure the values are filled in, you can check the array size like this:
for (l=0;l<length4;l++){
filled = jsonResponse4.getString(l+"");
fill.add_val(filled);
}
Log.d("TEST", String.valueOf(fill.get_size());
Remove all cases of the static keyword in ArrayClass. Static methods are class level methods, ie. are called on the class itself, rather than an instance of the class.
You can also try this, for ArrayList:
First do some changes in your ArrayClass. Use get And Set method to access your array.
public class ArrayClass {
private ArrayList<String> array = new ArrayList<>();
public ArrayList<String> getArray() {
return array;
}
public void setArray(ArrayList<String> array) {
this.array = array;
}
}
And your other class where you attempt to edit the ArrayList use getArray And SetArray method and some predefined method of ArrayList like this:
Store the data in ArrayList:
for (l=0;l<length4;l++){
filled = jsonResponse4.getString(l+"");
fill.getArray().add(filled);
}
Get Size of ArrayList:
fill.getArray().size();
And also you can store an another ArrayList like
ArrayList<String> tempArrayList = new ArrayList<String>();
tempArrayList.add("string 1");
tempArrayList.add("string 2");
tempArrayList.add("string 3");
tempArrayList.add("string 4");
fill.setArray(tempArrayList)

How to get data stored in ListArray

I want to store my API result in a Array List, need to store, ID and ImageURL.
I am able to store the data using my class ImgModel. But I can't figureout how to access it later on.
public class ImgModel{
private String url, id;
public ImgModel(String id, String url) {
this.id = id;
this.url = url;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getId() {
return id;
}
public void setId(String photoId) {
this.id = photoId;
}
}
in MainActivity I call the API
public class MainActivity ....{
...
List<ImgModel> photosList = new ArrayList<ImgModel>();
....
//>>in the result API... after parse the json
String id = imgOgj.getString("id");
String url = imgOgj.getString("url");
ImgModelp p = new ImgModel(id, url);
photosList.add(p); //THIS WORKS
}
This Part I don't know how to implement - pls help
Now in the ImagePreview Activity I want to access these images and Id to display in Image view.
public class ImagePreviewActivity ....{
//List<ImgModel> mProcessedImg= new ArrayList<ImgModel>(); //If I do this, that means I am creating a new list, and not accessing the store data right ?
ProcessedImg mProcessedImg;
ImageView mImageView;
onCreate{
....
mProcessedImg.size(); //Get the size .i.e how make images url
mImageView.setImage(mProcessedImg.getUrl(0);//sample how can I get the url of position 0 ?
}
}
The photosList variable that you have declared in MainActivity is a local variable, which means that its scope is limited to only the code block in which it has been declared. This is the reason that you cannot access the data you have stored in that variable elsewhere in your code.
In order to use and access that same variable again outside of the code block in which it was declared you could instead use an instance variable of the MainActivity class, by amending your class declaration as follows:
public class MainActivity extends Activity {
List<ImgModel> mPhotosList;
...
// Override the OnCreate method of Activity
#Override
public void onCreate(Bundle savedInstanceState) {
// Create the mPhotosList instance variable
mPhotosList = new ArrayList<ImgModel>;
...
}
// other methods where you call the API and store the data in mPhotosList
...
}
These pages may help to explain the differences between the types of variables that you can use in Java:
what is the difference between local and instance variables in Java
http://www.cs.umd.edu/~clin/MoreJava/Objects/local.html
In terms of the next part of your problem, to access the mPhotosList member variable from another Activity, the following post may help:
Passing a Bundle on startActivity()?
If you neeed to share a list between lots of activities,putting it into MyApp's instance maybe a solution.
Create constructor inside ImagePreviewActivity.class which allows one List parameter.
public class ImagePreviewActivity ....{
List<ImgModel> imgList;
ImageView mImageView;
public ImagePreviewActivity(List<ImgModel> imageList){
this.imgList = imageList;
}
onCreate{
mImageView.setImage(imageList.get(0).getUrl();
}
}
Creating a object of ImagePreviewActivity.class
public class MainActivity ....{
...
List<ImgModel> photosList = new ArrayList<ImgModel>();
....
String id = imgOgj.getString("id");
String url = imgOgj.getString("url");
ImgModelp p = new ImgModel(id, url);
photosList.add(p);
//Craeate Object of ImagePreviewActivity
ImagePreviewActivity ipa = new ImagePreviewActivity(photosList);
}

Get and sets in Java (Newbie programmer here)

I'm trying to create a guitar program. I have a guitar amp class that has a sound object I'm trying to access in my pickup class. My goal is to set the sound property in my pickup class the same as my sound property in the GuitarAmp class so I can then set all the sound properties of my strings. I'm not really sure how to go about doing this and the articles I've read about gets and sets in Java have been no help. I have my two classes listed below, any help with the get and set would be appreciated.
public class GuitarAmp
{
// This is what I want to get from this class
public GuitarAmpSound sound;
public GuitarAmp(GuitarAmpSound sound, int volume, int distortSetting) {
sound = new GuitarAmpSound();
sound.setVolume(64);
sound.setDistortSetting(GuitarAmpSound.JAZZ);
}
public void changeVolume(int newVolume)
{
sound.setVolume(newVolume);
}
}
Here is the pickup class.
public class GuitarPickup {
public GuitarAmpSound pickupSound;
public void Connect(GuitarString strings[], GuitarAmp amp)
{
pickupSound = new GuitarAmpSound();
//This is where I need to set it
for(int i = 1; i <= 6; i++)
{
strings[i].setSound(pickupSound);
}
}
}
You need to declare a field (a variable belonging to a particular instance of a class) to hold each piece of information for that object. The pickupSound field on GuitarPickup is an example.
It is strong convention in Java to use the same name for the field as for the getters and setters. In the case of your volume, for example, the relevant part of the code would look like this:
public class GuitarAmpSound {
private int sound = 0;
public void setSound(int sound) {
this.sound = sound; // "this.sound" means the sound field, not the parameter
}
public int getSound() {
return sound; // or this.sound
}
}
If you want to implement the necessary code for your for loop, your GuitarString class needs a GuitarAmpSound field of its own named sound and a corresponding getter and setter.
As a note, the conditions in your for loop have a number of problems. Arrays in Java are zero-based (so the strings on a 6-string guitar would go from 0 to 5), and you should never hard-code array sizes in a loop but should use strings.length instead. Finally, Java has a more convenient syntax if you just want to retrieve each element in an array (or collection):
for(GuitarString string : strings) {
string.setSound(pickupSound);
}
Your code makes no sense, replace the GuitarAmp class by this:
public class GuitarAmp {
//This is what I want to get from this class
private GuitarAmpSound sound;
public GuitarAmp() {
sound = new GuitarAmpSound();
sound.setVolume(64);
sound.setDistortSetting(GuitarAmpSound.JAZZ);
}
public void changeVolume(int newVolume){
sound.setVolume(newVolume);
}
public GuitarAmpSound getSound() {
return sound;
}
public void setSound(Sound sound) {
this.sound = sound;
}
}
for get and set the rule is simple:
public YourClassName getYourObjectName() {
return yourObjectName;
}
public void setYourObjectName(YourClassName yourObjectName) {
this.yourObjectName = yourObjectName;
}
it's really complicated understand what you really wanna..take a look some change..but
I think you need to reestructure the objects. what you wanna do? can you explain better?
public class GuitarAmp {
//This is what I want to get from this class
public GuitarAmpSound sound;
--> the sound you pass before call the constructor
sound.setVoolume(x);
sound.setDistortSetting(Y)
so you pass the object sound with the attributes full of information
public GuitarAmp(GuitarAmpSound sound){
this.sound = sound;
}
public void changeVolume(int newVolume){
this.sound.setVolume(newVolume);
}
}
Here is the pickup class.
public class GuitarPickup {
public GuitarAmpSound pickupSound;
public void Connect(GuitarString strings[], GuitarAmp amp)
{
pickupSound = new GuitarAmpSound();
//This is where I need to set it
for(int i = 0; i<strings.length; i++)
{
amp.setSound(strings[i].getSound());
}
}
}
Conceptually, there is little point in saving the same Sound value to each GuitarString.

Categories

Resources