Best practices of transferring data from service to activity - android

What is the best practice for transfer of some (not huge, but often) amount of data from several services to activity?
I'm using BroadcastReceiver in my MainActivity this way:
private class Receiver extends BroadcastReceiver{
public Receiver() {
super();
}
#Override
public void onReceive(Context context, Intent intent) {
// Receiving and showing received symbols
if (intent.getAction() == getString(R.string.key_recognized)) {
char key = intent.getCharExtra("key" , '0');
MainActivity.this.addText(key);
}
// Received transfer ending flag
if (intent.getAction() == getString(R.string.transfer_ended)) {
mServiceWorking = false;
MainActivity.this.mToneButton.setText(getText(R.string.send_text));
}
// Recieving an array (~2Kb) and drawing it on correspondig ImageView
if (intent.getAction() == getString(R.string.spectrum_ready)) {
Spectrum spectrum = (Spectrum) intent.getSerializableExtra("spectrum");
if (spectrum != null) {
drawSpectrum(spectrum);
}
}
}
}
Broadcasts are sended from services somehow like this:
Intent taskIntent = new Intent();
taskIntent.setAction(getString(R.string.key_recognized));
taskIntent.putExtra("key", key);
this.sendBroadcast(taskIntent);
Is this normal code or my hands should be cut off in some way?)

I don't see why passing the data via extras is not the best choice. For me, is the safest, and fastest way to pass data between activities or intents.

You can simple use post method from Handler class

For normal data up to a maximum of 1MB (see this answer) you can use extra data of intents.
For bigger data, or data that is not serializable, you could store that data in one of following:
In a file. Pass URI in extra data.
In the shared preferences.
In a special class that holds your data. Store it in a hashmap, where you pass the key in your extra data of the intent.
The class could look like this:
private static final Map<Long, Object> storage = new HashMap<>();
public static synchronized long store(Object tempData){
if(storage.size() < Long.MAX_VALUE) {
storage.put(storage.size() + 1l, tempData);
return storage.size();
}else{
return -1;
}
}
The store()-Method returns a long value, that can be used to identify the stored data. This way is faster than storing your data in a file and can be bigger than 1MB. But keep in mind, that you have limited memory in android, so don't store to many or too big data. Remove them as soon as you retrieved them.

Related

Check if activity is active, and if active pass data from service to activity?

I have a service called EventReceivingSevice which will get new data in onDataRefresh(JSONObject data) function.
private void onNewData(JSONData data) {
boolean isActive=isActivityActive(); //Check if activity is active
if(isACtive)
passData(data);
else
storeData(data);
}
An activity called HomeActivity will display the data. When EventReceivingService will get new data, it has to be checked if HomeActivity is active, it has to pass the data to HomeActivity, and if not it will store the data somewhere so that HomeActivity will later use that.
The data is in JSON format.
How can achieve this?
You can't reliably determine if an Activity is active. What you should do is to store the data somewhere (file, SQLite database, etc.), and then send a broadcast Intent that means "new data is available". Your Activity can register a listener that will get triggered by that broadcast Intent if the Activity is alive when the broadcast Intent is sent. It can then pick up the data from wherever you put it and do whatever you want with it.
There is a simple method but it doesn't require JSON data. you can just add data as public static data.
public static int GRID_COUNT = 2;
If you are using that data for reading purposes, you can do it like this.
public static final int GRID_COUNT = 2;

for Android is it a good idea to pass a list of object from one Activity to another ?

Is it a good idea to pass a list of object from one Activity to another for Android ?
It is quiet troublesome to pass a list of object in an intent, and I wonder whether it affect the performance if the object list is too large or the object is too complicated.
Is it a better solution to get the list of object from other place, for example, query the DB once more , or save the list of object in a temporary class and fetch it in new Activity?
As long as you are passing Parcelable objects' list, nothing's wrong when passing through Intent. Regarding performance, that is up to you and the amount of data you pass.
As per my experience, If you are passing data up to 1MB of size, it should be fine. Anything above that will fail as this seems to be the limit. Read this.
Besides, you are welcome to use preferences, SQLite, files or static-object-referencing methodologies to fetch your data anytime and anywhere.
Solution1 : Use Intent
Send :
Intent data = new Intent(Activity1.this,Activity2.class);
data.putParcelableArrayListExtra(Constant.LIST_OBJECT,
(ArrayList<? extends Parcelable>) getObjects());
receive :
List<YOUR_OBJECT> objects = data.getParcelableArrayListExtra(Constant.LIST_OBJECT);
Solution2 :
public class SessionData {
private static SessionData instance;
private final List< YOUR_OBJECT > listSessionObjects;
private SessionData() {
listSessionObjects = new ArrayList<>();
}
public static final SessionData getInstance() {
if (instance == null) {
instance = new SessionData();
}
return instance;
}
public List<YOUR_OBJECT> getListSessionObjects() {
return listSessionObjects;
}
public void setListSessionObjects(List<YOUR_OBJECT > objects) {
listSessionObjects = objects
}
}
to use it :
SessionData.getInstance().getListSessionObjects();
SessionData.getInstance(). setListSessionObjects(objects);

