RxJava subscribe onNext is not called when adding element asynchronously - retrofit2

I have a Observable like this
Observable<String> gitHubRepoModelObservable;
I have this code
repoNames = new ArrayList<String>();
gitHubRepoModelObservable = Observable.fromIterable(repoNames);
repoNames.add("Hello");
gitHubRepoModelObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
System.out.println(s);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
repoNames is just a list of string. When I am adding a string "hello" manually the onNext is getting called but when I am adding string from a API call like bellow
call.enqueue(new Callback<List<GitHubRepoModel>>() {
#Override
public void onResponse(Call<List<GitHubRepoModel>> call, Response<List<GitHubRepoModel>> response) {
for (GitHubRepoModel repo : response.body()) {
repoNames.add(repo.getName());
}
}
#Override
public void onFailure(Call<List<GitHubRepoModel>> call, Throwable t) {
}
});
I am adding strings from the API into the repoNames the "onNext" is not getting called.
I have seen
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
can be added while initializing retrofit but I want to better understand the rxjava so in this experiment it is not working.
Please help!

It can't not be work.
When you create you api request and try subscribe you list is emty, so Observable does not work.
You need to create Observable such, that your subcribe will run your request!
Observable<String> gitHubRepoModelObservable = Observable.create(
new Observable.OnSubscribe<String>() {
#Override
public void call(final Subscriber<? super String> sub) {
call.enqueue(new Callback<List<GitHubRepoModel>>() {
#Override
public void onResponse(Call<List<GitHubRepoModel>> call, Response<List<GitHubRepoModel>> response) {
for (GitHubRepoModel repo : response.body()) {
sub.onNext(repo.getName()); //send result to rx
}
sub.onCompleted();
}
#Override
public void onFailure(Call<List<GitHubRepoModel>> call, Throwable t) {
}
});
}
}
);
gitHubRepoModelObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onNext(String s) {
System.out.println(s);
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
});

Why would onNext get called if you are just adding element to plain List?
In the first example you are seeing onNext being called because modified list is passed through the stream during subscribe.
Create Subject ex. PublishSubject and pass list to Subject.onNext in onResponse, subscribe to it and you will get what you want.
Second option is adding RxJava2CallAdapterFactory and return Observable<Response<List<GithubRepoModel>>>. This way you don't need to create stream yourself.

Related

How to showing response string in a textview?

Result: {"Status":"OK","Message":"Report Genarated.","Result":"JVBERi0xLjUKJeLjz9MKMSAwIG9iago8PC9UeXBlL0ZvbnQvU3VidHlw"}
I am getting these response from the post api calling.Now How can i am get the Result string value.
Code:
holder.downloadBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
manager = new UtilityBillManager(context, AppBaseController.get().getUserSettings());
manager.UserTransactionReceiptReport(listener,billReceiptReport); //This the api calling
}
});
}
private final TransactionReportListener listener = new TransactionReportListener() {
#Override
public void didFetch(UserReportResponse response, String message) {
}
#Override
public void didError(String message) {
}
};
UserReponse is a Model which have String status,message, result.
Just use getResult() getter of Model class
See the below code
// add this condition to prevent app crashing.
if (response.body().getResult()!=null){
textView.setText(response.body().getResult()); // getResult() is your getters of the Model Class.
}
Hope it helps.

OKHttp DNS lookup asynchronously

public class OkHttpDns implements Dns {
#NotNull
#Override
public List<InetAddress> lookup(#NotNull String hostname) throws UnknownHostException {
MyLookUpUtility.getInstance.lookup(hostname, new MyLookUpUtility.lookupCallback()
{
#Override
public void onlookupResponseSuccess(JSONObject nslookupResponseJSON) {
Log.d("LookupResponse", nslookupResponseJSON.toString());
}
#Override
public void onlookupResponseFailure(String errCode) {
Log.d("LookupResponse", "Error Code : "+errCode);
}
});
}
}
In the above code, lookup method of DNS interface of OKHttp wants to return immediately. But my custom NSLookupUtility is an asynchronous call and I will have the ip address of the hostname only after a while. How to solve this problem? how to make the synchronous call to wait for the asynchronous call within it ?
Take a look at CompletableFuture. You’ll create an instance in lookup(), kickoff the async lookup, and then call future.get(). When your async call completes, call future.complete().
#Override
public List<InetAddress> lookup(#NotNull String hostName) throws UnknownHostException {
completableFuture = new CompletableFuture<>();
performLookUp(hostName);
try {
String ipAddress = completableFuture.get();
if (ipAddress != null) {
List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ipAddress));
return inetAddresses;
}
} catch (ExecutionException e) {
Log.d(TAG, "Error : ExecutionException : "+e );
e.printStackTrace();
} catch (InterruptedException e) {
Log.d(TAG, "Error : InterruptedException : "+e );
e.printStackTrace();
}
return Dns.SYSTEM.lookup(hostName);
}
private void performLookUp(#NotNull String hostName) {
MyUtiluty.getInstance().lookup(hostName,
new MyCallBack() {
#Override
public void onSuccess(String ip) {
completableFuture.complete(ip);
}
#Override
public void onFailure(String errCode) {
completableFuture.complete(null);
}
});
}

How can I return data in method from Retrofit i am confused?

