Spying an AsyncTask doesn't work - android

I am testing an AsyncTask. I want to stub an HttpURLConnection to return my mocked object. This is how I do it (PackageDownloader represents an AsyncTask):
...
PackageDownloader packageDownloader = new PackageDownloader();
packageDownloader.setParameters(URL, downloadFolder, downloadProgressCallback);
PackageDownloader mPackageDownloader = spy(packageDownloader);
HttpURLConnection connectionMock = Mockito.mock(HttpURLConnection.class);
doReturn(0).when(connectionMock).getContentLength();
doReturn(connectionMock).when(mPackageDownloader).createConnection(Mockito.any(URL.class));
mPackageDownloader.execute();
mPackageDownloader.get();
This is PackageDownloader:
public HttpURLConnection createConnection(URL url) throws IOException {
HttpURLConnection connection;
connection = (HttpURLConnection) url.openConnection();
return connection;
}
#Override
protected DownloadResult doInBackground(Void... params) {
HttpURLConnection connection;
URL downloadUrl = new URL(downloadUrlString);
connection = createConnection(downloadUrl);
long totalBytes = connection.getContentLength();
...
Here, createConnection returns real, not mocked object, and I can't figure out why.

Well I have found a solution, though haven't found an explanation why it works so.
The reason nothing worked was that doInBackground method is async, I assume, so I had to call it directly via reflection, like so:
Method method = mPackageDownloader.getClass().getMethod("doInBackground", Void[].class);
method.invoke(mPackageDownloader, new Void[] {null});

You can use robolectric to test the asynctask rather than reflection. Following the ShadowApplication.runBackgroundTasks() should invoke the doInBackground() method.
#RunWith(RobolectricTestRunner.class)
public class AcknowledgeAppRemovedTaskTest {
#Test
public void execute_shouldOpenInputStreamOfConnection() throws IOException {
DownloadTask spy = spy(new DownloadTask());
HttpURLConnection connectionMock = mock(HttpURLConnection.class);
doReturn(connectionMock).when(spy).createConnection(any(URL.class));
spy.execute();
ShadowApplication.runBackgroundTasks();
verify(connectionMock).getInputStream();
}
}
class DownloadTask extends AsyncTask<Void, Void, Void> {}
public HttpURLConnection createConnection(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
#Override
protected Void doInBackground(Void... voids) {
try {
HttpURLConnection urlConnection = createConnection(new URL("https://www.google.com/"));
urlConnection.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
https://github.com/robolectric/robolectric/blob/master/robolectric/src/test/java/org/robolectric/shadows/ShadowAsyncTaskTest.java
https://groups.google.com/forum/#!topic/mockito/mqF21aqTi5g

Related

How to access multiple URLs in a single AsyncTask

I'm a novice in Android programming and I was wondering whether it's possible to connect to multiple URLs using a single AsyncTask(70 different URLs). At the moment, I'm only able to connect to one URL through the use of HttpURLConnection.
class MyAsyncTaskClass extends AsyncTask<String, String, String>
{
private String rez;
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... f_url) {
try {
////////....
URL url = new URL(("url"));
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
/////////////////
} catch (Exception e) {
rez = "false";
}
return rez;
}
#Override
protected void onPostExecute(String rez)
{
}
}
I don't think you would like to access all there 70 links at the same time (too much connections at the same time will make your connection not responding well), but I think you prefer to execute it one by one.
Why not creare a function like:
public String connection(String url) {
URL cURL = new URL(url);
HttpURLConnection urlConnection = (HttpURLConnection) cURL.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
rez = ..........
return rez;
}
and then use:
public List<String> doInBackground() {
final ArrayList<String> cResult = new ArrayList<>();
cResult.add(connection(url1));
cResult.add(connection(url2));
cResult.add(connection(url3));
...
return cResult;
}
In this way you are accessing all links one_by_one.
However it seems you miss the part that GETs the results....

Android app crash after trying to launch a HttpURLConnection function

I'm trying to create a connection to a servlet and send a Json File, here is the part of the code where the app crashes:
findViewById(R.id.main_login_button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finalJson=createJfile();
try {
HTTPConnection(finalJson);
} catch (IOException e) {
e.printStackTrace();
}
}
});
The function is the following one:
public void HTTPConnection(String Json) throws IOException{
URL url;
url = new URL("http://192.168.0.136:8080/ProgettoProva/AndroidApp");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setRequestProperty("Accept", "text/json");
OutputStreamWriter writer =new OutputStreamWriter(conn.getOutputStream());
writer.write(Json);
writer.close();
conn.disconnect();
}
In the LogCat, nothing is showed. The app just crashed when it starts the function.
You should execute network code in a AsyncTask. Android does not allow Networking in the main Thread.
private class MyAsyncTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
//Do network code here
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onPreExecute() {}
#Override
protected void onProgressUpdate(Void... values) {}
}
Execute with
new LongOperation().execute();

