androidandroid-webviewandroid-websettings

Webview loading cache crashes on orientation change


I made an app that contains a webview, that shifts cache mode when needed. From load default to load cache else network. First problem I had was that the app crashes when orientation is changed while an alert dialog is active. What I did was that I controlled orientation change when I put:

    this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

on the method where the webview is built, and I put:

    this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

on methods that calls an alert dialog. But now, the problem is that the app crashes when cache mode is set to load cache else network, cache is being loaded, and orientation has been changed. I am sure that this error only occurs when cache is being loaded because based on my test, it works fine again when connection is again established.

Here are the complete codes on my main activity without the imports of course:

public class MainActivity extends Activity
{

    private WebView wv;
    private ProgressBar progress;
    private static String mycaturl="www.sampleurl1.com";
    private static String errurl="www.sampleurl2.com";

    @SuppressLint("NewApi")
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);     
        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
        StrictMode.setThreadPolicy(policy);
        if (reachable(this))
        {
            Toast.makeText(this, "Reachable", Toast.LENGTH_SHORT).show();
            buildwv( savedInstanceState, WebSettings.LOAD_DEFAULT, mycaturl );
        }
        else
        {
            Toast.makeText(this, "Unreachable", Toast.LENGTH_SHORT).show();
            eolc( savedInstanceState );
        }
    }


    @SuppressLint({ "SetJavaScriptEnabled" })
    public void buildwv(Bundle sis, int load, String url)
    {
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

        setContentView(R.layout.activity_main);

        //assigning objects to variables
        wv=(WebView) findViewById(R.id.wv);
        wv.setWebViewClient( new wvc() );
        progress=(ProgressBar) findViewById(R.id.progress);

        //websettings
        WebSettings ws = wv.getSettings();
        ws.setAppCacheMaxSize( 100 * 1024 * 1024 ); // 100MB
        ws.setAppCachePath( this.getCacheDir().getAbsolutePath() );
        ws.setAllowFileAccess( true );
        ws.setAppCacheEnabled( true );
        ws.setJavaScriptEnabled( true );
        ws.setCacheMode(load);

        //if instance is saved, to catch orientation change
        if(sis==null)
        {   wv.loadUrl(url);    }
    }


    public void eolc(final Bundle sis)
    {
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

        AlertDialog.Builder alertDialog = new AlertDialog.Builder( MainActivity.this );

        alertDialog.setTitle("PATAY ANG WA-LA!!");
        alertDialog.setMessage("Host is unreachable. Load from cache or exit.");
        alertDialog.setIcon(R.drawable.tick);
        //alertDialog.setCanceledOnTouchOutside(false);
        alertDialog.setCancelable(false);

        alertDialog.setPositiveButton( "Load from Cache", new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog,int which)
            {
                // Write your code here to execute after dialog
                Toast.makeText(getApplicationContext(), "You chose to load cache.", Toast.LENGTH_SHORT).show();
                buildwv( sis, WebSettings.LOAD_CACHE_ELSE_NETWORK, mycaturl );
            }
        });

        alertDialog.setNeutralButton( "Help", new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int which)
            {
                Toast.makeText(getApplicationContext(), "You chose Help. EOLC", Toast.LENGTH_SHORT).show();

                buildwv( sis, WebSettings.LOAD_CACHE_ELSE_NETWORK, errurl );
            }
        });

        alertDialog.setNegativeButton( "Exit", new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int which)
            {
                // Write your code here to execute after dialog
                Toast.makeText(getApplicationContext(), "You chose to exit.", Toast.LENGTH_SHORT).show();
                finish();
            }
        });

        alertDialog.create();
        alertDialog.show();
    }


    public void roe()
    {
        this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

        AlertDialog.Builder alertDialog = new AlertDialog.Builder( MainActivity.this );

        alertDialog.setTitle("TANGA ANG WA-LA!!");
        alertDialog.setMessage("Host is unreachable. Restart to load cache or exit.");
        alertDialog.setIcon(R.drawable.tick);
        alertDialog.setCancelable(false);

        alertDialog.setPositiveButton( "Restart", new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog,int which)
            {
                Toast.makeText(getApplicationContext(), "You chose to restart and load cache.", Toast.LENGTH_SHORT).show();
                Intent i = getBaseContext().getPackageManager()
                         .getLaunchIntentForPackage( getBaseContext().getPackageName() );
                i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );
                startActivity(i);
            }
        });

        alertDialog.setNeutralButton( "Help", new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int which)
            {
                Toast.makeText(getApplicationContext(), "You chose Help. ROE", Toast.LENGTH_SHORT).show();
                wv.stopLoading();
                wv.loadUrl( errurl );
            }
        });

        alertDialog.setNegativeButton( "Exit", new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int which)
            {
                Toast.makeText(getApplicationContext(), "You chose to exit.", Toast.LENGTH_SHORT).show();
                finish();
            }
        });

        alertDialog.create();
        alertDialog.show();
    }


    private class wvc extends WebViewClient
    {

          //when page started loading
          public void onPageStarted(WebView view, String url, Bitmap favicon)
          {
              //circular progress bar open
              progress.setVisibility(View.VISIBLE);

              //if reachable and setting cache on every new page
              //setcache(getApplicationContext()); 

              if (url==mycaturl)
              {
                  WebSettings ws = wv.getSettings();

                  if ( !reachable(getApplicationContext()) )
                  {
                      if ( ws.getCacheMode() == WebSettings.LOAD_DEFAULT )
                      {
                          roe();
                      }
                      else if ( ws.getCacheMode() == WebSettings.LOAD_CACHE_ELSE_NETWORK )
                      {
                          Toast.makeText(getApplicationContext(), "loading cache coz not reachable", Toast.LENGTH_SHORT).show();
                      }
                      else
                      {
                          Toast.makeText(getApplicationContext(), "websetting out of range", Toast.LENGTH_SHORT).show();
                      }
                  }
              }
          }


          //when page finished
          @Override
          public void onPageFinished(WebView view, String url) 
          {
              super.onPageFinished(view, mycaturl);
              Toast.makeText(getApplicationContext(), "PAGE DONE LOADING!!", Toast.LENGTH_SHORT).show();

              //circular progress bar close
              progress.setVisibility(View.GONE);
          }


          //when received an error
          @Override
          public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
          {
              wv.stopLoading();
              //wv.loadUrl("file:///android_asset/otherpages/errorpage.htm");
              WebSettings ws = wv.getSettings();

              if ( ws.getCacheMode() == WebSettings.LOAD_DEFAULT )
              {
                  Toast.makeText(getApplicationContext(), "Page unavailable", Toast.LENGTH_SHORT).show();
              }
              else
              {
                  Toast.makeText(getApplicationContext(), "Page not cached", Toast.LENGTH_SHORT).show();
              }
              roe();
          }
      }


    //checking connectivity by checking if site is reachable
    public static boolean reachable(Context context) 
    {
        final ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        final NetworkInfo netInfo = connMgr.getActiveNetworkInfo();

        if (netInfo != null && netInfo.isConnected()) 
        {
            try 
            {
                URL url = new URL(mycaturl);
                HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
                urlc.setConnectTimeout(5000); // five seconds timeout in milliseconds
                urlc.connect();
                if (urlc.getResponseCode() == 200) // good response
                {   return true;    } else {    return false;   }
            }
            catch (IOException e)
            {   return false;   }
        }
        else
        {   return false;   }
    }


    //options menu inflation
    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
    {
        // inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }


    //when back button is pressed
    public void onBackPressed ()
    {
        if (wv.isFocused() && wv.canGoBack())
        {   wv.goBack();    }   else {  finish();   }
    }


    //when options button is pressed
    @Override

    public boolean onOptionsItemSelected(MenuItem item)
    {
        switch (item.getItemId())
        {
            case R.id.item1:
                wv.loadUrl( errurl );
                break;
            case R.id.item2:
                String currurl=wv.getUrl();
                wv.loadUrl(currurl);   
                break;
            case R.id.item3:
                Intent i = getBaseContext().getPackageManager()
                 .getLaunchIntentForPackage( getBaseContext().getPackageName() );
                i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(i);
                break;
            case R.id.item4:
                finish();
                break;
            default:
                break;
        }
        return true;
    } 


    //saving instance state
    @Override
    protected void onSaveInstanceState(Bundle outState )
    {
        super.onSaveInstanceState(outState);
        wv.saveState(outState);
    }


    //restoring instance state
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState)
    {
        super.onSaveInstanceState(savedInstanceState);
        wv.restoreState(savedInstanceState);
    }

}

