I have an event in the main thread that creates another thread. This new thread must sleep for 60 seconds and then it must check the main thread state. My code is this:
public class Act extends Activity {
Object lock=new Object();
public class MainT implements LocationListener {
public String str="";
public void onLocationChanged(Location location) {
synchronized(lock) {
str=String.valueOf(location.getLatitude())+" "+String.valueOf(location.getLongitude());
new SecondT(str).start();
}
}
class SecondT extends Thread {
public String st;
SecondT(String s) {
st=s;
}
public void run() {
waitSeconds();
}
public void waitSeconds() {
try {
Thread.sleep(60000);
synchronized(lock) {
if (str.equals(st))
Log.d("SecondT", "Same string.");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void onProviderDisabled(String arg0) {
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String provider) {
// TODO Auto-generated method stub
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
// TODO Auto-generated method stub
}
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LocationManager locMan = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
MainT mt = new MainT();
locMan.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 50, mt);
}
}
The problem is that if a start that thread, it's the MainT that sleeps (the GPS event isn't called even if i pass new coordinates through the debug tool).
The problem is that if a start that thread, it's the MainT that sleeps (the GPS event isn't called even if i pass new coordinates through the debug tool).
As #Joni mentions, it's hard to see where main would sleep unless the synchronized section of code that updates the string value is doing some complex operations that could take a long time. Could it be that main is trying to fork 2 threads and the 2nd one is waiting for the lock? If you provide more code we may be able to see your problem.
In terms of sharing the st value between the forked-thread and the main thread, you might consider something like an AtomicReference<String>. This will allow you to set(...) the value in the forked thread and get() the value in main without locking.
Related
Right now I have one thread which populates the view of my activity. But I want another thread to add some textviews and imageviews in the same activity. I am using SurfaceView inside which I created this thread and I don't know how to add another thread so that it can contribute to the view of the current activity.
Help me out..
MyView view;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
view = new MyView(this);
setContentView(view);
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
view.pause();
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
view.resume();
}
public class MyView extends SurfaceView implements Runnable {
Thread threadstill = null;
boolean isitok = false;
SurfaceHolder holder;
public MyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
holder = getHolder();
}
#Override
public void run() {
// TODO Auto-generated method stub
while (isitok == true) {
if (!holder.getSurface().isValid()) {
continue;
}
canvas = holder.lockCanvas();
canvas.drawARGB(255, 255, 255, 255);
canvas.drawBitmap(<bitmapimage>, x, y, null);
holder.unlockCanvasAndPost(canvas);
}
}
public void pause() {
isitok = false;
while (true) {
try {
threadstill.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
threadstill = null;
}
public void resume() {
isitok = true;
threadstill = new Thread(this);
threadstill.start();
}
}
You cannot directly manipulate the user interface from another thread.
The main thread is responsible for all UI changes.
However, if you want to perform some expensive work in the background (e.g. loading pictures) you can send the results to the UI thread.
Hava a look at the official documentation: https://developer.android.com/training/multiple-threads/communicate-ui.html
Or at a similiar question: How can I manipulate main thread UI elements from another thread in Android?
I don't exactly know what is your issue but regarding to I don't know how to add another thread so that it can contribute to the view of the current activity. you can create a new thread easily with a nested class...simply call:
Thread myNewThread = new Thread(new Runnable() {
public void run() {
//do code here
}
}).start();
But be Aware you can only change views from the UIThread so i would recommend you the AsyncTask:
public void myAsyncTask exstends Asynctask<Void,Void,Void> {
....
}
AsyncTask Comes with several methods the most important is the doInBackground where you can do the heavy things like Network Connections or other stuff which would freeze the UI or is simply not allowed on the UIThread(Network stuff).
After doInBackground is finished onPostExecute is called which runs on the UiThread and where you can process changes on your views.
To get more Information look at this link:
http://developer.android.com/reference/android/os/AsyncTask.html
Hope it helps and it is what you asked for. ;)
Do it with AsyncTask class. In doInBackground method download image, and in onPostExecute method apply image to ImageView.
I did it here already https://github.com/bajicdusko/AndroidJsonProvider/tree/master/app/src/main/java/com/bajicdusko/ajp/tools.
You can use my class or make your own .
I'm trying to make a simple little program that will increment a number once a second. In this case, I'm implementing a thread that should loop once per second and add 1 to "potato" each time it loops. This works fine until it gets back to the display method potatoDisp(). For some reason this causes my app to crash. Removing potatoDisp() from run() fixes the problem, but the display is not updated as "potato" increases.
public int potato = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
potatoDisp();
start();
}
public void potatoDisp() {
TextView text = (TextView) findViewById(R.id.textView1);
text.setText("You currently have " + potato + " potatoes");
}
public void start() {
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
potato++;
potatoDisp();
}
}
I'm doing this for an Android app, if that helps. I've tried searching for an answer but I'm pretty lost when it comes to the proper way to work threads.
You need a runnable / handler like this:
private Runnable potatoRun = new Runnable() {
#Override
public void run () {
potatoDisp();
}
};
then change
potatoDisp();
to:
runOnUiThread(potatoRun);
You can't update the views when you're not on the UI thread.
You are probably getting an exception for updating the UI in the background. Since, potatoDisp(); is called from a background Thread but that function updates the UI it will give you problems. You need to call it with runOnUiThread().
#Override
public void run() {
while (true) {
try
{
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
potato++;
runOnUiThread(new Runnable()
{
#Override
public void run()
{
potatoDisp();
}
});
}
}
Something like this should work.
The issue is that you are trying to update the UI (calling text.setText(...)) on a thread other than the main UI thread.
While I would suggest using a TimerTask instead of calling Thread.sleep(...), there are two main ways to edit your current code to work as expected.
-- Use a Handler
Define a Handler class that will accept messages and update your UI as needed. For example:
private final String POTATO_COUNT = "num_potatoes";
Handler handler = new Handler() {
public void handleMessage(Message msg) {
int numPotatoes = msg.getData.getInt(POTATO_COUNT);
mText.setText("You currently have " + numPotatoes + " potatoes");
}
}
Then in your code where you want to call your handler to update your text view, whether or not you are on the main UI thread, do the following:
Bundle bundle = new Bundle();
bundle.putInt(POTATO_COUNT, potato);
Message msg = new Message();
msg.setData(bundle);
handler.sendMessage(msg);
-- Call runOnUiThread(...)
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
return;
}
potato++;
runOnUiThread(new Runnable()
{
public void run()
{
potatoDisp();
}
}
}
}
I think you should be using Async Task to update the UI from a thread: http://developer.android.com/reference/android/os/AsyncTask.html
Halo, the first i want to know the idle time at my android application. after that, i will do something if it is a idle time mode.
I follow this link.
Application idle time
my program work properly, but suddenly the problem show up. I can't move to the other page (for example to the login page) or pop up a message using alertdialog because its in a thread. Do you have any solutions?
public class ControlActivity extends Activity {
private static final String TAG=ControlActivity.class.getName();
/**
* Gets reference to global Application
* #return must always be type of ControlApplication! See AndroidManifest.xml
*/
public ControlApplication getApp()
{
return (ControlApplication )this.getApplication();
}
#Override
public void onUserInteraction()
{
super.onUserInteraction();
getApp().touch();
Log.d(TAG, "User interaction to "+this.toString());
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}}
here is my ControlApplication.java
public class ControlApplication extends Application {
private static final String TAG=ControlApplication.class.getName();
private Waiter waiter;
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Starting application"+this.toString());
//setContentView(R.layout.activity_main);
waiter=new Waiter(5*60*1000); //5 mins
waiter.start();
Toast.makeText(ControlApplication.this, "start", Toast.LENGTH_LONG).show();
}
public void touch()
{
waiter.touch();
Toast.makeText(ControlApplication.this, "touch", Toast.LENGTH_LONG).show();
} }
here is the Waiter.java
public class Waiter extends Thread implements Runnable{
private static final String TAG=Waiter.class.getName();
private long lastUsed;
private long period;
private boolean stop;
Context activity;
public Waiter(long period)
{
this.period=period;
stop=false;
}
#SuppressLint("ParserError")
public void run()
{
long idle=0;
this.touch();
do
{
idle=System.currentTimeMillis()-lastUsed;
Log.d(TAG, "Application is idle for "+idle +" ms");
try
{
Thread.sleep(5000); //check every 5 seconds
}
catch (InterruptedException e)
{
Log.d(TAG, "Waiter interrupted!");
}
if(idle > period)
{
idle=0;
//do something here - e.g. call popup or so
//Toast.makeText(activity, "Hello", Toast.LENGTH_LONG).show();
stopCounter();
}
}
while(!stop);
Log.d(TAG, "Finishing Waiter thread");
}
public synchronized void touch()
{
lastUsed=System.currentTimeMillis();
}
public synchronized void forceInterrupt()
{
this.interrupt();
}
//soft stopping of thread
public synchronized void stopCounter()
{
stop=true;
}
public synchronized void setPeriod(long period)
{
this.period=period;
}}
I tried to create a new class and call a method to intent. Its also fail. tried to pop up a message from that method its also fail.
do you guys have any other solutions for idle time? thanks.
Regards,
Alfred Angkasa
In you active activity, instead of this thread, do:
public class Graph extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
while(idle = 0) {
idle = System.currentTimeMillis()-lastUsed;
if(idle != period) {
Intent goNextActivity = new Intent(com.package.theactivity);
else {
idle == 0;
}
}
}
}
I just found by myself the answer by search on google and try for 5 hours.. :D
I hope my answer will help you too.
First, I mix the ControlApplication and Waiter with ControlActivity. Thats mean I don't need both files. My ControlActivity will extends the activity (its use for me to intent to the other page if in idle mode), and i will implements runnable(its use for me to run the thread).
after that i have a method called onUserInteraction(), this method help me to get the user interaction, whenever the user touch or click something.
in the onCreate, i initiate all the variable including lastUsed, period, and stop.
why should I initiate that? because you need to know how many seconds to know that your apps is on idle mode or not. that was period use. Stop variable is use for me to iterate and searching every 5 seconds(you can also make it every second to check idle or not) my apps is idle or not. I initiate lastUsed by calling method touch. I copied touch method from ControlApplication into my ControlActivity. By calling touch method, I can know when is my lastused. After that I start my thread.
in my run method, i set idle = 0. and do some looping to check. i check every 5 seconds to know my apps is on idle mode or not.
idle = System.System.currentTimeMillis()-lastUsed -> i used this to know if the idle is already suite with the period or not using if method.
if the idle is greater than period, my apps must be in idle mode. after that i stop the iteration and using handler to manage it.
i set handler.sendEmptyMessage(0), and create Handler. At handler i move to the other page.
this is my full code.
public class MainActivity extends Activity implements Runnable {
private static final String TAG= MainActivity.class.getName();
private long lastUsed;
private int period;
private boolean stop;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
period = 10000;
stop=false;
touch();
Thread currentThread = new Thread(this);
currentThread.start();
Toast.makeText(getApplicationContext(), "Start", Toast.LENGTH_SHORT).show();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public void onUserInteraction()
{
super.onUserInteraction();
touch();
Log.d(TAG, "User interaction to "+this.toString());
}
public synchronized void touch()
{
lastUsed=System.currentTimeMillis();
Toast.makeText(getApplicationContext(), "touch", Toast.LENGTH_SHORT).show();
}
public void moveIntent() {
Intent intent = new Intent(this, AfterIdle.class);
startActivity(intent);
}
public void validate(View view) {
switch (view.getId()) {
case R.id.button1 :
Intent intent = new Intent(this, AfterIdle.class);
startActivity(intent);
break;
}
}
#Override
public void run() {
// TODO Auto-generated method stub
long idle;
while (!stop) {
idle=System.currentTimeMillis()-lastUsed;
try
{
Thread.sleep(5000); //check every 5 seconds
}
catch (InterruptedException e)
{
Log.d(TAG, "Waiter interrupted!");
}
if (idle > period) {
idle = 0;
stop = true;
}
}
handler.sendEmptyMessage(0);
}
public Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
moveIntent();
}
};}
I hope this code will help another people if they have the same problem that i faced last time. I wish someone would correct the answer for me if my answer is wrong.
thanks.
Regards,
Alfred Angkasa
am trying to pause a thread and then resume it but when i do the pause it freeze the application. i try several things but with no luck.
on my main activity am calling the thread and have the button that will pause the thread and a second runnable class it start running and draw on my plot which include and the pause function. the code that i implement is the follow
Main activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDoctorDynamicXYDatasource= new DoctorDynamicXYDatasource(this, mHandler);
findViewById(R.id.Pause).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
synchronized(pp){
mDoctorDynamicXYDatasource.Pause();
Log.i("File", "Pause button ");
}
}
});
findViewById(R.id.Start).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
draw();
}
});
public void draw(){
///some code
pp= new Thread(data);
pp.start();
}
and on the **DoctorDynamicXYDatasource class** am doing the follow
public class DoctorDynamicXYDatasource extends Activity implements Runnable {
public void run() {
//loading and draw on the plot
}
public void Pause() {
synchronized (Thread.currentThread()) {
Log.i("File","pause");
try {
Thread.currentThread().wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
how can i pause the thread and then start it again. when i pause the thread it lock instead only the draw plot all the screen and the button without be able to make a selection
EDIT
i add the code that you tell me and from the main activity with the pause button am calling the pause fuinction from my DoctorDynamicXYDatasource class but it's not synchronized in order to wait the thread. am also not allow to call the pause function from other class?
mDoctorDynamicXYDatasource= new DoctorDynamicXYDatasource(this, mHandler);
findViewById(R.id.Pause).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i("File", "Pause button pressed");
mDoctorDynamicXYDatasource.pause();
}
});
You are calling pause on the UI thread, which indeed results in freezing the application.
you have to pause your specific thread from the inside, like this:
public void run() {
// Your code here, there is probably a loop
while (someCondition) {
// Loop work
synchronized(this) {
if (pause) {
pause = false;
wait();
}
}
}
}
public synchronized void pause() {
pause = true;
}
public synchronized void go() {
notify();
}
Calling pause will allow the thread to wait at the next iteration. Calling go will allow the thread to exit the wait state immediately.
Afaik, there is no way to force a Thread to pause unless you are in the thread.
I wrote this app that in the first screen it has an included Thread on it. So it has I timed it like 7 seconds then it will proceed to the next activity.
The problem is whenever I hit the home button the music will stop and it will go to android homescreen but after my timed is done which is the 7 seconds, the app will reappear and will show the next activity.
I tried putting finish(); in the onpause(); but it's still showing the next activity.
here's the actual code.
public class HelloWorldActivity extends Activity {
MediaPlayer mp;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
mp = MediaPlayer.create(this, R.raw.otj);
mp.start();
Thread LogoTimer = new Thread(){
public void run(){
try{
int LogoTimer = 0;
while(LogoTimer < 7000){
sleep(100);
LogoTimer = LogoTimer + 100;
}
startActivity(new Intent("com.example.HelloWorld.CLEARSCREEN"));
} catch (InterruptedException e) {
e.printStackTrace();
}
finally{
finish();
}
}
};
LogoTimer.start();
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
mp.release();
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
mp.pause();
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
}
}
First, that's a really inefficient way to run a timer. Try this way instead:
new Handler().postDelayed(new Runnable() {
public void run() {
// Do some work.
}
}, delayTimeInMs);
Second, your starting a new activity when that timer eventually fires. It doesn't matter that the originating activity is finished. Your startActivity() is running on it's own thread and will execute regardless.
It's possible the postDelayed() method will function like you expect. If not you'll need to have it check when it runs whether it should really start the activity. However, I think the Handler is attached to the default Looper which means it will stop (or rather, the message won't be posted) if the main activity finishes.
The application is still in the background and the thread is not destroyed so it will fire the startActivity.
I would not really setup a splash screen this way, or use a thread unless I wanted it off the UI for some reason, even then there are better options.
For educational purposes to take care of this you need to be able to abort the thread safely in onPause() one way to do so is below
Modifed Thread
Thread LogoTimer = new Thread() {
private volatile boolean abortThread = false;
public void run(){
long stopAt = System.currentTimeMillis() + 7000;
while (!abortThread && stopAt > System.currentTimeMillis())
yield();
if (!abortThread)
startActivity ...
}
public synchronized void stopThread() {
abortThread = true;
}
};