This question already has answers here:
How to pass an object from one activity to another on Android
(35 answers)
Closed 5 years ago.
I am trying to pass information (more specifically a class with information) from one activity to another. In my application I have a splash screen that is responsible for loading and initializing variables. The goal is to get this information to the actual game itself so it may be used but I don't know how to do so. In my splash screen class I have this method that is responsible for moving from the splash screen to the game once everything is loaded:
private void moveToGame() {
loop.setRunning(false);
act.startActivity(new Intent(splash, MainActivity.class));
act.finish();
return;
}
The main activity class then has this line of code to get to the actual game:
setContentView(new Environment(this, this));
The constructor for the Environment class is Environment(Context context, Activity act)
The goal is to change the constuctor to Environment(Context context, ActivityAct, LoadInfo li) but how do I pass the information all the way to the Environment constructor?
EDIT 1 - LoadInfo Class
public class LoadInfo {
private HashMap<String, Typeface> fonts;
private HashMap<String, Image> images;
private File logFile;
private File settingsFile;
private File gameDir;
public LoadInfo() {}
public LoadInfo(HashMap<String, Typeface> fonts, HashMap<String, Image> images, File logFile, File settingsFile, File gameDir) {
this.fonts = fonts;
this.images = images;
this.logFile = logFile;
this.settingsFile = settingsFile;
this.gameDir = gameDir;
}
public HashMap<String, Typeface> getFonts() {
return fonts;
}
public HashMap<String, Image> getImages() {
return images;
}
public File getLogFile() {
return logFile;
}
public File getSettingsFile() {
return settingsFile;
}
public File getGameDir() {
return gameDir;
}
public void setFonts(HashMap<String, Typeface> fonts) {
this.fonts = fonts;
}
public void setImages(HashMap<String, Image> images) {
this.images = images;
}
public void setLogFile(File logFile) {
this.logFile = logFile;
}
public void setGameDir(File gameDir) {
this.gameDir = gameDir;
}
public void setSettingsFile(File settingsFile) {
this.settingsFile = settingsFile;
}
public boolean fullyLoaded() {
return fonts != null && images != null && logFile != null && gameDir != null && settingsFile != null;
}
public String toString() {
if(logFile == null)
return "well no file to load";
return logFile.toString();
}
}
You can make your LoadInfo as Serializable like below,
public class LoadInfo implements Serializable {
// your code,
}
and in you Splash Activity you can send like this,
//LoadInfo loadInfo = new LoadInfo(); this may be your loadInfo object
Intent intent = new Intent(act, MainActivity.class);
intent.putExtra("load_info", loadInfo); // Add your LoadInfo object here
act.startActivity(intent);
In your MainActvity you can get like this,
LoadInfo loadInfo = (LoadInfo) getIntent().getSerializableExtra("load_info");
setContentView(new Environment(this, this, loadInfo));
Warning: Intent extra has a limit of 1Mb:
To pass information from one Activity to another it's normal to use Intent extra, but it has a limitation of 1MB of data. In your question, you are using LoadInfo class and I believe it can easily pass 1MB in size because it loads Game information.
Suggestion: You can choose to implement Application or Service (i.e. Bound Service) to store this instance of LoadInfo and all your activities can access this instance.
More Tip: You can also use a Singleton class that stores this instance ofLoaderInfo, but you should remove it after closing all activities of your game.
Obs: Splash Screen as an Activity, you must remember to remove it from Back Stack, otherwise, when the user clicks back, it will return to the splash Activity.
Related
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.
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
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);
}
I have an XML which contains an array of books. The array will contain url to download bimpat image. book id and book name.
e.g.
<result>
<books>
<book>
<bookId>101</bookId>
<bookname>java</bookname>
<bookurl><http://bookimage.jpg>
</book>
<book>
<bookId>102</bookId>
<bookname>c++</bookname>
<bookurl><http://bookimage.jpg>
</book>
<book>
<bookId>103</bookId>
<bookname>.net</bookname>
<bookurl><http://bookimage.jpg>
</book>
</books>
The above xml is downloaded and stored in array and individual image is downloaded from each URL. Mean while book data is set in activity.
If the image download is done serially it takes a lot of time. If done parallely , i do not come to know which image corresponds to which book.
public class myasync extends asynctask
{
doInBackground(String url)
{
Bitmap bitmap = getfilefromurl;
return bitmap;
}
}
public class activity1 extends Activity
{
OnCreate()
{
myasync myasyncobj = new myasync(this);
myasyncobj .execute("http://bookimage.jpg");
}
}
The image is returned to activity. But it is not known to which book id it correspond.
EDIT: I am planning to use hashmap to return URL and bitmap. But returning hashmap containing only one key value pair, might not be a good idea. Is there any construct for storing one string and bitmap?
EDIT2: I am sharing activity object. Can create an interface also.
Interface.setBitmap() , Interface.setURL(). But creating new interface for sharing one bitmap and one url, will be good programming practice?
EDIT 3: The solution I have used is to create an class for storing bitmap and url. This will be called from OnPostExecute.
class BitmapURLMap
{
Bitmap bmp;
String URL;
}
Class Book
{
String URL;
String BookID;
String IconImage;
//getter and setter methods.
}
Class model //model for mvp
{
ArrayList<Book> arrBook = null;
public void setBookArr(ArrayList<Book> arrBook )
{
this.arrBook = arrBook ;
}
public ArrayList<Book> arrBook getBookArr( )
{
return arrBook ;
}
}
public class myasync extends asynctask
{
Activity activity;
public myasync(Activity activity)
{
this.activity = activity;
}
doInBackground(String url)
{
Bitmap bitmap = getfilefromurl;
BitmapURLMap bmpURL = new BitmapURLMap(bitmap,url);
return bmp;
}
OnPostExecute()
{
activity.setMap(bmpURL);
}
public class activity1 extends Activity
{
OnCreate()
{
myasync myasyncobj = new myasync(this);
myasyncobj .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"http://bookimage.jpg");
}
setMap(BitmapURLMap bmpURL)
{
ArrayList<Book> arrBook= model.getBookArr();
for(Book book : arrBook)
String URL = book.getURL();
if(URL.equals( bmpURL.URL))
{
book.setIconImage(bmpURL.bmp);
break;
}
model.setBookArr(arrBook);
}
}
But I am sure this solution is not the right way to do it. Though it works.
Can someone tell me how to implement it the right way.
This question already has answers here:
How do I pass data between Activities in Android application?
(53 answers)
Closed 3 months ago.
I am making a card game and I have an activity for discarding cards and an activity for showing the scores. The problem is I want to pass some objects (player and dealer hands) to the other activity so that I can set imageViews in the scores to the cards that are in the players hands. How can I do this? I don't care about security or anything I just want the easiest way.
Using bundles inside the intent isn't about security, it's because the Android guys made it that way plain and simple. In my opinion using bundles and intents to pass larger objects is not a good idea. it gets too complicated to implement, makes you get the object down to the primitives (when using parcelable) and also makes a copy on the other side in memory (you take one object, set everything inside the intent and then re-create it on the other side making a new copy out of it) which for objects that have a bigger memory footprint isn't good.
I would suggest:
either using a singleton store
Using the application class (which also acts like a singleton)
I am often using a singleton which has a hashMap inside where an integer key is generated by me (from atomic Integer) and an object placed inside the map. You just send the ID inside the intent as an extra and retrieve it on the other side by getting the key from the intent and accessing your singleton to retrieve and remove the object (from that map) and use it in your new activity/service.
Here is a sample of something like this:
(Note: this is a part from my lib for rest requests (https://github.com/darko1002001/android-rest-client) in case you want to see more details on how everything is implemented). in your case you will need to strip some of the code and replace it with your own, but the general idea is the same.
/**
* #author Darko.Grozdanovski
*/
public class HttpRequestStore {
public static final String TAG = HttpRequestStore.class.getSimpleName();
public static final String KEY_ID = "id";
public static final String IS_SUCCESSFUL = "isSuccessful";
private static final HashMap<Integer, RequestWrapper> map = new HashMap<Integer, RequestWrapper>();
private final AtomicInteger counter = new AtomicInteger();
private static Class<?> executorServiceClass = HTTPRequestExecutorService.class;
private final Context context;
private static HttpRequestStore instance;
private HttpRequestStore(final Context context) {
this.context = context;
}
public static HttpRequestStore getInstance(final Context context) {
if (instance == null) {
instance = new HttpRequestStore(context.getApplicationContext());
}
return instance;
}
public static void init(final Class<?> executorServiceClass) {
HttpRequestStore.executorServiceClass = executorServiceClass;
}
public Integer addRequest(final RequestWrapper block) {
return addRequest(counter.incrementAndGet(), block);
}
public Integer addRequest(final Integer id, final RequestWrapper block) {
map.put(id, block);
return id;
}
public void removeBlock(final Integer id) {
map.remove(id);
}
public RequestWrapper getRequest(final Integer id) {
return map.remove(id);
}
public RequestWrapper getRequest(final Intent intent) {
final Bundle extras = intent.getExtras();
if (extras == null || extras.containsKey(KEY_ID) == false) {
throw new RuntimeException("Intent Must be Filled with ID of the block");
}
final int id = extras.getInt(KEY_ID);
return getRequest(id);
}
public Integer launchServiceIntent(final HttpRequest block) {
return launchServiceIntent(block, null);
}
public Integer launchServiceIntent(final HttpRequest block, RequestOptions options) {
if (executorServiceClass == null) {
throw new RuntimeException("Initialize the Executor service class in a class extending application");
}
if (isServiceAvailable() == false) {
throw new RuntimeException("Declare the " + executorServiceClass.getSimpleName() + " in your manifest");
}
final Intent service = new Intent(context, executorServiceClass);
final RequestWrapper wrapper = new RequestWrapper(block, options);
final Integer requestId = addRequest(wrapper);
service.putExtra(KEY_ID, requestId);
context.startService(service);
return requestId;
}
public boolean isServiceAvailable() {
final PackageManager packageManager = context.getPackageManager();
final Intent intent = new Intent(context, executorServiceClass);
final List<ResolveInfo> resolveInfo = packageManager.queryIntentServices(intent,
PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfo.size() > 0) {
return true;
}
return false;
}
}
You can use Bundle to share variables in other activities. If you want to pass your own class object in other activities use Parcelable to your class
Here's an example
public class Person implements Parcelable {
private int age;
private String name;
// Setters and Getters
// ....
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeInt(age);
}
public static final Parcelable.Creator<Person> CREATOR
= new Parcelable.Creator<Person>() {
public Person createFromParcel(Parcel in) {
return new Person(in);
}
public Person[] newArray(int size) {
return new Person[size];
}
};
private Person(Parcel in) {
name = in.readString();
age = in.readInt();
}
}
Insert your Person object in bundle
Intent i = new Intent();
Bundle b = new Bundle();
b.putParcelable("bob", new Person());
Getting Person object
Intent i = getIntent();
Bundle b = i.getExtras();
Person p = (Person) b.getParcelable("bob");
You can use either of Bundles or shared preferences for share variable or save variables for future use.
Example for shared preferences you can find here
Example for bundles you can find here
Singleton will be the best approach
You can use intent extras,
Intent intent = new Intent(getBaseContext(), NewActivity.class);
intent.putExtra("DATA_KEY", data);
startActivity(intent)
The docs for Intents has more information (look at the section titled "Extras").