I want to display a new activity on success callback of my WebService called by Retrofit.
And I have difficulties to find examples on how to use Retrofit callback result to launch new activity.
Is that a good way to do it ? Do I have to clean up some stuff before?
public void validate(View view) {
RetrofitWebServices.getInstance().webserviceMethod(params,new Callback<MyObject>() {
#Override
public void success(MyObject object, Response response) {
Intent barIntent = new Intent(FooActivity.this, BarActivity.class);
startActivity(barIntent);
}
#Override
public void failure(RetrofitError error) {
...
}
});
}
You can implement Callback with weak reference to Context
public class MyCallback implements Callback<MyObject> {
WeakReference<Context> mContextReference;
public MyCallback(Context context) {
mContextReference = new WeakReference<Context>(context);
}
#Override
public void success(MyObject arg0, Response arg1) {
Context context = mContextReference.get();
if(context != null){
Intent barIntent = new Intent(FooActivity.this, BarActivity.class);
context.startActivity(barIntent);
} else {
// TODO process context lost
}
}
#Override
public void failure(RetrofitError arg0) {
// TODO process error
}
}
Just remember - this solution will not work if Context lost occurred while request in progress but you may don't worry about potential memory leak which may be if you keep strong reference to Context object.
Hi have a solution that seems easier: use the getApplicationContext() function.
I am not 100% sure it is OK, but in my case it works as expected.
Your code would be:
public void validate(View view) {
RetrofitWebServices.getInstance().webserviceMethod(params,new Callback<MyObject>() {
#Override
public void success(MyObject object, Response response) {
Intent barIntent = new Intent(getApplicationContext(), BarActivity.class);
startActivity(barIntent);
}
#Override
public void failure(RetrofitError error) {
...
}
});
}
Related
Before marking the question as duplicate, read my issue first.
I have map fragment that execute a lot of process using firebase realtime database and the issue that I'm facing is that some callbacks do intensive processing which makes the map very laggy and sometimes crashes the app, so I made a new thread to execute that callback and inside it there's some code that update the UI, so I run it using runOnUiThread.
Everything worked fine once I open the fragment for the first time, after I press back and reopen it again, getActivity keeps coming null always!
I tried this famous workaround
FragmentActivity mActivity;
#Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = (FragmentActivity) context;
}
#Override
public void onDetach() {
super.onDetach();
mActivity = null;
}
And used mActivity instead of getActivity , but it didn't work..! mActivity keeps coming as null also.!
I don't know why this error happens! when I open the fragment again it added again on the backstack and attached again so the activity shouldn't be null, and why it worked when added on the first time launch only?!
The Code
void updateStudents() {
if (isRecursionEnable) {
return;
}
isRecursionEnable = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
if (!thread.isInterrupted()) {
studentQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(final DataSnapshot snapshot, String s) {
Student_User.add(snapshot.getValue(TestUserStudent.class));
if (getActivity() != null) { // NPE
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
showAllMarkers(snapshot); // show custom markers on the map
}
});
}
}
#Override
public void onChildChanged(final DataSnapshot snapshot, String s) {
Student_User.add(snapshot.getValue(TestUserStudent.class));
if (getActivity() != null) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
showAllMarkers(snapshot);
}
});
}
}
#Override
public void onChildRemoved(DataSnapshot snapshot) {
}
#Override
public void onChildMoved(DataSnapshot snapshot, String s) {
}
#Override
public void onCancelled(DatabaseError error) {
}
});
}
}
}, "");
thread.start();
}
The issue is quite difficult to solve because the UI code is entangled with the data loading logic. I think the best solution here would be to make those things independent. This will most likely solve the problem, or at least make it much easier to find and resolve. Also will improve the feature design in general.
Here is a short code snipit as requested, just to understand my idea. I won't write the whole thing though because this is beyond the question.
Create a state class, e.g. StudentState. The class will also provide a listener interface that will send updates to the fragment.
public class StudentState {
// You might want to store Student_User here instead
// I'm not sure how your data is currently represented.
private DataSnapshot dataSnapshot;
// Can also be a list of listeners if needed.
private StudentStateListener studentStateListener;
public void registerListener(StudentStateListener listener) {
studentStateListener = listener;
}
public void unregisterListener() {
studentStateListener = null;
}
...
public void setDataSnapshot(DataSnapshot dataSnapshot) {
this.dataSnapshot = dataSnapshot;
if (studentStateListener != null) {
studentStateListener.onDataSnapshotLoadFinished(dataSnapshot);
}
}
public interface StudentStateListener {
void onDataSnapshotLoadFinished(DataSnapshot dataSnapshot);
}
}
Implement it in your Fragment:
public class StudentFragment implements StudentStateListener {
// The state can be initialized in multiple ways.
// It can be a singleton, or stored in Application class, or
// defined in your base activity, etc. Up to you. Ideally
// should be injected via dependency injection if you use one, such as Dagger.
private StudentState state;
#Override
public void onCreate() {
// initialize state
// ...
}
#Override
public void onResume() {
state.registerListener(this);
// If the data has already been loaded you might also want the following.
// It's up to you.
if (state.getDataSnapshot() != null) {
showAllMarkers(state.getDataSnapshot());
}
}
#Override
public void onPause() {
state.unregisterListener();
}
...
#Override
public void onDataSnapshotLoadFinished(DataSnapshot dataSnapshot) {
if (!isAdded()) {
return;
}
showAllMarkers(snapshot);
}
public void updateStudents() {
// I'm not quite sure how to work with your API but basically the idea is to load a new data snapshot and store it in the state
// This should happen in background thread.
studentQuery.loadDataSnapshot(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot) {
state.setDataSnapshot(dataSnapshot);
}
});
}
}
I hope this helps.. Good luck!
This problem shows some dependency issues in your app. Perhaps you should find some other way to implment what you want.
Anyway, take a look at:
.setRetainInstance(true)
https://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)
This is not ideal, and you should avoid it. But it might solve your problem if nothing else works.
Hope it helps.
I have one case when I need to return an observable immediately, but then replace this observable with another one.
Here is an example
private Flowable<byte[]> mFlowableStream = Flowable.empty();
#Override
public Flowable<byte[]> startStreamRead() {
bindToService();
return mFlowableStream;
}
And then after binding to service I provide it a callback connection like that
#Override
public void bindToService() {
mAppContext.bindService(new Intent(mAppContext,StreamService.class), mServiceConnection, 0);
}
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mServiceInterection = ((StreamServiceInterection.LocalBinder) binder).getServiceInteractor();
mStreamDisposable = mServiceInterection.getStream()
.subscribe(new Consumer<byte[]>() {
#Override
public void accept(byte[] data) throws Exception {
}
});
}
What I want to do is to somehow replace returned previously mFlowableStream with a new observable that I got from service.
What are possible strategies to implement this ? Maybe I should return some other value, like Future.
Please suggest how to solve this problem
Thanks
You can use Flowable.create instead of Flowable.empty
Then when new data come, just push to flowable.
Like Example
final ArrayList<FlowableEmitter<Integer>> arrEmitter = new ArrayList<>();
Flowable<Integer> futureFlow = Flowable.create(new FlowableOnSubscribe<Integer>() {
#Override
public void subscribe(final FlowableEmitter<Integer> e) throws Exception {
e.onNext(1);
e.onNext(2);
arrEmitter.add(e); // hold emitter to use later
}
}, BackpressureStrategy.BUFFER);
futureFlow.subscribe(new ResourceSubscriber<Integer>() {
#Override
public void onNext(Integer integer) {
System.out.println("onNext: " + integer);
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
System.out.println("onComplete");
}
});
// =========== When data come
FlowableEmitter<Integer> holdEmitter = arrEmitter.get(0);
holdEmitter.onNext(3);
Or use you can use **Subject* type according to your need
Understanding RxJava Subject — Publish, Replay, Behavior and Async Subject
How can I make a proper separation between the Model layer and the View layer, when I have an operation in the Model that needs the current activity instance?
For example, I've integrated Linkedin SDK in my Android app (written in MVP).
In the auth process I have the following code snippet, when init() method's first argument type is Activity:
public void authWithLinkedin(final IAuth listener, Activity activity) {
LISessionManager.getInstance(MyApplication.getContext()).init(activity, buildScope(), new AuthListener() {
#Override
public void onAuthSuccess() {
listener.onSuccess();
}
#Override
public void onAuthError(LIAuthError error) {
listener.onError();
}
}, true);
}
If my Model layer should get to know Android framework components, what options do I have left to preserve the MVP architecture clean?
You can use software conventions / principles like
"dependency inversion principle"
"ports and adapters"
Your model layer should not know about Android if you can avoid it is the point.
Try something like this:
Model:
private final SocialLoginProvider socialLoginProvider;
public MyModel(SocialLoginProvider socialLoginProvider) {
this.socialLoginProvider = socialLoginProvider;
}
public void authWithLinkedin(final IAuth listener) {
socialLoginProvider.init(buildScope(), new SocialLoginProvider.Listener() {
#Override
public void onAuthSuccess() {
listener.onSuccess();
}
#Override
public void onAuthError() {
listener.onError();
}
}, true);
}
Factory:
public MyModel getModel(Context context) {
LISessionManager li = LISessionManager.getInstance(context);
SocialLoginProvider provider = new LinkedInSocialLoginProvider(context, li);
return new MyModel(provider);
}
Interface:
public interface SocialLoginProvider {
void init(Scope scope, Listener listener);
interface Listener {
void onAuthSuccess();
void onAuthError();
}
}
Adapter for SocialLoginProvider:
public class LinkedInSocialLoginProvider implements SocialLoginProvider {
private final Context context;
private final LISessionManager linkedInSessionManager;
public LinkedInSocialLoginProvider(Context context, LISessionManager linkedInSessionManager) {
this.context = context;
this.linkedInSessionManager = linkedInSessionManager;
}
#Override
public void init(Scope scope, Listener listener) {
linkedInSessionManager.init(context, scope,
new AuthListener() {
#Override
public void onAuthSuccess() {
listener.onSuccess();
}
#Override
public void onAuthError(LIAuthError error) {
listener.onError();
}
}, true);
}
}
Ideally it is ok to have Android Framework components in the Model layer. For example you will need the Context to store/access data locally using getDefaultSharedPreferences(Context) and/or to manage local DB using SQLiteOpenHelper.
The LISessionManager.getInstance(MyApplication.getContext()).init seems to be like a BroadcastReceiver as it is a type of listener that receives a particular result from an outside component. To handle such a case you can refer to this
I use the following class to make an API call in android using Retrofit
public Class Checkin {
public static void checkinViaApi(CheckinSendModel checkinSendModel) {
final ApiHandler apiHandler = new ApiHandler();
apiHandler.setApiResponseListener(new ApiResponseListener() {
#Override
public void onApiResponse(ApiResponseModel apiResponse) {
Log.i("CheckedIn","true");
}
#Override
public void onApiException(Error error) {
Log.i("fail",error.getErrorMessage());
}
});
List<CheckinSendModel> checkinSendModelList = new ArrayList<CheckinSendModel>();
checkinSendModelList.add(checkinSendModel);
Call<ApiResponseModel> request = RetrofitRestClient.getInstance().checkinToMainEvent(checkinSendModelList,Constant.API_KEY);
apiHandler.getData(request);
}
}
I call that method as follows:
Checkin.checkinViaApi(checkinSendModelObject);
Now, when the API call is successful, I want to execute a function checkedInSuccessfully() in the class from where I make the call. How can I do it?
Thanks in advance
Pass in the response interface.
public class Checkin {
public static void checkinViaApi(CheckinSendModel checkinSendModel, ApiResponseListener listener) {
final ApiHandler apiHandler = new ApiHandler();
apiHandler.setApiResponseListener(listener);
Other class - Call that method
CheckinSendModel model;
Checkin.checkinViaApi(model, new ApiResponseListener() {
#Override
public void onApiResponse(ApiResponseModel apiResponse) {
Log.i("CheckedIn","true");
checkedInSuccessfully();
}
#Override
public void onApiException(Error error) {
Log.i("fail",error.getErrorMessage());
}
);
Interface is your handy man. Create an interface like below.
Interface CheckInListener {
void onCheckIn();
}
Change the checkinViaApi() to below signature.
public static void checkinViaApi(CheckinSendModel checkinSendModel, CheckinListener listener) {
#Override
public void onApiResponse(ApiResponseModel apiResponse) {
Log.i("CheckedIn","true");
listener.onCheckIn();
}
}
When you call the above function you can provide an instance of the interface.
Checkin.checkinViaApi(checkinSendModelObject, new CheckInListener() {
#Override
void onCheckIn() {
//Do your action here
}
});
I'm using Syncano latest Android SDK (4.0.6).
Is there a way to have a async query with parameters?
Syncano.please(User.class).where()
Doesn't have a method to run it asynchronously.
But
Syncano.getInstance().getObjects(User.class)
Which has 'sendAsync()' but doesn't have 'where()' constrain.
Docs specifies:
Syncano.please(User.class).getAsync(callback);
But I don't see it in code only getAll().
You can make an async call when using please(). Just pass SyncanoCallback object.
Syncano.please(Item.class).get(new SyncanoListCallback<Item>() {
#Override
public void success(ResponseGetList<Item> response, List<Item> result) {
}
#Override
public void failure(ResponseGetList<Item> response) {
}
});
You're right that async get() method is missing when using where(). It has to be fixed in the library, but you can make this call anyway saving the reference to RequestBuilder.
RequestBuilder<Item> please = Syncano.please(Item.class);
please.where().eq(Item.COLUMN_NUMBER, 11);
please.get(new SyncanoListCallback<Item>() {
#Override
public void success(ResponseGetList<Item> response, List<Item> result) {
}
#Override
public void failure(ResponseGetList<Item> response) {
}
});
You can also use where(), without using please(). It will look like this:
Where<Item> where = new Where<>();
where.eq(Item.COLUMN_NUMBER, 11);
Syncano.getInstance().getObjects(Item.class).setWhereFilter(where).sendAsync(new SyncanoListCallback<Item>() {
#Override
public void success(ResponseGetList<Item> response, List<Item> result) {
}
#Override
public void failure(ResponseGetList<Item> response) {
}
});