How to use the EventBus onEvent method? - java

I'm using EventBus in my Android application. In my mainActivity, I have this handler method which sends live data to the EventBus as follows:
private final Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TGDevice.MSG_STATE_CHANGE:
EventBus.getDefault().postSticky(msg.arg1);
...
I'm using Fragments class and I need to receive the message from the handler.
I have registered the Fragment class in the onCreateView method as follows:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_data_log, container, false);
EventBus.getDefault().register(this);
tv = (TextView) view.findViewById(R.id.tv);
}
public void onEvent(Message message){
tv.setText("Signal" + message);
}
And I have the onEvent method which is suppose to be called when there is an Event. Unfortunately, this method is never called. I thought it might be method to be overridden but it doesn't seem to be.
What do i need to do to read from messages from the EventBus?
Also, in debugging modes, where can I see the number of threads being created? (I'm using Android Studio)

Unfortunately, this method is never called
That is because your onEvent() takes a Message, and (presumably) you are not posting a Message. You are posting whatever arg1 is.
Also, in debugging modes, where can I see the number of threads being created? (I'm using Android Studio)
Go into the Android Debug Monitor (Tools > Android > Android Debug Monitor from the main menu), and there's a threads view in DDMS inside of there.

Related

How can I turn off localbroadcast listener in recyclerview adapter? Of is there a better way to communicate with an activity called from it?

I'm new to SO and fairly new to coding, so please accept my apologies in advance if I break rules or expectations here.
I have an unusual setup involving two recyclerViews, which I'll explain here and also paste a simplified version of the code below (as there is so much not relevant to this question).
In what I'll call verticalRecyclerViewActivity, a verticalRecyclerViewAdapter is called, with data it fetches from Firebase and loads into arrayLists.
If the user clicks on an item in the vertical recyclerview, a new dialog fragment which I'll call horizontalRecyclerViewDialogFragment is inflated, and that loads what I'll call horizontalRecyclerView (which has similar items to the vertical one, in more detail, with options to click on them to review them).
If the user clicks on an item in the horizontalRecyclerView, a new activity which I'll call reviewItem is started (through an Intent). When the user submits their review, it finishes and returns (through the backstack) to the horizontal RecyclerView. That can also happen if they press the back button without actually submitting a review. That all works fine, but I need the horizontalRecyclerView to show that they have (or haven't) reviewed the item and state the score they gave it in a review.
Calling notifyDataSetChanged won't work for this because of how information comes through two recyclerViews and Firebase calls (or, at least, it would be very inefficient).
I've tried using startActivityForResult (I know it's deprecated, but if I could get that to work I could try using the newer equivalent which I don't yet understand) but the problem is that the result is returned to the original (VerticalRecylcerView) activity, which is two recyclerView adapters and one fragment beneath what needs to be updated, and I don't know how to pass that data to the horizontal Recyclerview.
I've also tried using interfaces but was unable to pass it through the Intent (tried using Parcelable and Serializable, but it seems neither can work in this situation?).
Since the review is updated on Firebase, I could have the horizontal Recyclerview listen for a change, but that seems very inefficient?
So I've found a solution using localBroadcast (which I know is also deprecated). The Intent (with the review score) is transmitted when it is reviewed and received in the horizontal recyclerView adapter. But when and how should I unregister the adapter? Ideally the receiver would be turned on when the user goes to the Review activity and turned off once the user returns from that activity and the (horizontal) recyclerView holder is updated, whether the review is successfully submitted or whether the user just presses the back button and never submits a review.
My question is similar to this one: How to unregister and register BroadcastReceiver from another class?
That is noted as a duplicate of this one: How to unregister and register BroadcastReceiver from another class?
There's a lot in those questions I don't understand, but the important difference I think between their and my cases is that I would just like the receiver to know when a review is submitted, and ideally be unregistered then, or possibly when the viewHolder is recycled, which I tried but also didn't work since it's not connected to the viewHolder (should it be?).
Any help would be much appreciated, thank you!
public class verticalRecyclerViewActivity extends AppCompatActivity {
// Loads an XML file and assembles an array from Firebase.
mVerticalRecyclerView = findViewById(R.id.verticalRecyclerView);
verticalRecyclerViewAdaptor mVerticalRecyclerViewAdaptor = new verticalRecyclerViewAdaptor (this); // also pass other information it needs
mVerticalRecyclerView .setAdapter(mVerticalRecyclerViewAdaptor);
}
public class verticalRecyclerViewAdaptor extends RecyclerView.Adapter<verticalRecyclerViewAdaptor.singleHolder> {
// Usual recyclerView content
holder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
horizontalRecyclerViewFragment mHorizontalRecyclerViewFragment = new horizontalRecyclerViewFragment();
// lots of arguments passed it needs.
FragmentManager fragmentManager = ((FragmentActivity) view.getContext()).getSupportFragmentManager();
mHorizontalRecyclerViewFragment.show(fragmentManager, null);
}
public class mHorizontalRecyclerViewFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity().getApplicationContext(); // Not sure why I need this, but it works.
View horizontalRecyclerViewView = getActivity().getLayoutInflater().inflate(R.layout.horizontal_recyclerview_holder, new CardView(getActivity()), false);
Dialog horizontalRecyclerViewDialog = new Dialog(getActivity());
horizontalRecyclerViewDialog.setContentView(horizontalRecyclerViewView);
mHorizontalRecyclerView = horizontalRecyclerViewView.getRootView().findViewById(R.id.horizontalRecyclerView);
mHorizontalRecyclerViewAdapter = new horizontalRecyclerViewAdapter (mContext)
// Other arguments passed
mHorizontalRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(),
LinearLayoutManager.HORIZONTAL, false));
mHorizontalRecyclerView.setAdapter(mHorizontalRecyclerViewAdapter);
}
public class horizontalRecyclerViewAdapter extends RecyclerView.Adapter<horizontalRecyclerViewAdapter.horizontalRecyclerViewHolder> {
public horizontalRecyclerViewAdapter(){}
// Blank constructor and also one with lots of arguments for it to work.
public horizontalRecyclerViewAdapter.horizontalRecyclerViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.horizontal_recyclerview_adaptor_holder, parent, false);
return new horizontalRecyclerViewAdapter.horizontalRecyclerViewHolder(view);
}
public void onBindViewHolder(#NonNull horizontalRecyclerViewHolder holder, int position) {
// Connect up various views.
holder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
LocalBroadcastManager.getInstance(mContext).registerReceiver(reviewSubmittedListener, new IntentFilter("reviewSubmitted"));
Intent reviewNow = new Intent(view.getContext(), ReviewActivity.class);
// Put extra details with the intent
view.getContext().startActivity(reviewNow);
}
BroadcastReceiver reviewSubmittedListener = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent reviewFinishedIntent) {
int reviewScore = reviewFinishedIntent.getExtras().getInt("reviewScore");
// Update the horizontal RecyclerView with the information received from the review Activity.
}
};
}
public class ReviewActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_review_item);
// Set up the review, using Firebase and data passed through the intent.
}
public void submitReview() {
// Check that the review is complete/valid and submit it through Firebase
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(ReviewItemActivity.this);
Intent reviewFinishedIntent = new Intent("reviewSubmitted");
reviewFinishedIntent.putExtra("reviewScore", overallScore);
LocalBroadcastManager.getInstance(this).sendBroadcast(reviewFinishedIntent);
finish();
}
If you are using RxJava you can use the RxBus else you can use one of many EventBus implementation for this.
If that is not the path you want to take then you can have a shared view model object that can be used only for communication between fragments see this article.

