How to update the RecyclerView Dataset from the background service.
The service maintains a socket connection with the server and when the server responds with data, the service has to update that in the recyclerview (that is in the MainActivity).
There is many way to send event from Serivce to Activity.
I recommend you the following way.
Bind and Callbacks
I think that Bind and Callbacks is official way.
Communication between Activity and Service
Example: Communication between Activity and Service using Messaging
EventBus
I think that EventBus is easy way.
https://github.com/greenrobot/EventBus
In Activity (or any where) :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
}
#Override
protected void onResume() {
super.onResume();
BusHolder.getInstnace().register(this);
}
#Override
protected void onPause() {
super.onPause();
BusHolder.getInstnace().unregister(this);
}
#Subscribe
public void onDatasetUpdated(DataSetUpdatedEvent event) {
//Update RecyclerView
}
}
BusHolder holds BusEvent instance:
public class BusHolder {
private static EventBus eventBus;
public static EventBus getInstnace() {
if (eventBus == null) {
eventBus = new EventBus();
}
return eventBus;
}
private BusHolder() {
}
}
The event posted:
public class DataSetUpdatedEvent {
//It is better to use database and share the key of record of database.
//But for simplicity, I share the dataset directly.
List<Data> dataset;
public DataSetUpdatedEvent(List<Data> dataset) {
this.dataset = dataset;
}
}
Send message from your Service.
BusHolder.getInstnace().post(new DataSetUpdatedEvent(dataset));
I hope this helps.
May be you should use some database like thing to store temporary data because I don't think it's a good thing to store data in an object on behalf of service component. It would be redundant to store whole list data into object as whether user comes back to app or not your object is going to cover memory which we should avoid throughout the development process. Best of luck.
Related
I'm new on Android and working an big app which has sending data to API and saving it on SQlite. All of this process is on one class file . But it leaves me on an error. Sometimes the device hanged. other scenario is the data is incomplete . I have read about Intent Service and Services and I want to learn about the two, but I'm wondering how to get all of my data from UI and put it on services. May I know How?
It depends on the nature of the application. If this should happen in response to a user input...you could well use an AsyncTask. Otherwise, a background service could also do the job.
What you should NEVER do is run a network operation and/or database access on the main UI thread.
Services can receive data via intents. The way to send these intents depend on the type of service (Started, Bound or both). There are plenty of resources out there you can read...here's one from Android documentation...
https://developer.android.com/guide/components/services
An Example of an AsyncTask
The example below shows an implementation of AsyncTask that fetches a user's details from a network resource...
public class FetchUserTask extends AsyncTask<String,Void, UserDTO> {
private FetchUserTaskListener listener;
#Override
protected UserDTO doInBackground(String...params){
if(params == null || params.length == 0)
return null;
String userID = params[0];
UserDataProvider provider = new UserDataProvider(userID);
try {
return provider.get(userID);
}
catch(Exception ex){
//log the error
return null;
}
}
#Override
protected void onPostExecute(UserDTO user){
if(listener != null)
listener.onCompleted(user);
}
public void setListener(FetchUserTaskListener listener){
this.listener = listener;
}
public interface FetchUserTaskListener{
void onCompleted(boolean success);
}
}
How'd you use this AsyncTask?
For example, in an Activity, you would use it as below...
public class UserDetailsActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//instantiate activity...
super.onCreate(savedInstanceState);
setContentView(R.layout.whatever_layout);
fetchUser(userId);
}
private void fetchUser(String userID){
FetchUserTask task = new FetchUserTask();
task.setListener(new FetchUserTaskListener<UserDTO>() {
#Override
public void onCompleted(UserDTO user) {
//CAUTION: make sure the activity hasn't been stopped before
//accessing any UI elements and/or context
}
}
task.execute(userID);
}
}
Note
You can (and will need to) make the example(s) above a bit more sophisticated. For example you can have the FetchUserTaskListener's onCompleted method return also an error message if an error occurred.
You will also need to check whether the activity has been paused or stopped before you access any context-bound data otherwise you might get an ILlegalStateException.
Make use of SQLiteOpenHelper class and it has methods to be overridden in your own class by extending SQLiteOpenHelper. Create Add, Update, Delete, Get methods as per your requirement in this class and keep this class as Singleton pattern. User Asynctasks to call those methids and you are done.
Hope that helps you visualise things in better way.
I have a rather general question.
Assuming I have a RecyclerView in some kind of a MainActivity. The content of this RecyclerView is being updated in multiple places in other activities.
For example there could be the possibility to make new entries by starting a new Activity and saving it there.In that case I would intuitively start that activity with startActivityForResult() and receive the changes in the onActivityResult() method in the MainActivity.
But lets say deeper inside the application, there is the possibility to delete all entries. Now how do we notify the MainActivity about this change in the dataset? I thought about setting a flag of some kind and clearing it after the content has been updated in the MainActivity. But somehow using global variables does not really follow the principle of proper encapsulation, does it?
Sorry for this vague question, but I find it quite hard to properly handle information flow in Android in a elegant manner, so here we are.
How about a local broadcast? You can find the idea of broadcast in this document. You need local broadcast and it is preferred if you want to pass data within your app only.
Android apps can send or receive broadcast messages from the Android system and other Android apps, similar to the publish-subscribe design pattern. These broadcasts are sent when an event of interest occurs. For example, the Android system sends broadcasts when various system events occur, such as when the system boots up or the device starts charging. Apps can also send custom broadcasts, for example, to notify other apps of something that they might be interested in (for example, some new data has been downloaded).
You can use Handler to pass the Message in Activity and then You have to update RecyclerView. Like,
1) In Activity.
public static Handler mHandler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
if(msg.what == 1223){
//update RecyclerView
}
return false;
}
});
2) pass message When you want to update RecyclerView
Message msg = new Message();
msg.what = 1223;
Activity1.mHandler.sendMessage(msg);
You can use EventBus to handle it.
Define a class for your event
public static class MyEvent {
int event;
/* define your fields */
}
And prepare your subscriber in main activity
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMyEvent(MyEvent myEvent) {
switch(myEvent.event) {
/* Do what you need */
}
};
Now where you need to make change, call your subscriber like this:
MyEvent myEvent = new MyEvent();
myEvent.event = 1;
EventBus.getDefault().post(myEvent);
You can read more about EventBus in here
If you were using RxJava2, RxAndroid. Then you could try this.
Create a Bus:
public final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject = BehaviorSubject.create();
public static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
}
In your WaitingActivity where you want to receive data(where you want not to use onActivityResult in your case)
Disposable disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
if (o instanceof DataObject) {
//((DataObject) o).getValue();
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
});
In your activity where you want to send data
RxBus.getSubject().onNext(dataObject);
startActivity(new Intent(CurrentActivity.class, WaitingActivity.class));
Finally don't forget to dispose your disposable to avoid memory leaks in your WaitingActivity
#Override
protected void onDestroy() {
super.onDestroy();
disposable.dispose();
}
Your data should be separate from view, in model. If some other activity changes data ideally recycler view must be updated from there. So no matter which activity does what, when you refresh data on load or resume of your recycler view you will always get correct results.
I am using a TextView inside of a Fragment.
I wish to update this TextView outside of the fragment (but also outside of an activity) from a callback class.
For example the user scrolls, the callback is called somewhere in my package, and I want the fragment view to be updated.
Can anybody explain how to do this? I did use a Local Broadcast Receiver but it wasn't fast enough in its updating.
Eventually looked at Otto but as we had Guava I implemented a singleton eventbus and used Guava publish/subscribe model to pass stuff around.
Otto however looks very similar.
Use Otto: http://square.github.io/otto/
public class UpdateEvent {
private String string;
public UpdateListEvent(String string) {
this.string = string;
}
public String getString() {
return string;
}
}
...
...
public void update() {
SingletonBus.INSTANCE.getBus().post(new UpdateListEvent(editText.getText().toString()));
}
...
public class FragmentA extends Fragment {
#Override
public void onResume() {
super.onResume();
SingletonBus.INSTANCE.getBus().register(this);
}
#Override
public void onPause() {
SingletonBus.INSTANCE.getBus().unregister(this);
super.onPause();
}
#Subscribe
public void onUpdateEvent(UpdateEvent e) {
//do something
}
}
public enum SingletonBus {
INSTANCE;
private Bus bus;
private SingletonBus() {
this.bus = new Bus(ThreadEnforcer.ANY);
}
public Bus getBus() {
return bus;
}
}
EventBus is a nice and elegant way for communication between modules in Android apps. In this way you should register your fragment as a event subscriber, and post a this specific event from other part of your code. Keep in mind that only UI thread can work with Views.
I don't exactly understand what you want to achieve and why BroadcastReceiver does not work for you, but you may either:
1) try using callbacks (if it is possible in your app design);
2) try using this or that event bus implementation;
Both would work pretty fast without much overhead, compared to broadcasting.
In case 2 you won't have to maintain callback dependencies/references.
I'm using SQLite database in android and want to listening for any database changes. How can I do this?
Thanks for all future help!
In fact, SQLite offers such functionality: SQLite Data Change Notification Callbacks
How it can be used in Android is another story though..
SQLite doesn't offer any change listener functionality; you have to monitor it yourself. The simplest way to achieve this would be to send a Broadcast (or even better, a LocalBroadcast) anytime you modify the database. Some of the database libraries already offer functionality that is similar to this - check out GreenDAO.
a simple implementation of changeListener for the database on Android
suppose that you have a class to handle your queries in your android app, we need to make the database methods observable.
and also we need some listeners to observe the abovementioned observable. let's make the database handler observable:
let's make the observable interface:
public interface DatabaseObservable {
//register the observer with this method
void registerDbObserver(DatabaseObserver databaseObserver);
//unregister the observer with this method
void removeDbObserver(DatabaseObserver databaseObserver);
//call this method upon database change
void notifyDbChanged();
}
now implement the observable in your database class
public class LocalStorageDb extends SQLiteOpenHelper implements DatabaseObservable {
LocalStorageDb lDb;
//make it Singleton
public static synchronized LocalStorageDB getInstance(Context context) {
if (mlLocalQuickChatDB == null) {
mlLocalQuickChatDB = new LocalStorageDB(context.getApplicationContext());
}
return mlLocalQuickChatDB;
}
//there are some methods to do some queries
public void createContact(Foo foo, Bar bar){
//some queries here
//call the Observable Method to let know the observers that it has changed
onDatabaseChanged();
}
//now override the DatabaseObservable method which is responsible to notify the listeners
#Override
public void onDatabaseChanged() {
for (DatabaseObserver databaseObserver:observerArrayList){
if (databaseObserver!= null){
databaseObserver.onDatabaseChanged();
}}
}
//also you need functions to **register** or **unregister** the observers:
#Override
public void registerDbObserver(DatabaseObserver databaseObserver) {
if (!observerArrayList.contains(databaseObserver)){
observerArrayList.add(databaseObserver);
}
#Override
public void removeDbObserver(DatabaseObserver databaseObserver) {
observerArrayList.remove(databaseObserver);
}
then we need an observer to observe the changes:
public interface DatabaseObserver {
void onDatabaseChanged();
}
now in your activity or fragment, there is a function to fetch the changes, like getLocalContact. implement the observer on the fragment for example:
public class ExampleFragment extends Fragment implements DatabaseObserver {
LocalStorageDB localStorageDB;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
localStorageDB = LocalStorageDB.getInstance();
}
#Override
public void onPause() {
super.onPause();
localStorageDB.removeObserver(this);
}
#Override
public void onResume() {
localStorageDB.registerObserver(this);
super.onResume();
}
public ExampleFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_example, container, false);
}
#Override
public void onDatabaseChanged() {
getLocalContact();
}
private void getLocalContact(){
//function to fetch contacts from database
}
}
Hi I'd like to add a new suggestion Incase of firebase usage. You can make a new json node for users messages and use On Data Change to detect the new number of unread messages then update your ui in the onDatachange. Is that smart or far away from the main idea?
I want to implement AsyncTaskLoader for my custom data source:
public class DataSource {
public interface DataSourceObserver {
void onDataChanged();
}
...
}
DataSource will keep list of registered observers and will notify them about changes. CustomLoader will implement DataSourceObserver. The question is how to properly notify CustomLoader since Loader.onContentChanged() must be called from UI thread but in my case DataSource operations (and calls to DataSourceObserver.onDataChanged()) will be done from background threads.
Updated with idea from Selvin tip:
public class CustomLoader extends AsyncTaskLoader<...> implements DataSource.DataSourceObserver {
private final Handler observerHandler;
public CustomLoader(Context context) {
super(context);
observerHandler = new Handler()
}
#Override
public void onDataChanged() {
observerHandler.post(new Runnable() {
#Override
public void run() {
onContentChanged();
}
});
}
}
I've had a lot of success using Local Broadcasts in a case that's very similar to yours. The method involves an AsyncTaskLoader implementation that will register a BroadcastReceiver listening for a particular String that describes what's changed. This BroadcastReceiver keeps a reference to the Loader and calls onContentChanged. When the data needs a refresh, make the Local Broadcast with the aforementioned String and the BroadcastReceiver will hear it and trigger the load. Here's some example code, it may not work perfectly if you drop it in, I've generalized some class names, but hopefully you'll get the idea:
Broadcast Receiver to be used in your Loader Implmentation:
public class LoaderBroadcastReceiver extends BroadcastReceiver
{
private Loader loader;
public LoaderBroadcastReceiver(Loader loader)
{
this.loader = loader;
}
#Override
public void onReceive(Context context, Intent intent)
{
loader.onContentChanged();
}
}
Loader Implementation registers the Receiver in onStartLoading()
private LoaderBroadcastReceiver loaderBroadcastReceiver = null;
#Override
protected void onStartLoading()
{
//... some code here
if(loaderBroadcastReceiver == null)
{
loaderBroadcastReceiver = new LoaderBroadcastReceiver(this);
LocalBroadcastManager.getInstance(getContext()).registerReceiver(loaderBroadcastReceiver, new IntentFilter("NEWDATASTRING"));
}
//... some more code here
}
Finally, here's how onDataChanged in DataSource will make the Broadcast. It'll need a Context to help send the Broadcast. Since this can be called from an arbitrary Thread, I'd use your ApplicationContext, since an Context from an Activity could cause problems if the Activity is destroyed.
public class DataSource
{
public interface DataSourceObserver
{
void onDataChanged(Context applicationContext)
{
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("NEWDATASTRING"));
}
}
...
}
You'll probably want to play with it a bit to see how it works for you. You can use different Strings to differentiate different data that needs loading. You'll also want to unregister the Receiver at some point, perhaps in onReset(). Let me know if any of this in unclear in the comments, I'll try my best to clarify.