I am developing an android application that rely very much on internet, I retrieve data frequently using this RestClient Class, that wrap some detail on using DefaultHttpClient to do network request.
And I always use different thread to do the HTTP request, I create a class like this:
public class AsyncWorker {
final String SERVER_URL = "http://api.blabla.com";
RestClient client = new RestClient();
public void requestHttp(final String url, final ArrayList<NameValuePair> params, final RequestListener listener) {
new Thread(new Runnable() {
public void run() {
try {
client.setUrl(url);
client.setParams(params);
client.Execute(RestClient.RequestMethod.POST);
String response = client.getResponse();
listener.onComplete(response);
} catch (Exception ex) {
Log.d("LOGIN", ex.getMessage());
}
}
}).start();
}
etc...
So whenever I need to do a HTTP request, I only need to create AsyncWorker object, and provide the RequestListener callback interface.
But the problem is, how can I cancel the HTTP Request when the user press the back/cancel button? and in this case the application still is in one activity, for example I create a dialog, and the user do a request from that dialog, and then back button pressed, the dialog dismissed, and I need to cancel the request on that time.
I had the same issue and was able to find a fix. Here is what I did:
I used CloseableHttpClient along with other related classes, instead of the DefaultHttpClient that by default comes with Android.
These classes are from https://hc.apache.org/downloads.cgi. OR for direcet access: http://apache.mirrors.hoobly.com//httpcomponents/httpclient/binary/httpcomponents-client-4.3.2-bin.tar.gz
With this, calling the abort() method on the Request object will actually halt the connection. However, using this library is not the solution; reason being that Android already has the outdated HTTPCLIENT library inbuilt, and most classes in the library pointed to by the above link would appear to be missing at runtime.
The problem is that both the packages in the above library and the inbuilt org.apache httpclient package have same namespace, and would result in the use of only the inbuilt org.apache classes provided by Android at compilation.
An example of this issue is found here: java.lang.NoSuchFieldError: org.apache.http.message.BasicLineFormatter.INSTANCE.
Thanks to the guys who provided http://code.google.com/p/httpclientandroidlib/ as an option (found in the answer section of java.lang.NoSuchFieldError: org.apache.http.message.BasicLineFormatter.INSTANCE)
Recommendation: one place to actually cancel an http request could be within OnCancel Listener of a progress dialog, instead of the AyncTask's onCancelled() callback method.
The RestClient object your using doesn't expose any interrupt() method of DefaultHttpClient (which is the backing object doing most of the work). Not a problem - the DefaultHttpClient doesn't seem to have any interrupt or abort functionality to expose in the first place.
So, your left with a blocking operation on client.Execute().
Your half way to having a solution - which is to put the blocking operation into a Thread. Where your falling down is your architecture - your using a Thread/Listener setup which doesn't give you alot of wiggle room.
Try switching your anonymous Thread to an AsyncTask. This won't solve the problem of you're client.Execute() from blocking but will allow you to throw away the listener (replacing it with onProgressUpdate() or onPostExecute()).
What this will do is allow you call task.cancel(), signalling to the Task it is no longer needed. This will allow you to reassign a new AsyncTask, orphaning the cancelled task, the orphaned thread will then finish quickly as its able and die quietly while the rest of your application gets on with what it needs to.
((On an unrelated note, "Execute()" is a method and shouldn't be capitalised))
I suggest you take a look at the ClientConnectionManager interface. This allows you to do stuff like releasing a connection, shutting down a connection etc. You may need to enhance the implementation of RestClient though - since your RestClient does not expose the underlying DefaultHttpClient object (from which you can get to the ClientConnectionManager using the getClientConnectionManager() method).
Use the threading primitives. Have a flag running and set it to true initially. Do your REST requests in a while(running) loop.
In your onPause(), set the running flag to false.
Related
I am trying to set up a test for my project - to test that a progress bar is displayed when my app performs a server request.
The code under test uses an AsyncTask to perform the network call.
I have created a blocking server (MockWebServer) to catch and hold the network call - it receives request but doesn't provide a response until i call ".release()". This allows me to verify before the server response occurs.
My logic flows like this:
// Mock server will catch the next network request
BlockingServer blockingServer = createBlockingServer();
// onResume() activity performs network request and shows Progress Spinner
activityTestRule.launchActivity(null);
// onView() waits on UiController.loopUntilIdle() <- Fails here due to timeout.
onView(withId(progressBar)).check(matches(isDisplayed()));
// Tells the server to respond to the network request
blockingServer.release();
onView(withId(progressBar)).check(matches(not(isDisplayed())));
My problem is that because the Code Under Test uses AsyncTask for the server request, Espresso naturally blocks on the verify call (onView()) in order to wait for the AsyncTask to complete before verifying.
What I need is to temporarily stop Espresso idling while waiting for AsyncTask in order to perform the verify while the server is blocking the app logic flow.
(Changing the Code Under Test is not an option)
Can someone help?
So... this is the answer I've arrived at and some working out behind it:
Espresso (specifically calls to onView(), onData(), injectEvent and Actions) uses UiControllerImpl.loopMainThreadUntilIdle() to wait until all "idle-causing" signals are false. It loops over AsyncTask, CompatAsyncTask and something called dynamicIdle to all be idle.
When this method returns the main flow continues.
loopMainThreadUtilIdle() checks an IdleNotifier to check the idle state of each of those three elements. Obviously if you want to stop espresso waiting for AsyncTask the asyncIdle is of particular interest to you.
The IdleNotifier classes are fed into UiControllerImpl at it's construction - this takes place via dagger so you'll need to look at DaggerBaseLayerComponent which uses Providers to grab the construction arguments and pass them into the UiControllerProvider to construct it.
Everything in all of these classes is locked down very tightly. Method and class visibility is usually protected or package-private and final.
The only way I found was to create my own Espresso.java class (onView() and onData()) which used custom DaggerBaseLayerComponent allowing me to use either: My own Providers or My own UiController.
I found however this doesn't solve the whole problem. There is one more mechanism that needs to be coded around - When you're starting activities they use a waitForIdleSync in the Instrumentation class. Usually this is the Runner which is provided in your gradle file. I created my own AndroidJUnitRunner and provided this in gradle to allow me to return from waitForIdleSync on command.
And finally, in startActivitySync in the Instrumentation base class, it uses an array of ActivityWaiter objects to hold up your launchIntent() calls. I couldn't think of a reasonable way of avoiding this so I cheated and created this method in my Runner:
public void clearActivityWaitQueue() {
Object mSync = Whitebox.getInternalState(this, "mSync");
List mWaitingActivities = Whitebox.getInternalState(this, "mWaitingActivities");
if (mSync != null && mWaitingActivities != null) {
mWaitingActivities.clear();
synchronized (mSync) {
mSync.notifyAll();
}
}
}
It uses PowerMock to give me the convenience Whitebox methods to set internal state of Instrumentation:
// Used to give access to Whitebox
androidTestImplementation 'org.powermock:powermock-reflect:1.6.5'
And that's it! Easy right?
(Please tell me it's easier than this and how!!)
I am new at android testing and I'm running into a problem. I am using RxJava and to test the UI I am using an IdlingResource. While idling resource is busy i cannot test UI.
For example: I have a button. onClick I'm doing a request. While requesting the button disables. After request the button is in enabled state. I want to test the following 3 steps:
Button is enabled before request
Button is disabled while requesting (onCLick)
Button is enabled when requesting ends and response message returns
I would be very very happy if you can help me in this issue...
If you need more information about my issue let me know it. I will edit my post
As I understood, you're trying to test your UI. If so, please, make sure, that you do it in right way:
1). You don't do REAL request.
Please, understand, that your test must always have same behaviour in similar situations. In other words, it must give same result, you're passing same input parameters.
Your input parameters for now:
1.1). Button is enabled before request
1.2). Button disabled during the request
1.3). Buttons enabled after request
As you can see from this list, you don't need to do a real request. It doesn't matter for you, what server will return you (error or success). You even don't need a server for this. All what you need, is just "something", that behaves like a real server. In other words, you have to mock your API client.
I suppose that you're using retrofit. If no, you have to create the interface wrapper for your client. If you're using retrofit, you just need to mock your interface.
Let's suppose, you have next interface:
public interface ApiClient{
#GET("/items")
Observable<MyResponse> doSomeRequest();
}
How do you usually create your API client:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
ApiClient service = retrofit.create(ApiClient.class);
How you should do it in tests:
import static org.mockito.Mockito.*;
and in test method:
ApiClient apiMock = mock(ApiClient.class);
when(apiMock.doSomeRequest())
.thenReturn(Observable.just(fakeResponse));
or
ApiClient apiMock = mock(ApiClient.class);
when(apiMock.doSomeRequest())
.thenReturn(Observable.defer(new Func0<Observable<MyResponse>>() {
#Override
public Observable<MyResponse> call() {
try{
Thread.sleep(2 * 1000) //2 seconds
}catch(Exception e){
return Observable.error(e);
}
return Observable.just(fakeResponse);
}
}));
P.S. Retrofit adds .subscribeOn(Schedulers.io()) to all Observable's by default. This mocked object doesn't do it. So, please, don't forget to add .subscribeOn(Schedulers.io()) in your code, or apply it to the result of Observable.defer(...)
In code above it will look like:
when(apiMock.doSomeRequest())
.thenReturn(Observable.defer(...).subscribeOn(Schedulers.io()));
And you should pass apiMock to Activity / Fragment which you try to test.
How to do it? See #2.
2). Use DI (dependency injection)
I will not write a lot about it.
I just recomend you to read the documentation on http://google.github.io/dagger/
And especially, how to organise project in way, when you can use real implementaions for production, and mock implementations for testing:
http://google.github.io/dagger/testing.html
In other words, when you're going to build app for usage, you provide real dependencies(in your case it will be real implementation of ApiClient), and when you're going to test some UI or business logic, you pass mock dependencies, which have behaviour specified before the test by you.
This is all, what I wanted to tell you. Hope this helped, and let me know if you have any other questions.
Small addition to Alexander's answer. I would use a Subject for "mocked" api. This allows you to control execution.
//setup your test
Subject<Response,Response> stubResponse = AsyncSubject.create();
ApiClient apiMock = mock(ApiClient.class);
when(apiMock.doSomeRequest()).thenReturn(stubResponse.asObservable());
//check first condition that button is enabled before executing action
//click on button
//test your second condition that button is disabled while waiting for response
stubResponse.onNext(fakeResponse); //return fake response
stubResponse.onCompleted();
//test your third condition that button is enabled when you get response back
Remark. Never use sleep in your test. It will slow down your tests and add flakiness.
I am referencing this post Where to keep Retrofit Adapter in Android App? but I am not allowed to comment there due to stackoverflow limitations [thank you stackoverflow for treating new users like kids].
Where does the Retrofit RestAdpater go when using Android? Can anybody please elaborate on #Jake Wharton 's answer of above post.
When I place the RestAdapter in my Activity, it will probably get GCed when the Activity is destroyed, so the Singleton loses its reference and needs to be recreated the next time (I assume).
Further, the first thing that I did for testing is exactly this and Android tells me I cannot do a network request on the Main thread. I understand that I can't do that, but I thought Retrofit would automatically create a separate thread for me.
Will I need to create an AsyncTask to host the RestAdapter? Or how exactly does this work for Android? Where is the adapter best instantiated? Which is the recommended point to attach the Retrofit reference?
So what #JakeWharton was saying is that the RestAdapter and the api interface instances should be created once. How you achieve that is pretty much an implementation details.
In a straight forward manner you could create a class which would hold a single instance to your RestAdapter. You would be responsible of making only a single instance of that class. You'd probably want to hold a reference to this class in your Application class. You could also approach this using the Singleton pattern
Here's a small class to get you started. I took this from a previous post which you can see here
public class RestApiDispencer {
private Map<String, Object> restApiInstances = new HashMap<String, Object>();
private RestAdapter restAdapter;
public RestApiDispencer(RestAdapter restAdapter) {
this.restAdapter = restAdapter;
}
public <T> T getRestApi(Class<T> clazz) {
T client = null;
if ((client = (T) restApiInstances.get(clazz.getCanonicalName())) != null) {
return client;
}
client = restAdapter.create(clazz);
restApiInstances.put(clazz.getCanonicalName(), client);
return client;
}
}
If you're familiar with dependency injection then that would be another way to go. Personally I prefer to use dependency injection when it comes to hiding implementation details from use.
Creating the RestAdapter directly into your Activity would not the way you'd want to go. Instead in your activity would want to get a reference to this RestApiDispencer class from above and have it return the instance of the rest api of your choise by providing its class like so.
MyClassApi myClassApi = restApiDispencer.getRestApi(MyClassApi.class);
There are other ways to achieve this but as I said it's up to you to decide which implementation fits best your needs.
As for Retrofit doing request on a separate thread, yes it does but you need to create your Api interfaces accordingly.
#GET(/some/rest/api/path)
Response getApiData() // Synchronous declaration as the Response is returned from the method.
#GET(/some/rest/api/path)
void getApiData(Callback<Response> callback); // Asynchronous as the Response is delivered in the callback.
#GET(/some/rest/api/path)
Observable<Response> getApiData(); // Asynchronous again but you'll need to read up on rx-java before using this.
Read on rx-java here
So if you do decide to create your rest api by using the asynchronous signature then you won't have to worry about threading when invoking your interface. If you use the synchronous signature then it's all up to you.
I have severals URLs I need to get data from, this should happen in order, one by one. The amount of data returned by requesting those URLs is relatively big. I need to be able to reschedule particular downloads which failed.
What is the best way to go? Shall I use IntentService, Loaders or something else?
Additional note: I would need not only to download, but also post process the data (create tables in db, fill it with data, etc). So DownloadManger can't be of help here.
I would use an IntentService.
It has a number of advantages that are suitable for your needs, including being able to download the data without your application running and supporting automatic restart of the service using setIntentRedelivery().
You can set a number of identifiers for the particular job, you need to perform using Intent extras, and you can keep track of the progress using SharedPreferences - that way you can also resume the work if it's been cancelled previously.
The easiest way is probably to use the system DownloadManager http://developer.android.com/reference/android/app/DownloadManager.html
(answering from my phone, so please excuse the lack of formatting)
I would suggest a service for this. Having service resolves many problems
It would allow reporting of progress asynchronously to the application so you can enable or disable a specific gui in application based on the download status of data
It will allow you to continue the download even if the user switches to other application or closes the application.
Will allow you to establish independent communication with server to prioritize downloads without user interaction.
Try a WakefulIntentService for creating a long-running job that uses wakelocks to keep your task alive and running https://github.com/commonsguy/cwac-wakeful .
Also, if your whole app process is getting killed, you may want to look into persisting the task queue to disk, using something like Tape, from Square
I think the way to go is loading urls in an array, then starting an AsyncTask, returning a boolean to onPostExecute indicating if the operation has success or not. then, keeping a global int index, you can run the AsyncTask with the next index if success, or the same index otherwise. Here is a pseudocode
private int index=0;
//this array must be loaded with urls
private ArrayList<String> urlsArray;
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
class MyDownloaderAsyncTask extends AsyncTask<String,String,Boolean>{
#Override
doInBackground(String... input){
//downlaod my data is the function which download data and return a boolean
return downloadMyData();
}
#Override
onPostExecute(Boolean result){
if(result)
new MyDownloaderAsyncTask().execute(urlsArray.get(++index));
else
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
}
}
hope this help
I have just completed an open source library that can do exactly what you need. Using droidQuery, you can do something like this:
$.ajax(new AjaxOptions().url("http://www.example.com")
.type("GET")
.dataType("JSON")
.context(this)
.success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
//since dataType is JSON, params[0] is a JSONObject
JSONObject obj = (JSONObject) params[0];
//TODO handle data
//TODO start the next ajax task
}
})
.error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
AjaxError error = params[0];
//TODO adjust error.options before retry:
$.ajax(error.request, error.options);
}
}));
You can specify other data types, which will return different object types, such as JSONObject, String, Document, etc.
Similar to #Murtuza Kabul I'd say use a service, but it's a little complicated than that. We have a similar situation related to constant internet access and updates, although ours places greater focus on keeping the service running. I'll try to highlight the main features without drowning you in too much detail (and code is owned by the company ;) )
android.permission.RECEIVE_BOOT_COMPLETED permission and a BroadcastReceiver listening for android.intent.action.BOOT_COMPLETED to poke the service awake.
Don't link the service to the Activity, you want it running all the time. eg we call context.startService(new Intent(context.getApplicationContext(), OurService.class))
The service class is just a simple class which registers and calls an OurServiceHandler (as in our case we fire off repeated checks and the Handler manages the 'ticks')
We have an OurServiceRunnable which is a singleton which is checked and called by the Handler for each test. It protects against overlapping updates. It delegates to an OurServiceWorker to do the actual lifting.
Sounds heavy handed, but you want to ensure that the service is always running, always ticking (via the Handler) but only running a single check at a time. You're also going to run into database issue if you use the standard SqlLite DbHelper paradigm, as you can't open the DB on multiple threads and you definitely want the internet access off the main thread. Our hack was a java.util.concurrent.locks.ReentrantLock protecting access to the DB, but you could probably keep DB access on the UI thread and pass DB operations via the Handler.
Beyond this it's just a matter of keeping the downloads atomic in terms of "get task, download task, complete task" or enabling it to pick up from a failed state eg downloaded OK, attempt to complete.
You should take a look at the volley library :
http://www.javacodegeeks.com/2013/06/android-volley-library-example.html
There is also an interesting video of the author that took place at google io 2013 :
http://www.youtube.com/watch?v=yhv8l9F44qo
Mainly because it eases the process of managing a lot of these fastidious tasks that are connection checking, connection interruption, queue management, retry, resume, etc.
Quoting from the javacodegeeks "Advantages of using Volley :
Volley automatically schedule all network requests. It means that Volley will be taking care of all the network requests your app executes for fetching response or image from web.
Volley provides transparent disk and memory caching.
Volley provides powerful cancellation request API. It means that you can cancel a single request or you can set blocks or scopes of requests to cancel.
Volley provides powerful customization abilities.
Volley provides Debugging and tracing tools"
Update from dennisdrew :
For large file, better use a variant of volley which authorize using another http client implementation. This link gives more details :
The volley article about this modification :
http://ogrelab.ikratko.com/android-volley-examples-samples-and-demos/
The github file detail :
https://github.com/ogrebgr/android_volley_examples/blob/master/src/com/github/volley_examples/toolbox/ExtHttpClientStack.java
public class FetchDataFromDBThread implements Runnable {
/*
* Defines the code to run for this task.
*/
#Override
public void run() {
// Moves the current Thread into the background
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
FetchDataFromDB();
}
}
I subclass an AsyncTask in my Android project.
I want to implement a feature that allow user to cancel the current AsyncTack and start a new one if the current task take too much time.
MyAsyncTask mat = new MyAsyncTask();
When the user click the cancel button, I will implement the following code and then start a new task.
mat.cancel(true);
However, I realize later that the new task doesn't start until the old task is finish. The old task tread is still executing.
I check the official document on google. It seems that I should call the following statement in doInBackGroud(Params... params).
if (isCancelled()) break;
The problem is I found that the code below is responsible for taking long time.
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
HttpResponse httpResponse = httpClient.execute(httpPost);
But how can I check whether the task is cancelled or not while httpClient.execute(httpPost) is executing?
Is there some method like onProgressChanged(int progress) that I can override in DefaultHttpClient?
I want to implement a feature that allow user to cancel the current
AsyncTack and start a new one if the current task take too much time.
Since you cannot start a new task until the old task is finish, you can create a new instance of the Asyntask. In fact, its a bad idea to use a instance of AsyncTask multiple times.
After your
HttpResponse httpResponse = httpClient.execute(httpPost);
You can check if it was canceled
if(!isCanceled()){
}
Look in the docs, last line under Threading Rules.
Update
as comment pointed out. Depends on your platform , so check the platform version before execute.
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) {
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else {
myTask.execute();
}
You need to abort your request by calling abort on your httpPost object. This will cause the execute method to return immediately and to stop blocking your thread.
I would actually avoid using AsyncTask altogether. AsyncTasks are prone to memory leaks and are poorly tied to the Activity lifecycle so you'll end up getting yourself into trouble.
Consider using RoboSpice as not only it will help you solve your question, but it'll avoid the problems with AsyncTasks and skip having to write stuff manually with HttpClient. I am not one of the devs for RoboSpice, but I've started using it recently and it's an excellent project.