Fragment subscribe to Observer - android

I'm trying to implement an Observer/Subscriber with RxJava for the first time.
I get the compile error:
cannot resolve method subscribe(android.support.v4.app.Fragment)
on the line indicated below. So I'm not subscribing correctly.
How do I do this?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Fragment myFragment = mTabsPageAdapter.getItem(2);
Observable<String> loadAndStoreDataObservable = Observable.create(
new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
try {
<get data from RESTful service>
<write data to SQLite db on device>
subscriber.onNext("Done");
subscriber.onCompleted();
}
catch (Exception e) {
subscriber.onError(e);
}
}
}
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(myFragment); // cannot resolve method subscribe(android.support.v4.app.Fragment)
}
}
import android.support.v4.app.Fragment;
public class MyFragment extends Fragment implements Observer<String> {
#Override
public void onNext(String s) {
System.out.println(s);
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
}
Edit: I changed the line suggested by Vladimir Mironov. This seems to be necessary but not sufficient. Implementing it I then get a compile error on the line after that:
Incompatible types: Required rx.Observable
Found rx.Subscription
It suggests casting to (Observable<String>) like so:
Observable<String> loadAndStoreDataObservable = (Observable<String>) Observable.create(...)
which does indeed compile error-free, but gives the runtime error:
java.lang.ClassCastException: rx.observers.SafeSubscriber cannot be cast to rx.Observable
Edit 2:
I think it should be: Subscription loadAndStoreDataObservable = ...

No one is stepping forward, so I'll aggregate the comments into an answer.
Cast to MyFragment:
MyFragment myFragment = (MyFragment) mTabsPageAdapter.getItem(2);
Change loadAndStoreDataObservable to be a Subscription
Subscription loadAndStoreDataObservable = ...
In OnDestroy(), unsubscribe:
protected void onDestroy() {
super.onDestroy();
if (loadAndStoreDataObservable != null) {
loadAndStoreDataObservable.unsubscribe();
}
}

Related

Dagger listener/interface injection

Hello everyone I've been struggling to understand how to inject a listener to a main activtity with Dagger2, I wonder if what I'm trying to do is possible or even a right move with dagger or should I just let it like it is right now I have read that I need to create another class with the implementation of that interface but is not possible(or recommended) to inject on the mainactivity?, thanks in advance to anyone who can help me, I have everything in short as follows:
//////////////////////////////////////MainActivity.class//////////////////////////////////////
public class MainActivity extends AppCompatActivity implements CustomListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//this is the object I want to inject in Dagger
LongProcess longProcess = new LongProcess(this);
longProcess.longRunningProcess();
}
#Override
public void onProcessStarted() {
Log.i(TAG, "onProcessStarted: CALLBACK!");
}
#Override
public void onProcessFailed() {
Log.e(TAG, "onProcessFailed: CALLBACK!");
}}
//////////////////////////////////////LongProcess.class//////////////////////////////////////
public class LongProcess {
private CustomListener customListener;
public LongProcess(CustomListener customListener) {
this.customListener = customListener;
}
public void longRunningProcess() {
try {
//some long process started...
customListener.onProcessStarted();
} catch (Exception e) {
//some long process failed...
customListener.onProcessFailed();
}
}
}
//////////////////////////////////////interface.java//////////////////////////////////////
public interface CustomListener {
void onProcessStarted();
void onProcessFailed();
}
You can take a look at Assisted Injection for this use case: https://dagger.dev/dev-guide/assisted-injection

Android MVP presenter unit test with Mockito causes "Wanted but not invoked" error