Android: Keep fragment running in the background

I have a fragment that starts a count and changes an icon status. After opening the app there is a count - 00:00 and a button that says START.
After clicking START the counts starts and the button changes to STOP.
After clicking STOP the count stops and the button changes to START. Pretty basic.
The thing is that after clicking START and minimalizing the app and opening it back (putting app in background and back) the count and button is always reverted back to START and 00:00.
So the question is: How can I keep the fragment alive after minimalizing the app?
Code:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View recordView = inflater.inflate(R.layout.fragment_record, container, false);
ButterKnife.bind(this, recordView);
return recordView;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
btnPause.setVisibility(View.GONE);
recordBtn.setColorPressed(getResources().getColor(R.color.colorPrimary));
}
#OnClick(R.id.btnRecord)
public void recordAudio(){
onRecord(mStartRecording);
mStartRecording = !mStartRecording;
}
private void onRecord(boolean start) {
Intent intent = new Intent(getActivity(), RecordingService.class);
if(start){
recordBtn.setImageResource(R.drawable.ic_media_stop);
//Toast.makeText(getContext(), "Started recording", Toast.LENGTH_LONG).show();
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.start();
getActivity().startService(intent);
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
recordingStatusTxt.setText("Recording now...");
} else {
recordBtn.setImageResource(R.drawable.ic_mic_white);
chronometer.stop();
chronometer.setBase(SystemClock.elapsedRealtime());
timeWhenPaused = 0;
getActivity().stopService(intent);
recordingStatusTxt.setText("Click the button to start recording");
}
}
You cannot keep your fragment alive.
System handles it itself. It can kill it anytime it wants to.
The thing is that after clicking START and minimalizing the app and
opening it back (putting app in background and back) the count and
button is always reverted back to START and 00:00.
The reason behind it actually pretty simple - your fragment basically recreates. And this is an expected behaviour. To continue you should learn Activity and Fragment lifecycle
And as I understand this is a Recorder app so you should get time elapsed from that service.
For example, you could Override onResume() method of your Fragment with setting time and button status using information gained from your service.
Good luck!
If you minimize the app while running, the android operating system can terminate your app and restart it when the user returns to it. You cannot guarantee it will remain in memory. If there is any data that needs to be maintained, you are responsible for saving it in the state handlers.
If you wish to have some process continue in the background, you'll need to start an Android Service and your fragment will need to communicate with it. The Fragment is just the UI piece of the app.

