0

I got an Android app which needs to load some data from a website and then display it. For that purpose, the user gets shown a loading bar, in a background thread loads the website and then it switches back to the main thread (with a Handler) and displays the information. So far so good, no problem here. But if the user turns off the phone while the information are being loaded and doesn't return when they are displayed, the app crashes.

So I need to interrupt the process when the user turns off his phone and retry it when he turns it on again. This shouldn't be a problem, as explained here, there are a lot of events to do such things.

But my debug messages showed, that the onStart()-Event (which would be perfect for my problem) is somehow called, when the user's phone is still turned off! This causes bugs. Is there any way to avoid that?

Edit: Here's my code (trimmed, of course)

(MainActivity.java)

private static MainActivity activity;
//Handler to run code from other threads in the main thread (needed to modify the GUI)
private static Handler handler;

private static ProgressBar progressBar;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);

    activity = this;
    handler = new Handler();

    [...]

}

@Override
public void onStart(){
    super.onStart();

    Log.e("s", "start");
    PageManager.mayContinue(true);

    if(PageManager.getCurrentPage() == null) {
        PageManager.loadPage(new StatusPage());
    }else{
        PageManager.loadPage(PageManager.getCurrentPage());
    }
}

@Override
public void onStop(){
    super.onStop();


    Log.e("s", "Stop");
    PageManager.mayContinue(false);
}

public static MainActivity getActivity(){
    return activity;
}

public static void runOnMainThread(Runnable r){
    handler.post(r);
}

(PageManager.java)

    private static Boolean isPageLoading = false;

private static Page currentPage;

private static boolean mayContinue;

public static void loadPage(final Page page){

    if(isPageLoading){
        return;
    }

    if(currentPage != null) {
        boolean allowedToLoadDifferentPage = currentPage.onPageLeft();
        if (!allowedToLoadDifferentPage) {
            return;
        }
    }

    isPageLoading = true;

    FragmentManager fragmentManager = MainActivity.getActivity().getFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    DisplayFragment fragment = new DisplayFragment();
    fragment.setPage(page);
    fragmentTransaction.replace(R.id.display_fragment, fragment);
    fragmentTransaction.commit();

    MainActivity.getProgressBar().setVisibility(View.VISIBLE);

    Runnable r = new Runnable(){

        @Override
        public void run() {

            //Load the required data
            page.loadPage();

            isPageLoading = false;

            if(!mayContinue){

                MainActivity.runOnMainThread(new Runnable() {
                    @Override
                    public void run() {
                        MainActivity.getProgressBar().setVisibility(View.INVISIBLE);
                    }
                });

                Log.e("PageManager", "Loading process got blocked!");

                return;
            }

            currentPage = page;

            MainActivity.runOnMainThread(new Runnable() {

                @Override
                public void run() {
                    MainActivity.getProgressBar().setVisibility(View.INVISIBLE);
                    //Display the loaded data
                    page.setViewValues();
                }

            });
        }


    };

    new Thread(r).start();
}

public static Page getCurrentPage(){
    return currentPage;
}

public static void mayContinue(boolean value) {
    mayContinue = value;
}

A part of StatusPage.java:

@Override
public void setViewValues() {

    RecyclerView rv = (RecyclerView)MainActivity.getActivity().findViewById(R.id.insert_values);
    LinearLayoutManager llm = new LinearLayoutManager(MainActivity.getActivity());
    llm.setOrientation(LinearLayoutManager.VERTICAL);
    rv.setLayoutManager(llm);

    RecyclerViewAdapter adapter = new RecyclerViewAdapter();
    rv.setAdapter(adapter);
    [...]
}

The LogCat:

  03-21 07:50:38.109 6121-6121/de.namnodorel.app E/s: start   //The Activity got launched
    03-21 07:50:38.261 6121-6121/de.namnodorel.app E/RecyclerView: No adapter attached; skipping layout
    03-21 07:50:38.342 6121-6121/de.namnodorel.app E/RecyclerView: No adapter attached; skipping layout
    //I turn the phone off
    03-21 07:50:39.541 6121-6121/de.namnodorel.app E/s: Stop
    //??? (I didn't turn it on)
    03-21 07:50:40.326 6121-6121/de.namnodorel.app E/s: start
    //The app crashes because the findViewById() returned null (which means there is nothing with that ID on the current screen)
    03-21 07:50:41.337 6121-6121/de.namnodorel.app E/AndroidRuntime: FATAL EXCEPTION: main

                                                                      Process: de.namnodorel.app, PID: 6121
                                                                      java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v7.widget.RecyclerView.setLayoutManager(android.support.v7.widget.RecyclerView$LayoutManager)' on a null object reference
                                                                          at

