i have the code bellow to execute a simulation for an android application of a car but it seems that threads are not well synchronized how can i fix this
public void Simulation()
{
ambientTemp = 20;
engTemp = 20;
mileage = 123456;
fuel = 100;
thread = new Thread()
{
menu1_Fragment f1 = new menu1_Fragment();
menu2_Fragment f2 = new menu2_Fragment();
menu3_Fragment f3 = new menu3_Fragment();
public void run()
{
for (int i=0; i<l; i++)
{
try {
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
speed = SPEED[i];
revs = ENGSPEED[i];
System.out.println(speed);
System.out.println(revs);
fuel -= 1;
engTemp += 0.5;
mileage += 1;
gear = AMP[i];
time += 1;
if (tachoFrag != null && tachoFrag.isVisible())
{
View item1 = findViewById(R.id.progressBar4);
f1.setRevs(item1,revs);
f1.setSpeed(speed);
f1.setFuelGauge(fuel);
final View item2 = findViewById(R.id.milage);
final View item3 = findViewById(R.id.ambienttemp);
final View item4 = findViewById(R.id.gear);
try {
Thread.sleep(1);
runOnUiThread(new Runnable() {
#Override
public void run() {
f1.setMileage(item2,mileage);
f1.setAmbientTemp(item3,ambientTemp);
f1.setGear(item4,gear);
transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.container, f1);
transaction.commit();
}
});
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
i've added some println to make suure that the loop is working and it seems fine but the UI is not refreshing as it should to be... how can i fix that?
The code you have presented is really bad written for Android. All the synchronization is handled via Handlers and the results are posted to one or another thread. For example if you do new Handler() you are creatting a "Thread handler" for the current thread, which by default is the main thread.
If you call handler.post(myRunnable) you will be running something in the UI thread, but you can do similar things with other threads or looping threads.
Given that, if your problem is that the UI is not being refreshed in the moment you want, the reason could be that you are not "posting" the results to the UI thread in the correct moment. So before starting the thread, create a UI handler, and from your thread post the results. Remember that you are not allowed to perform any UI operation outside the UI thread.
Quick tip
From my experience, using Thread.sleep(n) is not a good idea for synchronization between threads, use traffic lights or messages between them.
Are you calling .start() on your thread? Also why do you try a one ms Thread.sleep ?
As far as I understand, you cannot update the UI thread from other thread. To do that you have to send message to UI main thread
this may help
Updating Android UI using threads
Related
So I have this method called PredictionEngine(int) that I want to run a certain number of time with a certain time-delay between each run. The method goes like this:
private void PredictionEngine(int delay) throws Exception {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
enableStrictMode();
String val = null;
try {
if (tHighPass == 0 && tLowPass == 0 && tKalman == 1) {
//Magic
} else {
//Magic
}
} catch (Exception e) {
e.printStackTrace();
}
enableStrictMode();
new DropboxTask(side_output, "Result", val).execute();
}
}, delay);
}
As obvious, I am running a network operation in the main thread as this is a research app and no client is ever going to use it.
I want this whole function to run for say a 100 times with a certain delay, say 2 seconds. The initial thought was to do this:
for(loop 100 times){
PredictionEngine(int)
Thread.sleep(2000); //sorry for StackOverflow programming.
}
However I don't want to block the main thread as I am reading some sensor data there. Any ideas for the same would be very helpful!
Thanks.
The best way to solve this is by using rxJava library, because it allow to create, modify and consume streams of events. You can implement everything in a few lines of code and modify it so operatioin will be performed in background as well.
Observable.interval(1, TimeUnit.SECONDS)
.take(100)
// switch execution into main thread
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(t -> {
doSomethingOnMainThread();
});
On the other hand, there is another solution- you can use Handler, which is usually bein used for thread communication. It has method .postDelayed() allowing you to postpone execution of task. Handler can be conveniently used along with HandlerThread. But, rxJava is more convenient and simple way to solve your problem.
While creating your Handler, you can provide a looper as one of the constructors parameters that is based on different thread then the main thread:
HandlerThread thread = new HandlerThread("Thread name", android.os.Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
Looper looper = thread.getLooper();
Handler handler = new MyHandler(looper);
Messages received by MyHandler will be processed on a separated thread, leaving the UI thread clear from interferences.
To loop on the task periodically, use something like:
for (int i=0; i<100; i++){
handler.postDelayed(new Runnable(){
...
...
...
}, i*delay);
}
This way, in case you decide that the periodic tasks need to be canceled, you will always be able to invoke:
handler.removeCallbacksAndMessages(null);
I tried to solve the issue as follows without blocking the main Thread
I created the worker thread for looping and still running the predictionEngine() on main thread
MyThread t = new MyThread(2000, 3000); // delay and sleep
t.startExecution();
Worker thread class looks as follows
class MyThread extends Thread{
private int delay;
long sleep;
MyThread(int delay, long sleep){
this.delay = delay;
this.sleep = sleep;
}
#Override
public void run() {
for(int i = 0; i < 100; i++){
try {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
predictEngine(delay);
}
});
Log.i("Mtali","About to pause loop before next predict");
sleep(sleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
void startExecution(){
start();
}
}
Hop this helps!
Hi i have this code to modify a text view but it keeps telling me : Only the original thread that created a view hierarchy can touch its views.
here is my code :
public void Simulation()
{
ambientTemp = 20;
engTemp = 20;
mileage = 123456;
fuel = 100;
thread = new Thread()
{
menu1_Fragment f1 = new menu1_Fragment();
menu2_Fragment f2 = new menu2_Fragment();
menu3_Fragment f3 = new menu3_Fragment();
public void run()
{
for (int i=0; i<l; i++)
{
try {
Thread.sleep(99);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
speed = SPEED[i];
revs = ENGSPEED[i];
System.out.println(speed);
System.out.println(revs);
fuel -= 1;
System.out.println(fuel);
engTemp += 0.5;
System.out.println(engTemp);
mileage += 1;
System.out.println(mileage);
...
View item2 = findViewById(R.id.milage);
// f1.setMileage(item2,mileage);
View item3 = findViewById(R.id.ambienttemp);
f1.setAmbientTemp(item3,ambientTemp);
View item4 = findViewById(R.id.gear);
f1.setGear(item4,gear);
transaction.replace(R.id.container, f1);
transaction.commit();
}
f1.setMileage(item2,mileage); this one is causing the probleme ... how can i fix it please
put all your codes related to a view inside a ui thread
runOnUiThread(new Runnable() {
#Override
public void run() {
View item2 = findViewById(R.id.milage);
// f1.setMileage(item2,mileage);
View item3 = findViewById(R.id.ambienttemp);
f1.setAmbientTemp(item3,ambientTemp);
View item4 = findViewById(R.id.gear);
f1.setGear(item4,gear);
transaction.replace(R.id.container, f1);
transaction.commit();
}
});
Your application must create other threads and put long running work on non-UI threads. There are options on how to accomplish the creation of alternate threads. You can create and start your own java.lang.Thread. You can create and start an AsyncTask - Android’s own thread simplification mechanism. The non-UI thread then handles long running processing – like downloading a file – while the UI thread sticks to displaying the UI and reacting to user events. Life seems good again.
However, there is a problem in paradise. Unfortunately, the user interface (UI) cannot be updated by non-UI threads. For example, after successfully downloading a file, a separate (non-UI) thread can’t show an AlertDialog, update a TextView widget, otherwise make a UI change to indicate the file has been successfully downloaded. If you attempt to update the UI from a non-UI thread, the application will compile, but you get a CalledFromWrongThreadException thrown from the point your non-UI thread attempts to make the UI change. As the exception message will inform you, “Only the original thread that created a view hierarchy can touch its views.”
for reference, click this link http://www.intertech.com/Blog/android-non-ui-to-ui-thread-communications-part-1-of-5/
I am trying to write a simple app example that will run through a for loop incrementing its counter by 1 each time and then use the current value of the counter i to update the view and print out:
"i = #"
I get an error saying you can't update a view that was not created in that thread. i tried to address this by inflating the view from within the thread and also by creating a new TextView and calling "setContentView(myTextView)". but neither of these fixed the problem.
I tried a different version that used an AsyncTask but I got stuck on how to divid up the code into each of AsyncTask's methods. could someone help me see how to do this as it has shown me I am missing in my understanding on this point.
Thanks
Edward
ps. the commented out lines for the inflater and the setContentView are from my attempts to fix it.
my code from my original attempt that is trying to update my TextView "myTextView" in the main layout for the app:
public void loopForever(View view) {
Thread thread = new Thread(){
public void run(){
// LayoutInflater inflater = null;
// inflater.inflate(R.layout.activity_main, null);
TextView myTextView;
myTextView = (TextView) findViewById(R.id.myTextView);
// setContentView(myTextView)
for(int i = 0; i < 1000; i++){
myTextView.setText("i = " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
You can only access View elements from within the UI Thread (Activities, fragments etc. callbacks). You could either switch to an Asynctask and do the UI changes via the postexecute or publish progress callbacks (http://developer.android.com/reference/android/os/AsyncTask.html), or use runOnUiThread, example:
public void loopForever(View view) {
Thread thread = new Thread(){
public void run(){
for(int i = 0; i < 1000; i++){
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
(TextView) activity.findViewById(R.id.myTextView).setText("i = " + i);
}
});
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
As the other users have stated, you cannot update user interface elements from any thread other than the main thread. You should use the view class post() or postDelayed() method, your code should look something like this:
for(int i = 0; i < 1000; i++){
myTextView.postDelayed(new Runnable(){
public void run(){
myTextView.setText("i = " + i);
}
}, 1000);
}
here is a link:
http://developer.android.com/reference/android/view/View.html#post(java.lang.Runnable)
You might also want to take a look at android async task class
http://developer.android.com/reference/android/os/AsyncTask.html
You must update User Interface (UI) elements, like your TextView, from the "UI Thread". You cannot update them from other threads such as the one you have made.
This Android lesson might be useful to read:
Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren't running on your UI thread, they don't have access to UI objects. To move data from a background thread to the UI thread, use a Handler that's running on the UI thread.
If you use AsyncTask, you can override the onProgressUpdate(Progress...) method to update your TextView. onProgressUpdate(Progress...) deliberately runs on the UI thread to allow this.
I have an AsyncTask that fetches some data and then updates the UI with this new data. It has been working fine for months, but I recently added a feature that displays a notification when there is new data. Now when my app is launched through the notification, sometimes I get this exception and onPostExecute is not called.
This is what happens when the app is launched:
1) Expand the UI and find views
2) Cancel the alarm (through AlarmManager) that checks for new data and reset the alarm. (This is so that if the user disables the alarm it is cancelled before the next time he/she reboots.)
3) Start the AsyncTask. If the app was launched from the notification, pass in a little bit of the data and then cancel the notification.
I'm stuck on what could be causing this exception. It seems that the exception is from the AsyncTask code, so I'm not sure how I can fix it.
Thanks!
Here is the exception:
I/My App( 501): doInBackground exiting
W/MessageQueue( 501): Handler{442ba140} sending message to a Handler on a dead thread
W/MessageQueue( 501): java.lang.RuntimeException: Handler{442ba140} sending message to a Handler on a dead thread
W/MessageQueue( 501): at android.os.MessageQueue.enqueueMessage(MessageQueue.java:179)
W/MessageQueue( 501): at android.os.Handler.sendMessageAtTime(Handler.java:457)
W/MessageQueue( 501): at android.os.Handler.sendMessageDelayed(Handler.java:430)
W/MessageQueue( 501): at android.os.Handler.sendMessage(Handler.java:367)
W/MessageQueue( 501): at android.os.Message.sendToTarget(Message.java:348)
W/MessageQueue( 501): at android.os.AsyncTask$3.done(AsyncTask.java:214)
W/MessageQueue( 501): at java.util.concurrent.FutureTask$Sync.innerSet(FutureTask.java:252)
W/MessageQueue( 501): at java.util.concurrent.FutureTask.set(FutureTask.java:112)
W/MessageQueue( 501): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:310)
W/MessageQueue( 501): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
W/MessageQueue( 501): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
W/MessageQueue( 501): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
W/MessageQueue( 501): at java.lang.Thread.run(Thread.java:1096)
EDIT: Here is my onCreate method in my main activity (the one opened by the notification). There are some onClickListeners that I omitted to save space. I don't think they should have any effect, since the buttons they are attached to are not being pressed.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Call the parent
setContentView(R.layout.main); // Create the UI from the XML file
// Find the UI elements
controls = (SlidingDrawer) findViewById(R.id.drawer); // Contains the
// buttons
// comic = (ImageView) findViewById(R.id.comic); // Displays the comic
subtitle = (TextView) findViewById(R.id.subtitleTxt); // Textbox for the
// subtitle
prevBtn = (Button) findViewById(R.id.prevBtn); // The previous button
nextBtn = (Button) findViewById(R.id.nextBtn); // The next button
randomBtn = (Button) findViewById(R.id.randomBtn); // The random button
fetchBtn = (Button) findViewById(R.id.comicFetchBtn); // The go to specific id button
mostRecentBtn = (Button) findViewById(R.id.mostRecentBtn); // The button to go to the most recent comic
comicNumberEdtTxt = (EditText) findViewById(R.id.comicNumberEdtTxt); // The text box to Zooming image view setup
zoomControl = new DynamicZoomControl();
zoomListener = new LongPressZoomListener(this);
zoomListener.setZoomControl(zoomControl);
zoomComic = (ImageZoomView) findViewById(R.id.zoomComic);
zoomComic.setZoomState(zoomControl.getZoomState());
zoomComic.setImage(BitmapFactory.decodeResource(getResources(), R.drawable.defaultlogo));
zoomComic.setOnTouchListener(zoomListener);
zoomControl.setAspectQuotient(zoomComic.getAspectQuotient());
resetZoomState();
// enter the new id
imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // Used to hide the soft keyboard
Log.i(LOG_TAG, "beginning loading of first comic");
int notificationComicNumber = getIntent().getIntExtra("comic", -1);
Log.i(LOG_TAG, "comic number from intent: " + notificationComicNumber);
if (notificationComicNumber == -1) {
fetch = new MyFetcher(this, zoomComic, subtitle, controls, comicNumberEdtTxt, imm, zoomControl);
fetch.execute(MyFetcher.LAST_DISPLAYED_COMIC);
} else {
fetch = new MyFetcher(this, zoomComic, subtitle, controls, comicNumberEdtTxt, imm, zoomControl);
fetch.execute(notificationComicNumber);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
}
Log.i(LOG_TAG, "ending loading of new comic");
Log.i(LOG_TAG, "first run checks beginning");
// Get SharedPreferences
prefs = getSharedPreferences("prefs", Context.MODE_PRIVATE);
// Check if this is the first run of the app for this version
if (prefs.getBoolean("firstRun-" + MAJOR_VERSION_NUMBER, true)) {
prefs.edit().putBoolean("firstRun-" + MAJOR_VERSION_NUMBER, false).commit();
firstRunVersionDialog();
}
// Check if this is the first run of the app
if (prefs.getBoolean("firstRun", true)) {
prefs.edit().putBoolean("firstRun", false).commit();
firstRunDialog();
}
Log.i(LOG_TAG, "First run checks done");
// OnClickListener s for the buttons omitted to save space
EDIT 2: I've been digging through Android source code tracking down where the exception is coming from. This is lines 456 and 457 of sendMessageAtTime in Handler:
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
And this is enqueueMessage from MessageQueue:
final boolean enqueueMessage(Message msg, long when) {
if (msg.when != 0) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
//Log.d("MessageQueue", "Enqueing: " + msg);
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
this.notify();
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
this.notify();
}
}
return true;
}
I'm a little confused about what mQuiting is, but it looks like the previous time enqueueMessage was called msg.target was null.
This is due to a bug in AsyncTask in the Android framework. AsyncTask.java has the following code:
private static final InternalHandler sHandler = new InternalHandler();
It expects this to be initialized on the main thread, but that is not guaranteed since it will be initialized on whichever thread happens to cause the class to run its static initializers. I reproduced this issue where the Handler references a worker thread.
A common pattern that causes this to happen is using the class IntentService. The C2DM sample code does this.
A simple workaround is to add the following code to the application's onCreate method:
Class.forName("android.os.AsyncTask");
This will force AsyncTask to be initialized in the main thread. I filed a bug on this in the android bug database. See http://code.google.com/p/android/issues/detail?id=20915.
To generalize Jonathan Perlow's solution to the bug he identified specifically, I use the following in any class that uses AsyncTask. The looper/handler/post is how you can run something on the UI thread anywhere in an Android app without passing down a handle to an activity or other context. Add this static initialization block inside the class:
{ // https://stackoverflow.com/questions/4280330/onpostexecute-not-being-called-in-asynctask-handler-runtime-exception
Looper looper = Looper.getMainLooper();
Handler handler = new Handler(looper);
handler.post(new Runnable() {
public void run() {
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
});
}
We had run into the problem when trying to get unit tests to run. I found a workaround for that, but hadn't specifically identified the problem. We only knew that trying to use AsyncTask<> in Android JUnit test caused onPostExecute() not to be called. Now we know why.
This post shows how to run multithreaded async code in an Android JUnit test:
Using CountDownLatch in Android AsyncTask-based JUnit tests
For use with non-UI unit tests, I created a simple subclass of android.test.InstrumentationTestCase. It has an "ok" flag and a CountDownLatch. reset() or reset(count) creates a new CountDownLatch({1,count}). good() sets ok=true, count--, and calls.countDown() on the latch. bad() sets ok=false, and counts down all the way. waitForIt(seconds) waits for timeout or the coundown latch to zero. Then it calls assertTrue(ok).
Then tests are like:
someTest() {
reset();
asyncCall(args, new someListener() {
public void success(args) { good(); }
public void fail(args) { bad(); }
});
waitForIt();
}
Because of the AsyncTask static initialization bug, we had to run our actual tests inside a Runnable passed to runTestOnUiThread(). With proper static initialization as above, this shouldn't be necessary, unless the call being tested needs to run on the UI thread.
The other idiom I now use is to test whether the current thread is the UI thread and then run the requested action on the proper thread regardless. Sometimes, it makes sense to allow the caller to request sync vs. async, overriding when necessary. For instance, network requests should always be run on a background thread. In most cases, AsyncTask thread pooling is perfect for this. Just realize that only a certain number will run at once, blocking additional requests. To test whether the current thread is the UI thread:
boolean onUiThread = Looper.getMainLooper().getThread() == Thread.currentThread();
Then use a simple subclass (just doInBackground() and onPostExecute() are needed) of AsyncTask<> to run on a non-UI thread or handler.post() or postDelayed() to run on the UI thread.
Giving the caller the option to run sync or async looks like (getting a locally valid onUiThread value not shown here; add local booleans as above):
void method(final args, sync, listener, callbakOnUi) {
Runnable run = new Runnable() { public void run() {
// method's code... using args or class members.
if (listener != null) listener(results);
// Or, if the calling code expects listener to run on the UI thread:
if (callbackOnUi && !onUiThread)
handler.post(new Runnable() { public void run() {listener()}});
else listener();
};
if (sync) run.run(); else new MyAsync().execute(run);
// Or for networking code:
if (sync && !onUiThread) run.run(); else new MyAsync().execute(run);
// Or, for something that has to be run on the UI thread:
if (sync && onUiThread) run.run() else handler.post(run);
}
Also, using AsyncTask can be made very simple and concise. Use the definition of RunAsyncTask.java below, then write code like this:
RunAsyncTask rat = new RunAsyncTask("");
rat.execute(new Runnable() { public void run() {
doSomethingInBackground();
post(new Runnable() { public void run() { somethingOnUIThread(); }});
postDelayed(new Runnable() { public void run() { somethingOnUIThreadInABit(); }}, 100);
}});
Or simply:new RunAsyncTask("").execute(new Runnable(){public void run(){ doSomethingInBackground(); }});
RunAsyncTask.java:
package st.sdw;
import android.os.AsyncTask;
import android.util.Log;
import android.os.Debug;
public class RunAsyncTask extends AsyncTask<Runnable, String, Long> {
String TAG = "RunAsyncTask";
Object context = null;
boolean isDebug = false;
public RunAsyncTask(Object context, String tag, boolean debug) {
this.context = context;
TAG = tag;
isDebug = debug;
}
protected Long doInBackground(Runnable... runs) {
Long result = 0L;
long start = System.currentTimeMillis();
for (Runnable run : runs) {
run.run();
}
return System.currentTimeMillis() - start;
}
protected void onProgressUpdate(String... values) { }
protected void onPostExecute(Long time) {
if (isDebug && time > 1) Log.d(TAG, "RunAsyncTask ran in:" + time + " ms");
v = null;
}
protected void onPreExecute() { }
/** Walk heap, reliably triggering crash on native heap corruption. Call as needed. */
public static void memoryProbe() {
System.gc();
Runtime runtime = Runtime.getRuntime();
Double allocated = new Double(Debug.getNativeHeapAllocatedSize()) / 1048576.0;
Double available = new Double(Debug.getNativeHeapSize()) / 1048576.0;
Double free = new Double(Debug.getNativeHeapFreeSize()) / 1048576.0;
long maxMemory = runtime.maxMemory();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
}
}
I had the same problem on a device with Android 4.0.4 with the IntentService and solved it as sdw said with the Class.forName("android.os.AsyncTask"). The same didn't happen on Android 4.1.2, 4.4.4 or 5.0. I wonder if this Google resolved Martin West issue from 2011.
I added this code on my Application onCreate and it worked:
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
It would be nice to know if the version of Android need to be changed to something else.
AsyncTask.execute() must be executed on UI thread, i.e. inside Activity.
I have the same problem, it seems to happen when the AsyncTask is running during a suspend/resume.
EDIT:
Yeah, didnt think I had but I used this http://developer.android.com/guide/appendix/faq/commontasks.html#threading
to always start the AsyncTask on the UI thread and the problem has gone.
The problem appeared after I added the licensing function, siggghhhhh
Thanks
Even though this doesn't directly answer the OP's question, I think it will be useful for people searching for the solution of the same problem when running tests.
Overall, Peter Knego's answer sums it up well.
My problem was specifically with running a test on a class outside an Activity that made use of Android's AsyncTask for an API call. The class works in the application, since it is used by an Activity, but I wanted to run a test making an actual API call from the test.
While Jonathan Perlow's answer worked, I didn't like introducing changes to my application due solely to a test.
So, in the case of a test runTestOnUiThread can be used (#UiThreadTest cannot be used, since you cannot wait for a result in a test that uses that annotation).
public void testAPICall() throws Throwable {
this.runTestOnUiThread(new Runnable() {
public void run() {
underTest.thisMethodWillMakeUseOfAnAsyncTaskSomehow();
}
});
// Wait for result here *
// Asserts here
}
Sometimes though, especially in functional tests, Jonathan Perlow's answer seems to be the only one that works.
* Take a look here to see how to pause a test waiting for a result.
I had an activity which calls a thread for 10times one after another. However, if the network is slow or too much information loaded, force close will occur. Will adding sleep in each thread help to solve this problem? or is there any other ways to solve it?
public void run() {
if(thread_op.equalsIgnoreCase("xml")){
readXML();
}
else if(thread_op.equalsIgnoreCase("getImg")){
getWallpaperThumb();
}
handler.sendEmptyMessage(0);
}
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
int count = 0;
if (!myExampleHandler.filenames.isEmpty()){
count = myExampleHandler.filenames.size();
}
count = 5;
if(thread_op.equalsIgnoreCase("xml")){
pd.dismiss();
thread_op = "getImg";
btn_more.setBackgroundResource(R.drawable.btn_more);
}
else if(thread_op.equalsIgnoreCase("getImg")){
setWallpaperThumb();
index++;
if (index < count){
Thread thread = new Thread(GalleryWallpapers.this);
thread.start();
}
}
}
};
First step should be to check the stack trace which will give the offending line and cause. You can use Logcat for that.
Are you getting the Application not responding dialog (ANR) or does your app force close?
ANR appears when the UI looper thread takes too long to return from a call. Having 10 or 100 threads should not cause any problem as long as the handleMessage function returns in a timely fashion.
If you really want to limit the number of threads that should be running in one go look up ExecutorService
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(new Runnable() {
public void run() {
// This is your thread
}
});
You can submit all 10 jobs to the executor service and they'll run one after another with a maximum of 5 running simultaneously.