Sharing class between different activities - java

I'm currently creating a game that uses multiple activites.
The different activities need to all be able to communicate with my main ConnectionManager class that sends stuff to my backend server.
The connection is currently only available in one of the activites like this:
connectionManager = new ConnectionManager( this );
connectionManager.start();
So what i want is, in the first activity the app should start the connectionManager and keep it running. Then when the user switches to another activity that activity needs to be able to send data to the connectionManager.
One possible way would be passing data when changing intents:
startActivity(new Intent(gameLobbyManager.this, LobbyManager.class).putExtra( key,value)));
But then i cannot pass the entire class to the new Intent which is needed in my case.
Appreciate any help!

Related

passing data from class to Activity via interface and modifying the UI

I have a class that handles Bluetooth communication and this class needs to forward data that it received from Bluetooth to my activity class. I used to pass this data via Broadcasts but now I am trying to pass the data via an interface. The problem I am having is that upon receiving the data in my activity, I can`t make changes in the UI and I am getting an error that says "Only the original thread that created a view hierarchy can touch its views."
//My Activity class implements an interface and receiveDataFromBluetoothConnection is a method in that interface
//My bluetooth service sends data to my activity via this method.
public void receiveDataFromBluetoothConnection(String data) {
processIncomingBtMessage(data);
}
private void processIncomingBtMessage(String incomingMessage) {
//...
//...
//...
if (message == BtMessageIn.BT_MESSAGE_IN_SYSTEM_OFF) {
Log.d(TAG, "remoteControlBubblePillar: bReceiver: Setting Button to On");
arduinoPowerStatus = false;
LightsButtonsBackgroundUnpressed();
btnOnOff.setBackgroundResource(R.drawable.button_shape_round_corners_gradient_green);
btnOnOff.setText(R.string.On);
}
//...
//...
//...
}
Here is the full error message that I get
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7913)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1373)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5449)
at android.view.View.invalidateInternal(View.java:14825)
at android.view.View.invalidate(View.java:14761)
at android.view.View.invalidateDrawable(View.java:19051)
at android.widget.TextView.invalidateDrawable(TextView.java:6353)
at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:436)
at android.graphics.drawable.Drawable.setVisible(Drawable.java:820)
at android.view.View.setBackgroundDrawable(View.java:19522)
at android.support.v7.widget.AppCompatButton.setBackgroundDrawable(AppCompatButton.java:86)
at android.view.View.setBackground(View.java:19498)
at android.view.View.setBackgroundResource(View.java:19481)
at android.support.v7.widget.AppCompatButton.setBackgroundResource(AppCompatButton.java:78)
at com.bubblewall.saik.bubblewall.remoteControlBubblePillar.processIncomingBtMessage(remoteControlBubblePillar.java:354)
at com.bubblewall.saik.bubblewall.remoteControlBubblePillar.receiveDataFromBluetoothConnection(remoteControlBubblePillar.java:275)
at com.bubblewall.saik.bubblewall.BluetoothConnection$1.onCharacteristicChanged(BluetoothConnection.java:97)
at android.bluetooth.BluetoothGatt$1.onNotify(BluetoothGatt.java:400)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:177)
at android.os.Binder.execTransact(Binder.java:573)
Any ideas how can I fix this issue? Or should I just switch back to Broadcasts?
The problem is that your Bluetooth class doesn't run on the main/UI thread, as the activity does. In order to avoid race conditions, Android crashes the app to let you know that you're doing something wrong.
Fortunately, there is a simple way to fix this by using runOnUiThread(Runnable):
public void receiveDataFromBluetoothConnection(String data) {
runOnUiThread(() -> processIncomingBtMessage(data));
}
But I have a better suggestion. Instead of letting the activity handle this, it would be better to design your other class such that it makes sure that when it notifies your activity it does so on the proper thread.
new Handler(Looper.getMainLooper()).post(() -> listener.receiveDataFromBluetoothConnection(data))
It's better to do it this way because in case other activities use the Bluetooth class they don't have to worry about making sure they execute code on the proper thread, the Bluetooth class already handles that.
For a more in-depth explanation on exactly what's happening and what the proper way to handle it is you can read Communicating with the UI Thread, on developer.android.com.

Android communication between Activities without onActivityResult

In an App I am currently working on I am facing a problem regarding the communication between Activities.
Basicly I have a UI-Component, which is similar to a Combobox. However, the list of the possible values for this component has to be openend in a new Activity.
So when you clicked the component, a new Activity is opened, using startActivityForResult.
When you select the new value, it is put inside a Bundle and returned to the previous Activity. I then need to use the onActivityResult-method to get the selected value and set it to the component.
That means, that every Activity that uses this component needs to override onActivityResult and refresh the component with the new value.
What I want instead is, that the component takes care about all this stuff and you only have to register a Listener, just like you do it for a TextView and similar components.
But at the moment I just can't find a good way to do that, as the communication is tightly bound to the Activity and I just can't get the result of the Activity outside the onActivityResult.
Does anyone know a good solution for this problem?
Solution to this- use EventBus and post sticky event on it. By doing so you don't have to override onActivityResult.
The workflow will be as following:
- Create event object with your data
- Remove all sticky events of the same type from the Bus
- post new values by .sendSticky() method.
That event will be around until something remove it from the bus
- start another activity
- in this activity override method, subscribing to that event type, in it:
a) take and process event with your argument
b) remove it from the bus
- subscribe to the bus (second Activity) in onResume() method
- unsubscribe from the bus in .onPause() method
The point is, that this allow you to seamlessly handle lifecycle of second Activity, and you can subscribe/unsubscribe to the bus in base class
What about using BroadCastReceiver?
Basically you send a broadcast and every activity that is registered to receive that broadcast will receive that broadcast will receive the message in onReceive
First of all declare in the manifesto what you are going to listen for, something like:
<receiver android:name=".TestBroadCast”>
<intent-filter>
<action android:name="io.test.TEST"/>
</intent-filter>
</receiver>
Than simply extend BroadCastReceiver
public class TestBroadCastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//TODO: Handle the Intent received.
}
Example of how to send a broadcast:
public static final String INTENT_ACTION = "io.test.TEST";
public static final String INTENT_EXTRA = "someData";
Intent intent = new Intent();
intent.setAction(INTENT_ACTION);
intent.putExtra(INTENT_EXTRA,"test");
sendBroadcast(intent);
And you will get the Intent and than you can handle it as you wish :)!
UPDATE ~ Registering from code instead of manifesto
To avoid registering the service from the Manifesto you can do it from the code, with a code similar to the following listing:
IntentFilter intentFilter = new IntentFilter("io.test.TEST");
TestBroadCastReceiver mReceiver = new TestBroadCastReceiver();
context.registerReceiver(mReceiver, intentFilter);
P.S.
I suggest you using LocalBroadcastReceiver if you don't need other applications to be able to send results insted of the common BroadcastReceiver for security reasons
You can implement Observer Design Pattern, the better implementation of that are BroadcastReceiver and a Library which implement event bus design Otto
Hello #Alex Shutov was right Try this
https://github.com/greenrobot/EventBus
http://greenrobot.org/eventbus/
Use EventBus here is the link https://github.com/greenrobot/EventBus
only 3 steps and yes it is 3rd PartyLibrary. its very light should not affect your app size as well
Generally for such scenario I will keep the data in Main Activity as static data and use and update them in other activities.
For example you can declare and populate these 2 variables in Main Activity (i.e. MainActivity)
public static ArrayList arrayList ;
public static SparseBooleanArray sparseBooleanArray ;
now all other activities while populating list values, they can populate from array list using name MainActivity.arrayList. When user select or unselect a value only sparseBooleanArray will be updated with a boolean value.
while populating values first time in arrayList and sparseBooleanArray , ensure to use same index so they will be in sync
arrayList.add(1,"List Item 1") ;
sparseBooleanArray.put(1,false ) ; // represent selected value for List Item 1
In case you have plan to use this functionality for multiple activities, arraylist will remain same, however there will be one SparseBooleanArray for each activity to store activity wise selected values.

Android Application that periodically gets Location

I am trying to build an Android Application that retrieves the users location for every 'x' seconds and then displays it in a toast.
There are three main java files that I am using in my code. They are: MainActivity.java which extends Activity and uses the alarm service provided by the AlarmReciever.java, AlarmReceiver.java extends the broadcast receiver and LocationModules.java, extends Service and implements location listener, which contain code to retrieve location.
The sequence of flow is as follows:
MainActivity.java ------> AlarmReceiver.java ------> LocationModules.java
LocationModules.java has a location manager that needs the application context which needs to be passed from MainActivity.java (I am not sure if I am right here).
Can anyone please help me with this. Thanks in advance.
myLocationManager=(LocationManager)mContext.getSystemService(LOCATION_SERVICE);
If your LocationModules extends Service, then you can just use getSystemService method (I mean, call it without using mContext).

Another way that you can exchange data between activities

What is different between static field and other ways in store data when the application is run?
I'm using static fields for pass data between activities and worked perfectly:
I defined this class :
public class Info
{
public static int ID = 0;
public static String NAME = "TEST";
public static TestClass testclass = null;
}
and I can store my data anywhere:
Info.ID = 5;
Info.NAME = "USER!";
Info.testclass = new TestClass();
and I can get my data anywhere:
Info.ID
Info.NAME
Info.testclass
It is usual to pass data between activities using extras within the intent. Such data persists for the lifetime of the receiving activity (when finished with, the garbage collector can free up the memory).
Or you can store values using SharedPreferences. These will persist between sessions and are stored as key/value pairs in a file (so don't impact memory use as such).
Or you can hold values in static fields (as you are doing here) which persist for the lifetime of your application session. However there is a significant risk with this in that the garbage collector cannot free up memory that is referenced by such fields unless you set them to null when you no longer need the reference. You should never store a reference to an activity/context/view in a static field since you'll leak the entire activity which can amout to a significant amount of memory usage.
http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html
You can pass a class instance within an intent if it is Serializable, e.g.:
Intent intent = new Intent(this, whatever.class);
Bundle b = new Bundle();
b.putSerializable("data", my_object);
intent.putExtras(b);
startActivity(intent);
And in the receiving activity, cast it back to whatever class your object is:
Bundle b = getIntent().getExtras();
my_object = (whatever class is it) b.getSerializable("data");
Many java classes implement Serializable and it is very simple to make your own classes serializable too.
If you're changing activities I'm assuming you're using intents. What you can do is send data with the intent with myIntent.putExtra("some string",someString);.
Then you can receive the info in your new activity using
Intent intent = getIntent();
String someString = intent.getExtra("some string");
You can use intents for passing data between activities.
Your first Activity.java
public void onClick(View v)
{
Intent timer = new Intent (FirstActivity.this,SecondActivity.class);
timer.putExtra("beefType", 5000);
startActivity(timer);
}
Then in your SecondActivity.java file do:
nt beefType = getIntent().getIntExtra("beefType", -1);
You want to share data between activities,you can use intent or shared prefernce.
The difference in using these tow and static data is that,intent and shared prefrence ,at some static data can be empty or null.but sending data using above two methods gurantees that you will get data in next activity ,unless you forcefully remove preference
you can refer this link for more info Static class in Java (Android) - use or not use
There is something called an Application Class in android. Its like a global singleton.
In other words, that Application Class will be common for that entire application.
Application class will be that first class to load.
So it will be easier to store some randomly used values in the application class.
public class Info extends Application
{
public static int ID = 0;
public static String NAME = "TEST";
}
Then call it in any activity by:
Info info= ((YourApplication)this.getApplication());
And in your manifest:
<application
android:name="<your package name>.GlobalApplication">
........
........
</application>
Well, that way doesn't always work in Android. Your static values are hold only while your app is running. Imagine you are sharing some content with action SEND, so you are starting another app which actually share your content (Facebook, email, etc.). Android may decide to stop completely your app if there are no resources enough to launch other app. In that point, the process of your app is completely gone and, with it, your static values. When going back to your app from the app that shared the content, you've lost your values.
I'd better use Intent extras, combined with Parcelable objects if you need to serialize more complex data.
You can easily try it if you enable in a device the option Don't keep activities under developer options, which destroys every activity as soon as the user leaves it.

Is this a memory leak?

I have a ListActivity containing an object I've defined called a MessageItem. I want to pass the data in this MessageItem off to a Service to do some stuff, but I don't want the Activity to be data bound to the Service such that Android can't reclaim the Activity if it needs to (ergo memory leak).
What I'm doing is passing the MessageItem object to a method in a singleton class along with the application Context. The singleton method uses the application Context to start my Service. I pass to this Service an Intent. In this Intent, I put items from the MessageItem object like so:
intent.putExtra("subject", msg.getSubject());
intent.putExtra("summary", msg.getSummary());
intent.putExtra("created_on", msg.getCreatedDate());
intent.putExtra("read", msg.getIsRead());
Will this data bind my Activity to the Service the Intent gets passed into and cause a memory leak?
Would this be a better implementation?:
intent.putExtra("subject", new String(msg.getSubject()));
intent.putExtra("summary", new String(msg.getSummary()));
intent.putExtra("created_on", new Integer(msg.getCreatedDate()));
intent.putExtra("read", new Boolean(msg.getIsRead()));
You don't need to new String or Integer. When the data is transfered by intent,
It will be parceled and unparceled.
Will this data bind my Activity to the Service
No, the code above will the references in the activity to the intent. The string and boolean values have no pointer back to the instance.
Would this be a better implementation?
No! That would be much worse. A lot of data would be copied and a lot of objects would be created for naught.

Categories

Resources