Unable to start a WebView from an AsyncTask - java

I'd like to start a WebView from my AsyncTask but it doesn't seem to run. This is what my onPostExecute method looks like:
public class Viewer extends AsyncTask<URI, Integer, URI> {
private Activity objContext = null;
public Viewer(Activity objContext) {
this.objContext = objContext;
}
protected void onPostExecute(URI uriWebpage) {
WebView wbvBrowser = new WebView(this.objContext);
wbvBrowser.getSettings().setBuiltInZoomControls(true);
wbvBrowser.getSettings().setJavaScriptEnabled(true);
wbvBrowser.loadUrl(uriWebpage.toString());
}
}
My task is used by two activities in my application and therefore my global objContext variable is of type Activity. If I change the type of my objContext variable to the name of the calling class, it works fine but then I can't instantiate my task from the other calling class. I instantiate my task like this.
Viewer mytask = new Viewer(this);
How can I solve this?

Did you setContentView from original layout to webView or you have a container to put webview? otherwise I don't think webView UI appears.

Are you sure you execute your AsyncTask
#Override
protected void onPostExecute(Void result){
}
}.execute();
Also need to class name
Viewer mytask = new Viewer(YourclassName.this);
// or This may Helps you
private Context mcontext;
public Viewer(Context objContext) {
this.mcontext= objContext;
}
protected void onPostExecute(URI uriWebpage) {
WebView wbvBrowser = new WebView(((Activity)(mcontext)));
}

I have solved the issue. It has nothing to do with the use of Activity or Context. Both work just fine. Some URLs don't seem to load the WebView. I changed the URL of the WebView to point to a site like Google and it worked just fine. Seems like if the URL is incorrect, the WebView doesn't throw an exception but doesn't open up either.

I don't know why this happens, but why don't you just start the built in web browser through an Intent in your onPostExecute(Uri uriWebpage)?
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uriWebPage);
startActivity(intent);
If this solution doesn't satisfy you, then please post the error stack trace from LogCat.

Related

How to Access Activity from ActivityScenarioRule

