I am trying to make app sleep using Thread. I have got 2 solutions but one of them causes a problem.
This is a shorter code which works perfectly:
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome);
final Handler goToMenu = new Handler();
goToMenu.postDelayed(new Runnable() {
#Override
public void run() {
Intent i = new Intent(getApplicationContext(), Menu.class);
startActivity(i);
}
},5000);
}
But this one is problematic. When I put the time in millis to make app wait it works and the second Activity starts but the R.layout.welcome does not appear. The app just waits with a gray background until the startActivity(i) gets executed.
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome);
final Handler goToMenu = new Handler() {
#Override
public void handleMessage(#NonNull Message msg) {
Intent i = new Intent(getApplicationContext(), Menu.class);
startActivity(i);
}
};
Runnable run = new Runnable() {
#Override
public void run() {
try {
synchronized (this) {
wait(5000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
Log.d(TAG, "Waiting didnt work!!");
e.printStackTrace();
}
goToMenu.sendEmptyMessage(0);
}
};
Thread waitThread = new Thread(run);
waitThread.run();
What is wrong?
To start a new thread, use Thread#start() and not Thread#run().
run() just executes the Runnable synchronously i.e. blocking the current thread.
use
Handler.postDelayed(new Runnable(){}, DELAY_TIME) instead
for your case
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent i = new Intent(getApplicationContext(), Menu.class);
startActivity(i);
}
}, 5000);
To Show thelayout, Activity's onCreate() method should run completely. In your second code there is a thread. That thread stops your code compilation for 5000 milliseconds. That means your second code stops and wait 5 seconds in wait(5000); line. Therefore onCreate() method not completes.
You have used Handler().postDelayed() your first code. This method don't stuck the code. It is a background task. it works synchronously.
I hope your answer is here.
Related
I have a thread that will do some processing, then has runnable code which will display the results on the screen. The issue is that if the user presses the back arrow while the runnable is between two lines of display code, the next line will crash as the activity no longer exists.
The below code accomplishes the goal, but I hate having if statements before each line. Is there a better way?
#Override
public void onBackPressed() {
super.onBackPressed();
imgThread.interrupt();
}
private void processImage(){
final Handler mHandler = new Handler(Looper.getMainLooper());
progress = new ProgressDialog(this);
progress.setMessage("TEXT");
progress.setIndeterminate(true);
progress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progress.show();
imgThread = new Thread(){
#Override
public void run() {
//Process the image a bit
mHandler.post(new Runnable() {
#Override
public void run() {
if(!imgThread.isInterrupted())
imageView.setImageBitmap(mDisplayBitmap);
if(!imgThread.isInterrupted())
progress.setMessage("TEXT TEXT");
if(!imgThread.isInterrupted())
progress.show();
}
});
//More processing
mHandler.post(new Runnable() {
#Override
public void run() {
if(!imgThread.isInterrupted())
addScreenListener();
if(!imgThread.isInterrupted())
determineHelpToast("TEXT TEXT TEXT", Toast.LENGTH_LONG);
if(!imgThread.isInterrupted())
progress.dismiss();
}
});
}
}; imgThread.start();
}
The really safe way to do this is to modify your Runnable so that it never references the Android Context (Activity, etc.).
The easiest way to do this is to communicate changes to the Activity via an event bus.
I make an app that can count down. But it doesn't work and it just shows 100 in textview.
Here is my code:
public class MainActivity extends Activity {
private TextView textView;
private Button start;
Thread thread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView1);
start = (Button) findViewById(R.id.button1);
start.setOnClickListener(onStart);
thread = new Thread( //it's my thread
new Runnable() {
#Override
public void run() {
for (int i = 99; i > 0; i--) {
Log.i("Where am I?", "I'm in for loop .");
try {
textView.setText(String.valueOf(i));
} catch (Exception e) {
Log.e("Exception.getCause", String.valueOf(e.getCause()), e.getCause());
}
Log.i("INDEX", String.valueOf(i));
}
}
});
}
private View.OnClickListener onStart = new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.i("Where am I?", "I in View.OnClickListener .");
thread.start();
}
};
}
Update your TextView using runOnUiThread as below...
runOnUiThread(new Runnable() {
#Override
public void run() {
textView.setText(String.valueOf(i));
}
});
Update:
For delay in count you can use Handler as below. Create an object of Handler and create a Thread.
private Handler mTimerHandler = new Handler();
private Runnable mTimerExecutor = new Runnable() {
#Override
public void run() {
//add your code here which should execute after the specified delay
}
};
Then pass that thread inside postDelayed() method Handler with the time which should be delayed to execute the thread as below...
mTimerHandler.postDelayed(mTimerExecutor, 1000);
And if you want to cancel the thread to execute, use removeCallbacks() method as below...
mTimerHandler.removeCallbacks(mTimerExecutor);
Catching for Exception inside the Thread is kind of misleading. As matter of fact, textView.setText(String.valueOf(i)); executed in a Thread different from the UI Thread should make you app crashes for CalledFromTheWrongThreadException. You should use an Handler to execute that line in the UI Thread's context
textView.setText(String.valueOf(i));
has to be used in UI thread only.
textView.setText(String.valueOf(i));
This is UI action, You can handle UI action only in a main thread.
You can send a message to the handle of activity.
You cannot use Thread class to interact with UI you should use AsyncTask or Handler classes.
Sample tutorial: http://www.vogella.com/tutorials/AndroidBackgroundProcessing/article.html
Use AsyncTask instead.
Checkout:
http://www.vogella.com/tutorials/AndroidBackgroundProcessing/article.html
I have a Splash Screen (Logo Activity) to show the company name for 3 seconds before app starts. I start Main Activity from a thread, here is the code:
public class Logo extends Activity {
Thread t;
public boolean dead = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.logo);
t = new Thread() {
public void run() {
try {
Intent i = new Intent(Logo.this, Main.class);
Thread.sleep(3000);
if (!dead) {
startActivity(i);
}
finish();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t.start();
}
The Main Activity is called from a worked thread, is this correct? What are the differents with this code (using runOnUiThread)?
...
if (!dead) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Intent i = new Intent(Logo.this, Main.class);
startActivity(i);
}
});
}
...
I see no difference with this code in debug mode (The same threads, the same operation, etc.). Which is correct?
Starting an intent I think is not an UI operation. runOnUI thread runs UI operation on UI thread. So you can use either of thread (runOnUI or normal). May be normal thread will be good in this situation. But I would like to suggest you use timer instead.
To be honest, I don't like the Thread.sleep. PLease take a look at my solution:
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// Do your work here like... startActivity...
}
}, SPLASH_DURATION); // SPLASH_DURATION IS IN MILLISECONDS LIKE 3000
Also you can block the user to prevent the back key like this:
#Override
public void onBackPressed() {
// do nothing! disable user interaction!
}
You should use AsyncTask in "doInBackground" background thread and than sleep your thread(this thread not UIThread) "PostExecute" run on UI Thread than start your new activity
private class mSplashViewer extends AsyncTask<Void,Void,Void>{
protected void doInBackground(Void params){
Thread.currentThread().sleep(3000);
return null;
}
protected void onPostExecute(){
startActivity(...);
}
}
I'd like to execute a method 10 seconds after I launch an Intent:
Intent launchIntent = getPackageManager().getLaunchIntentForPackage(GOOGLE_VOICE_SEARCH_PACKAGE_NAME);
startActivity(launchIntent);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
SonrLog.d(TAG, "TIMEOUT, reconnecting!");
reconnectSONR();
}
}, 10000); //10 second timeout
The Intent launches, and my code steps over the Handler, but nothing gets printed or gets called.
How can I get this to work?
Well I tried the above code and it is working fine. And I can see the log Log.d(TAG, "TIMEOUT, reconnecting!"); printed in the log. Code that i used.
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent launchIntent = getPackageManager().getLaunchIntentForPackage(GOOGLE_VOICE_SEARCH_PACKAGE_NAME);
startActivity(launchIntent);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d(TAG, "TIMEOUT, reconnecting!");
}
}, 10000); //10 second timeout
}
});
NOTE: GOOGLE_VOICE_SEARCH_PACKAGE_NAME is pointed to some other package as it is just for testing.
I am using simple thread to execute the httpGet to server when a button is clicked, but I get this after execution.
Button b_back = (Button) findViewById(R.id.bback);
b_back.setOnClickListener(this);
Button b_sign_up = (Button) findViewById(R.id.signup_button);
b_sign_up.setOnClickListener(this);
#Override
public void onClick(View arg0)
{
// TODO Auto-generated method stub
switch (arg0.getId())
{
case R.id.bback:
Intent i = new Intent(this, MainSwitch.class);
finish();
startActivity(i);
break;
// More buttons go here (if any) ...
case R.id.signup_button:
if(username.getText().toString().equalsIgnoreCase("") ||
password.getText().toString().equalsIgnoreCase("") ||
email.getText().toString().equalsIgnoreCase(""))
{
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("Please fill in all the gaps!");
dialog.show();
}
else
{
//****** Call method that sends the information to server.
Thread background = new Thread (new Runnable()
{
public void run()
{
// Call the time consuming method
handler.sendEmptyMessage(0);
}
});
background.start();
}
}
}
private Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
Toast.makeText(getApplicationContext(),
"Done thread",
Toast.LENGTH_SHORT).show();
}
};
The errorline you get:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
is typically linked to problems where you try to do stuff to UI-elements on a non-UI-thread.
I suppose that by stating // Call the time consuming method you have left out some of your code. The fact that this time consuming method runs on a regulare Thread means it cannot interact with UI-elements.
If you post more code (and also which specify the line where the error occurs) we can probably provide more info on how to solve it.