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.