I am having issues using another thread in Android for checking the availability of a web server.
I start a new thread to avoid:
NetworkOnMainThreadException
This is the log cat:
E/AndroidRuntime(17753): FATAL EXCEPTION: Thread-2370
E/AndroidRuntime(17753): Process: com.example.c3po, PID: 17753
E/AndroidRuntime(17753): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
E/AndroidRuntime(17753): at com.example.c3po.MainActivity$1SecondThread.run(MainActivity.java:72)
The code used in the second thread
class SecondThread extends Thread {
public void run() {
TextView pingResult = (TextView) findViewById(R.id.checkStatus); // to display result
EditText userText = (EditText) findViewById(R.id.userData); // take in user url
String result = userText.getText().toString();
try {
InetAddress address = InetAddress.getByName(result); // user input is result (a URL)
boolean b = address.isReachable(3000);
String str = String.valueOf(b); // turning the value of the boolean into string
pingResult.setText(str); // value displays as true or false - LINE 72
}
catch (UnknownHostException e) {pingResult.setText("WRONG");} // will fill with helpful message later
catch (IOException e) {pingResult.setText("WRONG");}
}
And the button to trigger the thread:
Button sendPing = (Button) findViewById(R.id.pingButton);
sendPing.setOnClickListener(new OnClickListener() {
public void onClick (View activity_main) {
SecondThread thread = new SecondThread();
thread.start();
}
});
Line 72 is commented. I have tried googling the specific issue, but have got mixed results.
Any help would be appreciated.
Many thanks
You are updating ui from the background thread. You cannot do that. Ui needs to updated on the ui thread. Use AsyncTask.
You could use
runOnUiThread(new Runnable() {
#Override
public void run() {
pingResult.setText(str);
}
});
Similarly for pingResult.setText("WRONG");
But better to use AsyncTask as it would be a easier. You could do your background computation in doInBackground return result ie String in this case and update ui in onPostExecute
Related
I have a problem with my onCreate method. I have identified that when I switch to this activity the onCreate method gets called twice and therefore starting 2 threads which I dont want. Because the thread sends coordinates to a RaspberryPi and the 2nd unwanted thread always sends 0 0 0 which I dont want. I cant seem to find a fix for this so . . . I'd appreciate help if someone could tell me a fix so the thread starts only once.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
Joystick = (ImageView) findViewById(R.id.Joystick);
Regler = (ImageView) findViewById(R.id.Regler);
buttonFoto = (Button) findViewById(R.id.buttonFoto);
buttonVideo = (Button) findViewById(R.id.buttonVideo);
buttonNeu = (Button) findViewById(R.id.buttonNeu);
buttonSpeichern = (Button) findViewById(R.id.buttonSpeichern);
touchscreenJoystick = (TextView) findViewById(R.id.touchscreenJoystick);
touchscreenJoystick.setOnTouchListener(this);
touchscreenRegler = (TextView) findViewById(R.id.touchscreenRegler);
touchscreenRegler.setOnTouchListener(this);
RL = (RelativeLayout) findViewById(R.id.activity_main);
running = true;
firstTouch = true;
Bild = (WebView) findViewById(R.id.webView);
Bild.loadUrl("http://10.0.0.1:8080/stream");
thread = new Thread(new MainActivity.TransmitThread());
thread.start();
}
EDIT:
I tried something with SaveInstanceState
//Same onCreate-stuff as above
if(savedInstanceState == null)
{
thread = new Thread(new MainActivity.TransmitThread());
thread.start();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("message","crashed");
super.onSaveInstanceState(outState);
}
what this does is weird. Now I only have one thread that crashes immediately after sending the coordinates once.
Logs:
Log.i that i put before sending:
I/sent: //X: 0 //Y: 0 //Z: 0
Log that comes right after
I/System: core_booster, getBoosterConfig = false
Edit 2:
I've also tried starting the thread at an other timing. In my onTouch like this:
public boolean onTouch(View v, MotionEvent me)
{
xt = me.getX();
yt = me.getY();
int Aktion = me.getAction();
if(firstTouch)
{
thread = new Thread(new MainActivity.TransmitThread());
thread.start();
firstTouch = false;
}
//other stuff that i need to do here
}
But this results into the same as my try with SaveInstanceState a thread that transmits once but doesnt loop.
Edit 3:
I should probably post my thread too
class TransmitThread implements Runnable
{
#Override
public void run()
{
while(running)
{
delay();
xss = xs;
yss = ys;
zss = zs;
Log.i("sent","//X: " + xss + " //Y: " + yss + " //Z: " + zss);
transmit();
}
}
public void transmit()
{
try{
socket = new Socket(ServerIP,ServerPort);
OutputStream outputStream = socket.getOutputStream();
PrintStream printStream = new PrintStream(outputStream);
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
printStream.print(xs + " " + ys + " " + zs);
Akkustand = input.readLine();
handler.sendEmptyMessage(0);
}catch(UnknownHostException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}
}
public void delay(){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
FINAL EDIT:
I managed to do a workaround. I check before sending if the value is 0 if it is then I change it to 200. On the other end I check if its 200 and change it to 0 and ignore any 0 that I get.
First of all OnCreate will only be called one time for each lifetime of the Activity. However, there are a number of situations that can cause your activity to be killed and brought back to life. Thus, OnCreate get called again.
First thing you can debug the code and find the place where it called repeatedly.
Note: To avoid this situation you can save state information in onSaveInstanceState and restore it back in the state bundle you get in on create.
Thanks
OnCreate() already creates a main thread, and then you call another instance of the main class by manually creating a thread of the main class.
According to this answer all you dont need boolean check, just check if savedInstance is null, and only then start the second thread.
More general solution would be to initialise and start the thread in onStart() but dont forget to stop it in onStop() . If you want this thread to be long running in the background- I suggest start using something else- Service docs
Any configuration change will cause Android to restart your Activity like screen rotation, keyboard availability etc. what the system is actually doing is calling onDestroy() and then immediately calling onCreate(), meaning that the original Activity object is replaced by a new one without any knowledge of the executing background thread, so only the Activity object that started the thread knows that the thread was started.
A better way of handling this is through retaining thread in Activity.
The Activity class contains two methods for handling thread retention:
public Object onRetainNonConfigurationInstance()
called before configuration change occurs
public Object getLastNonConfigurationInstance()
Called in the new Activity object to retrieve the retained object returned in onRetainNonConfigurationInstance()
So you can implement this in following way.
private static MyThread t;
#Override
public void onCreate(Bundle savedInstanceState) {
Object retainedObject = getLastNonConfigurationInstance();
if (retainedObject != null) {
t = (MyThread) retainedObject;
}
else{
t = new MyThread();
t.start();
}
}
#Override
public Object onRetainNonConfigurationInstance() {
if (t != null && t.isAlive()) {
return t;
}
return null;
}
I'm developing a simple custom View to show a gauge composed by a background image above which i print a number (rpm) and draw a pointer.
To achieve it I defined a class (MyGauge) that extends ImageView class. In the onDraw() member I drawLine() and drawText() a number sent by the activity and collected by SetRpm() member; then I force an invalidate() call.
The activity runs a Thread (a Runnable instance) to generate a different number every second; the thread dispatches a Message to the activity; the Handler receives the message and sets the number by SetRpm() function.
The error I receive is:
03-12 09:39:33.280 1796-1827/com.stemmo.termi_up W/dalvikvm: threadid=16: thread exiting with uncaught exception (group=0xb178e678)
03-12 09:39:33.280 1796-1827/com.stemmo.termi_up E/AndroidRuntime: FATAL EXCEPTION: Thread-78
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:5908)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:869)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:4253)
at android.view.View.invalidate(View.java:10539)
at android.view.View.invalidate(View.java:10494)
at com.stemmo.termi_up.CustomViews.MyGauge.SetRpm(MyGauge.java:61)
at com.stemmo.termi_up.GaugeActivity$2.handleMessage(GaugeActivity.java:54)
at android.os.Handler.dispatchMessage(Handler.java:99)
at com.stemmo.termi_up.GaugeActivity$3.run(GaugeActivity.java:83)
at java.lang.Thread.run(Thread.java:841)
So, the handler is in the thread's context??
How can i solve it?
Do I need to AsyncTask object instead of the Runnable one?
A view in Android can only be updated from the UI thread. You can always post a Runnable to a view like this
MyGauge gauge = ... // your custom view
gauge.post(new Runnable() {
view.SetRPM();
});
I answer cause i better write down the code ..
This is the code on the onCreate() of the Activity:
final Handler myHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle data = msg.getData();
String v = data.getString("value");
myGauge.SetRpm(v);
}
};
// Simul thread
nValue = 0;
bUpDown = true;
Runnable thread = new Runnable() {
#Override
public void run() {
while(true) {
if (bUpDown) // Up
...
}
Bundle data = new Bundle();
data.putString("value", String.valueOf(nValue));
Message msg = new Message();
msg.setData(data);
myHandler.dispatchMessage(msg);
// Delay
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
new Thread(thread).start();
The SetRpm() member inside MyGauge is:
public void SetRpm(String newRpm)
{
m_strRpm = newRpm;
invalidate();
}
I found the issue with your code. Please replace your code line
myHandler.dispatchMessage(msg); with myHandler.sendMessage(msg);
It will fix the Exception "Only the original thread that created a view hierarchy can touch its views."
I have a few downloads that are submitted as tasks to a ThreadPoolExecutor. Now, I am creating this ThreadPoolExecutor in a global class that extends Application. I am storing all the submitted tasks in a HashMap with ids.
I have a ListView in a Fragment. This ListView item contains pause and resume buttons. When I click on the list item itself, the download FutureTask is submitted to the global pool executor. Now, when I click on the pause button of the ListView item, I want that particular thread to pause/wait.
I have the onClick method of the pause button in my list view's custom adapter. So, when I click the button, in my adapter class, I get all the threads that are currently running, put them in an array and then get the thread with the name I want from the array. I can see in my log that the thread I want to get is running with the name I set. So once I get that thread, I do wait() on it.
In the onclick of my resume button, I notify that particular thread and set the flag to pause as false so that the thread resumes. But, the download inside that thread actually keeps running even after clicking pause. I do not know where I am going wrong.
My GlobalState Class:
public class GlobalState extends Application{
HashMap<String, Future<?>> futureMapMain = new HashMap<String, Future<?>>();
ThreadPoolExecutor mainExec = new ThreadPoolExecutor(2, 2, 2000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new YourThreadFactory());
public void submitTaskMain(String name, Thread task){
Future<?> longRunningTaskFuture = mainExec.submit(task);
futureMapMain.put(name, longRunningTaskFuture);
}
public HashMap<String, Future<?>> getFutureMap(){
return futureMapMain;
}
public ThreadPoolExecutor getMainExeutor(){
return mainExec;
}
public class YourThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
return new Thread(r, gettName());
}
}
}
My Download Method that is written inside my fragment class. gets executed on list item click:
public void abDownloadTask(){
Thread dThread = new Thread(new Runnable() {
final Handler pdfHandler = new Handler();
#Override
public void run() {
for(something){
/* DOES SOME DOWNLOAD USING url.getcontent() with different urls in a loop and stores files to sd card. */
}
}
}
dThread.setName(threadname);
Log.d("Tel Frag", "Thread name set as: "+dThread.getName());
mainGs.settName(threadname);
mainGs.submitTaskMain(threadname, dThread);
}
onclick of my pause button inside custom list adapter:
pause.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
needToPause = true;
Runnable runnable = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
Log.d("CLA", "Threads running size: "+threadArray.length);
Thread neededThread = null;
for ( Thread thread : threadArray ){
Log.d("CustomListAdapter", "Thread name in array: "+thread.getName());
if (thread.getName( ).equals(threadname)){
neededThread = thread;
}
}
if(neededThread!=null){
while(needToPause){
synchronized (neededThread) {
try {
neededThread.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
};
new Thread(runnable).start();
notifyDataSetChanged();
}
});
onclick of my resume button written inside custom list adapter class:
resume.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
needToPause = false;
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Thread[] threadArray = threadSet.toArray(new Thread[threadSet.size()]);
for ( Thread thread : threadArray ){
if ( thread.getName( ).equals(threadname) ){
thread.notify();
}
}
notifyDataSetChanged();
}
});
I have been trying to figure it out for 2 days now but no luck. I went through a lot of examples and stackoverflow questions. I am just trying to get a thread by its name and then pause and resume it. Any help would be appreciated. Thank you!
You can't reliably use wait() and notify() on Thread objects. This is clearly stated in the Javadoc. You already have Future objects, you shouldn't be using other mechanisms anyway.
I have a fragment that displays weather data that runs a background thread that essentially just calls a function in my main UI to check whether my forecast is still valid. This function updates the UI so I am using a Handler and posting a Runnable to the main thread, like so:
public class WaveFxListFragment extends Fragment implements
LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener {
// .....
// handler for dealing with synchronising update thread with UI
private Handler mHandler;
private UpdateThread mUpdateThread;
private class UpdateThread extends Thread {
volatile boolean running = false;
#Override
public void run() {
running = true;
while (running) {
// get main UI thread to perform update check:
Log.d(TAG, "Handler is " + mHandler);
mHandler.post(new Runnable() { // getting null pointer error here!
#Override
public void run() {
checkValidTime();
}
});
try {
Thread.sleep(1000); // sleep 1 second
} catch (InterruptedException e) {
Log.d(TAG, "Thread was interrupted!");
e.printStackTrace();
}
}
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Reuse existing handler:
mHandler = getActivity().getWindow().getDecorView().getHandler();
}
#Override
public void onResume() {
super.onResume();
// start update checker:
mUpdateThread = new UpdateThread();
mUpdateThread.start();
}
#Override
public void onPause() {
// stop update thread
Log.d(TAG, "Asking thread to stop");
mUpdateThread.running = false;
super.onPause();
}
}
This works fine; the problem is when I change my screen orientation. The current activity gets destroyed and if the thread is running, it tries to post a Runnable to a UI thread that no longer exists. So, I put a running member variable in the UpdateThread class and set if to false when my activity goes calls onPause. However, even though I have set the UpdateThread.running variable to false, my thread still tries to post a Runnable, but the Handler is now null! It shouldn't get that far, but it is!
Am I doing this wrong? My log message "Asking thread to stop" gets printed out, so I know it is getting as far as setting running to false.
Can anyone offer an insight?
Thanks
A few things that you can do to resolve this.
Interrupt the thread after you set running to false. This should cause the thread to exit earlier, or at the very least for your error to appear earlier.
Check that your handler is not null in your update thread, and set it to null in onPause.
Move the assignment of mHandler to onResume to make sure that mHandler is valid when the update thread is called.
Can someone explain to me 2 things about the thread code that I finally made almost working the way it should. I want to do a periodic task in the background every x seconds and be able to stop and start it at will. I coded that based on the examples I found, but I'm not sure if I made it in the right way. For the purpose of debugging, the task is displaying a time with custom showTime().
public class LoopExampleActivity extends Activity {
TextView Napis, Napis2;
Button button1,button_start,button_stop;
Handler handler = new Handler();
Boolean tread1_running = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Napis = (TextView) findViewById(R.id.textView1);
Napis2 = (TextView) findViewById(R.id.textView2);
button1 = (Button) findViewById(R.id.button1);
button_stop = (Button) findViewById(R.id.button_stop);
button_start = (Button) findViewById(R.id.button_start);
button_stop.setOnClickListener(new OnClickListener() {
#Override
public void onClick (View v) {
tread1_running = false;
}
});
button_start.setOnClickListener(new OnClickListener() {
#Override
public void onClick (View v) {
tread1_running = true;
}
});
thread.start();
}// endof onCreate
final Runnable r = new Runnable()
{
public void run()
{
handler.postDelayed(this, 1000);
showTime(Napis2);
}
};
Thread thread = new Thread()
{
#Override
public void run() {
try {
while(tread1_running) {
sleep(1000);
handler.post(r);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
Now the questions are:
1)Will my thread quit forever if I stop it with the stop button?
2)Why can't I start it again with the start_button? If I add the tread.start() in a button, will it crash?
3) I tried a second version when I let the thread run and put a condition into the handler. The only way I can get it to work is to loop conditionaly in the handler by adding an
if (thread1_running) {
handler.postDelayed(this, 2000);
showTime(Napis2);
}
And changing the condition in a thread start to while (true) but then I have an open thread that is running all the time and I start and stop it in a handler, and it posts more and more handlers.
So, finally I get to the point it looks like that:
final Runnable r = new Runnable()
{
public void run()
{
if (thread1_running) handler.postDelayed(this, 1000);
showTime(Napis2);
}
};
Thread thread = new Thread()
{
#Override
public void run() {
try {
while(true) {
sleep(1000);
if (thread1_running) handler.post(r);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Is the proper way to do that is to start and stop a whole thread? Or that is the best way?
The best way to achieve something like that would be, in my humble opinion, to postDelayed(Runnable, long).
You could do something like this. Class definition:
private Handler mMessageHandler = new Handler();
private Runnable mUpdaterRunnable = new Runnable() {
public void run() {
doStuff();
showTime(Napis2);
mMessageHandler.postDelayed(mUpdaterRunnable, 1000);
}
};
And control true run/stop like this:
To start:
mMessageHandler.postDelayed(mUpdaterRunnable, 1000);
And to stop:
mMessageHandler.removeCallbacks(mUpdaterRunnable);
It's much much simpler, in my humble opinion.
Threads a described by a state machine in java.
When a thread get outs of its run method, it enters in the stopped state and can't be restarted.
You should always stop a thread by getting it out of its run method as you do, it s the proper way to do it.
If you want to "restart the thread", start a new instance of your thread class.
You should better encapsulate your thread and its running field. It should be inside your thread class and the class should offer a public method to swich the boolean. No one cares about your datastructure, hide them. :)
You should consider using runOnUIThread for your runnable, its much easier to use than handlers.