I know it was asked before, but i am currently diving into testing and i have the struggle to unit test presenter in MVP pattern with Mockito
My code setup:
Item class
public class ItemJSON {
#SerializedName("title")
String textHolder;
#SerializedName("id")
int factNumber;
public ItemJSON(String factText, int factNumber) {
this.textHolder = factText;
this.factNumber = factNumber;
}
//getters and setters
}
Contractor:
public interface Contractor {
interface Presenter {
void getPosts();
}
interface View {
//parse data to recyclerview on Succesfull call.
void parseDataToRecyclerView(List<ItemJSON> listCall);
void onResponseFailure(Throwable throwable);
}
interface Interactor {
interface onGetPostsListener {
void onSuccessGetPostCall(List<ItemJSON> listCall);
void onFailure(Throwable t);
}
void getPosts(onGetPostsListener onGetPostsListener);
}
}
API class:
#GET("posts")
Call<List<ItemJSON>> getPost();
Interactor class:
public class InteractorImpl implements Contractor.Interactor{
#Override
public void getPosts(onGetPostsListener onGetPostsListener) {
// NetworkService responsible for seting up Retrofit2
NetworkService.getInstance().getJSONApi().getPost().enqueue(new Callback<List<ItemJSON>> () {
#Override
public void onResponse(#NonNull Call<List<ItemJSON>> call, #NonNull Response<List<ItemJSON>> response) {
Log.d("OPERATION #GET","CALLBACK SUCCESSFUL");
onGetPostsListener.onSuccessGetPostCall (response.body ());
}
#Override
public void onFailure(#NonNull Call<List<ItemJSON>>call, #NonNull Throwable t) {
Log.d("OPERATION #GET","CALLBACK FAILURE");
onGetPostsListener.onFailure (t);
}
});
}
Presenter class:
public class PresenterImpl implements Contractor.Presenter, Contractor.Interactor.onGetPostsListener {
private final Contractor.View view;
private final Contractor.Interactor interactor;
public PresenterImpl (Contractor.View view,Contractor.Interactor interactor){
this.view = view;
this.interactor = interactor;
}
#Override
public void getPosts() {
interactor.getPosts (this);
}
#Override
public void onSuccessGetPostCall(List<ItemJSON> listCall) {
view.parseDataToRecyclerView (listCall);
}
}
So i try to ran some unit test on presenter, but they constanlty fail and i keep getting next error
Wanted but not invoked Actually, there were zero interactions with this mock
Unit test class:
#RunWith (MockitoJUnitRunner.class)
public class ApiMockTest{
#Mock
Contractor.View view;
private PresenterImpl presenter;
#Captor
ArgumentCaptor<List<ItemJSON>> jsons;
#Before
public void setUp() {
MockitoAnnotations.openMocks (this);
presenter = new PresenterImpl (view,new InteractorImpl ());
}
#Test
public void loadPost() {
presenter.getPosts ();
verify(view).parseDataToRecyclerView (jsons.capture ());
Assert.assertEquals (2, jsons.capture ().size ());
}
}
I try to understand what i am doing wrong and how to fix this issue, but as for now i am ran out of ideas. I will aprecciate any help.
Thanks in the adavance
UPD: in all cases in main activity presenter get called in onClick
Main Activity class:
public class MainActivity extends AppCompatActivity implements Contractor.View {
public Contractor.Presenter presenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
presenter = new PresenterImpl (this,new InteractorImpl ());
binding.getButton.setOnClickListener(view ->presenter.getPosts () );
...//code
#Override
public void parseDataToRecyclerView(List<ItemJSON> listCall) {
adapter.updateList(listCall); //diff call to put data into recyclerview adapter
}
}
}
I ran into this situation also, even using the mockk library. The problem is that your method is an interface method. You need to actually call it from a view which has implemented this interface.

RxJava - ReplaySubject only emitting data twice

