I want to have a sensormanager on a fragment, which is only active when the fragment is active. If the user changes the fragment, the listener should be removed.
Adding and removing the listener is pretty simple. I'm not aware of any listeners / function on the fragment side, when the fragment appears / disappears. Also a problem was, that on almost all functions, this.getActivity() returned a null pointer.
That's my solution. I tried to cut it out of my Fragment. If there is anything wrong / syntax issues, please let me know.
public class MyFragment extends Fragment implements SensorEventListener {
private SensorManager mSensorManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSensorManager = (SensorManager) this.getActivity().getSystemService(Activity.SENSOR_SERVICE);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.mylayout, container, false);
return rootView;
}
#Override
public void onSensorChanged(SensorEvent event) {
float x = event.values[0], y = event.values[1];
}
#Override public void onAccuracyChanged(Sensor sensor, int accuracy) { }
#Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
// First starts (gets called before everything else)
if(mSensorManager == null) {
return;
}
if(menuVisible) {
this.registerSensorListener();
} else {
this.unregisterSensorListener();
}
}
#Override
public void onStart() {
super.onStart();
if(this.getUserVisibleHint()) {
this.registerSensorListener();
}
}
#Override
public void onStop() {
super.onStop();
this.unregisterSensorListener();
}
private void registerSensorListener() {
mSensorManager.registerListener(this, mSensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0), SensorManager.SENSOR_DELAY_FASTEST);
}
private void unregisterSensorListener() {
mSensorManager.unregisterListener(this);
}
}
Hold a reference of Activity in your fragment to handle that nullpointerexception.
Here is an example of a fragment.
public class YourFragment extends Fragment {
private Activity mActivity;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mActivity = activity;
}
#Override
public void onResume() {
super.onResume();
// BIND sensor here with mActivity,
// could also be done in other fragment lifecycle events,
// depends on how you handle configChanges
}
#Override
public void onPause() {
super.onPause();
// UNBIND sensor here from mActivity,
// could also be done in other fragment lifecycle events,
// depends on how you handle configChanges
}
}
Debug that code do determine if you should handle the binding there or in another method e.g. onCreate of a fragment. I have not tested this code for your purpose.
Edit:
This is indeed as commented below a dirty fix and could easily resolve into exceptions in some cases. I just wanted to show how you can use fragment lifecycle methods to bind and unbind sensors with a reference to activity. I'm currently learning fragments for quite some time but still don't understand them thoroughly. I advice you to take a look at the source of Fragment and other components involved. This is the only place were fragments are documented thoroughly hence the documentation on reference in my opinion isn't that explanatory.
Some of the options regarding null value Activity:
If you want to be completely sure that getActivity doesn't return null you should wait for onActivityCreated to be called. This method tells the fragment that its activity has
completed its own Activity.onCreate(). After this getActivity() will not return null until initState() gets called by the FragmentManager.
// Called by the fragment manager once this fragment has been removed,
// so that we don't have any left-over state if the application decides
// to re-use the instance. This only clears state that the framework
// internally manages, not things the application sets.
void initState() {
mIndex = -1;
mWho = null;
mAdded = false;
mRemoving = false;
mResumed = false;
mFromLayout = false;
mInLayout = false;
mRestored = false;
mBackStackNesting = 0;
mFragmentManager = null;
mActivity = null;
mFragmentId = 0;
mContainerId = 0;
mTag = null;
mHidden = false;
mDetached = false;
mRetaining = false;
mLoaderManager = null;
mLoadersStarted = false;
mCheckedForLoaderManager = false;
}
Before you call getActivity you can always check if activity isn't null by calling isAdded() method. As you can see below this method checks if mActivity isn't null. Optionally you can create a recursive function with Handler.postDelayed that tries to return an non null Activity in intervalls (you should add a max try counter). But this is also a dirty trick.
//Return true if the fragment is currently added to its activity.
final public boolean isAdded() {
return mActivity != null && mAdded;
}
Related
I programmed a Vocabulary Trainer with Vocabulary Cards. The Vocabulary Cards are Entries in a Room Database created from an asset. I am displaying these Vocabulary Cards with ViewPager2 in an Activity. I have a 'correct' and a 'false' button and when the user clicks on either, I want to update the Vocabulary Card (-> The entry in the sqlite database) and automatically swipe to the next item of the ViewPager2.
If I implement the buttons in the ViewPager2Adapter, I can't find a way to change the position of the ViewPager2. If I implement the buttons in the activity the sqlite entry does not update properly (After it updates the entry, the activity is constantly refreshed, it seems like it never the leaves the OnClick methode of the button).
So is it possible to change the position of ViewPager2 from inside the ViewPager2Adpater?
Thanks for your help!
That is the relevant code if I have the buttons in my ViewPager2Adapter. Here I don't know how to change the position of the ViewPager2
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
VocabularyCard vocabularyCard = currentCards.get(position);
holder.btn_correct.setOnClickListener(view -> {
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
holder.btn_false.setOnClickListener(v15 -> {
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
That is the relevant code if I have the buttons in the Activity. Here the update function triggers an infinite updating of the Activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
btn_correct_2.setOnClickListener(view -> {
int currentPos = viewpager2.getCurrentItem();
vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
if (vocabularyCards.size() == currentPos){
Intent intent = new Intent(TestActivity.this, MainActivity.class);
startActivity(intent);
}else {
viewpager2.setCurrentItem(currentPos + 1);
}
VocabularyCard vocabularyCard = vocabularyCards.get(currentPos);
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
});
btn_false_2.setOnClickListener(view -> {
int currentPos = viewpager2.getCurrentItem();
vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
if (vocabularyCards.size() == currentPos){
Intent intent = new Intent(TestActivity.this, MainActivity.class);
startActivity(intent);
}else {
viewpager2.setCurrentItem(currentPos + 1);
}
VocabularyCard vocabularyCard = vocabularyCards.get(currentPos);
vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard);
});
});
Objects.requireNonNull(getSupportActionBar()).setTitle(getResources().getString(R.string.learn_new_words));
LiveData<List<VocabularyCard>> allNewCards = vocabularyViewModel.getAllNewCards(goal);
allNewCards.observe(this, vocabularyCards -> vocabularyViewModel.setCurrentCards(vocabularyCards));
vocabularyViewModel.getCurrentCards().observe(this, vocabularyCards -> {
viewPager2Adapter.setCurrentCards(vocabularyCards);
viewpager2.setAdapter(viewPager2Adapter);
viewpager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
}
#Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
});
The update function in the Room DAO is straightforward:
#Update
void updateSingleVocabularyCard(VocabularyCard vocabularyCard);
I left out all the code that is not relevant.
There are several ways to propagate an event from the adapter to the activity where you manage your cards using ViewPager2. Let's have a look how it can be done either using an interface or using the same view model. But in any case I strongly recommend you to update your database in a background thread to prevent any possible UI lags.
1. Using an interface
This option is more flexible since you can propagate events as well as pass data as parameters. You can also reuse this interface for other cases. As far as I See you have a holder that has 2 buttons for the users to make choices. So our event here would be something like ChoiceEventListener, let's call this interface like so. Then you'd have to add a method to handle this event from within anywhere you wanna hear this event, and let's call its handle method onChoice(). Finally we would need a variable to indicate what the choice is. Now that ready to implement, let's write the new interface...
ChoiceEventListener.java
public interface ChoiceEventListener {
void onChoice(VocabularyCard vocabularyCard, boolean choice);
}
The next thing to do is to implement this interface where you want to listen to this event. In this case it is in your activity. There are 2 ways to do this:
You make your activity to inherit its methods using the implements keyword
YourActivity.java
public class YourActivity extends AppCompatActivity implements ChoiceEventListener {
// Use a background thread for database operations
private Executor executor = Executors.newSingleThreadExecutor();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
// You must construct your adapter class with the listener
ViewPager2Adapter adapter = new ViewPager2Adapter(/* Other params... */, this);
}
#Override
public void onChoice(VocabularyCard vocabularyCard, boolean choice) {
if(choice) {
// User pressed the correct button
}
else {
// User pressed the false button
}
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
}
}
You can implement it as an anonymous function
YourActivity.java
public class YourActivity extends AppCompatActivity {
// Use a background thread for database operations
private Executor executor = Executors.newSingleThreadExecutor();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
// You must construct your adapter class with the listener
ViewPager2Adapter adapter = new ViewPager2Adapter(/* Other params... */, (vocabularyCard, choice) -> {
if(choice) {
// User pressed the correct button
}
else {
// User pressed the false button
}
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
});
}
}
Finally the ViewPager2Adapter class implementation would be something like this:
ViewPager2Adapter.java
public class ViewPager2Adapter extends RecyclerView.Adapter<ViewPager2ViewHolder> {
// Here is your listener to deliver the choice event to it
private final ChoiceEventListener listener;
// Constructor
public ViewPager2Adapter(/* Other params... */, ChoiceEventListener listener) {
/* Other inits */
this.listener = listener;
}
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
VocabularyCard vocabularyCard = currentCards.get(position);
holder.btn_correct.setOnClickListener(view -> {
listener.onChoice(vocabularyCard, true); // true for correct
});
holder.btn_false.setOnClickListener(v15 -> {
listener.onChoice(vocabularyCard, false); // false for false :)
});
}
}
2. Use the ViewModel for inter-communication
In this option we use a LiveData object to make page switching. The only thing you need to know in your activity is the current position which you get it from the adapter class. Once you update it in the adapter, set the current position value in live data so that you can switch the page in your activity.
VocabularyViewModel.java
public class VocabularyViewModel extends ViewModel {
public MutableLiveData<Integer> mldCurrentPosition = new MutableLiveData<>(0);
}
YourActivity.java
public class YourActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initAll();
vocabularyViewModel.mldCurrentPosition().observe(this, currentPosition -> {
if(currenPosition == null) return; // ignore when null
viewpager2.setCurrentItem(currentPosition + 1);
}
}
}
Finally the ViewPager2Adapter class implementation would be something like this:
ViewPager2Adapter.java
public class ViewPager2Adapter extends RecyclerView.Adapter<ViewPager2ViewHolder> {
// Use a background thread for database operations
private Executor executor = Executors.newSingleThreadExecutor();
public void onBindViewHolder(#NonNull #NotNull ViewHolder holder, int position) {
VocabularyCard vocabularyCard = currentCards.get(position);
holder.btn_correct.setOnClickListener(view -> {
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
// Then invoke switching to the next card
vocabularyViewModel.mldCurrentPosition.setValue(position + 1);
});
holder.btn_false.setOnClickListener(v15 -> {
// Update card in the background
executor.execute(()-> vocabularyViewModel.updateSingleVocabularyCard(vocabularyCard));
// Then invoke switching to the next card
vocabularyViewModel.mldCurrentPosition.setValue(position + 1);
});
}
}
I'm working on an alarm clock and I can't figure out how to sendEvent to React Native from MainActivity. This is what I managed to do so far:
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
ReactInstanceManager mReactInstanceManager = getReactNativeHost().getReactInstanceManager();
ReactApplicationContext context = (ReactApplicationContext) mReactInstanceManager.getCurrentReactContext();
if (context == null) {
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext context) {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // doesn't work. Should run after alarm manager starts app which previously had been killed
}
}
}
});
} else {
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
LauncherModule.startAlarm(mActivity); // works
LauncherModule.sendAlarmEvent(); // works and sends event only when app was left open
}
}
}
super.onCreate(savedInstanceState);
}
The code works only If app is left open and alarm manager restarts app itself. If I close the app and alarm manager starts it then it seems that only startAlarm function (it has sound effect) is beeing triggered..
No matter what I do whether I put sendEvent function inside Mainactivity or elsewhere (e.g. external module) it simply won't send event if I close the app. I also tried getReactInstanceManager().getCurrentReactContext() combined with while from this question Send data from Android activity to React Native to no avail.
Also tried to create bolean beeing set to true onCreate and then send event onStart or onRestart. Also to no avail.
Any suggestions?
EDIT: Here is how sendEvent function looks like:
public final void sendEvent(String eventName, boolean isAlarmOn) {
getReactInstanceManager().getCurrentReactContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, isAlarmOn);
}
SOLUTION
Well I think that the answer is not to use sendEvent method onCreate because (I might be wrong) listener seems to be initialized after the event had been sent. So nothing is going to listen to this event.
It seems to work pretty well inside onStart, onRestart, onPause though.
What can we do? React Native provides ReactActivityDelegate with initial props. And it does the job!
ReactActivityDelegate in MainActivity should look as below:
public class ActivityDelegate extends ReactActivityDelegate {
private Bundle mInitialProps = null;
private final #Nullable Activity mActivity;
public ActivityDelegate(Activity activity, String mainComponentName) {
super(activity, mainComponentName);
this.mActivity = activity;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
mInitialProps = new Bundle();
final Bundle bundle = mActivity.getIntent().getExtras();
if (bundle != null && bundle.containsKey("sendAlarm")) {
if (bundle.getString("sendAlarm").equals("sendAlarmOn")) {
mInitialProps.putBoolean("alarmOn", true);
}
}
super.onCreate(savedInstanceState);
}
#Override
protected Bundle getLaunchOptions() {
return mInitialProps;
}
};
#Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ActivityDelegate(this, getMainComponentName());
}
Then in your main app component (usually index.android.js) call your propTypes and use them to run your code:
static propTypes = {
alarmOn: PropTypes.boolean
}
componentDidMount() {
if (this.props.alarmOn === true) {
// your code
}
}
Voila!
You can find full example here: https://github.com/vasyl91/react-native-android-alarms
I am using a UI-less fragment to hold the state of ViewModel class. The state of ViewModel class is retained as it is expected.
Retain fragment:
public class LoveMatchActivityRetainFragment extends Fragment {
private LoveMatchViewModel loveMatchViewModel;
#Inject
public LoveMatchActivityRetainFragment() {}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setRetainInstance(true);
}
public void retainViewModel(LoveMatchViewModel loveMatchViewModel) {
Log.e("df", loveMatchViewModel + " ");
this.loveMatchViewModel = loveMatchViewModel;
}
public LoveMatchViewModel getViewModel() {
return this.loveMatchViewModel;
}
}
Activity class:
#Override
protected void onCreate(Bundle savedInstanceState) {
if (getSupportFragmentManager().findFragmentByTag("my_fragment") == null) {
mLoveMatchActivityRetainFragment = new LoveMatchActivityRetainFragment();
getSupportFragmentManager().beginTransaction().add(mLoveMatchActivityRetainFragment, "my_fragment").commit();
} else {
mLoveMatchActivityRetainFragment = (LoveMatchActivityRetainFragment) getSupportFragmentManager().findFragmentByTag("my_fragment");
}
mLoveMatchActivityRetainFragment.getViewModel();
if (mLoveMatchActivityRetainFragment.getViewModel() != null) mViewModel = mLoveMatchActivityRetainFragment.getViewModel();
bindingRecyclerViewAdapter = new BindingRecyclerViewAdapter<>(null);
mBinding.rcv.setLayoutManager(new LinearLayoutManager(this));
mBinding.rcv.setAdapter(bindingRecyclerViewAdapter);
Log.e(debugTag, bindingRecyclerViewAdapter + " adapter");
}
#Override
protected void onDestroy() {
super.onDestroy();
dismissDialog();
mLoveMatchActivityRetainFragment.retainViewModel(mViewModel);
mLoveMatchActivityRetainFragment = null;
}
I have a RecyclerView to populate a list of data. I initialize the RecyclerView inside onCreate() to get rid of 'No adapter attached, skipping layout' error. Also I have a function that as soon as I get data from network is called to populate the RecyclerView with the real data.
private void initializeRcView(final ArrayList<PetObj> data) {
bindingRecyclerViewAdapter.setItems(data);
Log.e(debugTag, this.bindingRecyclerViewAdapter + " adapter here: "+this);
bindingRecyclerViewAdapter.notifyDataSetChanged();
}
The first time activity loaded and I get data from network RecyclerView is populated as it is expected. The problem is when a rotate the screen. I
logged the instances of Activity and RecyclerView adapter inside onCreate() and inside initializeRcView() methods. Both new instances are being created inside onCreate() each time, but inside initializeRcView() the instances remain the same always. I removed setRetainInstance(true) from the retained fragment and RecyclerView populated correctly on rotation change. Can anybody address the issue? Why instances remain the same inside initializeRcView()?
When my MainFragment first loads, it checks for internet connectivity. If there is internet, it loads the content from online. However, if there is no internet connectivity, I replace the existing fragment MainFragment within the container main_browse_fragment with InternetConnectivityFragment.
Within InternetConnectivtyFragment, I have a Retry button that re-checks the internet, and if there is internet connectivity, I remove InternetConnectivityFragment and call popbackstack.
The problem is, when popbackstack is called, I dont know how to reload the online data from within MainFragment. The online data loading and internet check is done within onActivityCreated of MainFragment.java, but when popbackstack is called within InternetConnectivityFragment.java, the view of MainFragment is blank
Obviously this is because the fragment is not "created" and onActivityCreated is not called again, so no data is reloaded.
Here's the relevant code:
MainFragment.java:
public class MainFragment extends DetailsFragment
{
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
setupUIElements();
if (isConnectingToInternet() == true)
{
// Call methods to load online data
}
else
{
InternetConnectivityFragment internetError = new InternetConnectivityFragment();
getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, internetError).commit();
}
}
...
}
InternetConnectivityFragment.java:
public class InternetConnectivityFragment extends ErrorFragment
{
private static final String TAG = "InternetFragment";
private static final boolean TRANSLUCENT = true;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setErrorContent();
}
private void setErrorContent()
{
setImageDrawable(getResources().getDrawable(R.drawable.lb_ic_sad_cloud, null));
setMessage(getResources().getString(R.string.no_internet_message));
setDefaultBackground(TRANSLUCENT);
setButtonText(getResources().getString(R.string.retry_connection));
setButtonClickListener(new View.OnClickListener()
{
#Override
public void onClick(View arg0)
{
if (Singleton.getInstance().isConnectedToInternet() == true)
{
getFragmentManager().beginTransaction().remove(InternetConnectivityFragment.this).commit();
getFragmentManager().popBackStack();
// How to call methods to re-load online data from MainFragment????
}
}
});
}
}
I have tried to perform the following within the onClick method:
MainFragment mainFragment = new MainFragment();
getFragmentManager().beginTransaction().remove(InternetConnectivityFragment.this).commit();
getFragmentManager().popBackStack();
getFragmentManager().beginTransaction().add(R.id.main_browse_fragment, mainFragment).commit();
However, I believe this is wrong as it just adds a new fragment MainFragment on top of an already existing fragment.
Can someone point me in the right direction?
Thanks
I am creating an Activity which communicates with a Service to download some data from internet via POST method. To do this, I use Messenger. Here is my code to make it clearer for you:
My onCreated() method:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comments);
CommentsHandler commentsHandler = new CommentsHandler(this, savedInstanceState);
Messenger messenger = new Messenger(commentsHandler);
Intent serviceIntent = new Intent(this, WindowService.class);
serviceIntent.putExtra("messenger", messenger);
serviceIntent.putExtra("state", 888);
serviceIntent.putExtra("number", getIntent().getStringExtra("number"));
startService(serviceIntent);
}
The code in my Service's thread to post the result data to the Activity via the Messenger object:
/** ... **/
Messenger messenger = intent.getParcelableExtra("messenger");
/** ... **/
Message resultMsg = this.obtainMessage();
resultMsg.obj = jParser.getArrayList(); //This is an ArrayList of my downloaded data.
messenger.send(resultMsg);
The code in the Activity to handle the Message from the Service:
public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;
public CommentsHandler(Activity a, Bundle savedInstanceState) {
activity = (ActionBarActivity) a;
mSavedInstanceState = savedInstanceState;
}
#Override
public void handleMessage(Message msg) {
comments = (ArrayList<HashMap<String, String>>) msg.obj;
if (mSavedInstanceState == null && msg.arg1 != 793) {
activity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, new CommentsFragment()).commit();
} else if (msg.arg1 == 793) { //793 is my preferred code to determine
//if the internet connection could not be
//established when the Service was trying
//to download the data.
activity.finish();
}
}
}
The problem is: if I open the Activity and close it before the data is downloaded, this code .add(R.id.container, new CommentsFragment()).commit(); gives me the error Can not perform this action after onSaveInstanceState, because this code only gets executed after the data in my Service is processed and sent via the Messenger object, but at that time the Activity is already closed by the user so the Fragment cannot be added. How to solve this issue? How to check if the Activity is not closed/being closed before adding the Fragment? Or, better, how to stop the thread in which that code is running on Activity's onDestroy() method so it doesn't get executed if the Activity is closed? Thanks in advance!
In your activity, you should create a boolean to check if the activity is visible or not:
public ActionBarActivity extends Activity {
private boolean isActivityVisible = false;
#Override
protected void onResume(){
isActivityVisible = true;
}
#Override
protected void onPause(){
isActivityVisible = false;
}
public boolean isVisible(){
return this.isActivityVisible;
}
}
And then you modify your Handler class definition:
public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;
public CommentsHandler(Activity a, Bundle savedInstanceState) {
activity = (ActionBarActivity) a;
mSavedInstanceState = savedInstanceState;
}
#Override
public void handleMessage(Message msg) {
// here you check if your activity is no longer visible and then break up
if(activity == null || !activity.isVisible())
return;
comments = (ArrayList<HashMap<String, String>>) msg.obj;
if (mSavedInstanceState == null && msg.arg1 != 793) {
activity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, new CommentsFragment()).commit();
} else if (msg.arg1 == 793) { //793 is my preferred code to determine
//if the internet connection could not be
//established when the Service was trying
//to download the data.
activity.finish();
}
}
}
The smallest change would be to have a boolean field in the Activity, setting it to true in onResume() and to false in onPause(), and check its value in handleMessage() (i.e. ignore the message if the flag is currently false).
Another option, instead of using Messenger and handleMessage(), do this with a BroadcastReceiver. Register the receiver in onResume() and unregister it in onPause(). That way the broadcast from the service will be simply ignored.
Both solutions are basically the same, anyway, but broadcasts are somewhat "higher level".
This assumes that you're not interested in the Service's result if the activity is paused. If you are (for example, if you switch out of the application and back in, and you need to display the update) then you should put the received data in a field and process it on the following onResume().
Your way of doing this is different than how I would handle it but using what you have I would make these adjustments:
public static class CommentsHandler extends Handler {
Bundle mSavedInstanceState;
ActionBarActivity activity;
public CommentsHandler(Activity a, Bundle savedInstanceState) {
activity = (ActionBarActivity) a;
mSavedInstanceState = savedInstanceState;
}
public void setActivity(Activity a){
activity = a;
}
#Override
public void handleMessage(Message msg) {
if(activity == null){
return;
}
comments = (ArrayList<HashMap<String, String>>) msg.obj;
if (mSavedInstanceState == null && msg.arg1 != 793) {
activity.getSupportFragmentManager().beginTransaction()
.add(R.id.container, new CommentsFragment()).commit();
} else if (msg.arg1 == 793) { //793 is my preferred code to determine
//if the internet connection could not be
//established when the Service was trying
//to download the data.
activity.finish();
}
}
}
Then I would use your activities onPause()/onResume() methods to like this:
#Override
protected void onPause() {
super.onPause();
commentsHandler.setActivity(null);
}
#Override
protected void onResume() {
super.onResume();
commentsHandler.setActivity(this);
}