And here is my manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.msiecsapps.mycatlog"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> 

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.msiecsapps.mycatlog.Splash"
            android:label="@string/app_name" 
            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name="com.msiecsapps.mycatlog.MainActivity"
            android:label="@string/app_name" >
        </activity>
    </application>

</manifest>

Here are some of what was displayed on the Logcat:

08-23 15:23:13.615: V/MediaPlayer(31183): disconnect
08-23 15:23:13.615: V/MediaPlayer(31183): destructor
08-23 15:23:13.615: W/SurfaceView(31183): CHECK surface infomation creating=false formatChanged=false sizeChanged=false visible=false visibleChanged=true surfaceChanged=true realSizeChanged=false redrawNeeded=false left=false top=false
08-23 15:23:13.650: V/MediaPlayer(31183): disconnect
08-23 15:23:47.375: I/webclipboard(31183): clipservice: android.sec.clipboard.ClipboardExManager@41eaae10
08-23 15:23:47.470: V/webkit(31183): BrowserFrame constructor: this=Handler (android.webkit.BrowserFrame) {41eaac20}
08-23 15:23:47.610: E/SpannableStringBuilder(31183): SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
08-23 15:23:47.610: E/SpannableStringBuilder(31183): SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
08-23 15:23:47.705: I/GATE(31183): <GATE-M>DEV_ACTION_COMPLETED</GATE-M>
08-23 15:23:51.575: D/dalvikvm(31183): GC_CONCURRENT freed 215K, 9% free 13356K/14599K, paused 2ms+3ms, total 29ms
08-23 15:23:51.580: V/MediaPlayer-JNI(31183): native_finalize
08-23 15:23:51.580: V/MediaPlayer-JNI(31183): release
08-23 15:23:51.620: D/AndroidRuntime(31183): Shutting down VM
08-23 15:23:51.620: W/dalvikvm(31183): threadid=1: thread exiting with uncaught exception (group=0x410a82a0)
08-23 15:23:51.630: E/AndroidRuntime(31183): FATAL EXCEPTION: main
08-23 15:23:51.630: E/AndroidRuntime(31183): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.msiecsapps.mycatlog/com.msiecsapps.mycatlog.MainActivity}: java.lang.NullPointerException
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2100)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2125)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3553)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.app.ActivityThread.access$700(ActivityThread.java:140)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1233)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.os.Handler.dispatchMessage(Handler.java:99)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.os.Looper.loop(Looper.java:137)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.app.ActivityThread.main(ActivityThread.java:4898)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at java.lang.reflect.Method.invokeNative(Native Method)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at java.lang.reflect.Method.invoke(Method.java:511)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at dalvik.system.NativeStart.main(Native Method)
08-23 15:23:51.630: E/AndroidRuntime(31183): Caused by: java.lang.NullPointerException
08-23 15:23:51.630: E/AndroidRuntime(31183):    at com.msiecsapps.mycatlog.MainActivity.onRestoreInstanceState(MainActivity.java:332)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.app.Activity.performRestoreInstanceState(Activity.java:944)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1134)
08-23 15:23:51.630: E/AndroidRuntime(31183):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2078)
08-23 15:23:51.630: E/AndroidRuntime(31183):    ... 12 more
08-23 15:24:06.700: I/Process(31183): Sending signal. PID: 31183 SIG: 9
08-23 15:24:07.240: D/AndroidRuntime(1122): Shutting down VM
08-23 15:24:07.240: W/dalvikvm(1122): threadid=1: thread exiting with uncaught exception (group=0x410a82a0)
08-23 15:24:07.245: E/AndroidRuntime(1122): FATAL EXCEPTION: main
08-23 15:24:07.245: E/AndroidRuntime(1122): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.msiecsapps.mycatlog/com.msiecsapps.mycatlog.MainActivity}: java.lang.NullPointerException
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2100)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2125)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.app.ActivityThread.access$600(ActivityThread.java:140)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1227)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.os.Handler.dispatchMessage(Handler.java:99)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.os.Looper.loop(Looper.java:137)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.app.ActivityThread.main(ActivityThread.java:4898)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at java.lang.reflect.Method.invokeNative(Native Method)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at java.lang.reflect.Method.invoke(Method.java:511)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at dalvik.system.NativeStart.main(Native Method)
08-23 15:24:07.245: E/AndroidRuntime(1122): Caused by: java.lang.NullPointerException
08-23 15:24:07.245: E/AndroidRuntime(1122):     at com.msiecsapps.mycatlog.MainActivity.onRestoreInstanceState(MainActivity.java:332)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.app.Activity.performRestoreInstanceState(Activity.java:944)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1134)
08-23 15:24:07.245: E/AndroidRuntime(1122):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2078)
08-23 15:24:07.245: E/AndroidRuntime(1122):     ... 11 more

I think these logs were from the moment my splash screen disconnected, checked if the site is reachable, chose to load from cache (set cache mode to load cache else network) on the alert dialog, and when the cached page loaded, i tilted the phon to change orientation. And there, the VM was shut down.

Please help me. Thanks in advance.


Solution

  • Before you do anything else:

    1. Go to your settings on your device (phone/tablet), go to "developers settings" and select "USB-error-report" to on.
    2. attach your device to your PC by USB and voila: you will see the logcat logging from your device.

    To be able to handle problems / develop for android at all, you will need the above.

    After that: "First problem I had was that the app crashes when orientation is changed while an alert dialog is active" => go back one step, back to this problem. You should be able to handle this in another way, print the logcat logs of this problem here for us to be able to help you.