Saving data upon closing app and retrieving that data

I know, there are plenty of questions in regards to saving/retrieving data on here. I was doing find looking things up on my own and really thought I could manage to find my answers without having to "ask a question", but I began to wonder something that I haven't seen an answer for on here.
MY SITUATION:
Naturally, I'm making an app. Upon closing the app, I want to save a simple array of numbers (0 or 1) or boolean values as it were. Upon starting the app, I want to search for that array, if it exists, and retrieve it for use within the app.
I began placing my code into the activity in which the array would be used. But, I started wondering if I would have to copy/paste the overridden onStop() function into all of my activities? Or do I do it in the main activity and somehow link the other activities.
Basically, no matter what state/activity the app is currently on when the app is closed, I want to be able to save the array of int/bool and open it back up when the app is started.
Maybe I didn't know how to search for what I wanted, so explaining it felt like the right thing to do.
I don't mind doing more searching, but if someone would point me in the right direction at the very least, I'd be extremely grateful.
EDIT: If there's a better way to do what I want than what I described (i.e. using a different state instead of onStop(), for instance), please feel free to throw out ideas. This is my first time actually having to deal with the activities' lifecycles and I'm a bit confused even after looking through the android development tutorials. I really think they're poorly done in most cases.
When you application needs to save some persistent data you should always do it in onPause() method and rather than onStop(). Because if android OS kills your process then onStop() and onDestroy() methods are never called. Similarly retrieve data in onResume() method.
Looking at the purpose you want to fulfill, SharedPreferences is all you want.
The documentation states:
"SharePreferences provides a general framework that allows you to save
and retrieve persistent key-value pairs of primitive data types. You
can use SharedPreferences to save any primitive data: booleans,
floats, ints, longs, and strings. This data will persist across user
sessions (even if your application is killed)."
Use SharedPreference to store small amount of data or use SQLite to store large amount of data.
See this link
http://developer.android.com/guide/topics/data/data-storage.html
Serialize an object and pass it around which is more dependable than shared preferences (had lots of trouble with consistency with shared preferences):
public class SharedVariables {
public static <S extends Serializable> void writeObject(
final Context context, String key, S serializableObject) {
ObjectOutputStream objectOut = null;
try {
FileOutputStream fileOut = context.getApplicationContext().openFileOutput(key, Activity.MODE_PRIVATE);
objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(serializableObject);
fileOut.getFD().sync();
} catch (IOException e) {
Log.e("SharedVariable", e.getMessage(), e);
} finally {
if (objectOut != null) {
try {
objectOut.close();
} catch (IOException e) {
Log.e("SharedVariable", e.getMessage(), e);
}
}
}
}
Then use a class to use:
public class Timestamps implements Serializable {
private float timestampServer;
public float getTimestampServer() {
return timestampServer;
}
public void setTimestampServer(float timestampServer) {
this.timestampServer = timestampServer;
}
}
Then wherever you want to write to the variable use:
SharedVariables.writeObject(getApplicationContext(), "Timestamps", timestampsData);
Best way to achieve that is:
create a class. Call it MySettings, or whatever suits you
in this class, define the array of ints / booleans you are going to share, as static. Create getter & setter method (property) to access that (also as static methods)
add a static load() method to MySettings that reads from SharedPreferences. When you launch the app (in your first activity or better in a subclass of Application) call MySettings.load(). This load method sets the array
add a static save() method. Public also. Now you can save from anywhere in you app. This save() method reads the array and writes in SharedPreferences
Code sample:
public class MySettings {
private static List<Integer> data;
public static void load() {
data = new ArrayList<Integer>();
// use SharedPreferences to retrieve all your data
}
public static void save() {
// save all contents from data
}
public static List<Integer> getData() {
return data;
}
public static void setData(List<Integer> data) {
MySettings.data = data;
}
}

Passing a custom Object from one Activity to another Parcelable vs Bundle

I'd like to pass a custom Object from one activity to another, the Object consists of a String and a List of another custom Object which consists of an array of strings and an array of ints. I've read https://stackoverflow.com/a/2141166/830104, but then I've found this answer https://stackoverflow.com/a/7842273/830104. Which is better to use Bundle or Parcelable? What is the difference? When should I use this each? Thanks for your replies, Dan
Parcelable and Bundle are not exclusive concepts; you can even deploy both on your app at a time.
[1] Term Parcelable comes with Serialization concept in Java (and other high-level language such as C#, Python,...). It ensures that an object - which remains in RAM store - of such Parcelable class can be saved in file stream such as text or memory (offline status) then can be reconstructed to be used in program at runtime (online status).
In an Android application, within 2 independent activities (exclusively running - one starts then other will have to stop):
There will be NO pointer from current activity to refer to previous one and its members - because previous activity is stopped and cleared out form memory; so that to maintain object's value passed to next activity (called from Intent) the object need to be parcelable (serializable).
[2] While Bundle is normally the Android concept, denotes that a variable or group of variables. If look into lower level, it can be considered as HashMap with key-value pairs.
Conclusion:
Bundle is to store many objects with related keys, it can save any object in native types, but it doesn't know how to save a complex object (which contains an ArrayList for example)
Parcelable class is to ensure a complex instance of it can be serialized and de-serialized during runtime. This object can contains complex types such as ArrayList, HashMap, array, or struct,...
[UPDATED] - Example:
//Class without implementing Parcelable will cause error
//if passing though activities via Intent
public class NoneParcelable
{
private ArrayList<String> nameList = new ArrayList<String>();
public NoneParcelable()
{
nameList.add("abc");
nameList.add("xyz");
}
}
//Parcelable Class's objects can be exchanged
public class GoodParcelable implements Parcelable
{
private ArrayList<String> nameList = new ArrayList<String>();
public GoodParcelable()
{
nameList.add("Can");
nameList.add("be parsed");
}
#Override
public int describeContents()
{
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags)
{
// Serialize ArrayList name here
}
}
In source activity:
NoneParcelable nonePcl = new NoneParcelable();
GoodParcelable goodPcl = new GoodParcelable();
int count = 100;
Intent i = new Intent(...);
i.putExtra("NONE_P",nonePcl);
i.putExtra("GOOD_P",goodPcl);
i.putExtra("COUNT", count);
In destination activity:
Intent i = getIntent();
//this is BAD:
NoneParcelable nP = (NoneParcelable)i.getExtra("NONE_P"); //BAD code
//these are OK:
int count = (int)i.getExtra("COUNT");//OK
GoodParcelable myParcelableObject=(GoodParcelable)i.getParcelableExtra("GOOD_P");// OK

Saving data from activity(A) and reading in another Activity(history)

I have an activity that contain a list of data (TextView), i need to save this data that have been choose (onClick) in the list and been able to get and read it in another activity(history)
I understand that is exist a possibility with the "serializable" but i did not success to understand how it could help me.
i will be happy if someone can clarify this subject for me,example?.
Thank you for helping!!
If you're trying to pass a String to another activity, you can do this with putExtra and getStringExtra:
Intent intent = new Intent(this, OtherActivity.class);
intent.putExtra("parameter", myStringParameter);
startActivity(intent);
and then read it in OtherActivity's onCreate method:
String parameter = getIntent().getStringExtra("parameter");
The Serializable interface is useful for marshalling more complicated objects; you don't need this if you're just dealing with String.
Edit - if you need to store small amounts of data persistently you could use SharedPreferences:
final String TAG = "MyApplication";
SharedPreferences prefs = getSharedPreferences(TAG, MODE_PRIVATE);
prefs.edit().putString("parameter", myStringParameter).commit();
and then to read the preferences:
final String TAG = "MyApplication";
SharedPreferences prefs = getSharedPreferences(TAG, MODE_PRIVATE);
String parameter = prefs.getString("parameter", null);
This data will be available even after your application closes.
Yes, use classes that implement Serializable. See my answer on this question: How to pass several variables of different types from one function to another on android?
Create some model classes which will hold data:
public class Page implements Serializable {
private String name;
private String description;
//and so on...
public Page(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
}
Now you can create a Page object and fill it with data(name, description) via the constructor. Optionally make some setters.
Page p = new Page("James", "Hello World");
startActivity(new Intent(context, MyActivity.class).putExtra("Page", p));
Retrieve your Page in MyActivity in its onCreate method:
Page p = (Page)getIntent().getExtras().getSerializable("Page");
Toast.makeText(this, "Name: " + p.getName() + ", Description:" + p.getDescription(), Toast.LENGTH_LONG).show();
I am aware about the methods mentioned above. But just for alternative thoughts, since you are mentioning the history word, how about using SQLite for this purpose?
In first activity, you can save the data, and in second activity, retrieve the data.
You can share data between your activities in various ways depending on how much data you need to save and how long the data needs to be saved.
Intents : Useful when transferring small bits of data between 2-3 screens
Singleton / Static data store classes : useful when sharing lot of data between various activities
SQLite DB : Large amount of data to be shared , also useful to save the same between app launches
Shared Preferences : Small amount of data , between app launches.
For your use-case its best to use intents unless the data is shared between more than 2-3 activities where option 2 would be a better solution
Sending immutable stateful objects between activities (messaging) is commendable, IMHO. It can be argued that OOP is about messaging, not objects. Two suggestions. 1) use the fully qualified name for the name:value pair so do:
private void launchManagePassword() {
Intent i= new Intent(this, ManagePassword.class); // no param constructor
PasswordState outState= new PasswordState(lengthKey,timeExpire,isValidKey,timeoutType,"",model.getIsHashPassword());
Bundle b= new Bundle();
b.putSerializable("jalcomputing.confusetext.PasswordState", outState);
i.putExtras(b);
startActivityForResult(i,REQUEST_MANAGE_PASSWORD); // used for callback
}
This will minimize runtime casting errors.
2) When you have the app working well and the object interface is stabilized consider refactoring the code to a Parcel for speed.
JAL
EDIT: AS REQUESTED Code

Categories

Resources