I am trying to implement an architecture similar to the one presented at the Android Developer Summit 2015: https://github.com/yigit/dev-summit-architecture-demo. My application has a simple class that handles the network requests. The class uses Retrofit 2 for the requests. I am also using Dagger 2 for dependency injection.
I am trying to achieve something very simple. My activity tells a controller to fetch data. The controller then makes a call to my REST client to perform a network request. When the request completes successfully I want to broadcast an event to my Activity so that it can update the UI. However, the event is not being broadcast.
I am using the LocalBroadcastManager to broadcast events. My activity registers/unregisters for broadcasts in the onResume/onPause methods. My REST client has an instance of the application context which is provided through dependency injection. The REST client uses the application context to send the broadcast.
My first suspicion was that the broadcasts were not being sent because the network requests are executed on a worker thread whereas the activity is expecting broadcasts on the main thread. However, this type of scenario shouldn't be a problem if the Android documentation is correct.
This is my activity.
public class BaseActivity extends AppCompatActivity {
private ApplicationComponent mApplicationComponent;
private EventBroadcastReceiver mEventBroadcastReceiver = new EventBroadcastReceiver();
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mApplicationComponent = getMovieManagerApplication().getApplicationComponent();
}
#Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("now_playing");
LocalBroadcastManager.getInstance(this).registerReceiver(mEventBroadcastReceiver, intentFilter);
}
#Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mEventBroadcastReceiver);
}
protected MovieManagerApplication getMovieManagerApplication() {
return (MovieManagerApplication) getApplication();
}
protected ApplicationComponent getApplicationComponent() {
return mApplicationComponent;
}
protected void update(Intent intent) {
}
private class EventBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
update(intent);
}
}
}
And this is my REST client.
public class MovieRestClient extends BaseRestClient implements Callback<MovieResponse> {
#Inject
public MovieApiService mMovieApiService;
#Inject
public Context mApplicationContext;
public MovieRestClient(ApplicationComponent applicationComponent) {
super(applicationComponent);
getApplicationComponent().inject(this);
}
#Override
public void onResponse(Response<MovieResponse> response) {
if (response.isSuccess()) {
Parcelable parcelable = Parcels.wrap(response.body());
MovieResponse movieResponse = Parcels.unwrap(parcelable);
Intent intent = new Intent("now_playing");
intent.putExtra(MovieResponse.class.getName(), parcelable);
LocalBroadcastManager.getInstance(mApplicationContext).sendBroadcast(intent);
}
}
#Override
public void onFailure(Throwable t) {
}
public void getNowPlaying() {
mMovieApiService.getNowPlaying(API_KEY).enqueue(this);
}
public void getPopular() {
mMovieApiService.getPopular(API_KEY).enqueue(this);
}
public void getTopRated() {
mMovieApiService.getTopRated(API_KEY).enqueue(this);
}
public void getUpcoming() {
mMovieApiService.getUpcoming(API_KEY).enqueue(this);
}
}
Any help would be greatly appreciated.
Related
I am using Android Room to store my application data. After device reboot I need to retrieve them and do some functions. I am using LiveData for getting data from database. But I cannot bind Broadcast Reciever as owner. How should I resolve this problem?
public class BootReciever extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
if (context != null) {
ProductUIRepository mRepository = new ProductUIRepository(context.getApplicationContext());
mRepository.findAllProducts().observe(this, new android.arch.lifecycle.Observer<List<ProductUI>>() {
#Override
public void onChanged(#Nullable List<ProductUI> productUIS) {
NotificationMan.setAlarmForProducts(context, productUIS);
}
});
}
}
}
using "this" makes that applications isn't compiling.
I have one doubt about using services. I have a service that initializes an object, is it a bad practice to pass an instance of the service to the object so it can be used for that object? A simplified object would be:
public class MyService extends Service {
MyObject myObject = new MyObject(this);
...
}
public MyObject {
private MyService myService;
public MyObject(MyService myService) {
this.myService = myService;
}
...
private void exampleMethod() {
myService.method();
}
}
What do you think? Is it a bad practice? How could I solve that issue without passing the service's instance?
The fact is that I want to split the code in two classes because the features are different, the websocket is connected from the service class, but the methods to parse/send events through the websocket are in the second class. I want to do this way in order to avoid having one class with 2000 lines of code, and by splitting the code by features. The service handles the websocket connection, while the other class handles the other features. As everything is asynchronous, the second class needs an instance of the service class. For instance: if an error is received and parsed (on the second class), this second class must call the service class to update its status and do a reconnection.
EDIT:
I'm thinking about implementing the following solution:
public class MyService extends Service {
MyObject myObject = new MyObject() {
protected void onSuccess() {
...
}
};
...
}
public abstract class MyObject {
public MyObject() {
}
protected abstract void onSuccess();
...
private void exampleMethod() {
...
onSuccess()
}
}
The more I think about it, the better solution I think it is. What do you think?
Thank you very much in advance!
This makes no sense at all. I suggest you to use a interface if you need to pass a callback to a dao (the websocket controller). The thing is that you should use your service to implement your websocket controller.
Please add the websocket code, so we can suggest more changes.
EDIT:
public interface onGetData {
public void onSuccess(Object response) // here you pass the obj type you need in your service
public void onError(Object error) // same, but if things fail
}
public class MyService extends Service implements onGetData {
#Override
public void OnCreate() {
MyObject myObject = new MyObject(this);
}
#Override
public void onSuccess(Object response) {
}
#Override
public void onError(Object error) {
}
}
public MyObject {
private OnGetData onGetData ;
public MyObject(OnGetData onGetData) {
this.onGetData = onGetData;
}
private void onRequestSuccess(Object response) {
onGetData.onSuccess(response)
}
private void onRequestError(Object error) {
onGetData.onError(error)
}
}
An Activity contains a Fragment which in turn contains a child Fragment, which requests a Service. The app tries to implement dobjanschi rest architecture.
When the Service is done working it has to propagate operation result. I tried using a PendingIntent but it seems to only be caught by the activity, while I need the child fragment to get notified. Could you suggest anything? Binder? greenRobot Eventbus? RxJava (which I already have in the project)?
Thanks.
RxJava
A simple way con be to use a Singleton to wrap a synchronized ´PublishSubject´
* Singleton
*
* to send an event use EventBusRx.getInstance().topic1.onNext("completed");
*/
public class EventBusRx {
private static EventBusRx ourInstance = new EventBusRx();
public static EventBusRx getInstance() {
return ourInstance;
}
private EventBusRx() {}
/**
* Use of multiple topics can be usefull
* SerializedSubject avoid concurrency issues
*/
public final Subject<String, String> topic1 = new SerializedSubject<>(PublishSubject.create());
public final Subject<Integer, Integer> topic2 = new SerializedSubject<>(PublishSubject.create());
}
And You can send events from service
EventBusRx.getInstance().topic1.onNext("completed");
and respond to event in fragments or whenever you want
public class MyFragment extends Fragment {
// [...]
Subscription subscription_topic1;
#Override
public void onResume() {
super.onResume();
subscription_topic1 = EventBusRx.getInstance().topic2
.subscribeOn(AndroidSchedulers.mainThread()) // or on other sheduler
.subscribe(new Action1<Integer>() {
#Override
public void call(Integer integer) {
// update ui
}
});
}
#Override
public void onPause() {
// important to avoid memory leaks
subscription_topic1.unsubscribe();
super.onPause();
}
}
do not forget to unsubcribe the Subscription
The idea is similar to Roger'one use a singleton but enforce ThreadSafety wrapping PublishSubject.
there is no need for Observable.switchOnNext(subject)
EventBus Libraries
greenRobot Eventbus and Otto are nice and has the same functionality, but the disadvantage is that they make the connection more smoky (expecialy EventBus) . If you already use rx i think is better to stay with it
Here is an insipring article about the topic
Implementing an Event Bus With RxJava
LocalBroadcast
The classic way to do this is to use LocalBroadcastManager but in my aopinion they are a pain
I would suggest using an Event Bus for this sort of thing. It will allow you to send messages to components within your system, without requiring creating special handlers.
Otto is a popular open source library for this, and there are others. http://square.github.io/otto/
Try this way hope it help you.
For Example:
YourService
public class MyService extends Service{
public static MyServiceListener getMyServiceListener() {
return MyService.myServiceListener;
}
public static void setMyServiceListener(MyServiceListener myServiceListener) {
MyService.myServiceListener = myServiceListener;
}
private static MyServiceListener myServiceListener;
public interface MyServiceListener{
void onResult(String response);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
executeYourTask();
}
private void executeYourTask(){
String result = "SomeResultMaybeFromServer";
if(getMyServiceListener()!=null){
getMyServiceListener().onResult(result);
}
}
}
YourFragment
public class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = null; // some view
// Start service
MyService.setMyServiceListener(new MyService.MyServiceListener() {
#Override
public void onResult(String response) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
// To handle memory/window leaks
}
});
}
});
return v;
}
}
I'm currently developing a Bus based solely on RxJava. Since you already have RxJava on your project, you can use it for this. You should use a BehaviorSubject and Observable.switchOnNext().
For example:
private BehaviorSubject<Observable<Whatever>> subject = BehaviorSubject.create();
public void post(){
subject.onNext(...);
}
public Observable<Whatever> subscribe(){
return Observable.switchOnNext(subject);
}
You should have this as part of a Singleton so the same BehaviorSubject is used. All you have to do is post() from one fragment and subscribe() on the other one or in any other interested fragment or activity. You can have as many subscriptions as you want, plus if you implement it correctly then the last emitted Observable will survive orientation changes.
More info on BehaviorSubject can be found here: https://github.com/ReactiveX/RxJava/wiki/Subject
I'm currently using this Pub/Sub pattern with rxjava and enum class.
public enum Events {
public static PublishSubject <Object> myEvent = PublishSubject.create ();
}
//where you want to publish something
Events.myEvent.onNext(myObject);
//where you want to receive an event
Events.myEvent.subscribe (...);
I would use event bus, which is based on rx.
Make this as a sigletone and subscribe on particular class type.
public class RxBus {
private static final RxBus sBus = new RxBus();
private final Subject<Object, Object> mBus = new SerializedSubject<>(PublishSubject.create());
private RxBus() {
}
public static RxBus getInstance() {
return sBus;
}
public void send(Object o) {
mBus.onNext(o);
}
public Observable<Object> observe() {
return mBus;
}
#SuppressWarnings("unchecked")
public <T> Observable<T> observe(Class<T> c) {
return mBus.filter(o -> c.isAssignableFrom(o.getClass())).map(o -> (T) o);
}
}
usage:
class Message { public String result};
send a message:
Message m = new Message();
m.result = "Hello world";
RxBus.getInstance().send(m);
subscribe on a particular class type:
RxBus.getInstance().observe(Message.class).subscribe(msg -> Log.e(TAG, "Message was caught : " + msg));
I seem to be having one of two issues here. Either an error like:
java.lang.IllegalStateException: Event bus [Bus "default"] accessed from non-main thread Looper
Or, if I've managed to "address" that, I simply never receive the event in my subscriber.
Currently, I have a class, cobbled from a few sources, sub-classing Bus:
public class MainThreadBus extends Bus {
private static Bus _bus;
private Handler _handler = new Handler(Looper.getMainLooper());
public MainThreadBus() {
if (_bus == null) {
_bus = new Bus();
}
}
#Override public void register(Object obj) {
_bus.register(obj);
}
#Override public void unregister(Object obj) {
_bus.unregister(obj);
}
#Override public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
_bus.post(event);
} else {
_handler.post(new Runnable() {
#Override public void run() {
_bus.post(event);
}
});
}
}
}
Which is used in an Activity like this:
#Subscribe
public void requestProgressAvailable(RESTRequestProgress progress) {
processProgress(progress);
}
#Override
protected void onResume() {
super.onResume();
_bus = new MainThreadBus();
_bus.register(this);
}
#Override
protected void onPause() {
super.onPause();
_bus = new MainThreadBus();
_bus.unregister(this);
}
And publishing from an IntentService like this:
_bus = new MainThreadBus();
_bus.post(request.createRESTRequestProgress(RESTRequest.STATUS_STARTED));
And the messages are not received. An alternate configuration had me receiving the thread error, so I'm going with this, for now.
So, what am I missing, or doing wrong?
EDIT: Thanks to Andy below for pointing out that my code wasn't acting as I thought it was. The code above now reflects modifications based on that feedback.
Aside from the fact this implementation isn't a Singleton, when getting this error, you can use the threadEnforcer.ANY option in the constructor:
private static final Bus BUS = new Bus(ThreadEnforcer.ANY);
The problem is that your code is not interacting with the same bus instance. Instead of creating a new MainThreadBus in each case, you should access the same bus, for example a singleton obtained from a factory or via injection.
I'm having a problem writing a service, that should work with multiple activities.
I wrote a simple service and a mediator class the makes the bind and can return a service object. this is the simple service class:
public class ServerConnectionService extends Service{
private static final String TAG = "ServerConnectionService";
private final Binder binder=new LocalBinder();
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy(){
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class LocalBinder extends Binder {
ServerConnectionService getService() {
return ServerConnectionService.this;
}
}
}
this is the mediator class:
public class ServiceConnectionBinder{
private ServerConnectionService m_SrvConnection=null;
private ServiceConnection m_OnService;
private boolean m_IsBound;
private Activity m_Client;
public ServiceConnectionBinder(Activity i_Activity)
{
m_IsBound = false;
this.m_Client = i_Activity;
this.m_OnService=new ServiceConnection() {
public void onServiceConnected(ComponentName className,IBinder rawBinder) {
m_SrvConnection=((ServerConnectionService.LocalBinder)rawBinder).getService();
}
public void onServiceDisconnected(ComponentName className) {
m_SrvConnection=null;
}
};
doBindService();
Log.d("ServiceConnectionBinder", "finished Ctor");
}
private void doBindService() {
if(!m_IsBound)
{
m_Client.bindService(new Intent(m_Client, ServerConnectionService.class), m_OnService, Context.BIND_AUTO_CREATE);
m_IsBound = true;
}
if(m_SrvConnection == null)
{
Log.d("ServiceConnectionBinder",".doBindService cannot bind " + ServerConnectionService.class.toString() + " to " + this.toString());
}
}
public void doUnbindService() {
if (m_IsBound) {
// Detach our existing connection.
m_Client.unbindService(m_OnService);
m_IsBound = false;
}
}
public ServerConnectionService getServerConnectionService()
{
if(m_IsBound)
{
Log.d("ServiceConnectionBinder", "getServerConnectionService m_IsBound = " + m_IsBound);
}
return m_SrvConnection;
}
}
The client Activity has the following data members:
private ServiceConnectionBinder m_SrvcConnectionBinder=null;
private ServerConnectionService m_SrvConnection=null;
And in onCreate() the following code:
m_SrvcConnectionBinder = new ServiceConnectionBinder(this);
m_SrvConnection = m_SrvcConnectionBinder.getServerConnectionService();
problem is that after the onCreate(), the m_SrvConnection is always null.
If you have any other ways to implement this you are more than welcome to share..
problem is that after the onCreate(), the m_SrvConnection is always null.
Of course. The binding request will not even begin until the main application thread gets control again (i.e., you return control to the OS).
You cannot use m_SrvConnection until onServiceConnected() is called.
Resurrecting the old post as I had the similar question, but there is no clear answer here.
One of the ways to address this is like this:
protected void onCreate(Bundle savedInstanceState){
//... some stuff #1...
new AsyncTask<Void, Void, Integer>(){
protected void onPreExecute() { }
protected Integer doInBackground(Void... params) {
while(m_SrvConnection==null);
return new Integer(1);
}
protected void onPostExecute(Integer result) {
// service is up, m_SrvConnection is set
// what you wanted to do with the service in onCreate() goes here
}
}.execute();
//...some stuff #2...
}
Note that "some stuff #1" will run right when onCreate() is called, "some stuff #2" will be executed almost right after that, but what you put in onPostExecute() will be run much later.
The reason for doing it this way and not just putting the code into onServiceConnected() is that the ServiceConnectionBinder can now be put outside of the Activity (in some singleton, or Application for example) and be used by multiple activities without the need for each of them to bind to the service.
Note, it may not be obvious, but things in onPostExecute() may (will) actually be run after all other standard callbacks (like onResume() etc.).