LiveData observer's onchanged() method executes several times

I am new to the android development. Currently i am working with the architecture components. When I inflate my fragment for the first time the Observer onChanged() executed several times so the data in my ui got repeated. But it is working fine when I detach and attach the fragment.
MyFragment.java
private MyViewModel myViewModel;
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.my_fragment, container, false);
return rootView;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
myViewModel = ViewModelProviders.of(this).get(MealViewModel.class);
myViewModel.getBreakfast(randomNum).observe(getViewLifecycleOwner(), new Observer<List<BreakFast>>() {
#Override
public void onChanged(#Nullable final List<BreakFast> breakFasts) {
for (int i = 0; i < breakFasts.size(); i++) {
String food = breakFasts.get(i).getFood();
if (food.contains("||")) {
food = food.replace("||", "\n");
}
builder.append( food + "\n");
}
resultTextView.setText(builder.toString());
}
});
}
In the above code, the onChanged method gets executed many times when the view inflates for the first time.
LiveData is a type of observable. Its job is to publish changes when data hosted in it gets updated (either manually by calling postValue on it or automatically when new data is added)
If you want to get data only once, then LiveData is not an option for you. You can simply use Single to achieve this.
Another thing that you can do is - simply ignore changes when they are pushed to you second time onward through your onChanged() method. What I mean by this is, do not update your text view second time onward. However, you should really have a separation logic that's clean enough to distinguish in between the callback that you will use and other callbacks that you won't use.
One last thing that I can think of is remove observer - your current lifecycle owner (activity or fragment) once you have observed the changes once. By calling removeObserver() method on LiveData.
PS: All of these are just workarounds. You should really rethink about using LiveData if live changes to the data is not something that you wanted to begin with.

Call a method when fragment is visible to user