I am confused please can you help me? I read similar questions but its is not clear for me, thanks in advance for your patient and attention.
I want to return to onCreate data which retrieved from API call using Retrofit. Here is my function where i call Retrofit.
private void loadTimeZoneAPI(double latitude, double longitude, long timestamp, String apiKeyTz) {
String lat = Double.toString(latitude);
String lon = Double.toString(longitude);
String time = Long.toString(timestamp);
serviceTZ.getDataTZ(lat+","+lon, time, apiKeyTz).enqueue(new Callback<TimeZoneGoogle>() {
#Override
public void onResponse(Call<TimeZoneGoogle> call, Response<TimeZoneGoogle> response) {
TimeZoneGoogle result = response.body();
timeZone = result.getTimeZoneId();
}
#Override
public void onFailure(Call<TimeZoneGoogle> call, Throwable t) {
}
});
}
How i will return timeZone value to onCreate where i will use for calculation.
You need a callback listener for your data in MainActivity or any activity you have used for view.
for example:make interface like this
interface RetrofitListener{
onDataLoad(TimeZoneGoogle timeZoneGoogle);
}
then give a reference in Activity from which you are calling Retrofit class
public class ActivityTest extends AppCompatActivity implements ActivityTest.RetrofitListener {
RetrofitCall retrofitListener;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
retrofitListener = new RetrofitCall(this);
}
}
and here in RetrofitCall class
private class RetrofitCall {
private RetrofitListener retrofitListener;
public RetrofitCall(RetrofitListener retrofitListener) {
this.retrofitListener = retrofitListener;
}
void getData(){
getDataTZ(lat+","+lon, time, apiKeyTz).enqueue(new Callback<TimeZoneGoogle>() {
#Override
public void onResponse(Call<TimeZoneGoogle> call, Response<TimeZoneGoogle> response) {
TimeZoneGoogle result = response.body();
timeZone = result.getTimeZoneId();
}
#Override
public void onFailure(Call<TimeZoneGoogle> call, Throwable t) {
}
});
}
}

HazelCast max-idle-seconds :evict listener is not working

hazelcast configuration for the map is
<map name="test">
<max-idle-seconds>120</max-idle-seconds>
<entry-listeners>
<entry-listener include-value="true" local="false">com.test.listener.SessionListener</entry-listener>
</entry-listeners>
</map>
I have a listener configured for the evict action.
Listener is not able to catch the evict action consistently .
Hazelcast Version : 3.6.5
Listener Class Implemetation:
public class SessionListener implements EntryListener<String, Object> {
#Override
public void entryEvicted(EntryEvent<String, Object> evictData) {
try {
Session sessionObjValue = (Session) evictData.getOldValue();
String sessionId = sessionObjValue.getSessionId();
String userName = sessionObjValue.getUsername();
JSONObject inputJSON = new JSONObject();
inputJSON.put(Constants.SESSIONID, sessionId);
inputJSON.put(Constants.USER_NAME, userName);
//Operations to be performed based on the JSON Value
} catch (Exception exception) {
LOGGER.logDebug(Constants.ERROR, methodName, exception.toString());
}
}
Below are the recommendations:
Include Eviction policy configurations in your map config. Right now eviction is happening only based on max-idle-seconds.
Implement all the methods from EntryListener interface which inturn extends other interfaces.
Implement EntryExpiredListener listener also, to catch the expiry events explicitly though evict event also will be called during expiry.
Sample code:
public class MapEntryListernerTest implements EntryListener, EntryExpiredListener {
#Override
public void entryAdded(EntryEvent event) {
}
#Override
public void entryEvicted(EntryEvent event) {
}
#Override
public void entryRemoved(EntryEvent event) {
}
#Override
public void entryUpdated(EntryEvent event) {
}
#Override
public void mapCleared(MapEvent event) {
}
#Override
public void mapEvicted(MapEvent event) {
}
#Override
public void entryExpired(EntryEvent event) {
}
}

How to combine Retrofit 2 with Realm and RxJava

I want to save retrofit responses to realm on the background thread then pass it to the UI Thread, but its a bit tricky since Realm is very touchy with threads. so the code would look like something like this, please submit your edits to all better solutions :)
restApi.userRealmList()
.doOnNext(userRealmModels -> {
if (userRealmModels != null){
mRealm = Realm.getInstance(mContext);
mRealm.asObservable()
.map(realm -> mRealm.copyToRealmOrUpdate(userEntity))
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(Object o) {
Log.d("RealmManager", "user added!");
}
});
}})
.map(userEntityDataMapper::transformAll)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<User>>() {
#Override
public void onCompleted() {
hideViewLoading();
}
#Override
public void onError(Throwable e) {
hideViewLoading();
showErrorMessage(new DefaultErrorBundle((Exception) e));
showViewRetry();
}
#Override
public void onNext(List<User> users) {
showUsersCollectionInView(users);
}
});
You code doesn't look like it can compile? E.g. what is userEntity. Also your copyToRealmOrUpdate isn't inside an transaction, so that will also crash, but it has nothing to do with threads.
If you want to save some data as a side-effect before sending it to the UI, you should be able to do the following:
restApi.userRealmList()
.doOnNext(userRealmModels -> {
if (userRealmModels != null) {
Realm realm = Realm.getInstance(mContext);
realm.beginTransaction();
realm.copyToRealmOrUpdate(userRealmModels);
realm.commitTransaction();
realm.close();
}})
.map(userEntityDataMapper::transformAll)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<User>>() {
#Override
public void onCompleted() {
hideViewLoading();
}
#Override
public void onError(Throwable e) {
hideViewLoading();
showErrorMessage(new DefaultErrorBundle((Exception) e));
showViewRetry();
}
#Override
public void onNext(List<User> users) {
showUsersCollectionInView(users);
}
});

Resources