Android: Using WebView outside an Activity context
Correct me if I am wrong but the correct answer to this question is that there is NO possible way to use a WebView in the background while the user is doing other things on the phone without interrupting the user by means of an Activity.
I have applied both Randy and Code_Yoga's suggestions: Using an activity with "Theme.NoDisplay" to launch a background service with a WebView to do some work. However even though no view is visible the switching to that activity for that second to start the services interrupts the user (ex. like pausing a running game that was being played).
Totally disastrous news for my app so I am still hoping someone will give me a way to use a WebView that does not need an Activity (or a substitute for a WebView that can accomplish the same)
Showing an AlertDialog from a Webview outside of an Activity
You can display the Dialog
from the service, by setting the window type as TYPE_SYSTEM_ALERT
. Remember to communicate back the action taken by the user using the JsResult
instance passed to onJsAlert
.
// maintain reference to JsResult instance passed to onJsAlert, in order
// communicate back the action taken by the user.
private JsResult mResult;
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
mResult = result;
AlertDialog dialog = new AlertDialog.Builder(MyService.this)
.setTitle("Custom Dialog")
.setMessage("This is our custom dialog, not the one served by JsDialogHelper")
.setOnCancelListener(new CancelListener())
.setNegativeButton("Cancel", new CancelListener())
.setPositiveButton("Ok", new PositiveListener())
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
return true;
}
});
private class CancelListener implements DialogInterface.OnCancelListener,
DialogInterface.OnClickListener {
@Override
public void onCancel(DialogInterface dialogInterface) {
mResult.cancel();
}
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mResult.cancel();
}
}
private class PositiveListener implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
mResult.confirm();
}
}
Add the required permissions to the manifest file:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Alternate solution:
If the web application can be built specific to android webview, then you can create Javascript interfaces which will allow Javascript code to directly invoke the Android code which in turn displays the dialog for you. This avoids the JsDialogHelper
route.
- Define a javascript interface:
public class WebAppInterface {
Context context;
WebAppInterface(Context c) {
context = c;
}
// Annotation required for api >= 17
@JavascriptInterface
public void showDialog() {
AlertDialog dialog = new AlertDialog.Builder(context)
.setTitle("Custom Dialog")
.setMessage("This is our custom dialog, not the one served by JsDialogHelper")
.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
}
}
- Bind the interface between Javascript and Android code, using addJavascriptInterface.
webView.addJavascriptInterface(new WebAppInterface(this), "Android");
- Use the interface in Javascript code.
<input type="button" value="Display dialog" onClick="displayDialog()" />
<script>
function displayDialog() {
//alert("Javascript Dialog");
Android.showDialog();
}
</script>
</input>
Android - How can I Attach a WebView to an Activity after creating it with the Application Context
So, we've actually run into the same issue (using a WebView
in a retained Fragment
to prevent page reloads, while not leaking an Activity
context) and the short answer seems to be that there's no really clean way to do so. I've even tried a custom WebView
subclass that returned the host Activity
's window token instead of the WebView
's own token, to no success.
We ended up using reflection, as you suggested, to modify the underlying context:
public static boolean setContext(View v, Context ctx) {
try {
final Field contextField = View.class.getDeclaredField("mContext");
contextField.setAccessible(true);
contextField.set(v, ctx);
return (v.getContext() == ctx);
} catch (IllegalAccessException | NoSuchFieldException e) {
Log.e(TAG, String.valueOf(e), e);
return false;
}
}
which just sets the mContext
field on the View
instance and returns true if it was successfully modified. However, I saw another suggestion recently (and I haven't tested this, so YMMV) to use MutableContextWrapper
. So you would inflate the WebView
with the Activity
Context
, wrapped in a MutableContextWrapper
. Then, when you need to release the previous reference, you'd cast that WebView
's Context
to a MutableContextWrapper
, and then set the base Context
to the new Activity
. So something like this to inflate the layout:
MutableContextWrapper contextWrapper = new MutableContextWrapper(activity);
WebView webView = (WebView) LayoutInflater.from(contextWrapper)
.inflate(R.layout.your_webview_layout, theParent, false);
Then, to re-attach to a new Activity
:
if (webView.getContext() instanceof MutableContextWrapper) {
((MutableContextWrapper) webView.getContext()).setBaseContext(newActivity);
}
How to use WebView in an IntentService?
There are no GUI-less web browser available for Android
While I have not tried it recently, WebView
at least used to have no particular requirement for displaying its UI. I used WebView
from within a Service
, in my case for a JavaScript interpreter (back before we had better options for that).
Now, an IntentService
is not a good choice. WebView
is largely asynchronous, and IntentService
will destroy itself before WebView
gets a chance to do its work. Use a regular Service
, where you control the Service
lifetime, so you can call stopSelf()
only when you are ready to do so.
I want to use a WebView instance which can only run on an UI thread.
The relationship between WebView
and threads is complicated. But, back when I last tried it, IIRC, a WebView
that is not actually appearing on the screen did not need the main application thread. But, as I noted, WebView
does most of its work asynchronously. You may find that you do not need a background thread of your own.
Is there a way to serialize the context of my activity so I can use this context to create my WebView?
No, nor would it address any of your concerns.
How to use Androids ContextWrapper class to emulate an activity?
That is not possible, nor would it address any of your concerns.
Can WebView be used inside a service?
No, a WebView
should not be used inside a service, and it really doesn't make sense to, anyway. If you're loading your WebView
with the intention of scraping the html contained in it, you might as well just run an HttpGet request, like this --
public static String readFromUrl( String url ) {
String result = null;
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet( url );
HttpResponse response;
try {
response = client.execute( get );
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream is = entity.getContent();
BufferedReader reader = new BufferedReader(
new InputStreamReader( is ) );
StringBuilder sb = new StringBuilder();
String line = null;
try {
while( ( line = reader.readLine() ) != null )
sb.append( line + "\n" );
} catch ( IOException e ) {
Log.e( "readFromUrl", e.getMessage() );
} finally {
try {
is.close();
} catch ( IOException e ) {
Log.e( "readFromUrl", e.getMessage() );
}
}
result = sb.toString();
is.close();
}
} catch( Exception e ) {
Log.e( "readFromUrl", e.getMessage() );
}
return result;
}
Related Topics
How to Make Circle Custom Progress Bar in Android
Taking Pictures with Camera on Android Programmatically
Webview Link Click Open Default Browser
Stopping & Starting Music on Incoming Calls
Create and Share a File from Internal Storage
Android - How to Make Slide Menu Like Facebook, Spotify and Google +
Android Broadcast Receiver Bluetooth Events Catching
How to Open a PDF via Intent from Sd Card
How to Set Time to Device Programmatically
How to Resolve Target 'Android-Xx'
What Is the Benefit of Viewholder Pattern in Android
Android Remove Space Between Tabs in Tabwidget
Android Edittext - Finished Typing Event
Error:Execution Failed For Task ':App:Transformclasseswithjarmergingfordebug'