The setup: custom adapter that feeds data into ListView. Data is based on some numbers calculated by external service. I am using AIDL to bind the service and get the data I need.
Problem: How do I know when to unbind the service? Connection is private to adapter itself and the only way to detect that whole application is shutting down is overriding unregisterDataSetObserver. Or at least I didn't find another way and this one worked well with same adapter using content provider+content observer. Does not work with AIDL though - I'm getting the ServiceConnectionLeaked error.
I know I can add a "unbindFromService" method to my adapter and call it from onDestroy() of my activity but that isn't elegant enough for me. I'd prefer some "honest" trigger if possible.
Implement ActivityLifecycleCallbacks in your CustomAdapter and then from your activity call
mAdapter.setActivity(this);
Adapter class
public class CustomAdapter extends ArrayAdapter<String> implements ActivityLifecycleCallbacks{
Activity mActivity;
public void setActivity(Activity activity) {
/* or you could remove setactivity and do below operation in Constructor */
mActivity = activity;
mActivity.getApplication().registerActivityLifecycleCallbacks(this);
}
#Override
public void onActivityDestroyed(Activity activity) {
/*unregister so that you do not get callbacks after activity is destroyed*/
if(activity instanceof MainActivity)
mActivity.getApplication().unregisterActivityLifecycleCallbacks(this);
/*unregister your activity or in any other callbacks*/
}
/* skipped other dummy functions*/
}
Connection is private to adapter is wrong. Adapter is Context-less and so a service should not be private to the adapter.
Your adapter will never be able to bind to the service without a Context. Inherently, you have a function that provides a Context for your adapter to bind to the service. Therefore, the bound connection is not private to your Adapter: It is the Context passed that allowed its creation.
The real problem is that AFAIK there is no way to listen to the context. Meaning, say that you held the context instance inside your adapter: There is no way to allow the adapter to listen to this context and somehow know that the Activity is done. This leads you to accept the not-elegant-enough-for-you solution: Your activity should notify the adapter which is using its context! Otherwise, you will end up with a more hacked-up solution.
Please notice that the whole debate in the comments deals with your "service is private to the adapter" thingy. In reality, the elegant way to do this would be to have a ServiceProvider interface that provides the connection and this would be implemented by your activity. You adapter should be initialized with a ServiceProvider passed. Some functions would be getConnection and isRunning. In this way, your Activity will take care of the connection while the Adapter uses it. (Synchronization)
I would use a Service to insert fresh data into a SQLite table or to refresh a ContentProvider, then use a SimpleCursorAdapter (android.support.v4.widget.SimpleCursorAdapter which is backward compatible) and the LoaderManager, SQLiteCursorLoader/CursorLoader, Loader pattern to show the data on a List or ListFragment.
The SQLiteCursorLoader is not given by the Android SDK, but Mark Murphy have one on in GitHub, so you could take a look at it if you want to persist the fresh data in a local SQLite DB.
Since the Service can be independent from the underlying data in SQLite and the Adapter for the list you can unbind it when is finished inserting new data or refreshing the provider so you can procede with the recommended patterns to unbind Services (See this question: Android: How to safely unbind a service) and take a look at http://developer.android.com/reference/android/app/Service.html#ProcessLifecycle.
I really hope this gives you a hint. Good luck!
Related
What is the best way to pass data from an Activity to fragment, but the fragment is hosted by another activity.
Elaborating:
Activity A hosts Fragment A (content in activity A) <== Activity B
I have already achieved this, but apparently, my way of doing it has caused some memory leak.
An example would be to refresh a RecyclerView contained in a fragment when an activity is closed, but I do not want to put it in the onResume.
interface contained in the activity(is not the host)
public class Activity extends AppCompatActivity{
public static OnlistenClose delegate;
public interface OnlistenClose {
void refreshList();
}
}
//fragment that implements the interface
public class FragmentB extends Fragment implements Activity.OnlistenClose{
Activity.delegate = this;
#Override
public void refreshList(){
//my code
}
}
Using square/leakcanary indicates there are leaks.
What is the best way to pass data from an Activity to fragment, but the fragment is hosted by another activity.
Ultimately, you are passing data from one activity to another. If the data is stored permanently in a database or file, then the receiving activity should just read the data after the first one has updated it.
On the other hand, if you are passing data that only needs to be available in memory, then you can pass the data with an Intent.
As you have seen, keeping a reference to one activity in another activity causes memory leaks. The callback method which you are attempting to use is only valid for the activity which owns the fragment, not for a second activity. Instead, you should honor the activity lifecycle and use Intents for communication as appropriate.
There is no "best practice" for a general question or even a specific circumstance.
Standard ways of passing data:
Intents
Storage
Shared Preferences
Internal Storage
External Storage
SQLite Databases
Network Connection
Static class (no link necessary)
Etc, etc
There are many ways to accomplish a task as there are ways to describe that task in a sentence.
I finished my work.
I completed my task.
It's published in the Play Store.
I'm done.
I have nothing to do.
I have noticed that I can create a callback by using two methods:
Receive an interface at the constructor of the class implementing the callback.
Receive the activity itself at the constructor of the class implementing the callback.
First Approach
For example I could do this:
public MyClass(MyInterface listener) {
this.listener = listener;
}
And I could call myCallBackFunction() defined in MyActivity (which implements MyInterface) by writing listener.myCallBackFunction()
Second Approach
Or I could do this:
public MyClass(MyActivity activity) {
this.activity = activity;
}
And I could call myCallBackFunction() defined in MyActivity by writing activity.myCallBackFunction()
My concern: Is one approach better than the other? And if so, why?
Usually speaking, you'd better use first approach. The reason is here:
Suppose you have 4 classes, first is Vehicle, second is Bicycle, third is Bus and third is Subway. Bicycle, Bus and Subway are subclasses of Vehicle. There may be a method call drive(), which should have a parameter. Which one do you think best for parameter type? Bicycle, Bus, Subway, or Vehicle?
Apparently, passing Vehicle is best because you may want to add other kinds of vehicles in the future or you don't want to write nearly same code for different kinds of vehicles in your project. It is same to use Interface rather than specific class.
As a result, passing an interface to a method is always correct and better than passing a specific type of object to it. You can always implement the interface in other classes and they will also be parameter of that method. You don't need to think about actual type of the parameter, which will confuse you and make you think more about specific code for specific type. Instead, only one type, one piece of code macroscopically.
So the conclusion is: using MyActivity is good, but using MyInterface is better.
I think it can depend on what you're trying to achieve: the first approach may in general be better suited since MyClass is not required to know anything about the implementation of that interface method, so it's great for passing different objects (e.g. a RecyclerView Adapter being created with an OnItemClickedListener injected in the constructor can be re-used in different activities/fragments implementing the interface, whilst the adapter doesn't need to change). It helps to prevent coupling.
The second approach leaves one wondering: is MyClass tied to the activity lifecycle? It may still hold a reference to the activity after that activity has actually been destroyed by the system, which would leak memory as the Activity object is not garbage-collected. It's a matter of design, and can be seen as code smell, can you not achieve what you wanted within the activity itself, and rely on the lifecycle callbacks onCreate/.../onDestroy?
Is one approach better than the other? And if so, why?
Using Interface is the best way..
Assume that you are having
1) Activity MyActivity
2) class which extends Activity or View or Asynctask is Myclass.
Both MyActivity and Myclass are Implements MyInterface
If you are passing Activity you need to add one more constructor
public MyClass(MyActivity activity) {
this.activity = activity;
}
public MyClass(Myclass myclass) {
this.myclass= myclass;
}
If you are using interface
public MyClass(MyInterface listener) {
this.listener = listener;
}
that's it.
In one approach you are creating an instance of Interface and in another an instance of implementing activity. what is best is:
Interface interface;
public myClass(Activity acitivity)
{
interface = (Interface)activity;
}
i.e. typecast activity to interface. Now you can callback the overridden functions in Activity.
This way you can now loose information of Activity's functions and just access the overriden functions of interface from the activity.
You can avoid typecasting and create an object of Activity, if you need access to interface callbacks AND the activity's function/variables.
It depends on your needs.
I am working on an app that serves as a thin client. The app will be in constant TCP connection with a server and the amount of data flowing between them will be very small, only textual.
How should I handle the data, considering this is a typical scenario:
App is started, establish TCP connection to server
The server sends some data, save this data and display them in a ListView
User interacts with the app, server will asynchronously send data, it should be appended to the ListView
I was thinking there might be the main activity havin a member List where it would store the objects to show in the ListView. Upon (asynchronously) receiving some new data, the main thread would be notified and it would reflect the changes on the ListView.
Is there a better, cleaner way? Implementing a contentprovider and using a Loader seems a bit too much, considering I really don't need the data to persist.
You can create your own application extending android.app.Application and your List will be an attribute of your application.
Then using the Context into your AsyncTask you can add the content get asynchronously into your List.
Finally a nice way to update the ListView could be to register your ListActivity as a Listener of all changes of the List content. For example implementing :
public interface IListChangeListener {
public void onContentChange();
}
For each modifications of the List content your application will loop on all the listeners to invoke onContentChange();
On your list activity you will have to implement :
public void onContentChange(){
// Reload the list adapter content and all the notifyDataSetChanged here
}
Don't forget to deregister the ListActivity as listener when it will be destroyed...
I have followed this tutorial to use SQLite db in my android app.
Since I am a beginner I'm having problems understanding "context" parameter used in the example.
I want to call adapter and insert/update/delete records from a class that does not extend activity which in this example stands for context.
Now I don't know what to pass in the adapter as context, since I'm not calling adapter from activity.
Can someone please explain this?
Pass the ActivityName.this as class context as argument to the adapter class's constructor
the ActivityName is the name of the Activityclass in which you are calling the adapter
you could imagine that the context defines WHERE/WHEN the sqlite database exists. sqlite databases do not exist on their own, they exist within the confines of your activity, thus in your activity's context.
for the next steps you must understand that the context is a dynamic "thing" (in reallife you could imagine it as someone's HERE and NOW). the context is individual to the activity and its moment, just as your here and now are yours and yours only and change over time.
if you are calling a class from within your activity, then this does the trick (passing the activity's context from within the activity itself is OK - sorta like you saying to your buddy: this is how i am feeling NOW).
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context contextNew = this;
myClass(contextNew);
an easy all around solution (panacea) would be to create a static Context variable (contextVariable) inside MyActivity and access it directly from without via: MyActivity.contextVariable. but that does not work, because you get an error when you try to declare/use a static Context variable.
So, if you plan on using sqlite inside a service that is NOT called from within the main activity, like, for example, a service triggered by a broadcast receiver (neither a service nor a receiver have a context per se), then you must access the original application's context from within said receiver.
accessing the original activity's context is simple, but far from obvious.
this works for me (thanx #ZiGi and #Cristian):
import android.app.Service;
import android.content.Context;
public class BatchUploadGpsData extends Service {
public Context contextNew;
#Override
public void onCreate() {
contextNew = getApplicationContext();
this is an example from working code that uploads navigation data to a database on the web every time the android device connects to a WIFI network. i have a receiver listening to connectivity changes (existing as a separate class called directly "from" Manifest file).
i hope that this makes sense, if you want more detail on this, check out this post of mine where i have the complete (barebones) code for said receiver.
As you see in the example, there is a context passed to the ToDoAdapter. You can pass activity as a context or activity.getApplicationContext(). Read about context here.
I am working on an Application that require some interaction between two activities, and I am not sure of what is the best way to achieve it:
One of the Activities is a "Logbook" (Just a ListView that displays a bunch of events).
The other Activity allows the user to create the events, that will be sent (and displayed in the Logbook).
How do I notify my Logbook Activity when a new Event is ready to be added?
Also, where should I add the event to the database? From the Logbook Activity, when I add it to the ListView, or from the NewEvents Activity, as soon as it's ready?
Thanks!
Ok, I found how to do it, using a BroadcastReceiver:
In my Logbook activity, I just set up a new custom receiver onCreate():
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logbook);
registerReceiver(new EventReceiver(this), new IntentFilter("SEND_NEW_EVENT"));
Now, I can make the calls in my newEventActivity:
public void sendToLogbook(int eventId){
Intent i = new Intent("SEND_NEW_EVENT");
i.putExtra("newEvent", this.newEvents[eventId]);
sendBroadcast(i);
}
Of course, I had to create my CustomReceiver Class, and override the onReceive() method to do what I want:
public class EventReceiver extends BroadcastReceiver {
private ActivityLogbook activity;
public EventReceiver(ActivityLogbook activity) {
this.activity = activity;
}
public void onReceive(Context context, Intent i) {
this.activity.addToReport((Event)i.getParcelableExtra("newEvent"));
}
}
It works great so far, but if you do have comments/concerns about this, please tell me!
Thank you!
If I recall cporrectly the Notepad project which is included in the android sdk and is also part of the tutorials online is a good examaple which should satisfy your needs.
To borrow from MV-* (Model-View-something or other) patterns, separate your idea of the Model (in this case, your Event objects) and what is displaying them (the View, or in your case an Activity) and it'll become more clear.
If you have your events somewhere global where all activities can interact with them, then you can work with the model and display the model from wherever and however you choose.
One simple suggestion is have a class (EventController or something like that) that allows you to interact with the Events collection, and make it available through a derived Application class. I can explain further if that doesn't make sense. I have a pattern I use in my Android apps whereby all Activity classes have access to a custom global Application instance, so my model is a model and can be accessed by whatever Activities I want to have access.
This is merely one approach, and as always, there are many that may suit your needs.
One possibility would be:
The ListActivity gets all the data each time it is resumed and updates the ListView accordingly
The NewEventActivity does all the job of storing the Event and simply finishes
You can improve it a bit more:
The ListActivity gets all the data when it starts
The ListActivity starts the NewEventActivity expecting a OK/CANCELLED result
The NewEventActivity does all the job of storing the Event and returns a result saying OK or CANCELLED
Depending on the result it gets from the NewEventActivity, ListActivity reloads its data or not