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").
Related
I tried to use Realm to make a wish list in my android app. I put data from adapter to SinglePost Activity and I try to put them in realm and display in another activity.
In SinglePost Activity I got this error!
Here is my Activity
public class SinglePost extends AppCompatActivity {
String id, image, url, content, video;
Realm realm;
public Adapter adapter;
ModelPost postInfo;
private String postId;
public static final String Key_Post = "ID";
...
id = getIntent().getStringExtra("ID");
postTitle.setText(getIntent().getStringExtra("Title"));
postExpert.setText(getIntent().getStringExtra("Expert"));
image = getIntent().getStringExtra("Image");
postId = getIntent().getStringExtra("ID");
realm = Realm.getDefaultInstance();
RealmResults<ModelPost> result = realm.where(ModelPost.class).equalTo(Key_Post, postId).findAll();
if (result.size() != 0) {
bookmark.setImageResource(R.drawable.ic_bookmark_black_24dp);
} else {
bookmark.setImageResource(R.drawable.ic_bookmark_border_black_24dp);
}
Bookmark();
}
private void Bookmark() {
bookmark.setOnClickListener(v -> realm.executeTransaction(realm -> {
RealmResults<ModelPost> results = realm.where(ModelPost.class).equalTo(Key_Post, id).findAll();
if (results.size() == 0) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
}
realm.copyToRealm(postInfo);
bookmark.setImageResource(R.drawable.ic_bookmark_black_24dp);
Toast.makeText(getApplicationContext(), "Added", Toast.LENGTH_SHORT).show();
} else {
results.get(0).deleteFromRealm();
bookmark.setImageResource(R.drawable.ic_bookmark_border_black_24dp);
Toast.makeText(getApplicationContext(), "Removed", Toast.LENGTH_SHORT).show();
}
}));
}
Model:
Model Class
Config:
public class G extends Application
{
#Override
public void onCreate()
{
super.onCreate();
Realm.init(getApplicationContext());
RealmConfiguration configuration = new RealmConfiguration.Builder()
.name(Realm.DEFAULT_REALM_NAME)
.schemaVersion(0)
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(configuration);
}
}
Error:
java.lang.IllegalArgumentException: Invalid query: field 'ID' in class 'ModelPost' is of invalid type 'INTEGER'.
You are retrieving the required ID from the activity intent extras, as a string. Your model type uses an integer for the ID field. This is why the types do not match and you are getting an error.
Incidentally, you are creating two variables (postId and Id) that appear to have identical uses - are you sure you need both? I would keep one of these, but make it an int field. The rest of this answer assumes you are keeping id.
Firstly, check that this is also how you are adding the value to the intent (i.e. that you are calling intent.putExtra() with a string argument and not an integer). If it is an integer, then you can use getIntExtra() above instead of getStringExtra(), and assign directly to id. If it is indeed a string, then you need to convert to an integer as below.
String idAsString = getIntent().getStringExtra("ID");
id = Integer.valueOf(idAsString).intValue();
Then all should be good.
My office has a network library for Android written in Java which enables fast and easy multiplayer networking,
Now, I've been assigned to make the library work on Unity3D, I did my homework on Unity Plugins, AndroidJavaClass, AndroidJavaProxy, AndroidJavaRunnable etc,
So, I can get most Android/Java methods work Like this,
From Java Class -
Class MyClass{
public static Helper helperInstance;
Helper() {
helperInstance = this;
}
public static Helper GetInstance() {
if (helperInstance == null) {
helperInstance = new Helper();
}
return helperInstance;
}
public Context mContext;
public String Test(Context mContext) {
this.mContext = mContext;
Toast.makeText(mContext, "From Java With Love", Toast.LENGTH_SHORT).show();
return "This Works";
}
}
And can access the methods in the class or imported methods from C# like this -
AndroidJavaClass unityDefault = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject gameActivity = unityDefault.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject context = gameActivity.Call<AndroidJavaObject>("getApplicationContext");
AndroidJavaClass hClass = new AndroidJavaClass("com.nixonok.pluginunity.Helper");
AndroidJavaObject helperInstance = RightMeshClass.CallStatic<AndroidJavaObject>("GetInstance");
helloText.text = helperInstance.Call<string>("Test", context);
The problem occurs when I implement the listener for network state change listener
public interface StateListener {
int SUCCESS = 1;
int FAILURE = 2;
int DISABLED = 3;
int RESUME = 4;
void StateChanged(int var1, int var2);
}
public Class MyClass implements StateListener{
// Called By a Service on network event
#Override
public void meshStateChanged(MeshID meshID, int i) {
}
Helper() {
helperInstance = this;
}
public static Helper GetInstance() {
if (helperInstance == null) {
helperInstance = new Helper();
}
return helperInstance;
}
public Context mContext;
public String Test(Context mContext) {
this.mContext = mContext;
Toast.makeText(mContext, "From Java With Love", Toast.LENGTH_SHORT).show();
return "Even This doesn't Work now";
}
void init(){
nService nn = new nService(mContext, this);
// Sending context of the listener impliment to service
}
}
Now, The the plugin won't work at all, Not even the Test method,
I made an Interface callback in C# working using AndroidJavaProxy but without the implement, on the Java class, the service won't start :/
Any good idea for me to get this working without needing to change anything in the library?
Thanks in advance :)
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.
I have an activity on my app where a user can update their registered information stored in a remote database. When the update button is pressed the information in the database is being updated but the static variable is not changing. Here is my code thanks in advance for any help!
btUpdate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final String first_name = First_name.getText().toString();
final String last_name = Last_name.getText().toString();
final String email = Email.getText().toString();
Response.Listener<String> responseListener = new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonResponse = new JSONObject(response);
boolean success = jsonResponse.getBoolean("success");
if (success) {
LoginActivity.first_name = jsonResponse.getString("first_name");
LoginActivity.last_name = jsonResponse.getString("last_name");
LoginActivity.email_address = jsonResponse.getString("email");
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(UpdateInfoActivity.this);
builder.setMessage("Submission Failed")
.setNegativeButton("Retry", null)
.create()
.show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
UpdateInfoRequest updateInfoRequest = new UpdateInfoRequest(first_name, last_name, email, userID, responseListener);
RequestQueue queue = Volley.newRequestQueue(UpdateInfoActivity.this);
queue.add(updateInfoRequest);
Intent intent = new Intent(UpdateInfoActivity.this, MainActivity.class);
UpdateInfoActivity.this.startActivity(intent);
}
});
Change your code to this
if (success) {
LoginActivity.first_name = first_name;
LoginActivity.last_name = last_name;
LoginActivity.email_address = email;
}
and I wouldn't be using static variables like that if you want to have a global user profile you could do this
class User {
private static User user = null;
public String firstName = "";
private User() {}
public static synchronized User getInstance() {
if (user == null) user = new User();
return user;
}
}
to retrieve data from anywhere in project call this
String name = User.getInstance().firstName;
And to modify the data do this
User.getInstance().firstName = UserName;
First Understand that static variables are shared by all objects and methods of the class.
So we only have one instance of the static variable.
The ways to Update static variable from other class -
1.Through object.
2.Through Class name.
Enclosing the code sample.
class A{
static int val;
public A(){val=0; }
//....
}
class B{
A obj= new A();
public void updateStatic(){
obj.val=10; // updates values through object to 10
A.val=100; //updates values through class name to 100
}
//..
}
Hope it Helps
Transfer of data between activities using the static variable is not a better way in my opinion. It is bad practice. Transferring data using intents or save data in storage media and accessing from there will be the better solution.
but the static variable is not changing.
Should be... You told the code to do that
if (success) {
LoginActivity.first_name = jsonResponse.getString("first_name");
LoginActivity.last_name = jsonResponse.getString("last_name");
LoginActivity.email_address = jsonResponse.getString("email");
}
Just want to mention...
1) You update a String, not any TextView or EditText in your question, so if you expected to see a "visual change" in your app, then no, nothing will happen unless you call setText.
2) That code is wrapped in a try-catch, and could error, so check the logs for a JSONException. If those keys aren't sent back from the server, then sure, they won't update. For example, the JSON is only {"success": true }
Still, SharedPrefences should largely be preferred over static variables here.
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.