My problem is that my two Activities GameActivity and StoreActivity should share same Player, and every change in Player made in any Activity should be seen in all Activities, but when I buy Upgrade in StoreActivity and change from StoreActivity the state of object is from before purchase.Player is Serializable.
OnClickListener from StoreActivity:
LIST_VIEW.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Upgrade upgrade = (Upgrade)LIST_VIEW.getAdapter().getItem(position);
if(upgrade.getCost() > player.getMoney()) {
LIST_VIEW.getChildAt(position).setEnabled(false);
} else {
player.buyUpgrade(upgrade);
CURRENT_MONEY.setText(getString(R.string.current_money) + player.getMoney());
//Notify GameActivity, but how?
}
}
});
The android.app.Activity class has an onResume() method.
It will be called when an activity is back interacting with the user.
So perfectly for your use case, because when the user wants to return, GameActivity will be unpaused andonResume() gets called.
You can implement it like this:
public class GameActivity extends AppCompatActivity {
#Override
protected void onResume() {
super.onResume();
//refreshing changed list items
}
}
You can tie your Player object to the app lifecycle by extending your Application class and storing your instance there. It would allow you to have a reference for it on every Activity without leaking during it's lifecycles. You can check for it's value and show some loading and create an callback for when it is set, so all activities can be notified about.
This great Codepath article explains how to do it.
Your main Application could look like this:
public class MyCustomApplication extends Application {
private Player player;
private ArrayList<WeakReference<OnPlayerSetCallback>> callbacks;
public void addCallback(WeakReference<OnPlayerSetCallback> callbackRef) {
this.callbacks.add(callbackRef);
}
public void removeCallback(WeakReference<OnPlayerSetCallback> callbackRef) {
this.callback.remove(callbackRef);
}
public void setPlayer(Player player) {
this.player = player;
for (WeakReference<OnPlayerSetCallback> callbackRef : callbacks) {
OnPlayerSetCallback callback = callbackRef.get();
if (callback == null) {
// remove invalid callback references (if an activity was destroyed, for example)
callbacks.remove(callbackRef);
} else {
// notify activities that a player was set
callback.onPlayerSet(player);
}
}
}
public Player getPlayer() {
return this.player;
}
interface OnPlayerSetCallback {
Player onPlayerSet();
}
}
Updating your Player shouldn't be a problem, as every activity would be using the same instance. However, if you need to update UI without having any lifecycle to re-render it in between, you can create an update listener in the callback.
You can use Singleton design Pattern.
You create one object from PlayerRepository with player field and in your app you just get it and each time
you want you change it.
public class PlayerRepository {
private Player player;
private static final PlayerRepository ourInstance = new PlayerRepository();
public static PlayerRepository getInstance() {
return ourInstance;
}
private PlayerRepository() {
}
public void setPlayer(Player player)
{
this.player=player;
}
public Player getPlayer()
{
retrun this.player;
}
}
In your Activities
PlayerRepository playerRepository=PlayerRepository.getInstance();
Player player=playerRepository.getPlayer();
player.buyUpgrade(upgrade);
Related
I think this question may simple but I didn't find any solution for this,
I there any way in Android that if any one of an activity calls onPause() I need to show Toast message or any notification kind of thing need to show. Generally I want to get notified when activity calls onPause() but I need it in one place since I may have some 15 activity I don't want to add it in all the activity.
ex:If I have activity when any one of the activity calls onPause I need to get notified but that notification code should be in one place and we should not add any line of code onPause() Is it possible to do this.
Thanks.
Create a baseActivity, which has for example :
open class BaseActivity : AppCompatActivity() {
override fun onPause() {
super.onPause()
Toast.makeText(this, "notified", Toast.LENGTH_SHORT).show()
}
}
Then you can extends this in your activities and handle the on pause call in BaseActivity
If your minSdkVersion >= 14, you can use Application.ActivityLifecycleCallbacks: ActivityLifecycleCallbacks
You have to define a custom Application class and you can register for this callbacks afterwards:
public class MyApplication extends Application {
private class LifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
#Override
public void onActivityCreated(final Activity activity, final Bundle savedInstanceState) {
//nothing to do
}
#Override
public void onActivityDestroyed(final Activity activity) {
//nothing to do
}
#Override
public void onActivityPaused(final Activity activity) {
// TODO Do your stuff, e.g. show toast.
}
#Override
public void onActivityResumed(final Activity activity) {
//nothing to do
}
#Override
public void onActivitySaveInstanceState(final Activity activity, final Bundle outState) {
//nothing to do
}
#Override
public void onActivityStarted(final Activity activity) {
}
#Override
public void onActivityStopped(final Activity activity) {
}
}
private final LifecycleCallbacks callbacks;
public void onCreate() {
super.onCreate();
callbacks = new LifecycleCallbacks();
application.registerActivityLifecycleCallbacks(callbacks);
}
}
Create a BaseActivity which contain all the methods you want to use in all other activities.
Then extend every activity with BaseActivity to call onPause() method.
I have two activities SplashScreen and MainActivity.
MainActvity download some data and show it.
Now I want to start both activities on application start, MainActivity in background and SplashScreen in foreground and when the data of MainActivity download complete I want to make MainActivity foreground and finish SplashScreen.
I have seen many solution but no one is going good in my situation.
Please give me some suggestion or example .
I think it's better to make splash screen to download data and when it completed show new activity (MainActivity). In onStart metod use downloaded data stored on phone.
Your example: The only way You can have resolve this your way is by start MainActivity first and from onStart start SplashScreen:
class SplashScreen extends Activitiy {
public void onStart() {
EventBus.register(this);
}
#Subscribe
public void finishedDownload(FinishedDownloadEvent) {
this.finish()
}
}
class MainActivity extens Activity () {
public void onStart () {
open(new Intent(SplashScreen.class)); // We would like to have
download();
}
public void download (OnFinish onFinish) {
... download
EventBus.post(new FinishedDownloadEvent());
}
}
I forgot:
class EventBus {
private final static Bus instance = new Bus();
public static void register (Object obj) {
instance.register(obj)
}
public static void post (Event obj) {
instance.post(obj)
}
}
EventBuss
I have an Activity which implements a listener. My concern is that the
activity can get re-created and the callback will then have a reference
to an object that is null.
This means we must update the controller with a new reference that references
the newly created activity.
What pattern is best to use even if the callbacks are async?
Is there perhaps a safe way to update the controllers reference in a thread > safe way.
OR
Should one rather use a Headless fragment and use the onAttach method get the
updated reference.
OR
Should one rather not use these patterns and use a Handler for
all your callbacks?
I suspect that my updateListener method will not work in all cases e.g.
1) init is busy and is just about to call the callback, line marked with
*10*
2) the activity gets recreated and updates the controller with
a new reference but the updateListener method is blocked because the callback is about to take place.
3) the callback executes and fails as the listener reference variable is stale.
public class Controller {
UserActionListener listener
static Controller instance;
public static synchronized Controller getInstance(UserActionListener listener) {
if (instance == null) {
instance = new Controller();
}
this.listener = listener;
return instance;
}
private Controller() {
//empty, enforce getInstance
}
private init() {
// do some very long running operation in a separate thread.
//.... on completion we update the UI
synchronized(Controller.class) {
/*10*/ listener.handle("SHOW DIALOG");
}
}
public void updateListener(UserActionListener listener) {
synchronized(Controller.class) {
this.listener = listener;
}
}
public class MainActivity extends AppCompatActivity implements UserActionListener {
static Controller controller;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
controller = Controller.getInstance(this);
if (savedInstanceState == null) {
//do not run on re-create
controller.init();
}
}
#Override
protected void onPostResume() {
super.onPostResume();
controller.updateListener(this);
}
#Override
public void handleAction(String userAction) {
switch (userAction) {
case "SHOW DIALOG" :
Toast.makeText(getActivity(),"Hello",Toast.LENGTH_LONG).show();
}
the direct answer to your question is a simple subscription pattern.
on the activity you call:
#Override
public void onStart(){
controller.updateListener(this);
}
#Override
public void onStop(){
controller.updateListener(null);
}
and inside the controller check for null before calling anything on the listener.
But there's a fundamental flaw on the logic.
With the following code:
static Controller controller;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
controller = new Controller(this);
}
the static controller having a reference to the activity is leaking the activity, avoiding it to be garbage collected.
also, even thou the controller is static, you're creating a new one every time the activity is created, also inside the controller init() you have the following:
// do some very long running operation
//....
that means this very long running operation is:
running in the UI thread. This will block your app initialisation, the user will think it's broken and the system will probably show a message to the user asking to close it.
there's nothing to guarantee that your process won't be killed either by the user or by the system before the "very long operation" finishes. If you want to run a long operation you MUST user a Service instead.
Very sample, Use WeakReference to activity
The project that I'm working uses a view-presenter abstraction.
Here is a simplified version of all the main classes.
The abstract activity (wire Presenter instance, with View)
public abstract class MvpActivity<Presenter extends MvpPresenter>
extends ActionBarActivity {
protected Presenter mPresenter;
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = getPresenterInstance();
}
#Override protected void onResume() {
super.onResume();
mPresenter.onResume(this);
}
#Override protected void onPause() {
mPresenter.onPause();
super.onPause();
}
}
The view interface
public interface MyView {
void redirect();
}
The view implementation
public class MyActivity
extends MvpActivity<MyPresenter>
implements MyView {
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_view);
Button myButton = (Button)findViewById(R.id.my_button);
myButton.setOnClickListener(v -> mPresenter.onButtonPressed());
}
#Override protected MyPresenter getPresenterInstance() {
return new MyPresenter();
}
#Override void redirect(){
startActivity(new Intent(this, MyOtherActivity.class));
}
The abstract presenter
public abstract class MvpPresenter<ViewType> {
private ViewType mView;
public void onResume(ViewType view) {
mView = view;
}
public void onPause() {
mView = null;
}
protected ViewType getView() {
if (mView == null) {
throw new IllegalStateException("Presenter view is null");
}
return mView;
}
}
And the presenter implementation
public class MyPresenter extends MvpPresenter<MyView> {
#Override public void onResume(MyView myView){
super.onResume(myView);
Log.("MyPresenter", "Presenter resumed");
}
#Override public void onPause(){
super.onPause()
Log.("MyPresenter", "Presenter paused");
}
public void onButtonPressed(){
getView().redirect();
}
}
The issue comes up as an "IllegalStateException: Presenter view is null" triggered by getView().redirect(); when called from the MyPresenter.onButtonPressed() method.
This doesn't make any sense to me, as the view should always be not null if the listener is fired. The view is only set to null if the MvpPresenter.onPause() is executed which is only being called from MvpActivity.onPause(). I wouldn't expect to receive any click events after this happens, so what am I missing here?
Sadly, I can not reproduce this issue by manually testing the application. The reports are coming in from Crashlytics.
Note: retrolambda is in use for the button click listener
Update 10/07/2017
Some ways of fixing this issues:
-
https://developer.android.com/reference/android/view/View.html#cancelPendingInputEvents()
-
https://github.com/JakeWharton/butterknife/blob/master/butterknife/src/main/java/butterknife/internal/DebouncingOnClickListener.java
Short answer: don't do that.
Unfortunately, you're relying on an order of events that is undefined. Activity lifecycle events and Window events are two different things, even though they're often closely related. You'll get onPause() when the activity is paused for any reason. But the View touch events aren't unhooked until the View's window loses focus.
It's very common for an activity to pause right when its window loses focus--for instance, when the screen is locked or when another activity is launched. But as you've seen, you can get pauses without a focus change and focus changes without a pause. Even when the two events occur together, there's a narrow window of time when onPause() has been called but the window input handlers are still active.
As with any undefined behavior, the actual results you see will vary by OS version and hardware type.
If you need to make sure that you don't receive View messages after onPause, you should unhook your handlers in onPause.
From a view (a custom status bar) I need to know when the activity is paused and resumed. Is it possible?
Is it possible?
Yes and very easily.
Declare a interface and provide its implementation via your inner view to the outer activity.
Call the respective delegate in the onResume and onPause of activity to notify your inner view.
Simple. Hope this helps :)
Something like this
class MyActivity extends Activity {
public interface ActivityLifeCycleLister{
onResumeCalled();
}
public ActvityLifeCycleLsistener listener;
public void setActivityLifeCycleListener(ActvityLifeCycleLsistener listener) {
this.listener = listener;
}
#Override
public void onResume(){
super.onResume();
if(listener != null) {
listener.onResumeCalled();
}
}
class MyInnerView extends View {
// Some init method
void init() {
MyActivity.setActivityLifeCycleListener(new ActivityLifeCycleListener() {
void onResumeCalled() {
// Do whatever here
}
});
}
}
}
This approach can be used for all the components of your application who wants to listen life cycle events.
You can just create two methods onResume() and onPause() of your custom View (you can call this two methods whatever you want). Then in your activity you will have override onResume and onPause and call customView.onResume() and customView.onPause().