I am developing an app where I am using clean architecture. In presenter, when something comes to method onCompleted then I must call function from Main activity.
this is my Presenter:
public class CheckInPresenter implements Presenter {
UseCase postCheckInUseCase;
Context context;
#Inject
CheckInPresenter(#Named("putCheckIn") UseCase postCheckInUseCase){
this.postCheckInUseCase = postCheckInUseCase;
}
public void initialize(){this.initializeCheckIn();}
public void initializeCheckIn(){this.putCheckIn();}
public void putCheckIn(){
this.postCheckInUseCase.execute(new CheckInSubscriber());
}
#Override
public void resume() {
}
#Override
public void pause() {
}
#Override
public void destroy() {
}
private final class CheckInSubscriber extends DefaultSubscriber<EventEntity>{
#Override
public void onCompleted() {
Log.d("onCompleted", "OnCompleted");
}
#Override
public void onError(Throwable e) {
Log.d("onError", "OnError: " + e.getMessage());
}
#Override
public void onNext(EventEntity eventEntity) {
Log.d("onNext", "OnNext");
}
}
}
And this is my function from MainActivity that I have to call:
public void getDataForToolbar() {
SharedPreferences sharedPreferences = getSharedPreferences(getResources().getString(R.string.Account_json), Context.MODE_PRIVATE);
final String account = sharedPreferences.getString(getResources().getString(R.string.account_json), null);
if (account != null) {
Gson gson = new Gson();
mAccount = gson.fromJson(account, AccountModel.class);
for (CompanyModel com : mAccount.getCompanies()) {
String name = com.getName();
company_name.setText(name);
logo_url = com.getLogo_url();
}
if (logo_url == null || logo_url.isEmpty()) {
Picasso
.with(this)
.load(R.drawable.default_company)
.resize(70, 58)
.transform(new RoundedTransformation(8, 0))
.into(toolbarImage);
} else {
picassoLoader(this, toolbarImage, logo_url);
}
String username = mAccount.getUsername();
if(mAccount.getStatus()){
aUsername.setText(username + "/" + getResources().getString(R.string.on_duty));
aUsername.setBackgroundColor(ContextCompat.getColor(mContext, R.color.colorGreen));
}else{
aUsername.setText(username + "/" + getResources().getString(R.string.off_duty));
aUsername.setBackgroundColor(ContextCompat.getColor(mContext, R.color.colorWhite));
}
}
}
Could someone helps me how to call this function into my onCompleted method in Presenter? Thanks in advance.
If you want to call some Activity's function from another object, you'll have to pass Activity's reference to that object. This means that you need to add Activity parameter to presenter's constructor.
Side note
I know that what you're implementing is called a "clean architecture" in many places (including the official MVP tutorials by Google), but you might want to read my posts on the subject in order to get an alternative view on what "clean" on Android should look like.
Why activities in Android are not UI elements
MVC and MVP architectural patterns in Android
Create interface IView and make your Activity to implement it.
In IView create method void getDataForToolbar();
I see #Inject annotation in your CheckInPresenter, so if you are using Dagger 2, update you Dagger module's constructor with IView, create a provider method for it and provide it for CheckInPresenter in this module.
Place IView in constructor of CheckInPresenter and update provider method for CheckInPresenter in Dagger module and Dagger component initialization in your Activity.
For example, it might look like this:
public class YourActivity implements IView {
...
}
#Module
public class YourModule {
private IView view;
public YourModule(IView view) {
this.view = view;
}
#Provides
public IView provideView() {
return view;
}
#Provides
#YourScope
public Presenter providePresenter() {
return new YourPresenter(view);
}
}
Just complete your existing Presenter and Module with IView.
After that call in your onCompleted method view.getDataForToolbar().
Related
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 1 year ago.
I am trying to implement "Retry" mechanism while using pagination library android, in case of no internet or device went offline while loading items.
When I click the "retry" image I get error - "Attempt to invoke virtual method 'void com.myapp.myapp.repository.FeedDataSource.retryPagination()' on a null object reference"
FeedDataSource.java
public class FeedDataSource extends PageKeyedDataSource<Long, Feed> {
.....
//for retry
private LoadParams<Long> params;
private LoadCallback<Long, Feed> callback;
........
#Override
public void loadAfter(#NonNull LoadParams<Long> params, #NonNull LoadCallback<Long, Feed> callback) {
this.params = params;
this.callback = callback;
Log.e(TAG, "Loading rage" + params.key + " Count" + params.requestedLoadSize);
networkState.postValue(NetworkState.LOADING);
RestApi restApi = RetrofitApi.create();
Call<FeedResponse> call = restApi.fetchFeed(params.key, params.requestedLoadSize, username);
try
{
Response<FeedResponse> response = call.execute();
if(response.isSuccessful()){
FeedResponse feedResponse = response.body();
if(feedResponse !=null){
networkState.postValue(NetworkState.LOADED);
List<Feed> responseItems = feedResponse.getFeeds();
callback.onResult(responseItems, params.key + 1);
}
}
........
}
public void retryPagination()
{
loadAfter(params, callback);
}
}
FeedDataFactory.java
public class FeedDataFactory extends DataSource.Factory {
private MutableLiveData<FeedDataSource> mutableLiveData;
private FeedDataSource feedDataSource;
private AppController appController;
public FeedDataFactory(AppController appController){
this.appController = appController;
this.mutableLiveData = new MutableLiveData<FeedDataSource>();
}
#Override
public DataSource create() {
feedDataSource = new FeedDataSource(appController);
mutableLiveData.postValue(feedDataSource);
return feedDataSource;
}
public MutableLiveData<FeedDataSource> getMutableLiveData(){
return mutableLiveData;
}
}
FeedViewModel.java
public class FeedViewModel extends ViewModel {
..............
public void retry() {
FeedDataFactory feedDataFactory = new FeedDataFactory(appController);
feedDataFactory.getMutableLiveData().getValue().retryPagination();
}
FeedAdapter.java
public class FeedListAdapter extends PagedListAdapter<Feed, RecyclerView.ViewHolder> {
......
private final Callback callback;
public FeedListAdapter(#NonNull DiffUtil.ItemCallback<Feed> diffCallback, Callback callback, Context context) {
super(Feed.DIFF_CALLBACK);
this.context = context;
this.callback = callback;
}
public void bindView(NetworkState networkState) {
if (networkState != null && networkState.getStatus() == NetworkState.Status.FAILED) {
binding.refresh.setVisibility(View.VISIBLE);
binding.refresh.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
binding.refresh.setVisibility(View.GONE);
binding.progressBar.setVisibility(View.VISIBLE);
callback.onRetryClicked();
}
});
}
}
public interface Callback {
void onRetryClicked();
}
}
HomeFragment.java
public class HomeFragment extends Fragment implements ClickHandler, FeedListAdapter.Callback {
private FeedListAdapter feedListAdapter;
private FeedViewModel feedViewModel;
..............
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
feedViewModel = new ViewModelProvider(this).get(FeedViewModel.class);
feedViewModel.init(appController);
feedListAdapter = new FeedListAdapter(Feed.DIFF_CALLBACK, this, getContext());
recyclerView.setAdapter(feedListAdapter);
feedViewModel.getArticleLiveData().observe(getViewLifecycleOwner(), pagedList -> {
feedListAdapter.submitList(pagedList);
});
}
}
Please help, what am I doing wrong!
First of all, I want to thank you because your solution is a hint for me to implement the network retry thing, I really appreciate that.
Second, about the NullPointerException you're getting, it's because in your FeedViewModel.java, when the retry button is triggered, you create a whole new factory for datasource. It's not either necessary or right because the Factory wont create any new DataSource unless the previous one was invalidated. You should retrieve the DataSource through your current Factory.
Another problem I want to mention is that, if your request is failed, you don't call onResult(...) on the callback. Based on the documentation:
A callback can be called only once, and will throw if called again. It
is always valid for a DataSource loading method that takes a callback
to stash the callback and call it later. This enables DataSources to
be fully asynchronous, and to handle temporary, recoverable error
states (such as a network error that can be retried)
Here is my implementation in DataSource when onFailure() in Retrofit is called:
#Override
public void onFailure(Call<HotItemsResponse> call, Throwable t) {
loadingState.postValue(Contants.LoadingState.SUB_LOAD_ERROR);
//when the load is fail, dont call onResult() on the call back,
//just ignore it, update the loading state for the UI to handle reload
//callback.onResult(new ArrayList<ProductItem>(), currentPage);
}
So I'm using the Observer pattern in my app in order to get notified of changes in another class without having to look for them.
I have a Singleton class which extends Observable. Inside this class I have two CountDownTimer type variables. Eachs of these contains two methods: onTick() and onFinished().
Let's call those Timers A and B for the sake of simplicity.
Every time A.onTick(), A.onFinished(), B.onTick(), B.onFinished() are called, I must call notifyObservers() to notify my Observer that something has changed.
Until here everything works fine. The problem is that I know something has changed, but I don't know what exactly has changed. Depending on which one notified me, I must execute some code on the Observer side.
How do I know which of these methods notified me?
Use LiveData instead of Observable. LiveData is quite useful because not only it's observable but also it binds to your activity's lifecycle so you don't have to worry about handling it yourself.
Maybe this example will help you:
public class MyTimerWrapper {
public static MyTimerWrapper getInstance() {
// Your singleton logic
createTimers();
return instance;
}
private CountDownTimer timerA;
private CountDownTimer timerB;
private MutableLiveData<TimerEvent> timerALiveData = new MutableLiveData<TimerEvent>();
private MutableLiveData<TimerEvent> timerBLiveData = new MutableLiveData<TimerEvent>();
public LiveData<TimerEvent> startTimerA() {
timerA.start();
return timerALiveData;
}
public LiveData<TimerEvent> startTimerB() {
timerB.start();
return timerBLiveData;
}
private void createTimers() {
createTimerA();
createTimerB();
}
private void createTimerA() {
timerA = new CountDownTimer(30000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
// If you're running on another thread
timerALiveData.postValue(TimerEvent.TICK);
// Otherwise
timerALiveData.setValue(TimerEvent.TICK);
}
#Override
public void onFinish() {
// If you're running on another thread
timerALiveData.postValue(TimerEvent.FINISH);
// Otherwise
timerALiveData.setValue(TimerEvent.FINISH);
}
}
}
private void createTimerB() {
// Same as createTimerA, but with timerB
}
}
public enum TimerEvent {
TICK,
FINISH
}
Now to observe that data in your activity:
MyTimerWrapper timerWrapper = MyTimerWrapper.getInstance();
timerWrapper.startTimerA().observe(this, new Observer {
#Override
public void onChanged(TimerEvent timerEvent) {
// Here you'll be able to see whether timerA is ticking or finished
}
})
You can create a custom EventType class and pass it to Observable.notifyObservers(Object arg):
public class EventType {
String eventType; //"onTick" or "onFinish"
TimerType timerType;
EventType(String eventType, TimerType timerType){
this.eventType = eventType;
this.timerType = timerType;
}
}
TimerType is an enum type:
public enum TimerType {
A,
B;
}
and create TimerA and TimerB classes extending CountDownTimer:
private class TimerA extends CountDownTimer {
final EventType onTickEvent = new EventType("onTick", TimerType.A);
final EventType onFinishEvent = new EventType("onFinish", TimerType.A);
#Override
public void onTick(long millisUntilFinished) {
notifyObservers(onTickEvent);
}
#Override
public void onFinish() {
notifyObservers(onFinishEvent)
}
}
The Observerwill receive the EventType instance via its update(Observable o, Object arg); in the arg argument
Firebase -> PhoneAuthProvider -> VerifyPhoneNumber is leaking. I believe, it might be OnVerificationStateChangedCallbacks, which we are sending on call to verifyPhoneNumber.
Steps to reproduce:
Launch the app
Select "PhoneAuthActivity" for phone based authentication
Send Phone Number.
Click back.
When clicking back, the leaked memory appear
Does someone have the same problem? any solution?
public void FirebasePhoneUser(String phoneNumber) {
mCallback = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
Log.d("Completed","");
}
#Override
public void onVerificationFailed(FirebaseException e) {
Log.d("Error","");
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken forceResendingToken) {
Log.d("onCodeSent", "");
}
};
phoneAuthProvider = PhoneAuthProvider.getInstance();
phoneAuthProvider.verifyPhoneNumber(
phoneNumber,
30,
TimeUnit.SECONDS,
TaskExecutors.MAIN_THREAD,
mCallback
);
}
Given that API is terrible and there's no option to unsubscribe, you have several options to work this around.
Proxy or Decorator. You create another OnVerificationStateChangedCallbacks which delegates method calls to another instance:
// this class must be either top-level or 'static'!
public /*static*/ final class DelegatingVerificationStateCallbacks
extends PhoneAuthProvider.OnVerificationStateChangedCallbacks
implements Closeable {
#Nullable private PhoneAuthProvider.OnVerificationStateChangedCallbacks delegate;
public DelegatingVerificationStateCallbacks(
#NonNull PhoneAuthProvider.OnVerificationStateChangedCallbacks delegate
) {
this.delegate = delegate;
}
#Override public void onCodeSent(
#NonNull String verificationId,
#NonNull PhoneAuthProvider.ForceResendingToken forceResendingToken
) {
if (delegate != null) delegate.onCodeSent(verificationId, forceResendingToken);
}
#Override public void onCodeAutoRetrievalTimeOut(#NonNull String s) {
if (delegate != null) delegate.onCodeAutoRetrievalTimeOut(s);
}
#Override public void onVerificationCompleted(#NonNull PhoneAuthCredential phoneAuthCredential) {
if (delegate != null) delegate.onVerificationCompleted(phoneAuthCredential);
}
#Override public void onVerificationFailed(#NonNull FirebaseException e) {
if (delegate != null) delegate.onVerificationFailed(e);
}
#Override public void close() {
delegate = null;
}
}
I've implemented Closeable for cleanup but you could implement RxJava's Disposable or whatever instead.
The usage pattern here is obvious and well-known:
public final class SomeScreen extends ActivityOrFragmentOrControllerOrWhatever {
private final ArrayList<Closeable> disposeBag = new ArrayList<>();
private void performAuth() {
DelegatingVerificationStateCallbacks callbacks =
new DelegatingVerificationStateCallbacks(
new OnVerificationStateChangedCallbacks() { … }
);
disposeBag.add(callbacks);
phoneAuthProvider.verifyPhoneNumber(…, callbacks);
}
#Override protected void onDestroy() {
for (Closeable c : disposeBag) {
try { c.close(); }
catch (IOException ignored) { }
}
disposeBag.clear();
}
}
Result: Firebase leaks a reference to empty and cheap DelegatingVerificationStateCallbacks, not to Activity.
Nulling references out yourself. You can take approach presented above to clear your own references to Activity. This implies these reference must be explicit, i. e. class must not be anonymous or inner to your activity. You must take full control of class constructor and fields, top-level class or nested static class is a good fit.
Weak reference. This is less explicit and involves some indirection but still works: you instantiate top-level or nested static class, pass Activity to constructor, wrap it in a WeakReference, and assign to a field. That's all, after some time WeakReference#get will start returning null.
Reflection. Very bad and unstable option which could help in some other situations. Sometimes your Activity could be leaked by Android SDK or vendor-specific code, and options from above don't apply. Then you can null out some private fields yourself. Don't do this for Firebase.
Following a brilliant tutorial I've come stuck with a casting issue .
The app is basically a stackoverflow api which uses retrofit and makes calls to retrieve questions and question details etc...
This all said and done I'm lost why it longer launches.
Something is wrong with the cast inn the base activity:
BaseActivity
public class BaseActivity extends AppCompatActivity {
private ControllerCompositionRoot mControllerCompositionRoot;
protected ControllerCompositionRoot getCompositionRoot() {
if(mControllerCompositionRoot == null) {
mControllerCompositionRoot = new ControllerCompositionRoot(
((CustomApplication) getApplication()).getCompositionRoot(),
this
);
}
return mControllerCompositionRoot;
}
}
which gets called inn this class activity
public class QuestionsListActivity extends BaseActivity implements QuestionsListViewMvcImpl.Listener {
private StackoverflowApi mStackoverflowApi;
private QuestionsListViewMvc questionsListViewMvc;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
questionsListViewMvc = getCompositionRoot().getViewMvcFactory().getQuestionListViewMvc(null);
questionsListViewMvc.registerListener(this);
mStackoverflowApi = getCompositionRoot().getStackoverflowApi();
setContentView(questionsListViewMvc.getRootView());
}
#Override
protected void onStart() {
super.onStart();
fetchQuestions();
}
private void fetchQuestions() {
mStackoverflowApi.fetchLastActiveQuestions(Constants.QUESTIONS_LIST_PAGE_SIZE)
.enqueue(new Callback<QuestionsListResponseSchema>() {
#Override
public void onResponse(Call<QuestionsListResponseSchema> call, Response<QuestionsListResponseSchema> response) {
if (response.isSuccessful()) {
bindQuestions(response.body().getQuestions());
} else {
networkCallFailed();
}
}
#Override
public void onFailure(Call<QuestionsListResponseSchema> call, Throwable t) {
networkCallFailed();
}
} );
}
private void bindQuestions(List<QuestionSchema> questionSchemas) {
List<Question> questions = new ArrayList<>(questionSchemas.size());
for (QuestionSchema questionSchema : questionSchemas) {
questions.add(new Question(questionSchema.getId(), questionSchema.getTitle()));
}
questionsListViewMvc.bindQuestions(questions);
}
private void networkCallFailed() {
Toast.makeText(this, R.string.error_network_call_failed, Toast.LENGTH_SHORT).show();
}
#Override
public void onQuestionClicked(Question question) {
QuestionDetailsActivity.start(this, question.getId());
}
}
I would understand first why the cast has to be made which is what I was following in the tutorial buttheres little way I can keep plugging at this wiithout understanding the whole concept of MVC etc..and the implications of how to implement that in an android app.
Any help would be greatly welcome and elaborations as well also.
repo - github
Register the custom application class in manifest.
I checked the repo and found the custom class is not registered. So your custom class is not used as application class.
I know there are similar questions but it still doesn't answer my question in the manner I need for my current situation.
I have three activity presenters that each need to call a certain data remotely which will therefore call the activity presenter back when data arrives. To create this data listener I created an interface listener and since all three Presenters ask for the same data and retrieve it, all three presenters implement this interface listener.
Interface listener:
interface ListenerInterface {
onGotData();
}
Presenter one:
class PresenterOne implements ListenerInterface{
public void getData() {
DataManager dataManager = new DataManager();
dataManager.getData(this);
}
#Override
public void onGotData(Data data) {
//Do something with data
}
}
Presenter two very similar to presenter one:
class PresenterTwo implements ListenerInterface{
public void getData() {
DataManager dataManager = new DataManager();
dataManager.getData(this);
}
#Override
public void onGotData(Data data) {
//Do something with data
}
}
Assume Presenter three is exactly the same as the previous. The data manager class:
class DataManager {
public void getData(final ListenerInterface listener) {
//Gets data
addOnCompleteListener(new OnCompleteListener<Data data > () {
#Override
public void onComplete (#NonNull DataCall < Data > dataCall) {
listener.onGotData(dataCall.getResults());
}
});
}
}
Would doing so someone call all three presenters since the interface is the one doing the calling or only call the presenter that is passed? Is there anything I should worry about if I followed way? If anyone who knows the Android framework well could provide a detailed answer so I could learn from it more that would be great.
The reason I want to do this is I want to communicate through interfaces between classes.
Sorry if this question is simple for some people but I am still learning.
Thank you very much in advance.
you can use RxBus implementation to make global event (e.g. your onGotData).
First you have to create RxBus class.
public class RxBus {
private static RxBus instance;
private PublishSubject<Event> subject = PublishSubject.create();
public static synchronized RxBus getInstance() {
if (instance == null) {
instance = new RxBus();
}
return instance;
}
private RxBus(){}
public void postEvent(Event event){
subject.onNext(event);
}
public Observable<Event> getEvents(){
return subject;
}
}
And now, you should subscribe to it in BaseActivity or something like this (depends or your project structure).
private RxBus rxbus;
private Subscription rxBusSubscription;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rxBus = RxBus.getInstance();
}
#Override
protected void onResume() {
super.onResume();
if (shouldSubscribeRxBus()) {
rxBusSubscription = rxBus.getEvents()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> {
if (event.getEventType() == Event.EventType.DATA_RECEIVED) {
onGotData(data);
}
});
}
}
Now implement you onGotData as you want.
When you catch data received call:
class DataManager {
public void getData(final ListenerInterface listener) {
//Gets data
addOnCompleteListener(new OnCompleteListener<Data data > () {
#Override
public void onComplete (#NonNull DataCall < Data > dataCall) {
RxBus.getInstance().postEvent(new GotDataEvent(dataCall.getResults()));
}
});
}
}
You can create your Event classes structure as you want.