Okay, this might be stale, but i really need to understand what the best practice will be and not how to easily bypass this either by disabling screen orientation or any other means.
I have a login screen and when the user clicks on login button it should go to the server and authenticate and return a response.
My problem is if the screen rotates my fragment might not receive a callback of the response data.
I'm trying out an MVP design pattern on android.
public void registerSignInEvent(){
this.signInBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String username = usernameEdit.getText().toString();
String password = passwordEdit.getText().toString();
authPresenter.loginUser(username, password, 1);
}
});
}
I've thought of the following...
Use a service to handle the login to the server, when its done the service updates the storage e.g is_login=false or true then use a LocalBroadcastManager to broadcast the event to the view(Fragment)
so it can query the presenter to know the login state.
Use a Fragment with setRetainIntance(true); to handle the presenter initialization and the presenter will trigger callback to methods of the activity e.g onLoginSuccess //confusing myself
Problem
A. the problem with my no.1 thought is that when my loginFragment is onPause at that moment, the broadcast receiver is unregistered, so it might not receive the event. plus i don't even know if it makes sense.
B. Its looks complicated with MVP pattern
The pattern really might not matter, i don't really need code snippet tho, I just need to understand the process that best fits the situation.
NOTE: My Presenter communicates with the view(fragment/activity) via the view interface, vice-versa.
You could try storing the user in the database/sharedprefs whenever you receive the response from the login, if a rotation occurs and the login-fragment gets reattached without receiving the necessary callbacks (which is the problem you're describing) you could add a check if the user is "already" logged in (by checking if the user exists in the db/sharedprefs in onResume of the loginactivity) and forward the user to the next activity or fragment from there.
First of all I use this cool method to keep presenter alive even if activity recreated: Presenter surviving orientation changes with Loaders. It detaches and attaches activity in onStop and onStart.
Need to mention also, that your second choice with persistent fragment in widely used, e.g. by Fernando Cejas. I've learned clean architecture approach with his articles, and he uses setRetainState(true).
And still your question is driving me crazy as well. Only solution I've found so far is ugly as hell. But it should work. Idea: after work done, I check if view is attached. If so, I proceed normally. I there is no view, that we are in the middle of rotation. So I have flag, that indicate, that work is done. I turn it on. Also I cache any needed data. And wait for the next view attaching. Where I check that flag.
Here is my code snippet. I'm not proud of it thought.
class SplashPresenter extends BasePresenter<SplashView> {
private final SplashInteractor splashInteractor;
private boolean isSplashWorkStarted;
private boolean isSplashWorkFinished;
private boolean isSplashWorkError;
private Throwable splashWorkError;
#Inject
SplashPresenter(SplashInteractor splashInteractor) {
this.splashInteractor = splashInteractor;
}
#Override
public void attachView(SplashView mvpView) {
super.attachView(mvpView);
if (isSplashWorkFinished) {
getMvpView().showApplicationUi();
} else if (isSplashWorkError) {
getMvpView().showError(splashWorkError.getMessage());
}
}
void executeSplashWork() {
if (!isSplashWorkStarted) {
splashInteractor.execute(new SplashInteractorSubscriber());
isSplashWorkStarted = true;
}
}
#Override
public void onDestroyed() {
splashInteractor.unsubscribe();
}
private final class SplashInteractorSubscriber extends Subscriber<Void> {
#Override
public void onCompleted() {
if (isViewAttached()) {
getMvpView().showApplicationUi();
} else {
isSplashWorkFinished = true;
}
}
#Override
public void onError(Throwable e) {
if (isViewAttached()) {
getMvpView().showError(e.getMessage());
} else {
isSplashWorkError = true;
splashWorkError = e;
}
}
#Override
public void onNext(Void v) {
}
}
}
Related
I have some questions regarding the Presenter’s start(), stop() method. What would you normally put into these methods to prevent memory leaks or any potential problem.
For example, I have an Activity that host a VideoView. The videoPath passed to the Activity will be passed to the Presenter to a VideoUtility to trim the original video into a shorter one before getting passed back to the Activity to be played with the VideoView.
Here’s the confusion: I don’t know where is the appropriate place to call the trimVideo() method as it essentially only need to happen once (unlike in the Android Architect Blueprint, the task is updated with latest data, and thus it’s put in the onResume()).
Please see the code snippet below:
VideoEditorContract:
public interface VideoEditorContract {
interface View extends BaseView<Presenter> {
void playTrimVideo(String trimmedVideoPath);
}
interface Presenter extends BasePresenter {
}
}
VideoEditorActivityBase:
public class VideoEditorActivityBase extends AppCompatActivity implements VideoEditorContract.View {
private VideoEditorContract.Presenter mPresenter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_editor);
String videoPath = getIntent().getStringExtra(RequestCode.EXTRA_VIDEO_PATH);
mPresenter = new VideoEditorPresenter(videoPath, this);
}
#Override
public void onResume(){
super.onResume();
mPresenter.start();
}
#Override
public void playTrimVideo(String trimmedVideoPath) {
final VideoView vv = findViewById(R.id.act_video_editor_videoView);
vv.setVideoPath(trimmedVideoPath);
vv.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
vv.start();
}
});
}
#Override
public void setPresenter(VideoEditorContract.Presenter presenter) {
//do nothing as this activity has already init a presenter
}
}
VideoEditorPresenter:
public class VideoEditorPresenter implements VideoEditorContract.Presenter {
private final VideoEditorContract.View mVideoEditorView;
#NonNull
private String mVideoPath;
public VideoEditorPresenter(#NonNull String videoPath, #NonNull VideoEditorContract.View videoEditorView) {
mVideoPath = checkNotNull(videoPath);
mVideoEditorView = checkNotNull(videoEditorView, "videoEditorView cannot be null!");
mVideoEditorView.setPresenter(this);
//trimVideo(); //should I do it here since this task is only need to be done once
}
#Override
public void start() {
//trimVideo(); //I can do it here but then I need to introduce a control variable; not sure if this is the best practice
}
private void trimVideo() {
//trim video stuff
}
// Currently it doesn't have a stop() method. But if it have one,
// what should I put in it? Releasing and clean up the
// VideoUtility I suppose?
}
I got the answer from Francesco Cervone in Medium about this matter (his article is also an excellent resource on MVP, btw. Very well in tune with the Android Architect Blueprint). I leave the relevant bit here for future reader.
Hi, thank you.
Well, I think that the video should be trimmed in the Presenter#start(). Then, after the video has been trimmed, the presenter should call view.playTrimmedVideo(). You shouldn’t do anything in the presenter constructor.
I suppose the video “editing” is something expensive, so you should do that in a separate thread (using for example an async task). You need to implement the Presenter#stop() method because you have to cancel ongoing operations if there are any, unless you retain the presenter.
You said that the trimVideo should be called just once. You could cache/persist in some way the result of trimVideo so that if the video has been already trimmed, you use it.
I hope I answered your question.
"Could you elaborate more on why shouldn’t we put anything in the Presenter’s constructor? I’ve seen the Presenter’s bare minimal constructor in a couple of places but I don’t understand the reason behind it."
First, it’s a responsibility problem: you are going to create an instance of Presenter, and I don’t think that the video editing is something that belongs to the construction of that object.
Second, you don’t know when the presenter is being instantiated, so you shouldn’t execute expensive tasks in the constructor. If you use some dependency injection framework, the construction of the Presenter would be managed by the framework itself and it needs to be efficient. The construction of other objects could depend on the presenter one.
I would like to know if it's possible to clear all activities from an old one. I would like to use enventBus to do this.
Example of a stack of activities:
startActivity(A) then startActivity(B) then startActivity(C) then startActivity(D)...
Activity B is registered onEvent(ClearStackFromHere()) with eventBus.
And from Activity D I want to post the event post(new ClearStackFromHere) with eventBus too.
So, is it possible to clear the stack of activities from B ?
What should I write inside my ClearStackFromHere().
Thanks,
I have implemented a similar solution in one of my projects.
What I needed was a way to keep only the most recent 3 activities in the back stack, and clear the others before them. This only applies to a certain Navigation flow within my application where it becomes possible that an infinite amount of Activities can be added to the back stack.
e.g. A opens B - which opens C, C can then open another instance of A or B... etc.
I should note that this solution uses EventBus 2.4.0 and there may be a better way to implement it with 3.0+.
First off, I defined a helper called ActivityTracker. It keeps track of what Activities are currently active, as well as an identifier for each activity. It also has methods that can be called to finish all activities in the back stack except for the most recent n amount.
public class ActivityTracker {
private static ArrayList<String> activityStack = new ArrayList<>();
//Notify the Tracker of a new Activity to track
public static void activityActive(String uuid){
addToBackStack(uuid);
}
//Notify the tracker of an Activity that should no longer be tracked
public static void finishing(String uuid){
removeFromBackStack(uuid);
}
//Call this to clear entire back stack
public static void killAllBackStackActivities(){
killPreviousActivities(0);
}
//Call this to clear back stack while keeping most recent X amount
private static void killPreviousActivities(int keepAmount){
if(activityStack.size() <= keepAmount) {
return;
}
//Copy to not manipulate while looping.
String[] tempList = activityStack.toArray(new String[activityStack.size()]);
int counter = activityStack.size();
for(String id : tempList){
if(counter == keepAmount){
return;
}
counter--;
//Send notification to kill specific activity
EventBus.getDefault().post(new ActivityShouldDieEvent(id));
}
}
private static void addToBackStack(String uuid){
if(!activityStack.contains(uuid)){
activityStack.add(uuid);
killPreviousActivities(3); //Always kill all activities except most recent 3.
}
}
private static void removeFromBackStack(String uuid){
if(activityStack.contains(uuid))
activityStack.remove(uuid);
}
}
Then, I defined a subclass of AppCompatActivity called BackStackTrackActivity. All relevant Activities in the app extend this class. The subclass looks like this:
public class BackStackTrackActivity extends AppCompatActivity {
//Random ID for activity to be identified by
protected String uuid = UUID.randomUUID().toString();
//Receive notification that activity should finish
public void onEvent(ActivityShouldDieEvent ev){
if(ev.getUuid().equals(this.uuid)){
finish();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
//Unregister from EventBus
EventBus.getDefault().unregister(this);
//Tell tracker to stop tracking
ActivityTracker.finishing(uuid);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Register for events
EventBus.getDefault().register(this);
//Tell tracker to track activity
ActivityTracker.activityActive(uuid);
}
}
With some work, I think you will be able to adapt this solution into something that meets your needs.
I hope that helps.
#first Sorry for my bad english.
I have created a own Listener. I want to change a TextView, when the Listener is called in the MainActivity from a Service. The idea for my own Listener is from:
http://tseng-blog.nge-web.net/blog/2009/02/17/how-implement-your-own-listener-android-java/
In the Code Example the TriggerMethod() ist called from a Calculation Thread, running in the Service.
I solved the Problem, but I find, it isn't pretty nice, because in every new Activity I have to make a new Thread. Is it possible to create an interface/listener that automatically can change the UI?
Used to solve the Problem:
http://developer.android.com/guide/components/processes-and-threads.html
ResultListener.java:
public interface ResultListener {
public void onResultAvailable(double result);
}
SimuService.java:
public class SimuService extends Service {
private ResultListener mResultListener = null;
public void setResultListener(ResultListener listener){
mResultListener=listener;
}
public void triggerMethode(){
observeResultDouble=getObserveDouble;
mResultListener.onResultAvailable(observeResultDouble);
}
MainActivity:
public class MainActivity extends FragmentActivity{
TextView txtView;
ResultListener mResultListener;
SimuService mSimuService;
protected void onCreate(Bundle savedInstanceState) {
txtView = (TextView) findViewById(R.id.txtServiceTime);
//Create Service .....an Bind
mResultListener = new ResultListener() {
#Override
public void onResultAvailable(double result) {
txtView.setText("Result: "+result);
}
};
mSimuService.setResultListener(mResultListener);
}
MY SOLUTION:
ResultListener = new ResultListener() {
#Override
public void onResultAvailable(double result) {
this.result=result;
runOnUiThread(setNewDataToUI);
}
};
private Thread setNewDataToUI = new Thread(new Runnable() {
#Override
public void run() {
txtView.setText("Result: "+result);
}
});
First of all: If you reference a Service in an Activity, the Service becomes pretty much useless. The advantage of services are, that they are loose coupled and can work indepenendtly form activities (=what the user sees) and its lifecycle and might even be in their own process. Thus activity-service communication is through intents or inter-process language AIDL, not through callbacks. If you want something executed asynchronosly use AsyncTask.
To your main problem: as you found out, you can only modify the UI on the UI-thread. So by design, leave changing UI in the component, thats responsibly for that (either activtiy or fragment), that will prevent the need of runOnUiThread()
Your code seems like txtView.setText("Result: "+result); will be executed in the Activity, but it wont. It will be executed in the Service, which (as I impleied before) does not run on the UI-thread. The problem is, I dont get the intent, what exactly you want to achieve so it is hard to give you an alternative solution.
When doing an orientation change in the emulator (ADT v17.0.0), or on a device (original Droid), I noticed that the activity will go through more than one create-destroy cycle sometimes. I found this blog post that mentions the problem, but offers no solution.
My app (API 8) currently does a 'last-chance' save of user data in onPause, and retrieves that data in onCreate/onStart, which works fine for a single restart, but gets into concurrency issues if the cycles come back-to-back. Specifically if a load starts before a save is complete, the 'last-chance' data is lost.
I did read the Faster Screen Orientation Change Android-developer article which mentions transferring an Object via onRetainNonConfigurationInstance/getLastNonConfigurationInstance. I tried using onRetain/getLast... like a flag to tell if the activity is "restarting", which works, but I still have the main issue of not knowing if/when any existing save/load operations are completed.
Concurrency & thread-management are not my strongest suits, so I'm looking for a solution that does at least one save & load across any number of back-to-back restarts, without memory leaks. Since fast create-destroy cycles could happen for non-orientation reasons, ideas that just involve breaking/handling orientation changes alone aren't really what I'm after.
Here's a log file excerpt with some cycles, you can see where the activity gets created-destroyed twice when going from landscape to portrait. Here's an excerpt of what I've got now:
onPause() {
file_manager.saveTemporaryPattern(); // writes to OutputStream on UI thread
}
onStart() {
findViewById (R.id.main_screen).post (new Runnable() {
file_manager.loadTemporaryPattern(); // reads from InputStream on UI thread
});
if (getLastNonConfigurationInstance() != null) {
// DO SOMETHING HERE?
}
}
onRetainNonConfigurationInstance() {
return dummy_object;
}
Managed to solve the concurrency issue by sending the Load/Save calls to a Handler thread linked to the Application context, rather than the Activity context. Had to setup a Load vs Save token in the calls to check for a Save->Load->Save pattern, avoiding the odd behavior of getting an incomplete activity lifecycle on a landscape->portrait orientation change.
It works in development & on test devices, and doesn't leak the activity context, so we'll see how it does in the wild.
In YourApp extends Application:
private Handler fileAccessThread = null;
public void onCreate() {
super.onCreate();
if (fileAccessThread == null) {
fileAccessThread = new Handler();
token = 0;
}
}
public void postCallbackFileAccess (int _token, Runnable _callback) {
switch (_token) {
case TOKEN_SAVE:
// Save must follow load, not another save
if (token == TOKEN_SAVE) { return; }
token = _token;
break;
case TOKEN_LOAD:
// Have to allow load->load, otherwise data gets lost/deleted
// if (token == TOKEN_LOAD) { return; }
token = _token;
break;
}
fileAccessThread.post (_callback);
}
In YourActivity:
protected void onStart() {
super.onStart();
((YourApp) getApplication()).postCallbackFileAccess (
YourApp.TOKEN_LOAD, new Runnable() {
#Override
public void run() {
file_manager.load();
}
});
}
protected void onPause() {
super.onPause();
((YourApp) getApplication()).postCallbackFileAccess (
YourApp.TOKEN_SAVE, new Runnable() {
#Override
public void run() {
file_manager.save();
}
});
}
I'm working with a fairly common situation right now - download some data over the web, then update a view to display it. Clearly, I want to do the web download in the background, and then update the view on the main UI thread. Now looking at my code, I'm a little worried about my Activity and its UI elements being killed off before I update them. Here's the essence of what I have in mind:
Thread update = new Thread() {
public void run() {
final Data newData = requestData();
if (newData != null) {
post(new Runnable() {
public void run() {
Toast.makeText(MyClass.this, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();
}
});
}
}
};
update.start();
It seems possible that while I'm downloading data, the activity may be destroyed. What happens then? Will my thread continue to execute? Will I end up trying to access dead objects?
Usually I do this by AsycTask, but the work seemed simple enough this time to just inline the threads-launching-threads stuff. Will I make things any better by using an AsyncTask instead?
If your Context is an Activity, you can check if it is finishing or has finished with the isFinishing() method:
if ( context instanceof Activity ) {
Activity activity = (Activity)context;
if ( activity.isFinishing() ) {
return;
}
}
Toast.makeText(context, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();
What you really want to use is an AsyncTaskLoader. These are my new favorite classes in the Android API. I use them all the time and they were made to solve problems just like this. You won't have to worry about when to stop your download or anything like that. All the threading logic is taken care of for you, including telling the thread to stop if the activity has been closed. Just say what it is you want to do in the loadInBackground() method. Note that if you are developing for an API lower than 3.0, you can still access all the loaders via the Android Support Package.
If you use anonymous classes, they will have an internal reference to the outer class, so it's not like it becomes inaccessible all of a sudden because other references have been cleared. AsyncTask actually doesn't change anything, it uses similar mechanics for notifying about results.
You can use loaders, they are designed to be in sync with the activity lifecycle. They are available only since Android 3.0, but you can use support package to work with them on any device with 1.6 or later.
There is even a simpler solution, you can just use a boolean field which indicates whether activity has gone away. You should set this field in onPause() (or whenever you think you won't need the notifications anymore) and check for it when you show toast. You won't even have to use synchronization, since this field is confined to the main thread, so it's absolutely safe. By the way, if you change this field somewhere else than in onDestroy(), don't forget to add a statement which resets your field back in the counterpart method.
public class MyActivity extends Activity {
private boolean activityDestroyed = false;
#Override
protected void onDestroy() {
activityDestroyed = true;
}
private void updateData() {
new Thread() {
#Override
public void run() {
final Data newData = requestData();
if (newData == null) return;
runOnUiThread(new Runnable() {
public void run() {
if (activityDestroyed) return;
Toast.makeText(MyActivity.this, "Blah",
Toast.LENGTH_SHORT).show();
}
});
}
}.start();
}
}
I usually use Weak Reference to avoid leaking context in views
Weak Reference for Context
private var mContext: WeakReference<Context?>? = null
Assign Context
mContext = WeakReference(appContext)
Get Context
mContext .get()
Validate Context
if (mContext?.get() is Activity &&
(mContext?.get() as Activity).isFinishing){
return
}
Kurtis is right. However, if you REALLY want to keep it simple, you can try this:
class MyActivity extends Activity {
static MyActivity context;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
MyActivity.context = this;
}
#Override
public void onDestroy() {
super.onDestroy();
MyActivity.context = null;
}
}
And then you just use MyActivity.context in your class (and check for null there). If you want the toast to not even show up when your app is in the background, use onPause/onResume instead.
Again, this is the quick and lazy approach. AsyncTask or AsyncTaskLoader is how you should be doing things.