de.namnodorel.app.game.status.StatusPage.setViewValues(StatusPage.java:47)
                                                                          at de.namnodorel.app.game.PageManager$1$2.run(PageManager.java:75)
                                                                          at android.os.Handler.handleCallback(Handler.java:739)
                                                                          at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                          at android.os.Looper.loop(Looper.java:135)
                                                                          at android.app.ActivityThread.main(ActivityThread.java:5294)
                                                                          at java.lang.reflect.Method.invoke(Native Method)
                                                                          at java.lang.reflect.Method.invoke(Method.java:372)
                                                                          at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
                                                                          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:699)
                                                                          at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:115)

I would expect onStart and onStop() to be also called, when the screen is turned off. The docs say, they're called, when the Activity isn't visible anymore/visible again. And because when the screen is turned off, nothing is visible (or the LockScreen at least) I expect them to be called. They are, but onStart() is called in the wrong place.

Namnodorel
  • 385
  • 5
  • 17
  • onStart is not defined in terms of the screen being turned on and off. It's defined in terms of the activity being visible to the user. Do you really need to trigger on screen on/off, or do you need to trigger on visibility? They're different things. – Doug Stevenson Mar 20 '16 at 21:31
  • @Doug Stevenson In some way, both. After loading the data, the process tries to display it. This doesn't work if the Activity is not visible or the screen is turned off – Namnodorel Mar 20 '16 at 21:34
  • Actually, there is no problem updating the view hierarchy of an activity that is stopped and not visible (but not yet destroyed). The changes are just not visible until the next onStart. – Doug Stevenson Mar 20 '16 at 21:38
  • @Doug Stevenson Maybe it's because I use a RecyclerView in the layout? findViewById() returns null, and there's no way it could've been removes – Namnodorel Mar 20 '16 at 21:42
  • Maybe you should show the code and explain what exactly is not working the way you expect. – Doug Stevenson Mar 20 '16 at 21:45
  • onPause() and onResume() may help you... – Aditi Parikh Mar 21 '16 at 08:15
  • @Aditi Parikh No, doesn't work either – Namnodorel Mar 21 '16 at 08:31

4 Answers4

1

Why is onStart() perfect for your problem? Why not onCreate()?

One way to overcome this issue is to have a global boolean value set to true in the

onRestart()

method. And in the

onStart()

method check if that boolean is true, if it is, do not go on with the process that causes bugs, otherwise, go on. After that, in the

onStart()

method set that boolean to false.

mantlabs
  • 352
  • 2
  • 6
  • onCreate() is just called when the Activtiy starts; I need something that calls when the screen is turned off/on ;) I tried something similar (with onStop() instead of onRestart()); I set the boolean to false in onStop() (if an action is running, it stops before getting to the bug) and set it to true at onStart() (Also I'm restarting the loading process 'cause there shouldn't be a blank screen). But because onStart() is called when the screen is off, if a running process comes to the bug, it doesn' t stop. And that's the problem which would occour in your solution too, I think. – Namnodorel Mar 20 '16 at 21:17
  • onStart is not called when the screen is off, it is called when it turning on and making this Activity visible. Could you please share the bug or the logcat to see the error? – mantlabs Mar 20 '16 at 21:39
  • The error is at StatusPage.java line 47, could you please share that portion of the code? The code that manipulates your RecyclerView. And where do you define it. If it is inside a fragment you need to use getActivity().findViewById() or the rootView.findViewById() if you have the rootView of the fragment available. – mantlabs Mar 21 '16 at 07:10
  • Did my above suggestion fixed the problem? – mantlabs Mar 21 '16 at 07:17
  • No, the `onRestart()` event wasn't even called – Namnodorel Mar 21 '16 at 07:27
0

May be you can do it another way. Register broadcast receiver with Intent.ACTION_SCREEN_OFF, Intent.ACTION_SCREEN_ON

Artem Zelinskiy
  • 2,201
  • 16
  • 19
0

OK, I've figured it out. In the onStart()-Method I simply check if the screen is turned on or not. If it's not, the call is not handled. That's maybe not a solution, but at last a workaround. This is what my code now looks like:

    @Override
public void onStart(){
    super.onStart();

    if(isScreenOn()) {
        PageManager.mayContinue(true);

        if(PageManager.getCurrentPage() == null) {
            PageManager.loadPage(new StatusPage());
        }else{
            PageManager.loadPage(PageManager.getCurrentPage());
        }
    }

}


@Override
public void onStop(){
    super.onStop();

    PageManager.mayContinue(false);
}

    public boolean isScreenOn(){
    PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

    if (Build.VERSION.SDK_INT < 21) {
        return pm.isScreenOn();
    }else{
        return pm.isInteractive();
    }
}
Namnodorel
  • 385
  • 5
  • 17
  • Btw it didn't completely fix the Bug, as onStart() is also sometimes called when the phone isn't unlocked yet. But that's not what the question was about. – Namnodorel Mar 21 '16 at 13:01
0

When a phone is turned off,is can be waked up by notifications or some other system services,may make your activity be active.

onStart() : Called when the activity is becoming visible to the user.

onResume(): Called when the activity will start interacting with the user.

You may change to execute your code in onResume().

If this is too late for you,you can add a judgement if phone is turned off by PowerManager.

banking
  • 480
  • 2
  • 9