I am new to ReactiveX and I have a case where I want my observable to emit data to a late subscriber(whenever the observer subscribes, observable should emit the same data that it emitted previously). I made this Observable class that provide ReplaySubject's same instance to all observers (it is singleton class).
public class AccountsObservable {
private static ConnectableObservable<String> hotObservable;
private static AccountsObservable accountsObservable;
public static AccountsObservable getObject() {
if (accountsObservable == null) {
accountsObservable = new AccountsObservable();
}
return accountsObservable;
}
public ConnectableObservable<String> getObservable() {
if (hotObservable == null) {
Observable<String> observable = ReplaySubject.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("XYZ");
emitter.onComplete();
}
});
hotObservable = observable.replay();//publish
}
return hotObservable;
}
}
Similarly, this is the observer class that creates new observer instance.
public class AccountsObserver {
AccountsFetchListener listener;
public AccountsObserver(AccountsFetchListener listener) {
this.listener = listener;
}
public Observer<String> getObserver() {
return new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String accounts) {
listener.onSuccess(accounts);
}
#Override
public void onError(Throwable e) {
listener.onFailure();
}
#Override
public void onComplete() {
}
};
}
public interface AccountsFetchListener {
void onSuccess(String accounts);
void onFailure();
}
}
Here is the function where I test these observables
private void testObs() {
ConnectableObservable<String> observable = AccountsObservable.getObject().getObservable();
Observer<String> observer = new AccountsObserver(new AccountsObserver.AccountsFetchListener() {
#Override
public void onSuccess(String accounts) {
Log.e("DATA -> ", accounts);
}
#Override
public void onFailure() {
}
}).getObserver();
observable.subscribe(observer);
observable.connect();
}
I called this function "testObs()" 5 times but it emitted data only 2 times. The problem seems to be in AccountsObservable class where I provide ReplaySUbject's instance. Thanks
Your code runs fine as it is, your logs are being suppressed in logcat as per this:
We declared an application as too chatty once it logs more than 5 lines a second. Please file a bug against the application's owner that is producing this developer-verbose-debug-level class logging spam. The logs are 256KB, that means the application is creating a DOS attack and shortening the logs timepan to 6 seconds(!) making it useless for all others.
You can avoid this behaviour by whitelisting your app for logcat:
adb logcat -P '<pid or uid of your app>'

Observable/Subscriber in AsyncTask

I'm trying to implement an Observable/Subscriber with RxJava on the onPostExecute() of an AsyncTask and I don't get how to make the connection.
I create the Observable in the onPostExecute method. I want MyFragment to subscribe to this. How do I set this up?
public class LoadAndStoreDataTask extends AsyncTask<String, Integer, String> {
...
#Override
protected void onPostExecute(String result) {
// create the observable
Observable<String> myObservable = Observable.create(
new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext(result);
subscriber.onCompleted();
}
}
);
myObservable.subscribe(mySubscriber);
}
}
public class MyFragment extends Fragment {
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Subscriber<String> mySubscriber = new Subscriber<String>() {
#Override
public void onNext(String s) { System.out.println(s); }
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
};
}
...
}
Actually RxJava is supposed to replace AsycTask. In fact I can say with confidence that AsyncTask is a subset of RxJava.
In RxJava, a Subscriber would be analogous to AsyncTask.progressUpdate or onPostExecute and Observable to the process in doInBackground. Data are emitted from Observable to Subscriber and any alteration in this stream is done with mapping methods. You probably don't need mapping now so I would reconfigure my RxJava like this:
Observable<String> myObservable = Observable.create(
new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
try{
String res = ...//your original doInBackground
subscriber.onNext(res);
// onNext would be comparable to AsyncTask.onProgressUpdate
// and usually applies when backgorund process runs a loop
subscriber.onCompleted();
}catch (SomeException e){
// if the process throws an exception or produces a result
// you'd consider error then use onError
subscriber.onError(e);
}
}
}
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) // If subscriber runs on UI thread
.subscribe(new Subscriber<String>() {
#Override
public void onNext(String response) {
// result from Observable.onNext. The methods below correspond
// to their Observable counterparts.
}
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
});
AndroidSchedulers is available in RxAndroid. To use it add this line to build.gradle :
compile 'io.reactivex:rxandroid:0.24.0'

