I have an app that displays a splash screen. The splash screen activity creates a new Runnable which simply sleeps for 1 second and and then launches the main activity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
UKMPGDataProvider.init(this.getApplicationContext(), Constants.DATABASE_NAME);
Thread splashThread = new Thread() {
#Override
public void run() {
try {
sleep(ONE_SECOND_IN_MILLIS);
} catch (InterruptedException e) {
} finally {
Intent intent = new Intent(SplashScreen.this, MainScreen.class);
finish();
startActivity(intent);
}
}
};
splashThread.start();
}
Is it OK to launch the main activity (and therefore the whole app except for the splash screen) on this new thread?
We hear a lot about the "UI thread" in Android. Does this new thread now become the UI thread, or is the UI thread special in some way?
Yes, that's fine. startActivity(intent) asks the system to launch your main Activity. You're not actually loading it yourself in the thread you call that from.
Basically it's a single-thread model where only one thread can modify the UI because the Android UI toolkit is not thread-safe.
It's same in Blackberry as well. See Why are most UI frameworks single-threaded?
Related
I tring to create an animation to my activity.
The animation working as it need to but it working only once.
Intent i = new Intent(a, UserDataActivity.class);
i.putExtra("userData", t);
a.startActivity(i);
a.overridePendingTransition(R.anim.spin_anim, R.anim.static_anim);
I starting the activity from other thread if it's matters.
a is a pointer to the main activity.
I know that the overridePendingTransition is working when calling the startActivity and the finish methods because of that I don't need to kill the calling acticity.
I solved the problem by running the code in the UI Thread
a.runOnUiThread(new Runnable()
{
#Override
public void run()
{
Intent i = new Intent(a, UserDataActivity.class);
// putting in the intent the user data
i.putExtra("userData", t);
a.startActivity(i);
a.overridePendingTransition(R.anim.spin_anim, R.anim.static_anim);
}
});
I would like if someone will explain me why this didn't work on other thread?
Ive been struggling with the concept of threads on android. I thought the following code was running on a different thread to the main UI thread but I am not 100% sure so I thought i would come here for clarification as the android docs arent written in any language i understand. below is my code.
public void retrieveImages(final ImagePresenterInt imagepresenter) {
storedImages = new ArrayList<Image>();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
File imagedir = new File("/mnt/shared/images");
File [] myfiles = File.listRoots();
String canread = String.valueOf(imagedir.canRead());
String isfile = String.valueOf(imagedir.isFile());
String isdir = String.valueOf(imagedir.isDirectory());
Log.d("Can Read?","Canread from file :" + canread);
Log.d("is file?","Is a file ? :" + isfile);
Log.d("is dir?","Is a Dir file :" + isdir);
File [] rootfiles =myfiles[0].listFiles();
for (File item : rootfiles)
{
Log.d(item.getAbsolutePath(),item.getName());
}
if(Looper.myLooper() == Looper.getMainLooper())
{
Log.d("main thread ?", "YES");
}
}
}, 2000);
}
my understanding of the above code is that I create a handler. which is associated with the main thread or UI thread. It has a message queue and a looper associated with it. this code is passed to the message queue and run by the looper on a seperate thread to the main UI thread? I could be well wrong here. but mainly I want to know if this is running on the main thread. And how would i get it onto a different thread if not? I tried to verify that the code is running on a different thread using code i found in this question
How to check if current thread is not main thread
this apparently tells me Iam still running in the main thread. thanks for your help
The Handler you create in retrieveImages() is bound to the thread which this function is called from.
The doc on Handler says:
Default constructor associates this handler with the Looper for the current thread. If this thread does not have a looper, this handler won't be able to receive messages so an exception is thrown.
So if retrieveImages() is called from the UI thread, the Handler created in it is also bound to the UI thread.
UPDATE: If you want your code to be executed in different thread, the easiest way is to use AsyncTask.
The Handler is created in the calling thread, which is probably the UI-Thread in your case. If you like to start a new Thread, there are three possibilities I know of: the first is to simple start a new thread:
thread = new Thread() {
#Override
public void run() {
//Do your thing here
}
};
thread.start();
The thread will die, if your Activity gets killed.
The second is to define an IntentService:
public class SimpleIntentService extends IntentService {
public SimpleIntentService() {
super("SimpleIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
//Do your thing here
}
and start it via
Intent intent = new Intent(this, SimpleIntentService.class);
intent.putExtra("SomeString", "SomeValueYouNeed");
startService(intent);
The IntentService will run on, until onHandleIntent() is done and than close itself.
The third possibility is an AsyncTask:
private class TestTask extends AsyncTask<Datatype1, Datatype2, Datatype3>{
protected Long doInBackground(Datatype1... params) {
// Do your thing here
}
protected void onProgressUpdate(Datatype2... progress) {
//Do a Progress-Bar or something like that
}
protected void onPostExecute(Datatype3 result) {
//Do something, when your work is done
}
}
And in your Activity:
new TestTask().execute(params);
The docs state you shouldn't use Async-Tasks for very long calulations, but I'm not shure why. It might be easier to get your data back to the UI-Thread if you use an Asynctask instead of the Intentservice, but I for myself don't use them very often, so I'm maybe not the best person to ask here.
Edit: I forgot this:
IntentService is executed once for every ntent you pass, the Asynctask will be callable just once.
Furthermore the IntentService has to be declared in the Manifest.
I'm wondering if I can rely on a task which is executing in a separate non UI thread if I'm leaving an Activity. Looking on example below I can see the Runnable in a separate Thread is executing even if I'm on another Activity. But when would it be killed?
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
runTask();
startActivity(new Intent(this, SecondActivity.class));
}
private void runTask() {
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
Log.i("Dev", "here I am");
SystemClock.sleep(1000);
}
}
}).start();
}
}
The java.lang.Thread is not tied to the Android UI component lifecycle, so just like in a normal JVM it will keep going until its run() method returns... unless Android terminates your entire application process before that happens, which it can do if you app is in the background and another app needs the RAM.
Incidentally, because the thread is not tied to the UI component lifecycle, you cannot simply call into another UI component from that thread. You'll have to post to a Handler or issue a broadcast or something if you want to get back to the UI, but you may already know that.
Finally, in your sample code, the Thread instance (through the anonymous Runnable) will keep the enclosing Activity class from being garbage collected, so you may want to re-think that construct.
If you want to do background work that's not tied to any single Activity, you're generally better off modelling it as an IntentService.
you can call Interrupt() that will post a interrupt request for your thread.
http://developer.android.com/reference/java/lang/Thread.html
I want to open a new Activity:
Intent intent = new Intent(homeScreen.this, EmployeeService.class);
Bundle b = new Bundle();
b.putInt(Constants.SERVICE_DETAIL_L1_ID_MSG, ServiceIndex.SRV_L1_EMPLOYMENT);
b.putInt(Constants.SERVICE_DETAIL_FOCUS_POS_MSG, 2);
intent.putExtras(b);
startActivity(intent);
But it takes so long to make destination Activity (EmployeeService) become visible. From Logcat, I see:
05-14 23:43:31.727: INFO/ActivityManager(59): Displayed activity fr.playsoft.happylille/.employee.EmployeeService: 7050 ms (total 7050 ms)
I cannot believe it take more than 7 seconds just to open a new Activity. I add a log in onCreate() but see it only take 5ms to finish onCreate.
Can anyone tell me how to find the root of this problem?
You should move the code Html.fromHtml(desc); to a thread to have it asynchronous. This thread can safely be started during the onCreate() of the newly opened Activity.
At the end of that thread, you can run tvDesc.setText() from the UI thread:
class ExampleThread extends Thread {
#Override
public void run() {
final Spanned spannedText = Html.fromHtml(desc);
yourNewlyOpenedActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
tvDesc.setText(spannedText);
}
});
}
}
More generally, 7 seconds on a device maybe means 20 on another, so beware the ANR!
(Edited further to Boy's comment below, the former version of this answer was no longer accurate/valid)
Shlublu's answer is incorrect, you are not allowed to do UI updates from non-UI threads!
It seems to work at first, but that is just luck, timing.
just a quick ugly test proves that it will go wrong at the 'right' timing. Here I set a text every 100 ms. Will crash in about 1 second:
new Thread() {
#Override
public void run() {
while (true) {
someTextView.setText("bla");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
It will throw this error: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I think you should execute the Html.fromHtml(desc) via an AsyncTask or RxJava
your open new activity code put in Thread . and run code may be less time to required open another activity..may be helpfully.
Android doc says about runOnUiThread: "If the current thread is not the UI thread, the action is posted to the event queue of the UI thread."
My question is, will different activities share the same event queue or each activity has its own event queue?
Suppose activity A starts a thread to do something and finally updates UI using runOnUiThread, but at the same time it starts Activity B like the code below:
public class HelloAndroid extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Thread myThread = new MyThread();
myThread.start();
Intent intent = new Intent(this, B.class);
startActivity(intent);
}
private class MyThread extends Thread {
public void run() {
/* Do somthing expensive */
......
/* Update UI */
HellowAndroid.this.runOnUiThread(new Runnable() {
#Override
public void run() {
/* Do UI update for activity A */;
}
});
}
}
}
What if when the thread is executing the code "HellowAndroid.this.runOnUiThread(new Runnable...)", the visible activity is already B, and the stack is currently A B, with B at the top. Will the code "HellowAndroid.this.runOnUiThread(new Runnable...)" still be executed to update activity A? What will happen? Will activity A's UI be updated or not in this case?
Thanks.
The Activity A thread code will still run and try to update the Activity A UI. But be warned, doing this you are at serious risk of running into runtime errors if the system has stopped your activity for any reason(such as running out of memory.)
It is much better practice to start threads on onResume and stop them again in onPause.