I need execute a method when the fragment is visible (to the user).
Example:
I have 2 buttons (button 1 and button 2) ,
2 fragments(fragment 1 and fragment 2)
and the method loadImages() inside the class fragment 2.
when I press "button2" I want to replace fragment 1 by fragment 2
and then after the fragment 2 is visible (to the user) call loadImages().
I tried to use onResume() in the fragment class but it calls the method before the fragment is visible and it makes some delay to the transition.
I tried setUserVisibleHint() too and did not work.
A good example is the Instagram app. when you click on profile it loads the profile activity first and then import all the images.
I hope someone can help me. I will appreciate your help so much. Thank you.
Use the ViewTreeObserver callbacks:
#Override
public void onViewCreated(View v, Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
final View view = v;
// Add a callback to be invoked when the view is drawn
view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
// Immediately detach the listener so it only is called once
view.getViewTreeObserver().removeOnDrawListener(this);
// You're visible! Do your stuff.
loadImages();
}
});
}
I'm a little confused by what you are trying to do. It sounds like the images are loading too fast for you... so does that mean that you have the images ready to display? And that is a bad thing?
My guess (and this is just a guess) is that Instagram does not have the profile pictures in memory, so they have to make an API call to retrieve them, which is why they show up on a delay. If the same is the case for you, consider starting an AsyncTask in the onResume method of the fragment. Do whatever loading you need to do for the images in the background, and then make the images appear in the onPostExecute callback on the main thread. Make sure you only start the task if the images are not already loaded.
However, if you already have the images loaded in memory, and you just want a delay before they appear to the user, then you can do a postDelayed method on Handler. Something like this:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
Edit
As kcoppock points out, the handler code is pretty bad. I meant it to be a quick example, but it is so wrong I should not have included it in the first place. A more complete answer would be:
private Handler handler;
public void onResume(){
super.onResume();
if(handler == null){
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
loadImages();
}
}, 1000);
}
}
public void onDestroyView(){
super.onDestroyView();
handler.removeCallbacksAndMessages(null);
handler = null;
}
Use the onActivityCreated() callBck

Android: getting error near runOnUiThread when i use it near fragment

I am new to Android development and I want to link a button with the animation. I am getting error near runOnUiThread() and getApplication(). When I add this in as an activity it is fine, but when declared in MainFragment it gives error. However when I fix the errors, it creates a method and returns false.
public class MainFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.activity_main, container, false);
ImageButton btnFacebook = (ImageButton)rootView.findViewById(R.id.facebook2);
final Animation alpha = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_alpha);
btnFacebook.setOnClickListener(new Button.OnClickListener(){
#Override
public void onClick(View arg0) {
arg0.startAnimation(alpha);
Thread thread = new Thread()
{
#Override
public void run()
{
try
{
Thread.sleep(1000);
}catch(InterruptedException e){
}
runOnUiThread(new Runnable() {
#Override
public void run() {
startActivity(new Intent(getApplication(),FacebookActivity.class));
}
});
}
};
thread.start();
}});
return rootView;
}}
In the XML file I only have the facebook imagebutton. When I click that, it has to trigger the animation and then onclick event has to happen, but I don't know why this error is popping up:
The method runOnUiThread(new Runnable(){}) is undefined for the type new Thread(){}
And near getapplication() method
The method getApplication() is undefined for the type new Runnable(){}
If I create the two methods the error goes away, but then when I click onto the button it will not go to the facebookActivity.java file.
Can anyone tell/help what should I add to solve this issue. Thanks.
runOnUIThread(...) is a method of Activity.
Therefore, use this:
getActivity().runOnUIThread(...);
But, beware. You're dealing with asynchronous threads, so your Fragment might be detached from its Activity, resulting in getActivity() returning null. You might want to add a check if(isAdded()) or if(getActivity() != null) before executing.
Alternatively, use an AsyncTask, which handles this running on the ui thread by itself.
runOnUiThread(new Runnable(){}) is method in Activity not in the Fragment.
so you need to change
runOnUiThread(new Runnable(){})
into
getActivity().runOnUiThread(new Runnable(){})
For your requrement it is better to use Handler instead of Thread for waiting 1000 milli seconds..
Please use getActivity() instead of using getApplication() .
which returns the activity associated with a fragment.
The activity is a context.

Categories

Resources