spiceManager.isDataInCache is empty/null in Fragment

I would like to ask assistance for my error. I used robospice-retrofit for my api and I want to get the cache. In my other sample program the I could get the cache and the value but when i used it in the fragment, I always have a null value of my cache. Btw, I created another class that would handle all my request, is there a problem with this?
Please check my codes: This is created from separate class.
public void roboretroGetTempString(final DomainResponseCallback callback,SpiceManager spiceManager){
boolean isHasCache = false;
TestStringRequest graphRequest = new TestStringRequest();
try {
if(spiceManager.isDataInCache(String.class,"graph",DurationInMillis.ONE_MINUTE).get()){
Log.e(TAG,"onRefresh:Has Cache"+spiceManager.getDataFromCache(String.class,"graph"));
}else{
/*Execute if cache has expired*/
Log.e(TAG,"onRefresh:No Cache");
spiceManager.execute(graphRequest, "graph", DurationInMillis.ONE_MINUTE, new RequestListener<String>() {
#Override
public void onRequestFailure(SpiceException spiceException) {
spiceException.printStackTrace();
callback.onApiResponse("Error", null);
}
#Override
public void onRequestSuccess(String str) {
Log.e(TAG,"onRequestSuccess:result-"+str);
}
});
}
} catch (CacheCreationException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (CacheLoadingException e) {
e.printStackTrace();
}
}
This is the code in my Fragment: DomainResponseCallback is my callback interface. I also passed the SpiceManager as a parameter that I used in the code above.
private void roboLoadTest(){
GraphController graphController = new GraphController();
graphController.roboretroGetTempString(new DomainResponseCallback() {
#Override
public void onApiResponse(String result, Object obj) {
progressBar.setVisibility(View.GONE);
Log.e(TAG,"onApiResponse-"+result);
}
#Override
public void onApiError(String error) {
Log.e(TAG,"onApiResponse-"+error);
}
},getSpiceManager());
}
This is How I set up my Fragment based on the Sample Code and extends my Fragment to this.
public class RoboFragment extends Fragment{
private String TAG = RoboFragment.class.getSimpleName();
/***
* With {#link com.octo.android.robospice.UncachedSpiceService} there is no cache management. Remember to declare it in
* AndroidManifest.xml
*/
private SpiceManager spiceManager = new SpiceManager(RoboSpiceService.class);
#Override
public void onStart() {
super.onStart();
spiceManager.start(getActivity());
}
#Override
public void onStop() {
// Please review https://github.com/octo-online/robospice/issues/96 for the reason of that
// ugly if statement.
if (spiceManager.isStarted()) {
spiceManager.shouldStop();
}
super.onStop();
}
protected SpiceManager getSpiceManager() {
return spiceManager;
}
}
This is my TestRequest`
public class TestStringRequest extends RetrofitSpiceRequest<String,RetrofitApi> {
public TestStringRequest() {
super(String.class, RetrofitApi.class);
}
#Override
public String loadDataFromNetwork() throws Exception {
return getService().getTestRetrofit();
}
}
My RetrofitApi
#GET(api_reciever+"mytestretrofit")
public String getTestRetrofit();
I don't have any idea on whats missing on my code.
Looking forward for your assistance.
A new spice manager is created/started every time a new fragment is open and that spice manager also will close. That is why you will have an empty/null cache. Check this [post].1
In my scenario, I just created a singleton extends with application and created that spicemanager in my main activity(the parent of the fragments). And It works. I don't know if this is the best solution, still searching for more better approach.
public class AppSingleton extends Application{
private static AppSingleton ourInstance = new AppSingleton();
private SpiceManager spiceManager= new SpiceManager(RoboSpiceService.class);
public static AppSingleton getInstance() {
return ourInstance;
}
public SpiceManager getSpiceManager() {
return spiceManager;
}
}

Categories

Resources