Should the "sub"-activities in a TabActivity bind services? - android

I am building an application based on a tabbed activity. There are about 12 tabs at any given time.
I have built a download service, for my own use, which requires binding to it. I've found a few questions about the issue binding Services from tabs, but I haven't found anything discussing the best way to design this.
My paths seem to be:
Bind the download service in the tab's activities using getParent() or getApplicationContext() as the context to bind to.
Bind the download service once in the TabActivity and then expose it via a static method to other Activities making up the tabs.
Redesign the download service so that it does not require binding. (I'm not really sure this is a viable option or buys me much)
I'm basically in a toss up between 1 and 2. It seems like #1 seems to make the activities more independent, but I'm not sure if its going to cause problems having the tab "sub" activities binding the same service 12 times on the tab activitie's context. Similarly, I'm not sure if it is good practice to expose state-dependent objects, like a Service, via a static method to other Activities. I'm concerned it may create a number of race conditions that need to be accounted for depending on when the binding happens and when the tab activities are started.
What seems like the better design?

The main purpose of a service is to run long term tasks in the background, even being able to live longer than the app that started the service. By the description on your downloadservice, it seems like it's only handling short term actions, during the lifetime of your app. Therefore I would recommend creating a singleton DownloadManager class that can manage the caching and handle the downloads using worker threads.

I would design this using Context.startService() instead of binding. In my mind, when you bind, you are tying the lifecycle of the service to the lifecycle of the binding activity. If the lifecycles aren't related, you should instead use startService().
If you use startService, you can simply send out a broadcast whenever a download completes to notify the activities (or to update progress) and when all downloads have completed, you can call Service.stopSelf() to shutdown the service.

You can have single activity for all of 12 tabs and change the content/view based on current tab. You can bind the service to the activity object. I think this approach is relevant as all tabs are dealing with same kind of task. Use something like
public class LifeLine extends TabActivity implements TabHost.TabContentFactory {
public void onCreate(Bundle savedInstanceState) {
TabHost tabHost = getTabHost();
TabHost.TabSpec spec
spec = tabHost.newTabSpec("Day").setIndicator("Day",
res.getDrawable(R.drawable.icon))
.setContent(this);
tabHost.addTab(spec);
...
tabHost.setOnTabChangedListener(new OnTabChangeListener() {
#Override
public void onTabChanged(String tag) {
if ("Day".equals(tag)) {/* Change the view as needed.*/
view.setDuration(1);
} else { /* Change the view as needed.*/}
view.invalidate();
Log.v("life", tag);
}
});

Related

Having an a fragment communicate via interface to activity that did not launch it

I have a fragment that communicates to an activity via an interface. This is cool and all but, is it possible to have the fragment communicate with an activity that did not launch it?
The reason why is I don't want one activity to be a million lines long of code implementing all interface methods for the fragments when I could just create "helper" activities to implement all the interfaces.
Currently I am using the Google Navigation drawer template so, maybe I could create new activities and group fragments around them. Im not sure if it will break my navigation drawer if I try to launch new activites.
Because of the activity lifecycle parameters of an activity are usually separated of the activity. You can store them in the savedInstancestate and retrieve them back. This way you make sure that your activity has the right information when it is restored.
If content changes somewhere during the app lifecycle it is good to store that content somewhere permanently (SharedPreferences, Database, File).
If you want to notify several parts of your app of an event a good way to go is a Local Broadcast.
Having said that it seems strange to me that another activity than the currently running one (and the one containing your fragment) should be notified of an event. When it is resumed it would collect the necessary information and update itself.
You're breaking android development practices. Fragments are encapsulated within an Activity. And an Activity is encapsulated within itself. An Activity should not communicate with another Activity via references.
Activities are communicating via references here, so I totally would not recommend doing this. But here's how you can do what you're asking.
class HelperActivity { // implement here
public static HelperActivity context = this;
public MyFragment myFragment = new MyFragment(this); // cast to implementation
}
class NormalActivity {
void onCreate {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_attach, HelperActivity.myFragment)
.commit();
}
}

What is faster Communicating, sending broadcast VS calling activity method directly in Android?

In my app, I have to call an activity method from the fragment.
I know I can do this in two ways:
1. Via sending a broadcast to activity:
Intent intent = new Intent("FILTER");
intent.putExtra("EXTRA", 1);
sendBroadcast(intent);
2. Or Calling the activity method directly:
((MyActivity) getActivity()).method();
I would like to know which way is faster and safe to communicate. Any help would be appreciated.
Thank you.
Loosely Coupled Fragment?
I am not sure about the speed. But on the design perspective You should use an interface to communicate with an Activity rather calling Activity method directly from your Fragment. ie ((MyActivity) getActivity()).method();
Because using an interface makes your Fragment independent from your
Activity. Let's say in future you want to use your fragment in Some
other Activity then you will not have to change anything in your
Fragment.
Interface
public interface Somelistener {
public void someMethod();
}
Your Loosely coupled Fragment
YourFragment extends Fragment {
Somelistener listener;
public void onActivityCreated(Context context){
listener = (SomeLisner)context;
}
public void buttonClick()
{
listener.someMethod();
}
}
So if you are using in your MainActivity. No problem
MainActivity implements SomeListener{
#Override
public void someMethod()
{
// Activity method
}
}
In future you want to use Fragment in SomeOtherActivity. No problem
SomeOtherActivity implements SomeListener{
#Override
public void someMethod()
{
// somethother method
}
}
BroadcastReceiver Approach?
TBH I have seen this approach for Service-Activity Communication. Not for Activity - Fragment communication.
For communicating between Fragments and the Activity that contains it, there's actually a much better 3rd option.
The better option is to use an Interface as a callback method. This is described very well in this documentation: https://developer.android.com/training/basics/fragments/communicating
Using an interface is much more preferred over your two methods because it's both safer and more efficient.
For your first method of using Broadcast Receivers, this is actually a very inefficient solution due to Broadcast Receivers not being meant for a task like what you're after.
Let me quote you something from the Android documentation:
Warning: Limit how many broadcast receivers you set in your app. Having too many broadcast receivers can affect your app's performance and the battery life of users' devices. For more information about APIs you can use instead of the BroadcastReceiver class for scheduling background work, see Background Optimizations.
https://developer.android.com/guide/topics/manifest/receiver-element
So yes, Broadcast Receivers will have a bigger effect on your app's performance and the device's battery life over the other method you suggested and the method I suggested.
Additionally, don't forget that a Broadcast Receiver is meant to listen to broadcasts. The type of Broadcast Receiver you're using in your example is actually a Global Broadcast where you didn't explicitly limit it's "range", so any Broadcast Receiver could potentially "listen" in to your broadcast. In terms of security, using a Global Broadcast like this isn't safe either.
You also don't want other apps to potentially fire off a Broadcast that coincidentally coincides with your app's Broadcast Receiver, causing it to receive data not meant for it and crashing due to this accidental and coincidental naming.
Honestly, there's more potential issues of using a Broadcast Receiver in a way it's not meant for.
As for your second method of directly calling the Activity's method... this is actually very inefficient for managing code. You're basically tying the Fragment tightly together with that specific Activity.
However, Fragments, by design, makes it common to be swapped into other Activities or Fragments... you'll basically have to do multiple if statements and casts each time you want to run code from it's parent.
Also, keep in mind that if you later change code in MyActivity, it can cause problems for this fragment due to you forgetting how tightly bound it is to the Activity.
But if you use the more preferred Callback Interface approach, it's simply a middleman meant to deliver a "Hey, DO something for me" message. Quick and direct. It's also plays friendly with any Activity or Fragment you want to attach this Fragment to later since those Activities or Fragments simply have to implement the Interface and the callback bridge between both parent and child is formed.
It is better to use interface to communicate from fragment to activity rather than a Local broadcast.
Activity will implement the interface and fragment will call the methods.

Android Calling objects from fragments

I'm not sure the best way to go about what I intend to do. I have an app that involves three fragments, each navigated to by a single activity that has a navigation drawer.
I have a text to speech class that initialises the text to speech engine. The problem is, is that it needs to be used by multiple different fragments. My idea was to create an object of the TTS class in the main Activity and extend functions so that they can be called by the fragments, like so:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mTextToSpeechService = ((NavigationActivity)this.getActivity()).GetTextToSpeechService();
}
The problem I see here is, what if I need to do something such as change the language within one fragment. That would mean I'd have to expose another function to set the TTS class within the activity, which doesn't seem right.
What is the best way to go about this?
There are three options:
Inherit from application and store singleton there. You can see how to do that here.
Create started service and use it from activity.(not bounded) See the link.
Store language setting in SharedPreferences and apply them to textspeech engine in activity lifecycle methods (onResume method).
Services are designed for tasks which are not connected to UI. If it's too much work to be done you may find third option to be a candidate for this particular problem.

Bind service to activity or fragment?

I am working on a music player app. I have a main activity which has multiple fragments, each displaying songs on the device album wise, artist wise etc..
I have a music service which handles all the playback and other stuff.
What I'm confused about is the binding of this service with various fragments I have.
Right now I'm binding the main activity and each fragment individually with the service and its working pretty much fine. But I was wondering if this is really the best practice? Is there any way to just bind the main activity with the service and then some how use it in its child fragments?
I maybe missing some very basic concept of activity or fragments or services. So someone please guide me in this regard.
I guess it's more of a conceptual question so any code isn't needed. But still if it's required then please let me know.
EDIT :
My question is: What would be a better way to bind a service with an activity with multiple child fragments(each of which would be using the service)?
Bind the Service to your activity and not the Fragment. The description of your application, one activity with multiple Fragment that are swapped in and out, makes this the most (and really only) practical approach.
When you bind a Service to an Activity you are tying its lifecycle to that of the Activity. See Bound Services. Each time you add or remove a Fragment in your activity that Fragment is created and destroyed. You do not want to try to link a service to this process because then you would have to create and destroy the service each time a new fragment is created or destroyed.
Instead bind to the host Activity. You can then interact with your host activity from your fragments with an interface to access the bound service or by Intent.
I think the cleaner architecture is to bind directly from the fragment. Regarding the problem outlined in Rarw's answer, you can bind to the service from your activity and from your fragments too. This way you are sure that the service will be there until the activity is not destroyed.
I can see two main advantages in connecting from the fragment:
Service connection is async, so inside the fragment you are never really sure that the service you are getting from the activity is not null. This will lead you at least to some null pointer checks and to some mechanism to refresh the fragment both when it's created and when the service connects (so you are sure you will display the data no matter which of the two happens first).
You do not depend on the particular activity your fragments lives in. To get the service from the activity I think you are doing a cast to the activity specific class. You could create an interface like BoundActivity with a method like getBoundService to obtain the service from it, but I think it's an overhead considering the advantage of point 1. And what if you have multiple services.
UPDATE
Here is a very simple project showing this.
https://github.com/ena1106/FragmentBoundServiceExample
You can access your activity from a fragment by getActivity()
you can tray using the event bus pattern with this library , eventbus publish/subscribe pattern.https://github.com/greenrobot/EventBus check the project site for more information.
you can send events from/to service to active or fragments
IF you need to get some data from your service to your fragment at the beginning of the fragment lifecycle, the onServiceConnected on activity could not be called yet, mainly when you rotate your device, and you'll get a NPE.
I think is a good idea let fragment make his own connections since the service is started with startService() and not with bindService().
I bind service in My Host Activity,and pass Ibinder's object by Bundle which is setted in arguments.
and my code may like this:
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
//put service in bundle
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
The only method I have found to be reliable is to use a loader in Fragment:
create a Loader in the fragment
use the context of the Loader (set to activity in initLoader at when the Fragment's onCreate is called)
bind the service in onStartLoading, using a ServiceConnection which calls forceLoad() as soon as the service is bound
optionally register callbacks in onStartLoading/onStopLoading
unbind the service in onStopLoading

Android: is it possible to refer to an Activity from a 2nd Activity?

This is a pretty simple question, but I have been unable to find anyway to accomplish what I am trying to do...
I want to launch a new Activity to display some complex information. Because of the complexity, it is undesirable to serialize the information into the intent's parameters. Is it possible for the the new Activity to get a reference to the launching activity, so it can call its methods?
If you use a custom application class, you can store information that will be kept between the activities.
See a tutorial here for instance.
The lifetime of an Activity cannot be depended upon. In this case, one way of sharing data is to have a singleton which holds the data to be shared between the two activities.
You can add a public static field to the first activity containing this (the first activity).
But beware that the first activity could be destroyed by Android while you are using the second activity, so you will have to implement a fallback method if the first activity is destroyed.
And don’t forget to unset the public static variable in the onDestroy() callback of the first activity or you will leak memory.
Is it possible for the the new Activity to get a reference to the launching activity, so it can call its methods?
Please do not do that. Android can and will destroy activities to free up memory.
Complex information like you describe should not be owned by an activity. It should be held in a central data model, like you would in any other application. Whether that central data model is mediated by a Service or a singleton or a custom Application object depends a bit on the type of data, caching models, risks of memory leaks, and so on.
You can make your complex objects public and static in ActivityA, and access them in ActivityB like this:
MyCustomObjectType complexFromA = ActivityA.complexObject;
this will work, however while in ActivityB, you can't always be sure that static objects from ActivityA will exist(they may be null) since Android may terminate your application.
so then maybe add some null checking:
if(null == ActivityA.complexObject) {
//go back to ActivityA, or do something else since the object isn't there
}
else {
//business as usual, access the object
MyCustomObjectType complexFromA = ActivityA.complexObject;
}
You could also use a Singleton object which extends Application. You would have the same problem when Android terminates your application. always need to check if the object actually exists. Using the Singleton extending Application approach seems to be the more organized way - but adds more complexity to implementation. just depends what you need to do and whatever works for your implementation.
You should create a separate class that both the activities can use.
public class HelperClass{
public void sharedFunction(){
//implement function here
}
}
I would recommend staying away from static variable in android. It can cause some unexpected behavior.
Use getParent() from new activity and call parent's method
Android Activity call another Activity method

Categories

Resources