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.
Related
This question is mostly to solicit opinions on the best way to handle my app. I have three fragments being handled by one activity. Fragment A has one clickable element the photo and Fragment B has 4 clickable elements the buttons. The other fragment just displays details when the photo is clicked. I am using ActionBarSherlock.
The forward and back buttons need to change the photo to the next or previous poses, respectively. I could keep the photo and the buttons in the same fragment, but wanted to keep them separate in case I wanted to rearrange them in a tablet.
I need some advice - should I combine Fragments A and B? If not, I will need to figure out how to implement an interface for 3 clickable items.
I considered using Roboguice, but I am already extending using SherlockFragmentActivity so that's a no go. I saw mention of Otto, but I didn't see good tutorials on how to include in a project. What do you think best design practice should be?
I also need help figuring out how to communicate between a fragment and an activity. I'd like to keep some data "global" in the application, like the pose id. Is there some example code I can see besides the stock android developer's information? That is not all that helpful.
BTW, I'm already storing all the information about each pose in a SQLite database. That's the easy part.
The easiest way to communicate between your activity and fragments is using interfaces. The idea is basically to define an interface inside a given fragment A and let the activity implement that interface.
Once it has implemented that interface, you could do anything you want in the method it overrides.
The other important part of the interface is that you have to call the abstract method from your fragment and remember to cast it to your activity. It should catch a ClassCastException if not done correctly.
There is a good tutorial on Simple Developer Blog on how to do exactly this kind of thing.
I hope this was helpful to you!
The suggested method for communicating between fragments is to use callbacks\listeners that are managed by your main Activity.
I think the code on this page is pretty clear:
http://developer.android.com/training/basics/fragments/communicating.html
You can also reference the IO 2012 Schedule app, which is designed to be a de-facto reference app. It can be found here:
http://code.google.com/p/iosched/
Also, here is a SO question with good info:
How to pass data between fragments
It is implemented by a Callback interface:
First of all, we have to make an interface:
public interface UpdateFrag {
void updatefrag();
}
In the Activity do the following code:
UpdateFrag updatfrag ;
public void updateApi(UpdateFrag listener) {
updatfrag = listener;
}
from the event from where the callback has to fire in the Activity:
updatfrag.updatefrag();
In the Fragment implement the interface in CreateView do the
following code:
((Home)getActivity()).updateApi(new UpdateFrag() {
#Override
public void updatefrag() {
.....your stuff......
}
});
To communicate between an Activity and Fragments, there are several options, but after lots of reading and many experiences, I found out that it could be resumed this way:
Activity wants to communicate with child Fragment => Simply write public methods in your Fragment class, and let the Activity call them
Fragment wants to communicate with the parent Activity => This requires a bit more of work, as the official Android link https://developer.android.com/training/basics/fragments/communicating suggests, it would be a great idea to define an interface that will be implemented by the Activity, and which will establish a contract for any Activity that wants to communicate with that Fragment. For example, if you have FragmentA, which wants to communicate with any activity that includes it, then define the FragmentAInterface which will define what method can the FragmentA call for the activities that decide to use it.
A Fragment wants to communicate with other Fragment => This is the case where you get the most 'complicated' situation. Since you could potentially need to pass data from FragmentA to FragmentB and viceversa, that could lead us to defining 2 interfaces, FragmentAInterface which will be implemented by FragmentB and FragmentAInterface which will be implemented by FragmentA. That will start making things messy. And imagine if you have a few more Fragments on place, and even the parent activity wants to communicate with them. Well, this case is a perfect moment to establish a shared ViewModel for the activity and it's fragments. More info here https://developer.android.com/topic/libraries/architecture/viewmodel . Basically, you need to define a SharedViewModel class, that has all the data you want to share between the activity and the fragments that will be in need of communicating data among them.
The ViewModel case, makes things pretty simpler at the end, since you don't have to add extra logic that makes things dirty in the code and messy. Plus it will allow you to separate the gathering (through calls to an SQLite Database or an API) of data from the Controller (activities and fragments).
I made a annotation library that can do the cast for you. check this out.
https://github.com/zeroarst/callbackfragment/
#CallbackFragment
public class MyFragment extends Fragment {
#Callback
interface FragmentCallback {
void onClickButton(MyFragment fragment);
}
private FragmentCallback mCallback;
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt1
mCallback.onClickButton(this);
break;
case R.id.bt2
// Because we give mandatory = false so this might be null if not implemented by the host.
if (mCallbackNotForce != null)
mCallbackNotForce.onClickButton(this);
break;
}
}
}
It then generates a subclass of your fragment. And just add it to FragmentManager.
public class MainActivity extends AppCompatActivity implements MyFragment.FragmentCallback {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction()
.add(R.id.lo_fragm_container, MyFragmentCallbackable.create(), "MY_FRAGM")
.commit();
}
Toast mToast;
#Override
public void onClickButton(MyFragment fragment) {
if (mToast != null)
mToast.cancel();
mToast = Toast.makeText(this, "Callback from " + fragment.getTag(), Toast.LENGTH_SHORT);
mToast.show();
}
}
Google Recommended Method
If you take a look at this page you can see that Google suggests you use the ViewModel to share data between Fragment and Activity.
Add this dependency:
implementation "androidx.activity:activity-ktx:$activity_version"
First, define the ViewModel you are going to use to pass data.
class ItemViewModel : ViewModel() {
private val mutableSelectedItem = MutableLiveData<Item>()
val selectedItem: LiveData<Item> get() = mutableSelectedItem
fun selectItem(item: Item) {
mutableSelectedItem.value = item
}
}
Second, instantiate the ViewModel inside the Activity.
class MainActivity : AppCompatActivity() {
// Using the viewModels() Kotlin property delegate from the activity-ktx
// artifact to retrieve the ViewModel in the activity scope
private val viewModel: ItemViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.selectedItem.observe(this, Observer { item ->
// Perform an action with the latest item data
})
}
}
Third, instantiate the ViewModel inside the Fragment.
class ListFragment : Fragment() {
// Using the activityViewModels() Kotlin property delegate from the
// fragment-ktx artifact to retrieve the ViewModel in the activity scope
private val viewModel: ItemViewModel by activityViewModels()
// Called when the item is clicked
fun onItemClicked(item: Item) {
// Set a new item
viewModel.selectItem(item)
}
}
You can now edit this code creating new observers or settings methods.
There are severals ways to communicate between activities, fragments, services etc. The obvious one is to communicate using interfaces. However, it is not a productive way to communicate. You have to implement the listeners etc.
My suggestion is to use an event bus. Event bus is a publish/subscribe pattern implementation.
You can subscribe to events in your activity and then you can post that events in your fragments etc.
Here on my blog post you can find more detail about this pattern and also an example project to show the usage.
I'm not sure I really understood what you want to do, but the suggested way to communicate between fragments is to use callbacks with the Activity, never directly between fragments. See here http://developer.android.com/training/basics/fragments/communicating.html
You can create declare a public interface with a function declaration in the fragment and implement the interface in the activity. Then you can call the function from the fragment.
I am using Intents to communicate actions back to the main activity. The main activity is listening to these by overriding onNewIntent(Intent intent). The main activity translates these actions to the corresponding fragments for example.
So you can do something like this:
public class MainActivity extends Activity {
public static final String INTENT_ACTION_SHOW_FOO = "show_foo";
public static final String INTENT_ACTION_SHOW_BAR = "show_bar";
#Override
protected void onNewIntent(Intent intent) {
routeIntent(intent);
}
private void routeIntent(Intent intent) {
String action = intent.getAction();
if (action != null) {
switch (action) {
case INTENT_ACTION_SHOW_FOO:
// for example show the corresponding fragment
loadFragment(FooFragment);
break;
case INTENT_ACTION_SHOW_BAR:
loadFragment(BarFragment);
break;
}
}
}
Then inside any fragment to show the foo fragment:
Intent intent = new Intent(context, MainActivity.class);
intent.setAction(INTENT_ACTION_SHOW_FOO);
// Prevent activity to be re-instantiated if it is already running.
// Instead, the onNewEvent() is triggered
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(intent);
There is the latest techniques to communicate fragment to activity without any interface follow the steps
Step 1- Add the dependency in gradle
implementation 'androidx.fragment:fragment:1.3.0-rc01'
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.
I have created a class that is extending from CountDownTimer, It has a a method onFinish() which calls when timer expires.
There are 6 Activities, user can be in any activity when timer expires, So in CounterTimerwhen Finish() method calls , i need to show an Alert Message to the user,along with i need to redirect user to Login page.
Things getting confusing, as i cannot call Intent class in the Normal Class, I can also not pass the context, as user can be in any activity.
I have written following code, but its not helping out.
I am using context here, but its giving error message on passing context to Intent
public class CounterClass extends CountDownTimer implements ITMServiceEvent {
#Override
public void onFinish() {
if(sql_code.equalsIgnoreCase("0")) {
String resultCode = command1.getString("result");
context.startActivity(context.getApplicationContext(), MainActivity.class);
}
Calling Timer at the Start of Wizard, in a Fragment
CounterClass counterClass= new CounterClass(180000,1000);
counterClass.setTextView(tvTimer);
counterClass.start();
There are two parts of your question, first is how you can clean up the Activity stack and start a new Activity on Top of them, I suppose this would be the LoginActivity in your case.
To do this, you need to set the Flag of your LoginActivity Intent when you want to start it,
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
And the second part is, you want to be able to finish the current activity after showing a dialog to the user. I assume your Timer is a Service Class, which runs in the background. The way to tell your current activity that the Time is Up ! is to send a Broadcast Message. Preferably, LocalBroadcastManager can help you out. You can have a BaseActivity class where all of your 6 Activities can be extended from it and you can register/unregister LocalBroadcastManager to/from those activities in the BaseActivity class (register in onResume and unregister in onPause). After you register them you just need to implement and handle the onReceive method where you can show a dialog and start the LoginActivity after finishing the current one.
This question already has answers here:
Checking if an Android application is running in the background
(35 answers)
Closed 4 years ago.
I have implemented a BroadcastReceiver which is triggered by the AlarmManager. The AlarmManager is initialized on BOOT_COMPLETED. So i have to declare the receiver in the manifest.
My problem is that i want the BroadcastReceiver only to do something when none of my own activities are in the foreground (aka the user is not interacting with my application). I pull information from a remote server and don't want to notify the user if he is currently in my application anyways.
So far i have not managed to find a way to determine if my application is in the foreground. Is there a way to do such thing? The ActivityManager tells me if my application is running but not whether it is in the foreground.
The problem is pretty much the same as described here: Inform Activity from a BroadcastReceiver ONLY if it is in the foreground
SOLUTION:
After evaluating several solutions i want to quickly outline what i think is the best method to deal with activities in the background/foreground.
The preferred way is to register a broadcast receiver in the onResume method of your activity and to deregister it on the activities on onPause. Any service or other background element will than need to send a broadcast intent with a specific action that your activity will intercept.
If your activity is in the foreground it will have its intent receiver registered and is able to directly deal with the intent send from your service. If it is not in the foreground it will not receive the intent but the service that invokved the broadcast will know that nobody intercepted its broadcast intent and will be able to deal with that itself. Eg it could than launch the desired activity, show a notification etc.
The following answer: "Is application running in background", summarizes solutions available for background/foreground checking.
Note:
Previously this answer suggested to use ActivityManager.getRunningAppProcesses(), however that method appeared to be not completely reliable and its usage is discouraged. Check the link above for the details.
Your activity can track its own state as to whether it is in the foreground (set boolean to true in onStart(), to false in onStop()). Alas, that boolean is not provided to you by Activity automatically.
ActivityManager#getRunningAppProcesses() returns a List of RunningAppProcessInfo. Each RunningAppProcessInfo has a field called importance. importance equal to RunningAppProcessInfo.IMPORTANCE_FOREGROUND seems to show which activity is actively being observed by the user. There is also RunningAppProcessInfo.IMPORTANCE_VISIBLE which is lower but might be worth checking out.
check out my solution for determining if an activity is in the foreground: http://www.mannaz.at/codebase/android-activity-foreground-surveillance/
It should be easy to revert the logic from "in the foreground" to "not in the foreground".
I have implemented a BroadcastReceiver which is triggered by the AlarmManager. The AlarmManager is initialized on BOOT_COMPLETED. So i have to declare the receiver in the manifest.
My problem is that i want the BroadcastReceiver only to do something when none of my own activities are in the foreground (aka the user is not interacting with my application). I pull information from a remote server and don't want to notify the user if he is currently in my application anyways.
So far i have not managed to find a way to determine if my application is in the foreground. Is there a way to do such thing? The ActivityManager tells me if my application is running but not whether it is in the foreground.
There doesn't seem to be a direct way to determine if one of your activities is the current running foreground activity. However, you can get the desired effect by using an ordered broadcast and two broadcast receivers. One broadcast receiver needs to be registered in OnResume() and unregistered in OnPause(). The 2nd broadcast receiver will be declared in your manifest as you've already done. Set the android:priority for your receivers such that if the dynamically registered receiver is registered, it will receive the intent first, then you can eat the intent so that the broadcast receiver you registered in your manifest is never notified.
You can test if the window has focus - but as stated in dev docs this is not the same as if activity is in foreground.
I'd use ActivityLifecycleCallbacks to get much cleaner solution.
It can be insecure, read this before you decided to use example below in production. It works in 'home development' for my device and OS version.
public class App extends Application implements Application.ActivityLifecycleCallbacks {
private boolean inForeground;
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
#Override
public void onActivityResumed(Activity activity) {
inForeground = activity instanceof YourActivity;
}
public boolean isInForeground() {
return inForeground;
}
Register App in AndroidManifest:
<application
android:name=".App" />
And the final piece of the puzzle:
public class YourReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
App app = (App) context.getApplicationContext();
if(app.isInForeground()){
// do some stuff
}
}
}
I have 2 activities.
I start one, and at a point I start another one:
Intent enabler = new Intent(this, cprompt.class);
startActivity(enabler);
I want to call a method from the old one, but I want to pass some data too.
This is how I tried:
Activity1.sendcommand(DATA);
And I got this:
Cannot make a static reference to the non-static method sendcommand(String) from the type Activity1
And I don't want change the method to static.
The only way if I make a Listener? If I have to, you can describe for me to how?
The java is new for me... :/ But if this problem is solved I think I am done with my program :)
Only one Activity is active at a time. Are you trying to trigger the method just because you want to pass some data?.You can do it via an Intent
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("YOUR_DATA_KEY", "Data Value");
startActivity(intent);
and in the SecondActivity , you can retrieve the data and process it
String data = getIntent().getStringExtra("YOUR_DATA_KEY");
It's not just Strings that you can send this way.You can even transmit Serializable and Parcelable Extras. For more information refer - Intents on Android Developer Website
You can declare an interface and make Activity1 implements that interface..
Then when you need to call Activity1's method call the inetrface's method...
Your interface:
public interface OnSendCommand{
public void onSendCommand(values);
}
Activity1:
public Activity1 implements OnSendCommand {
public void onSendCommand(values){}
}
Are you returning to the first activity from the second when executing this method? If so you could use startActivityForResult() following this tutorial. If not, then something is wrong with the structure of your application if this method can not be static, and this method should be in a separate class not inheriting from Activity.