Android: Use AsyncTask in multiple Activitys

I am currently wondering how to use AsyncTask in multiple Activitys without copying it.
I used this Guide to do it in one Activity and that worked just fine. But to load and use this information in more than one Activity seems to me like to much work. I tried to put my LoadUrl function into another Class and just pass my Textfield I want to be edited. But my App crashes when i start it. (I am not sure if this is the right approach )
public class LoadFromUrl {
public void loadAccountInfo(String key) {
if( key != null ) {
new DownloadWebpageTask().execute();
}
}
private class DownloadWebpageTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
// params comes from the execute() call: params[0] is the url.
try {
return downloadUrl(urls[0]);
} catch (IOException e) {
return "Unable to retrieve web page. URL may be invalid.";
}
}
// onPostExecute displays the results of the AsyncTask.
#Override
protected void onPostExecute(String result) {
//textView.setText(result);
}
// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string.
private String downloadUrl(String myurl) throws IOException {
InputStream is = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
int response = conn.getResponseCode();
//Log.d(DEBUG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
}
// Reads an InputStream and converts it to a String.
public String readIt(InputStream stream, int len) throws IOException, UnsupportedEncodingException {
Reader reader = null;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
}
}
}
and calling it in:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
selectedAccount.setKey("google.com");
loadUrl.loadAccountInfo(selectedAccount.getKey());
}
All I want to do is, load the information of an URL and use this to fill my activitys (like multiple TextViews). Every activity uses different urls and structures.
Create DownloadWebPageTask in a separate file, as a public class. Then override its constructor to pass anything you need (a textfield, key, etc).
Put DownloadWebPageTask in a separate class. Then in onPostExecute, run a callback to the activity or fragment that will update its UI. This is done by having an activity implement a callback which is an inner interface inside the the DownloadWebpageTask (doesn't have to be an inner interface!). As you can see, the inner interface I put with your code is WebpageCallbacks.
This is your asynctask in another class (the spacing isn't perfect sorry...):
public class DownloadWebpageTask extends AsyncTask<String, Void, String> {
/**
* Any activity or fragment that implements WebPageCallbacks
*/
private WebPageCallbacks callbacks;
//start by referencing your activity to call onURLLoaded() for onPostExecute()
public DownloadWebpageTask(WebPageCallbacks callbacks) {
this.callbacks = callbacks; //note: I think weak references are preferred though
}
//no changes here
#Override
protected String doInBackground(String... urls) {
// params comes from the execute() call: params[0] is the url.
try {
return downloadUrl(urls[0]);
} catch (IOException e) {
return "Unable to retrieve web page. URL may be invalid.";
}
}
// onPostExecute displays the results of the AsyncTask by callback's onURLLoaded()
#Override
protected void onPostExecute(String result) {
//each activity or fragment will has a method to change their UI
callbacks.onURLLoaded(result);
}
// Given a URL, establishes an HttpUrlConnection and retrieves
// the web page content as a InputStream, which it returns as
// a string. No changes here
private String downloadUrl(String myurl) throws IOException {
InputStream is = null;
// Only display the first 500 characters of the retrieved
// web page content.
int len = 500;
try {
URL url = new URL(myurl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000 /* milliseconds */);
conn.setConnectTimeout(15000 /* milliseconds */);
conn.setRequestMethod("GET");
conn.setDoInput(true);
// Starts the query
conn.connect();
int response = conn.getResponseCode();
//Log.d(DEBUG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the InputStream into a string
String contentAsString = readIt(is, len);
return contentAsString;
// Makes sure that the InputStream is closed after the app is
// finished using it.
} finally {
if (is != null) {
is.close();
}
}
/**
* Any Activity or fragment that implements this will have
* onURLLoaded() method to update its own UI.
*/
public interface WebpageCallbacks {
void onURLLoaded(String result);
}
}
I would then add implements DownloadWebpageTask.WebpageCallBacks to all your fragments and activities that will use this asynctask.
Here is your activity:
public class ExampleActivity extends AppCompatActivity implements DownloadWebpageTask.WebpageCallBacks {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
selectedAccount.setKey("google.com");
//changed your oncreate at line below to run your accountInfo
runAccountInfo(selectedAccount.getKey());
}
.......................
//runs the asynctask to load url info from account info like your old loadURLInfo()
public void runAccountInfo(String key) {
if( key != null ) {
//get url with getURL(key)
new DownloadWebpageTask(this).execute(getURL(key));
}
}
//this will be run from onPostExecute from the asynctask
#Override
public void onURLLoaded(String result) {
textView.setText(result);
}
}
If you have the time though, I suggest to not use AsyncTask at all and look into other libraries like rxJava/rxAndroid. I hope this code is ok........

Why connection.getResponseCode() fails outside of doInBackground()?

public String urltobody(String createdurl){
URL url = new URL (createdurl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int responseCode = connection.getResponseCode();//fails here
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream is = connection.getInputStream();
String content = convertInputStream(is, "UTF-8");
is.close();
return content;
}
}
The above function works if I call it inside of doInBackground but does not workin onPostExecute. I really need to call this function independent to pre, background, post prdocedure.
This is my class definition private class FetchTask extends AsyncTask < Void, Void, String>
Please let me know if you can help me with one of the below questions.
Do you know how I can call my function outside of doInbackground?
Do you know what happens when I call new FetchTask().execute()?
Any other solution?
Thank you in advance
The reason is that doInBackground is run in a background thread while onPreExectute onPostExecute are run in the UI-thread.
==> Url Connections should not be done from UI thread, (because it may freeze the UI while it is loading) that is why Android throws an NetworkOnMainThreadException whenever you try to do that.
==> This is the reason why you should establish network connections only asynchronously from the background thread not UI thread. That is why it only works in doInBackground.
You can try this code
private class FetchTask extends AsyncTask<Void, Void, Boolean>
instead of
private class FetchTask extenends AsyncTask < Void, Void, String>
May be solve your problem.
also for example for your understanding
private class ResponseLocDepts extends AsyncTask<Void, Void, Boolean> {
#Override
protected Boolean doInBackground(Void... params) {
URL url = new URL (createdurl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
int responseCode = connection.getResponseCode();//fails here
if (responseCode == HttpURLConnection.HTTP_OK) {
InputStream is = connection.getInputStream();
String content = convertInputStream(is, "UTF-8");
is.close();
return true;
}
else
{
return false;
}
}
#Override
protected void onPostExecute(final Boolean success) {
// somethings
}
}
String content should be globally diclare.
Please try it.

General AsyncTask, use one AsyncTask in multiple contexts

I'm using AsyncTask and all the examples I found about an AsyncTask is inside an activity.
I'm trying to make an application with a lot of activity and some must download a HTML page. I don't really want to copy-paste the same code all the time in every activity. I find this to be dirty.
So I need to do it as a special class Async HttpGet and pass the function with an argument. I will execute after the doinbackground (different for every activity).
Is this possible or do I need to copy-paste my code in every activity and change the do in background to do what I need after downloading the HTML page?
Here's an AsyncTask that will download data from a url and update the calling activity.
Make sure your calling activity implements the interface DownloadDataTask.DownloadCompleteHandler and that it passes itself as parameter to the DownloadDataTask constructor.
public class DownloadDataTask extends AsyncTask<String, Integer, String> {
public interface DownloadCompleteHandler
{
public void handleDownloadComplete(String result);
}
private DownloadCompleteHandler handler;
private String url;
public DownloadDataTask(DownloadCompleteHandler handler, String url) {
this.handler = handler;
this.url = url;
}
/* AsyncTask methods */
#Override
protected String[] doInBackground(String... empty) {
return downloadData(url);
}
#Override
protected void onPostExecute(String result) {
handler.handleDownloadComplete(result);
}
/* Downloading Data */
private String downloadData(String urlStr) {
InputStream is = null;
String result = new String();
try {
is = getInputStream(urlStr);
BufferedReader in = new BufferedReader(new InputStreamReader(is));
String inputLine;
while ((inputLine = in.readLine()) != null) {
result += inputLine;
} catch (MalformedURLException ex) {
return "Malformed URL: " + ex.getMessage();
} catch (SocketTimeoutException ex) {
return "Connection timed out";
} catch (IOException ex) {
return "IOException: " + ex.getMessage();
}
finally {
if (is != null)
is.close();
}
return result;
}
private InputStream getInputStream(String urlStr) throws IOException
{
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(7000);
conn.setConnectTimeout(7000);
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.connect();
return conn.getInputStream();
}
}
Just create a class that extends AsyncTask that you can reuse.
public abstract class MyAsyncTask extends AsyncTask<Void, Void, String> {
private final String url;
public MyAsyncTask(String url){
this.url = url;
}
#Override
protected String doInBackground(Void... params){
// get data from url.
return null;
}
}
And then to call it, just create an instance of that class.
new MyAsyncTask("http://www.google.com"){
public void onPostExecute(String result){
// update your views.
}
}.execute();
Well what you can do is create an listener for AsyncTask completion, which listens when your AsyncTask is completed and return you the data. I had created an example to execute database queries in background thread and then returning the data to the Activity. Just check it and you can create similar AsyncTask for your problem.
UPDATE:-
Also you can use BroadCastReceiver as a Listener when your AsyncTask is completed and return the value.
Interface is another option for creating a Listener for AsyncTask.
Here is a demo from my github

Categories

Resources