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.
Related
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.
Inside my activity I have a broadcast receiver that I initialize as such:
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateViews();
}
};
updateViews() is a function inside the activity that is only used within the broadcast receiver. How can I create this receiver as a separate class that can play with the views (rotate, delete etc.) of my activity?
In addition, I have a compass within the activity. It works, however I would also like to make the compass a separate class that can send data to the activity. It will not change the views of the activity but only update certain double/float values.
#Override
public void onSensorChanged(SensorEvent event) { }
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { }
How can I create this receiver as a separate class...?
This answer assumes that "separate class" means you want a BroadcastReceiver that is defined in its own source file and is not an inner class of an activity. Before offering a solution, I'll ask, what do you expect to gain by this? Do you have multiple activities that will use the receiver? If not, it's best to leave it as an inner class of the single activity that uses it. If you don't like using the anonymous inner class in the code you posted, you can declare is as an inner class:
private class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// As an inner class of the activity, this receiver has
// access to all activity members.
updateViews();
}
}
If you really want it as a standalone receiver, the example code below is a starting point. Be sure to register/unregister the receiver in your activity's onResume/onPause callbacks. The example includes code for a less safe approach, using a plain activity reference, and a safer approach using a weak reference. Only one is needed.
public class MyBroadcastReceiver extends BroadcastReceiver {
// For less safe use
private MyClientActivity mActivity;
// For more safe use
private WeakReference<MyClientActivity> mActivityRef;
public MyBroadcastReceiver(MyClientActivity activity) {
mActivity = activity;
mActivityRef = new WeakReference<MyClientActivity>(activity);
}
#Override
public void onReceive(Context context, Intent intent) {
// Less safe
mActivity.findViewById(R.id.action_bar);
mActivity.someActivityMemberMethod();
// etc
// More safe. Guards against failure to unregister
// this receiver when activity is paused.
MyClientActivity act = mActivityRef.get();
if (act != null && !act.isDestroyed()) {
mActivity.findViewById(R.id.action_bar);
mActivity.someActivityMemberMethod();
// etc
} else {
// Error: Activity failed to unregister this receiver
}
}
}
I would also like to make the compass a separate class that can send
data to the activity
Same assumption and question as above: Will the compass be used by multiple activities? If not, it's probably best to make it an inner class of the single activity. If there are multiple client activities, consider using a started-service. It could notify activities of sensor events using a local broadcast or an event bus such as greenrobot.
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 have an android application that is connected to the computer via USB cable. I use a TCPServer Class to send messages and listen. For example:
When I send a message like: request:x
I get the response: response:x:55
I need to make changes on my activity according to the response I get. At the moment I temporarily solved the problem by passing activity and activity class object to the TCPServer's constructor
public TCPServer(int portNum, Activity activity, IntroActivity ia) {
super();
port = portNum;
this.activity = activity;
this.ia = ia;
}
Then after I receive the response:
void updateButton(final int color, final String txt) {
activity.runOnUiThread(new Runnable() {
public void run() {
ia.getConnectionButton().setBackgroundColor(color);
ia.getConnectionButton().setText(txt);
}
});
}
As you see, this is not effective at all. I need to somehow notify the activity whenever a relevant variable is received. I use a Class for GlobalVariables and change those static variables after listen(), however I am having troubles notifying the activity.
First of all, it is almost always bad practice to pass Activity instances around. This is a time when it's bad.
Define an interface and use a callback to let the activity know that a response has been received.
public interface ResponseReceivedListener {
void onResponseReceived(int arg1, string arg2); // <- add arguments you want to pass back
}
In your TCPServer class:
ArrayList<ResponseReceivedListener> listeners = new ArrayList<>();
// ...
public void setResponseReceivedListener(ResponseReceivedListener listener) {
if (!listeners.contains(listener) {
listeners.add(listener);
}
}
public void removeResponseReceivedListener(ResponseReceivedListener listener) {
if (listeners.contains(listener) {
listeners.remove(listener);
}
}
When you receive a response:
for (ResponseReceivedListener listener : listeners) {
listener.onResponseReceived(arg1, arg2);
}
In your Activity:
public class MainActivity extends Activity implements ResponseReceivedListener {
// ...
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
tcpServer.setResponseReceivedListener(this);
// ...
}
public void onResponseReceived(int arg1, string arg2) {
// do whatever you need to do
}
// ...
}
All from memory so please excuse typos.
This approach decouples the classes. The TCP Server has no knowledge of the activities. It simply calls back to any listeners registered. Those listeners might be Activities, they might be services. They might be instances of MySparklyUnicorn. The server neither knows nor cares. It simply says "if anyone's interested, I've received a response and here are the details".
In android i am using two services and i want to move some details from one to another service. Becouse of this i create new class Settings with set and get methods but i don't know how to connect two services with this one class file that i can move details from one service to another and remember each one.
for example:
i want to transfer property Boolean from service 1 to service 2 and then in service 2 check if this property is true and if is true then i execute some code in this seocnd service... hope is better explanation
class example:
public class Settings {
private int currentAudioManager;
private Boolean isChanged;
public int getCurrentAudioManager() {
return currentAudioManager;
}
public void setCurrentAudioManager(int currentAudioManager) {
this.currentAudioManager = currentAudioManager;
}
........
Hope you understand what i want.
You can register broadcast receiver and send broadcasts parceable data between your services.
public class Service1 extends Service {
#Override
public void onCreate() {
super.onCreate();
registerReceiver(recevier1, new IntentFilter("YOUR_SERVICE_ACTION1"));
// if something happen in your service"
sendBroadcast(new Intent("YOUR_SERVICE_ACTION2")); // send to second service
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
public void onDestroy() {
unregisterReceiver(recevier1);
};
Service1Receiver recevier1 = new Service1Receiver();
private class Service1Receiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
intent.getExtra("me.SERVICE");// handle your data
}
}
}