I am using ActivityScenarioRule for Espresso UI Testing and I wanted to get access to the method getStringArray(), calling which requires the Activity . So, is there any way to retrieve the Activity by the ActivityScenarioRule , maybe something similar to getActivity in ActivityTestRule.
#Rule
public ActivityScenarioRule activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);
I am not using ActivityTestRule, because it is deprecated!
Since it appears you're using Java, here's how you'd do it:
#Rule
ActivityScenarioRule<MainActivity> activityScenarioRule = new ActivityScenarioRule<>(MainActivity.class);
#Test
public void test() {
activityScenarioRule.getScenario().onActivity(activity -> {
// use 'activity'.
});
}
Please read the documentation for more info on these new ways of interacting with the activity under test.
For anyone who wants Activity, but that without need to re-write all tests to run on UI-thread, a fairly straightforward Java way to get it:
Waiting for UI
Assume you want to test if a dialog is shown after some delay, the onActivity(...) hook runs on UI-thread, which means waiting in there would cause the dialog to be nerver shown.
In such cases you need to keep a strong-reference to ActivityScenario (as that prevents Activity close).
Test should wait for onActivity(...) hook to be called, then keep passed Activity's reference.
Finally, move test logic out of onActivity(...) hook.
Example
private ActivityScenario mActivityScenario;
#After
public void tearDown() throws Exception {
if (mActivityScenario != null) {
mActivityScenario.close();
}
mActivityScenario = null;
}
#Override
public Activity getActivity() {
if (mActivityScenario == null) {
mActivityScenario = ActivityScenario.launch(getActivityClassForScenario());
}
return tryAcquireScenarioActivity(mActivityScenario);
}
protected static Activity tryAcquireScenarioActivity(ActivityScenario activityScenario) {
Semaphore activityResource = new Semaphore(0);
Activity[] scenarioActivity = new Activity[1];
activityScenario.onActivity(activity -> {
scenarioActivity[0] = activity;
activityResource.release();
});
try {
activityResource.tryAcquire(15000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail("Failed to acquire activity scenario semaphore");
}
Assert.assertNotNull("Scenario Activity should be non-null", scenarioActivity[0]);
return scenarioActivity[0];
}
Espresso states the following:
At the same time, the framework prevents direct access to activities
and views of the application because holding on to these objects and
operating on them off the UI thread is a major source of test
flakiness.
When there is no other way I use the following method to get an arbitrary activity from an ActivityScenarioRule. It uses onActivity mentioned in the accepted answer:
private <T extends Activity> T getActivity(ActivityScenarioRule<T> activityScenarioRule) {
AtomicReference<T> activityRef = new AtomicReference<>();
activityScenarioRule.getScenario().onActivity(activityRef::set);
return activityRef.get();
}
Any onView(...) code inside onActivity led to a timeout in my testcases. So, I extracted the activity and used it with success outside the onActivity. Beware tho! See the statement above.
#Test
fun checkForUpdate() {
val scenario = ActivityScenario.launch(MainActivity::class.java)
scenario.onActivity {
UpdateTool.checkForUpdate(it)
}
}

Reset AsyncTask to multiple execution

Is there any way to use AsyncTask.execute() multiple times?
Im using AsyncTask to check, if User exist in my Room Database.
My Login.class looks like this:
public class Login extends AsyncTask<String, Boolean, Boolean> {
public Login(Context context, LoginListener listener){
db = ApplicationDatabase.getDatabase(context); //i get Room here
this.context = context; //context of app
this.listener = listener; //my interfece for observe Boolean, works ok
}
#Override
protected Boolean doInBackground(String... body){
try {
user = db.userDao().getUser(body[0], body[1]);
if (user != null)
return Boolean.TRUE; //we find user with credentials
else {
return Boolean.FALSE; //we not find user with that credentials (from body)
}
}
catch(Exception e){
return null;
}
}
protected void onPostExecute(Boolean result) {
listener.onLoginPerformed(result); //Boolen to activity
selfRestart(); //i want to restart task here
}
private void selfRestart(){
//maybe something to do here? its my own method
}
private ApplicationDatabase db;
private User user;
private LoginListener listener;
private Context context;
I call Task in this way (my Activity.class):
login = new Login(getApplicationContext(), this);
//this is an interface that i implements in Activity definition
loginButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
//execute() here, i cuted some not necesery code from here
try {
// im using get because i want to get valu from AsyncTask
login.execute(email, password).get();
}
catch(Exception e){ }
}
I Read, that we can reset AsyncTask by making new AsyncTask (Task = new Login()) StackOverflow Thread but it dont work for me. When i try to make something like this in my Login class:
private void selfRestart(){
Login task = new Login(context, listener);
task.execute("");
//im calling it in onPostExecute()
}
My android app crashes. My question is, what is the best way to reset AsyncTask that is implemented in diffrent file then my Activity class? Or maybe there is better way to make Login activity than implemented whole logic for login in AsyncTask?
EDIT:
Logcat:
2019-01-24 15:45:31.407 1048-1048/com.example.admin.keystroke_dynamics E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.admin.keystroke_dynamics, PID: 1048
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Boolean.booleanValue()' on a null object reference
at com.example.admin.keystroke_dynamics.Activities.LoginActivity.onLoginPerformed(LoginActivity.java:62)
at com.example.admin.keystroke_dynamics.Login.onPostExecute(Login.java:38)
at com.example.admin.keystroke_dynamics.Login.onPostExecute(Login.java:14)
at android.os.AsyncTask.finish(AsyncTask.java:692)
at android.os.AsyncTask.-wrap1(AsyncTask.java)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:709)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6523)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
You say,
I call Task in this way (my Activity.class):
login = new Login(getApplicationContext(), this);
//this is an interface that i implements in Activity definition
loginButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
//execute() here, i cuted some not necesery code from here
try {
// im using get because i want to get valu from AsyncTask
login.execute(email, password).get();
}
catch(Exception e){ }
}
, but no, you are not "calling" your task that way. You are creating a single instance of the task, and setting up an event handler that executes that task -- that specific instance -- whenever the loginButton is clicked. Since each AsyncTask instance may be executed only once, that will fail the second time the login button is clicked (if not sooner, for some other reason).
You also say,
I Read, that we can reset AsyncTask by making new AsyncTask (Task = new Login())
, but no, that does not reset anything, and indeed AsyncTask objects cannot be reset. The advice you read was to replace the used AsyncTask with a fresh instance. Instantiating a new AsyncTask has no particular effect on others. If you want to pursue that approach then it might look something like this:
loginButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
Login myLoginTask = login; // keep a reference to the current task
login = new Login(... arguments ...); // create a new task for the next click
try {
// use the original task
myLoginTask.execute(email, password).get();
}
catch(Exception e){ }
}
That specific implementation requires login to be non-final, so probably an instance variable of the containing class, not a local variable of the method from which your code was excerpted.
HOWEVER, your best way forward might very well be to ditch AsyncTask altogether. When you use it like this:
login.execute(email, password).get();
... you defeat the entire purpose. You are making the thread in which that runs block until the task completes (that's what AsyncTask::get is for), so the task is effectively synchronous. If that's what you require then you should just do the wanted work more directly instead of wrapping it up in an AsyncTask.

how to pass android activity context from typescript file to Java file in nativescript

I have been doing InAppBrowser(Webview to load an url) in
Nativescript. From nativescript, I want to use InAppbrowser native
android webview code for some reason.
So I got one sample link to look out:
https://www.codeday.top/2017/10/23/52017.html
With the help of reference I tried to pass activity context from
nativescript file to typescript.
Error : But I'm getting cannot convert object to context error in Command prompt.
Below I have posted the code what I have been tried so far:
app.component.ts:
var activity:any = android.app.Activity;
export class AppComponent {
onTap(args: EventData) {
org.example.MyToast.showToast(activity); --> Here I'm passing object.So it doesn't take activity context reference. That's why I'm getting error.
}
}
MyToast.java:
public class MyToast {
public static void showToast(Context context) {
final Dialog openDialog = new Dialog(context);
}
}
I don't know how to pass activity context reference in nativescript to typescript file. Could anyone help me out.
android.app.Activity is not an instance of the Activity, it's a class descriptor.
You can find information on accessing the current foreground activity at the official {N} docs:
https://docs.nativescript.org/cookbook/application#tracking-the-current-activity
import * as app from "tns-core-modules/application";
const androidApp = app.android;
if (androidApp.foregroundActivity === androidApp.startActivity) {
////console.log("We are currently in the main (start) activity of the application");
}

Android: Why won't this code run in onCreate, or how can I make it work without a thread?

I have this piece of code that runs alright when I put it in Eclipse, but for some reason it does not want to execute when I put it in an activity's onCreate method in Android studio.
Here is the code:
public class ItemListView extends AppCompatActivity{
String itemURL;
int listSize;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_item_list_view);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Bundle itemData = getIntent().getExtras();
if(itemData==null){
return;
}
//Gets URL from search bar
itemURL = itemData.getString("itemURL");
try {
Document doc = Jsoup.connect("https://www.amazon.com/s/ref=nb_sb_ss_c_1_6?url=search-alias%3Daps&field-keywords=rx+390&sprefix=rx+390%2Caps%2C166&crid=2MTUBA4KGNY06").get();
String link = doc.select("h2#s-result-count").first().text();
System.out.println(link);
System.out.println(link.substring(1));
if (link.substring(1, 2).equals("-")) {
System.out.println("run1");
listSize = Integer.parseInt(link.substring(2, 3));
System.out.println(listSize);
try {
listSize = Integer.parseInt(link.substring(2, 4));
System.out.println(listSize);
} catch (Exception e) {
}
} else {
System.out.println("run2");
listSize = Integer.parseInt(link.substring(0, 1));
System.out.println(listSize);
try {
listSize = Integer.parseInt(link.substring(0, 2));
System.out.println(listSize);
} catch (Exception e) {
}
}
} catch (Exception e) {
}
System.out.println("listSize: " +listSize);
...
}
}
I need listSize to create a variable array depending on the URL, but when I print the value to make sure it's working it always gives me 0. I have tried running the code in a separate Java Class with AsyncTask and it works, but by the time the code executes in onPostExecute, it's too late since the class above has already tried to initialize the array.
Any advice would be appreciated, thanks!
What you need is a callback to allow you to initialize variable onPostExecute:
interface OnCallCompleteCallBack {
void onCallComplete(int listSize);
}
In your AsyncTask do this:
public class MyTask extends AsyncTask < ... > {
// Maintain a ref to callback
OnCallCompleteCallBack callback;
MyTask(OnCallCompleteCallBack callback) {
this.callback = callback;
}
...
#Override
protected void onPostExecute(Void aVoid) {
if (callback != null) {
callback.onCallComplete(listSize);
}
}
}
Make your Activity implement OnCallCompleteCallBack and start the AsyncTask like this:
new MyTask(this).execute();
You can then use the value inside your activity's implementation of onCallComplete()
Before I answer, just a few observations:
Naming your activity ItemListView seems wrong (an Activity is not a View).
You should never perform networks calls on the UI thread.
If you want to log, you should use Log instead of System.out.println().
And now to the answer. You should execute the code that fetches the data in an AsyncTask (as you mentions it works). Once the data is fetched, you should update array and at that point update the UI in onPostExecute().
Android works pretty well using the MVC (Model-View–Controller) pattern and your problem is a classic case where you need to update the model using data from the server and update the views accordingly. The activity represents controller in this case.
Please go through the topic in android developer site Android Developer, Read the section "Introducing Network Operations on a Separate Thread" - To avoid creating an unresponsive UI, don't perform network operations on the UI thread. By default, Android 3.0 (API level 11) and higher requires you to perform network operations on a thread other than the main UI thread; if you don't, a NetworkOnMainThreadException is thrown.
Thanks Ashish

