I'm trying to build a simple program using Asycn Task that on clicking get button loads google home page in webview. I have no errors showing up in code however when I click the get button it crashes with the following error:
An error occured while executing doInBackground()
java.lang.RuntimeException:java.lang.Throwable:A WebView method was called on thread 'AsyncTask #1'.All WebView methods must be called on the same thread
This is my MainActivity.java
package com.example.asyncts2;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
Button button1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
button1.setEnabled(false);
DownloadWebPageTask task = new DownloadWebPageTask();
task.execute(new String[] {"http://www.google.com/"});
}
});
}
private class DownloadWebPageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
WebView webView = (WebView) findViewById(R.id.webView1);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(urls[0]);
return "Done";
}
protected void onPostExecute(String result) {
Toast.makeText(MainActivity.this, "result", Toast.LENGTH_SHORT).show();
}
}
This is Log Cat errors
Can anyone point me to where I am going wrong? Many thanks!
easy way to handle that is to, whenever and wherever you want to access or modify the WebView, do it in the uithread
runOnUiThread(new Runnable() {
#Override
public void run() {
//shake the webView here!
}
});
You should not use an AsyncTask to load an URL in a webview. If you want to trigger an action when loading is complete, do like this :
webView.setWebViewClient(new WebViewClient()
{
public void onPageFinished(WebView view, String url)
{
// do something
}
});
doInBackground() method not runs in the UI thread. It runs as a separate thread which will not interact with UI. Thats why, It is not able to access your WebView which is present in UI thread. I really dont see the purpose of using an AsyncTask here. You can load your webview inside your onClick method itself. Otherwise, use onPostExecute() method to update your Webview.
Otherwise, As #eduyayo told, put the runOnUiThread() method inside your doInBackground() method and do the WebView updates inside the run() method.
You have to make those calls inside your main thread, i.e. inside your activity itself. Since AsyncTask create another thread, android restricts lots of action here. one simple way to go ahead is to mimmic call back handler . Steps to guide you ,
Create interface :
public interface IWebViewHandler {
void handleWebCall();
}
Implement this interace by your activity and override the method with your action and call this method from your asynctask.doInBackground, like
public class MainActivity extends Activity implements IWebViewHandler {
#Override
public void handleWebCall() {
//your work like action on WebViews
/* WebView webView = (WebView) findViewById(R.id.webView1);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(urls[0]);*/
}
private class DownloadWebPageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
handleWebCall();
return "Done";
}
protected void onPostExecute(String result) {
Toast.makeText(MainActivity.this, "result", Toast.LENGTH_SHORT).show();
}
}
}
P.S : the call to handleWebCall is just for sample, modify calling method and the method implementation /signature as per your neeed.
Related
I'm new to Android and I stuck. I have a Textview on Activity which should show result, but for some reason it is updating TextView only if you click on Button which is not doing anything or if you close app and reopen it again from menu of running apps. I suppose it's somehow connected with updating activity. Thank you in advance!
package com.example.visacheck;
import android.content.Intent;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class FourthActivity extends AppCompatActivity {
String aplicationNumber;
String type;
String year;
#Override
protected void onCreate(Bundle savedInstanceState) {
this.getSupportActionBar().hide();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fourth);
Intent intent = getIntent();
final Button button = findViewById(R.id.resultButton); //Result appearing only after clicking button
aplicationNumber = intent.getStringExtra("aplicationNumber");
type = intent.getStringExtra("type");
year = intent.getStringExtra("year");
class MyJavaScriptInterface {
#JavascriptInterface
#SuppressWarnings("unused")
public void processHTML(String html) {
TextView text = findViewById(R.id.textView);
text.setText(html);
}
}
final WebView myWebview = findViewById(R.id.webview);
myWebview.getSettings().setJavaScriptEnabled(true);
myWebview.addJavascriptInterface(new MyJavaScriptInterface(), "HTMLOUT");
myWebview.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
myWebview.loadUrl(("javascript:document.getElementById('edit-ioff-application-number').value = '" + aplicationNumber + "';void(0);"));
myWebview.loadUrl(("javascript:" + "document.getElementById('edit-ioff-application-code').value = '" + type + "';void(0);"));
myWebview.loadUrl(("javascript:document.getElementById('edit-ioff-application-year').value = '" + year + "';void(0);"));
myWebview.loadUrl(("javascript:document.getElementById('edit-submit-button').click();"));
myWebview.loadUrl("javascript:window.HTMLOUT.processHTML(document.getElementsByClassName('alert alert-warning')[0].innerText);");
myWebview.loadUrl("javascript:window.HTMLOUT.processHTML(document.getElementsByClassName('alert alert-success')[1].innerText);");
myWebview.loadUrl("javascript:window.HTMLOUT.processHTML(document.getElementsByClassName('alert alert-danger')[0].innerText);");
}
});
myWebview.loadUrl("https://frs.gov.cz/ioff/application-status");
}
}
I'm not going to do a very good job at this, as I can hardly touch on the subject myself. Maybe someone with more knowledge can go into further detail, but the general idea here is that this code is being called by the JS thread as opposed to your UI thread (Which is the only thread that is allowed to handle UI updates), and I'm surprised this code doesn't crash when doing so, honestly. The post() method adds this to the view's message queue, which means that the text will be updated AFTER the view has been rendered, and has no other tasks to perform. I know I did a bad job at explaining this, but for more information, please refer to these:
What exactly does the post method do?
Alternately, you can user runOnUIThread(), example:
How do we use runOnUiThread in Android?
I'm sure that a lot of people out there have already explained this better than I have. But the most important thing to understand here is that you must not update UI from anything other than the UI thread
Please note that I chose myWebView arbitrarily, and this should work if posted to the fragment's main view aswell.
class MyJavaScriptInterface {
#JavascriptInterface
#SuppressWarnings("unused")
public void processHTML(final String html) {
myWebview.post(new Runnable() {
#Override public void run() {
TextView text = findViewById(R.id.textView); text.setText(html);
}
});
}
}
I am trying to use a code from this link https://developer.android.com/training/connect-devices-wirelessly/nsd.html
"from Discover Services on the Network."
I copy and paste code as the following:
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button aButton = (Button) findViewById(R.id.MyButton);
aButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// there is an error in the next line, ";" expected.
// but I do not know where to put ; exactly
public void initializeRegistrationListener() {
mRegistrationListener = new NsdManager.RegistrationListener() {
#Override
public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) {
// Save the service name. Android may have changed it in order to
// resolve a conflict, so update the name you initially requested
// with the name Android actually used.
mServiceName = NsdServiceInfo.getServiceName();
}
#Override
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
// Registration failed! Put debugging code here to determine why.
}
#Override
public void onServiceUnregistered(NsdServiceInfo arg0) {
// Service has been unregistered. This only happens when you call
// NsdManager.unregisterService() and pass in this listener.
}
#Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
// Unregistration failed. Put debugging code here to determine why.
}
};
} }
});
}
}
But there is an error in the this Line "public void initializeRegistrationListener()", ";" expected. but I do not know where to put ";" exactly or there is something else wrong that I cannot see it, can someone guide me, please?
PS: I am trying to make my phone discovers Mdns service that I created on my laptop using javascript, I have no experience in Java but I need to run the previous code to test the service that I have created already.
First of all you need an xml that contains an Button(this button will have android:id="#+id/mybutton", and that's the id you have to use on findViewById(R.id.mybutton).
In onCreate method of your activity you will write that code that you showed us, and you are good to go.
Another small step, if you wrote your own xml, make sure to have this line in onCreate
setContentView(R.layout.yourxml)
In an android application that I am developing Im using a thread, and to make sure I dont get the "java.lang.IllegalStateException: System services not available to Activities before onCreate()" I use a boolean called donecreate. Problem is that Android studio says I have a "java.lang.NullPointerException at picLoop.run(picLoop.java:24)"
Code main class:
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.Display;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
public class main extends Activity {
public Boolean donecreate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new eyeCanvas(this));
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
docreate();
}
public void docreate(){
donecreate = true;
}
public void checkHead(){
AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
if(am.isWiredHeadsetOn()){
Toast.makeText(getApplicationContext(), "HEADPHONES", Toast.LENGTH_LONG).show();
}
}
}
Code: pic loop
import android.graphics.Canvas;
//**Threading
public class picLoop extends Thread {
private eyeCanvas eye;
private main main = new main();
public picLoop(eyeCanvas eye) {
this.eye = eye;
}
#Override
public void run(){
Canvas c = null;
while(true) {
if(main.donecreate){ //<-- where error is
main.checkHead();
}
try {
// head.onCreate(Bundle savedInstanceState);
c = eye.getHolder().lockCanvas();
synchronized (eye.getHolder()) {
eye.onDraw(c);
}
} finally {
if (c != null) {
eye.getHolder().unlockCanvasAndPost(c);
}
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Also if you guys could give me feedback on how I submitted, It would help :)
You can't create activities like you're trying to do. You can NEVER EVER do 'new Activity()', as the activity needs to be launched by the system to get set up properly and go through its lifecycle as intended.
So remove the line private main main = new main();.
To do what you're trying, make the boolean a static variable.
Change
public Boolean donecreate;
to
public static Boolean donecreate;
Then you can access it like you're trying to do, without creating an instance of main Activity.
There are a large number of things wrong with the assumptions you're making. Firstly, if your Thread requires your Activity to be created, don't start it until your Activity is created. Manage the lifecycle of this object within the Activity itself, i.e.:
#Override
public void onStart() {
super.onStart();
// Start your work here
}
#Override
public void onStop() {
super.onStop();
// Stop your work here
}
Secondly, please don't use the static access approach being recommended -- that makes the assumption that there is only one Activity instance (which is wrong immediately on a configuration change, and/or if you start another instance of that Activity in the task). And even if that assumption were true, you would need to set it back to false in onDestroy() (still, don't do that).
try setting donecreate to false initially
public class main extends Activity {
public Boolean donecreate = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
....
You can request system service on application context, look at this answer.
So create a static variable in application class, initialize it like instance = this; in onCreate of Application class and then you'll be able to get app context whenever you want.
I have a simple webview which loads a page. This page has a few links that opens within the webview. That's what it supposed to do, so it's working all fine.
But there is one single link from that page which should load as a popup, so I want it to open in the normal browser when people click it. But as I stated, all links are opening in the webview, so that link does it also.
My question is, how can I make this link open in the normal browser as a kind of a popup? Is it even possible? The link is variable so it's changing always, it cannot be hardcoded within the application to open in a new browser browser.
Is it possible and how can I do it?
Here's an example of overriding webview loading to stay within your webview or to leave:
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class TestWebViewActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView webView = (WebView) findViewById(R.id.webview);
webView.setWebViewClient(new MyWebViewClient());
}
}
class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.contains("somePartOfYourUniqueUrl")){ // Could be cleverer and use a regex
return super.shouldOverrideUrlLoading(view, url); // Leave webview and use browser
} else {
view.loadUrl(url); // Stay within this webview and load url
return true;
}
}
}
public class WebViewActivity extends Activity {
private WebView webView;
private ProgressDialog progress;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webview);
WebView myWebView = (WebView) findViewById(R.id.webView1);
myWebView.setWebViewClient(new MyWebViewClient());
myWebView.loadUrl("https://www.example.com");
}
private class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("https://www.example.com")) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
}
I'm trying to use the Android tutorial to build an app that loads a web view to a mobile site that I built. The problem is with following the tutorial the startActivity function is undefined and the Android tutorial isn't helping. I've done Ctrl+Shift+O to verify all the proper modules are loaded.
package com.mysite;
import android.content.Intent;
import android.net.Uri;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.mysite.com")) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
Update
Ok, now my code reads:
package com.myapp;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
public class MyApp extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init webview
WebView DCWebView = (WebView) findViewById(R.id.webview);
WebSettings webViewSettings = DCWebView.getSettings();
//when a link is clicked, use the WebView instead of opening a new browser
DCWebView.setWebViewClient(new MyWebViewClient() {
#Override
public void launchExternalBrowser(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
});
//enable javascript
webViewSettings.setJavaScriptEnabled(true);
}
}
But I'm showing 2 errors:
Description Resource Path Location Type
The type new MyWebViewClient(){} must implement the inherited abstract method MyWebViewClient.launchExternalBrowser() DealClippings.java /MyApp/src/com/myapp line 21 Java Problem
The method launchExternalBrowser(String) of type new MyWebViewClient(){} must override or implement a supertype method MyApp.java /DealClippings/src/com/myapp line 23 Java Problem
There really is no startActivity method for WebViewClient. You can check the docs. You'll have to signal the Context (probably your Activity) to execute those lines of code instead. There are many possible approaches including adding listeners or simply calling an abstract method which you implement in an anonymous instance of this class when setting the WebViewClient of your WebView in your Activity.
For example:
public abstract class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.mysite.com")) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
launchExternalBrowser(url);
return true;
}
public abstract void launchExternalBrowser(String url);
}
And then in your activity:
WebViewClient client = new MyWebViewClient() {
#Override
public void launchExternalBrowser(String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
}
};
Although I'm not sure why you want this behavior exactly, but it should work more or less.
I don't know if there's much point answering your edit now, 18 months on, but it seems like this question gets a bit of traffic so I'll post this here for posterity.
From your errors, it sounds like you haven't provided an argument to the abstract method in the abstract class definition of MyWebViewClient. That is, you have this:
public abstract void launchExternalBrowser();
when you should have this:
public abstract void launchExternalBrowser(String url);
The cause of the error is that Java treats two methods with the same name but different arguments as two distinct methods. So launchExternalBrowser(String) is a different method to launchExternalBrowser().
Hope this helps someone!
To answer original question before your edit..
Had the same problem, and figured out that the MyWebViewClient is meant to be an inner class inside the activity.
package com.myapp;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MyApp extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init webview
WebView DCWebView = (WebView) findViewById(R.id.webview);
WebSettings webViewSettings = DCWebView.getSettings();
//when a link is clicked, use the WebView instead of opening a new browser
DCWebView.setWebViewClient(new MyWebViewClient());
//enable javascript
webViewSettings.setJavaScriptEnabled(true);
}
private class MyWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.mysite.com")) {
// This is my web site, so do not override; let my WebView load the page
return false;
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
}