The example is pretty straightforward: i want to let the user know about what the app is doing by just showing a text (canvas.drawText()). Then, my first message appears, but not the other ones. I mean, i have a "setText" method but it doesn't updates.
onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(splash); // splash is the view class
loadResources();
splash.setText("this");
boundWebService();
splash.setText("that"):
etc();
splash.setText("so on");
}
The view's text drawing works by doing just a drawText in onDraw();, so setText changes the text but doesn't show it.
Someone recommended me replacing the view with a SurfaceView, but it would be alot of trouble for just a couple of updates, SO... how the heck can i update the view dinamically at runtime?
It should be quite simple, just showing a text for say 2 seconds and then the main thread doing his stuff and then updating the text...
Thanks!
Update:
I tried implementing handler.onPost(), but is the same story all over again. Let me put you the code:
public class ThreadViewTestActivity extends Activity {
Thread t;
Splash splash;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
splash = new Splash(this);
t = new Thread(splash);
t.start();
splash.setTextow("OA");
try { Thread.sleep(4000); } catch (InterruptedException e) { }
splash.setTextow("LALA");
}
}
And:
public class Splash implements Runnable {
Activity activity;
final Handler myHandler = new Handler();
public Splash(Activity activity) {
this.activity=activity;
}
#Override
public void run() {
// TODO Auto-generated method stub
}
public synchronized void setTextow(final String textow) {
// Wrap DownloadTask into another Runnable to track the statistics
myHandler.post(new Runnable() {
#Override
public void run() {
TextView t = (TextView)activity.findViewById(R.id.testo);
t.setText(textow);
t.invalidate();
}
});
}
}
Although splash is in other thread, i put a sleep on the main thread, i use the handler to manage UI and everything, it doesn't changes a thing, it only shows the last update.
I haven't hit this yet, but I think the usual pattern is to do lengthy initialization in a background thread, and use Handler.post() to update the UI. See http://developer.android.com/reference/android/widget/ProgressBar.html for a different, but possibly related, example.
Also see this answer, especially the first paragraph:
The problem is most likely that you
are running the splash screen (some
sort of Dialog such as ProgressDialog
I assume) in the same thread as all
the work being done. This will keep
the view of the splash screen from
being updated, which can keep it from
even getting displayed to the screen.
You need to display the splash screen,
kick off an instance of AsyncTask to
go download all your data, then hide
the splash screen once the task is
complete.
Update (based on your update and your comment): You are not supposed to update the UI in any thread except the one where your Activity is created. Why is it impossible for you to load your resources in a background thread?
First: onCreate is executed on main UI thread of application so no UI updates until you leave it. Basically you need one thread to execute long running tasks and some mechanism to push updates into the UI.
Most usual approach is to extend AsyncTask see this link for further info
i suppose that your view is an extended view and you call onDraw for drawing the view, so, maybe the view isnĀ“t 'refresh' their state, so try this
onCreate(Bundle bundle) {
setContentView(splash); // splash is the view class
loadResources();
splash.setText("this");
splash.invalidate();
boundWebService();
splash.setText("that"):
splash.invalidate();
etc();
splash.setText("so on");
splash.invalidate();
}
Related
In my app I need to use a specific view (WebView) for use some javascript code in background. This mean the webview have to be offscreen. Initially I created this view inside the UI thread using runOnUiThread but I noted when I make some operation into UI (touch the screen for move some object) my background view seem to slow down and is not "resposnive" as required. From here the idea to "execute" this view in a secondary thread for make it independent. I don't know if this will be possible anyway. I started making this code:
class WebViewThread extends Thread
{
public WebView MyWebView = null;
public void run()
{
Looper.prepare();
MyWebView = new WebView(MainActivityContext);
Looper.loop();
}
}
The problem is if I try to "interact" with the view calling some method from UI thread I got a runtime error telling me that the view can be touched only by the same thread who create it. Unfortunately this thread is "locked" into loop and I have no idea regarding how to "inject" the interacting code inside this thread.
Now the question: my idea to create and manage a view inside a secondary thread can be done or is a really bad idea that can not work?
As to whether or not that's a good idea, someone more experienced is going to have to elaborate on that subject. For what you're trying to do, though, assuming that you're not touching any UI elements, you could try a handler to pass the code to your webview. See if this works:
Handler handler;
class WebViewThread extends Thread
{
public WebView MyWebView = null;
public void run()
{
Looper.prepare();
MyWebView = new WebView(MainActivityContext);
//the handler will run on the thread
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
//do stuff to my webview
}
};
Looper.loop();
}
}
and then you can make the handler do stuff with
handler.sendMessage(message);
I've written a pretty large custom view which overrides onSaveInstanceState() and onRestoreInstanceState(Parcelable state).
I wanted to populate a LinearLayout with my custom view, so I wrote the following code:
public class MainActivity extends Activity {
private LinearLayout mRootLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRootLayout = (LinearLayout) findViewById(R.id.root_layout);
int i;
// Test: adding 10 instances of MyCustomView.
for (i = 0; i < 10; ++i) {
MyCustomView cv = new MyCustomView(this);
// I set an ID for this view so that onSaveInstanceState() and
// onRestoreInstanceState(Parcelable state) will be called
// automatically.
cv.setId(++i);
mRootLayout.addView(cv);
}
}
// ...
}
It works fine - mRootLayout is indeed being populated with 10 instances of MyCustomView, and each instance of MyCustomView is being properly restored after, for example, screen rotation.
I've noticed that due to the fact that MyCustomView is pretty large, my code is being heavy on the UI thread.
To solve the issue and take some effort off of the UI thread, I decided to use a custom AsyncTask, which will create an instance of MyCustomView in doInBackground() and add it to the the main layout ( mRootLayout ) in onPostExecute().
The following code is my custom AsyncTask:
private class LoadMyCustomViewTask extends AsyncTask<Void, Void, MyCustomView> {
private Context mContext;
private LinearLayout mLayoutToPopulate;
private int mId;
public LoadMyCustomViewTask(Context context, LinearLayout layout, int id) {
mContext = context;
mLayoutToPopulate = layout;
mId = id;
}
#Override
protected MyCustomView doInBackground(Void... params) {
MyCustomView cv = new MyCustomView(mContext);
return cv;
}
#Override
protected void onPostExecute(MyCustomView result) {
result.setId(mId);
mLayoutToPopulate.addView(result);
}
}
In MainActivity I use it as follows:
public class MainActivity extends Activity {
private LinearLayout mRootLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRootLayout = (LinearLayout) findViewById(R.id.root_layout);
int i;
for (i = 0; i < 10; ++i) {
new LoadMyCustomViewTask(this, mRootLayout, ++i).execute();
}
}
// ...
}
This code works too, but there is only one problem - MyCustomView is not being restored at all.
For debug purposes I put a Log.d(...) in MyCustomView's onSaveInstanceState() and in onRestoreInstanceState(Parcelable state), and I've noticed that onRestoreInstanceState(Parcelable state) isn't being called.
Do you have any idea why onRestoreInstanceState(Parcelable state) isn't being called when I use an AsyncTask to populate mRootLayout, but it is indeed being called when I create MyCustomView completely on the UI thread?
Thank you.
Edit: I'm posting the methods onSaveInstanceState() and onRestoreInstanceState() of MyCustomView
#Override
protected Parcelable onSaveInstanceState() {
debug("onSaveInstanceState()");
Bundle state = new Bundle();
state.putParcelable(_BUNDLE_KEY_PARENT_STATE, super.onSaveInstanceState());
state.putBooleanArray(_BUNDLE_KEY_CLICKED_VIEWS, mClickedViews);
return state;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
debug("onRestoreInstanceState(Parcelable state)");
if (state instanceof Bundle) {
Bundle bundle = (Bundle) state;
mClickedViews = bundle.getBooleanArray(_BUNDLE_KEY_CLICKED_VIEWS);
state = bundle.getParcelable(_BUNDLE_KEY_PARENT_STATE);
}
super.onRestoreInstanceState(state);
}
View state restoration begins at the root view and moves down to all of the child views attached at that time. This can be seen in the ViewGroup.dispatchRestoreInstanceState method. This means that Android can only restore your views if they are part of the view hierarchy at the time Activity.onRestoreInstanceState is called.
Using the AsyncTask, you are creating your views asynchronously and then scheduling them to be added some time later when the main looper is idle. Considering the lifecycle, Android only lets your AsyncTask.onPostExecute run after Activity.onStart, Activity.onRestoreInstanceState, Activity.onResume, etc. are called. Your views are being added to the layout too late for automatic restoration to take place.
If you add log statements to those methods mentioned above, as well as to your AsyncTask.onPostExecute, you will be able to see how the ordering/timing plays out in reality. The following code runs after Activity.onRestoreInstanceState even though it all happens on the main thread, simply because of the scheduling:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
new Handler().post(new Runnable() {
#Override
public void run() {
Log.i("TAG", "when does this run?");
}
});
...
}
Smells like a false observation... creating a view on a background thread should not affect your activity lifecycle.
That said, doing anything at all with View objects on background threads is a no-no and I'm surprised you got this far with such an approach. All View code should be quick and avoid blocking. If you have long-running work to do then separate that work into the background thread, post the results of that complex computation to the main thread, and keep all the actual View/presentation stuff on the main thread where it belongs.
I remember having read about how onSaveInstanceState()/onRestoreInstanceState() work a time ago and it was not a "fixed-situation-rules" thing. As I can't find the references to it at this moment, I'll try to explain it with my own words:
Basically, a factor on which depends the calling of those methods is the resources left that the device has. Both methods will get in action when a second Activity gets focused and the first one gets killed due to lack of resources. Concretely, onRestoreInstanceState should be triggered when that Activity was killed and restarted, so it gets the previous state.
Although you've not posted your MyCustomView implementation, my guess is that when you do that entirely on the main UI Thread, you're involving some action that makes the MainActivity lose its focus and once the MyCustomView is created, it needs to restore its state. Doing this in a separate thread (as AsyncTask does) makes creating those Views in paralell, so your main Activity doesn't lose its focus and thus it doesn't get killed, so those methods are not called.
Concluding this, don't worry if those methods are not always called as they don't have to be called everytime, just when needed, and that doesn't mean there's something going wrong.
I recommend you to connect SDK sources to your IDE and walk there with debugger through the whole process related with the View class onRestoreInstanceState(). Since I don't have your code, looking at sources I can only guess what might have gone wrong, but from what I see, that might be related to problem:
1) try to set an Id to every view you generate
2) try to use Fragment as a host to your views (instead of MainActivity).
My Activity in onCreate() performs long computations that take some time.
In the same onCreate() I call setContentView() to set the appearance of the activity.
The point is that, since it takes a while to performs the above mentioned computations the screen of the Activity loads only after long time.
Please, any suggestion on how to avoid this?
I have tried to call setContentView() in onCreate() and start the computations in onResume(), but again the Activity screen is loaded only at the end.
There is no other way than to use e.g. an AsyncTask. The reason is that the actual rendering does not take place asynchronously; in other words, setContentView will only set some data but nothing will be displayed at that point in time.
AsyncTask, however, is not necessarily meant for "long" computations. But if your app relies on the result, and no other computations take place in parallel, it may still be the simplest way for you to achieve what you want. If not, you may have to use a Thread even.
Update Since everybody keeps bombarding the original poster with more use AsyncTask answers of various quality, I'd like to stress one more time that AsyncTask is intended for short operations (to quote the reference: a few seconds at the most) while the OP has given no indication on how long his computations really take. Also, an AsyncTask is a one-shot-only object which can only run once.
One more very important point to consider is the following. Android assigns AsyncTask a background task priority. This means that, besides the lower scheduling priority, the computations in AsyncTask will take ten times as long as if they were performed in the foreground, because Android runs all tasks which have background priority with an artificial limit of 10% CPU cycles. However, AsyncTasks can be lifted out of this group by raising its priority "just a little bit". For an AsyncTask, it would be done like so:
public R doInBackground(I... is) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND +
Process.THREAD_PRIORITY_MORE_FAVORABLE);
...
}
You have to implement AsyncTask
public class AsyncTaskActivity extends Activity implements OnClickListener {
Button btn;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button) findViewById(R.id.button1);.
//because we implement OnClickListener we only have to pass "this" (much easier)
btn.setOnClickListener(this);
}
public void onClick(View view){
//detect the view that was "clicked"
switch(view.getId())
{
case R.id.button1:
new LongOperation().execute("");
break;
}
}
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
for(int i=0;i<5;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return "Executed";
}
#Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed"); // txt.setText(result);
//might want to change "executed" for the returned string passed into onPostExecute() but that is upto you
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
If all computations take long time to execute, your UI is 'locked' and not updated.
You need to do all long work in an AsyncTask
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
call setContentView(layoutID) in onCreate method before you start initializing views, create a AsyncTask and start AsyncTask thread after you called setContentView in onCreate method only. Something like given below
onCreate(....){
--
--
setContentView(layoutID);
---
--
new asynchTask(); // load your ui in AsyncTask by creating an inner class in your activity by extending AsyncTask class
}
here is a tutorial of how to implement AsyncTask
Right now I have my ApplicationActivity, this activity is responsible for managing multiple views (GLSurfaceViews). Can / Should I have all the views set the renderer to a "global" renderer?
Code:
public class ApplicationActivity extends Activity
{
private static final String TAG = ApplicationActivity.class.getSimpleName();
private final Stack<Screen> screens = new Stack<Screen>();
private GlRenderer glRenderer;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.d(TAG, "Main Activity Created");
setupGraphics();
ChangeScreen(new MainMenu(this, glRenderer)); //Creating a new Screen sets the renderer
}
private void setupGraphics()
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
glRenderer = new GlRenderer(this);
}
public void Draw() //Is called by the glRenderer onDrawFrame() { mainActivity.Draw() }
{
}
}
Its the same activity switching between GLSurfaceViews and by my knowledge I believe that the method setRenderer sets the view renderer and then starts the rendering thread (creating a new thread) but I don't want to recreate the thread every time I switch between views - may create potential problems.
So in the end I want a Renderer class just to keep graphics sepreate from business logic and such but, I don't know if using one Renderer is even possible, without setting the thread again?
You can only use Multiple Views with the same Renderer only if you properly switch out between them with GLSurfaceView.onPause() / .onResume();
My specific case:
#Override
protected void onPause() //Overrides onPause from Activity
{
surfaceViews.peek().onPause();
super.onPause();
}
So everytime the activity pauses I would have to pause the current View. And if the Activity resumes then resume the View also.
I also have a method called SetView which will either (pause and remove then change to another View) or (pause and then change to another View) this is accomplished using a Stack
public void SetView(View screen)
{
if (!screens.empty())
{
screens.peek().onPause();
screens.pop();
}
screens.push(screen);
setContentView(screens.peek());
}
Of course though because we are using Views instead of Activities now we must Override the onBackPressed() to go back to previous Views.
#Override
public void onBackPressed()
{
if (screens.size() == 1)
super.onBackPressed();
else
{
screens.pop();
setContentView(screens.peek());
screens.peek().onResume();
}
}
By doing new GLRenderer() you create new instance of your class. So there is no problem to have the same renderer used in different activities.
EDIT: I seem to misunderstand your question - if you want many GL surfaces visible at once, then no, it is not possible. But it got nothing to do with reusing renderer code.
i have an activity or screen which contains mainly 3 views (ex.textview1,textview2,textview3) and a progress bar.
While progress bar running or loading if i click on either of textview1,textview2,textview3,
corresponding next activity or screen is opening.
But i want like while progress bar running relevant screen should disable(not tappable) or should not move to next screen if i click on any of these views.
please reply with an example
Just create a boolean for when the you have something in progress and then check for it in you onClickListener.
boolean isRunning;
#Override
public void onClick(View v) {
if(isRunning)
return;
//handle code normally
}
You can use AsyncTask class to do this
FYI, AsyncTask is known as Painless Threading in Android.
onCreate() {
...
MyTask task = new MyTask();
task.execute();
}
class MyTask extends AsyncTask
{
onPreExecute() {
// disable your views here
}
doInBackground() {
// do your loading here in background
}
onPostExecute() {
// enable your views to start activities
}
}