How do I run an AsyncTask over and over?

I have an onCreate method that runs the code below. In a nutshell the code retrieves data from the server and shows it on the screen for a messaging program. It only does it once, but I would like it to run the AsyncTask every 3 seconds (to try to simulate a chat). I'm pretty sure this is not the way to go about having a chat system but, I just need something that works for now (as a proof of concept) and I'll focus on the correct way of implementing it later.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat_box);// sd
final Functions function = new Functions();
final SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(getBaseContext());
whatroom = prefs.getString("chat", "null");
new AsyncTask<String, Void, String>() {
#Override
protected String doInBackground(String... args) {
return function.getInbox(args[0]);
}
#Override
protected void onPostExecute(String result) {
TextView inbox = (TextView) findViewById(R.id.inbox);
ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar1);
progressBar.setVisibility(View.GONE);
inbox.setText(result);
}
}.execute(whatroom);
}
I've tried putting a simple while statement around the asynctask but, it just force closes.
You cannot reuse an AsyncTask instance. You would need to create fresh instances each pass of your loop.
Without additional information, it's difficult to give you a specific answer. However look into abstracting everything using a Loader, using a Service, etc
Regarding Loaders:
They are available to every Activity and Fragment.
They provide asynchronous loading of data.
They monitor the source of their data and deliver new results when the content changes.
They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data.

Categories

Resources