In my android application, on a certain activity I need to create screenshots of views without actually displaying them. I have been successful in achieving this by inflating the views and saving them as bitmaps.
But in some scenarios the number of these bitmaps is large enough and it takes a lot of time to create them. As such the UI on the phone becomes non responsive. Is there any way I can do this whole process in the background? I have already tried implementing it in an Async Task but that does not work because its not allowed to inflate views in an Async task.
Any suggestions are highly appreciated.
AsyncTask doBackground method works on another Thread, That's the reason you are not able to inflate the views.
First whether u have one layout or many. If you have many then try below.
I have not test this. Just a sample for you.
public class Task extends AsyncTask<Void, Integer, Void>
{
private ArrayList<Integer> layoutIds;
private View currentView;
private LayoutInflater inflater;
private Object lock = new Object();
public Task(Context context) {
super();
inflater = LayoutInflater.from(context);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... params) {
Bitmap temp;
for (int i = 0; i < layoutIds.size(); i++) {
temp = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
Canvas canvas = new Canvas(temp);
synchronized (lock) {
publishProgress(i);
try {
// Wait for the UI Thread to inflate the layout.
lock.wait();
}
catch (InterruptedException e) {
}
}
currentView.draw(canvas);
// Now save this bitmap
try {
FileOutputStream stream = new FileOutputStream(new File(Environment.getExternalStorageDirectory(), "File_" + i + ".png"));
temp.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.flush();
stream.close();
} catch (Exception e) {
}
finally
{
if(temp != null)
{
temp.recycle();
temp = null;
}
}
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
synchronized (lock) {
currentView = inflater.inflate(layoutIds.get(values[0]), null);
// Notify async thread that inflate is done.
lock.notifyAll();
}
}
}
EDITED
Here we have two thread one is AsyncTask which is a Thread Pool and another is UI Thread.
With synchronized block we could make sure that only one thread could use the object lock as long as it is not in sleeping or waiting for another thread.
So if one thread is executing the block inside synchronize then it will start monitoring that object and make sure no other thread which also has a synchronize block for that object will be executed. i.e., another thread has to wait for as long as the active thread goes to sleep or completed its execution inside synchronized block.
For more explanation, See this
Here, we used the synchronize block to wait for UI thread to complete.
So as it execute lock.wait(), the AsyncThread will wait till another thread calls notify on the same object. And when lock.notifyAll() is called all the thread (AsyncThread) which are waiting will be resumed.
AsyncTask is divided to onPreExecute(), onProgressUpdate() and onPostExecute(), all happens in the main UI thread allowing you to inflate the view. Only doInBackground() is where things actually happen in the other thread. If you can do al your calculation on this method and only inflate onProgressUpdate() or onPostExecute() it might help.
Also if you can give us your code it might help us to find a way to make it more CPU efficient.
Anyhow, android will try to force close your app if the UI thread isn't responding for more than 5 seconds, and theres isn't much to do about it (As far as I know).
Related
I am Recording Framelayout using AsyncTask for a certain amount of time. (suppose 5 seconds)
The code is working fine but it's making my activity freeze.
private class AsyncTask extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... strings) {
try {
framlayout.setDrawingCacheEnabled(true);
for (int i = 0; i < 5; i++) {
Bitmap bitmap = Bitmap.createBitmap(framlayout.getDrawingCache());
// collecting bitmaps and at the end making video
}
framlayout.setDrawingCacheEnabled(false);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
There is no issue with the code for creating video
My issue is activity is freezing even if I am using Asynctask
Please help
Thank you in advance.
this line do its job in UI thread, as framlayout (probably) belongs to some Activity or Fragment
framlayout.setDrawingCacheEnabled(true);
I'm surprised that this doesn't throw "wrong non-UI thread" exception, but on the other hand it doesn't change any param of this View...
setDrawingCacheEnabled itself makes View generate bitmap (save/store some byte-array) with own look, this is heavy and may provide some small freeze
if you need this print screen keep drawing cache enabled from start, don't toggle this on runtime (from UI or any other thread)
use this code and manage your loop inside it
var exec = Executors.newScheduledThreadPool(1)
var check = 0
exec!!.scheduleAtFixedRate({
if (check < 5) {
Bitmap bitmap =Bitmap.createBitmap(framlayout.getDrawingCache());
}
check++
}, 0, 100, TimeUnit.MILLISECONDS)
I am trying to write a simple app example that will run through a for loop incrementing its counter by 1 each time and then use the current value of the counter i to update the view and print out:
"i = #"
I get an error saying you can't update a view that was not created in that thread. i tried to address this by inflating the view from within the thread and also by creating a new TextView and calling "setContentView(myTextView)". but neither of these fixed the problem.
I tried a different version that used an AsyncTask but I got stuck on how to divid up the code into each of AsyncTask's methods. could someone help me see how to do this as it has shown me I am missing in my understanding on this point.
Thanks
Edward
ps. the commented out lines for the inflater and the setContentView are from my attempts to fix it.
my code from my original attempt that is trying to update my TextView "myTextView" in the main layout for the app:
public void loopForever(View view) {
Thread thread = new Thread(){
public void run(){
// LayoutInflater inflater = null;
// inflater.inflate(R.layout.activity_main, null);
TextView myTextView;
myTextView = (TextView) findViewById(R.id.myTextView);
// setContentView(myTextView)
for(int i = 0; i < 1000; i++){
myTextView.setText("i = " + i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
You can only access View elements from within the UI Thread (Activities, fragments etc. callbacks). You could either switch to an Asynctask and do the UI changes via the postexecute or publish progress callbacks (http://developer.android.com/reference/android/os/AsyncTask.html), or use runOnUiThread, example:
public void loopForever(View view) {
Thread thread = new Thread(){
public void run(){
for(int i = 0; i < 1000; i++){
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
(TextView) activity.findViewById(R.id.myTextView).setText("i = " + i);
}
});
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
As the other users have stated, you cannot update user interface elements from any thread other than the main thread. You should use the view class post() or postDelayed() method, your code should look something like this:
for(int i = 0; i < 1000; i++){
myTextView.postDelayed(new Runnable(){
public void run(){
myTextView.setText("i = " + i);
}
}, 1000);
}
here is a link:
http://developer.android.com/reference/android/view/View.html#post(java.lang.Runnable)
You might also want to take a look at android async task class
http://developer.android.com/reference/android/os/AsyncTask.html
You must update User Interface (UI) elements, like your TextView, from the "UI Thread". You cannot update them from other threads such as the one you have made.
This Android lesson might be useful to read:
Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread. Because tasks that you run on a thread from a thread pool aren't running on your UI thread, they don't have access to UI objects. To move data from a background thread to the UI thread, use a Handler that's running on the UI thread.
If you use AsyncTask, you can override the onProgressUpdate(Progress...) method to update your TextView. onProgressUpdate(Progress...) deliberately runs on the UI thread to allow this.
Basically, do I have to put code I want to run on another thread inside doInBackground, or can I call another function/class/whatever-it-is-functions-are-called-in-JAVA within doInBackground and have it run asynchronously? IE: (example code I found online)
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();
}
}
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
return null;
}
is how I have seen it done, but can I instead do:
protected String doInBackground(String... params) {
postToServer(x,y,z,h);
}
and have it call a function I already wrote and then have that function run in another thread? Sometimes my HTTP server is a bit slow to respond (it is but a lowly testing server at the moment) and Android automatically pops up the kill process box if my postToServer() call takes more than 5 seconds, and also disables my UI until the postToServer() call finishes. This is a problem because I am developing a GPS tracking app (internally for the company I work for) and the UI option to shut the tracking off freezes until my postToServer() finishes, which sometimes doesn't ever happen. I apologize if this has been answered, I tried searching but haven't found any examples that work the way I'm hoping to make this work.
You can do that, but you will have to move the UI updates to onPostExecute as it is run on the UI thread.
public MyAsyncTask extends AsyncTask<foo, bar, baz> {
...
protected String doInBackground(String... params) {
postToServer(x,y,z,h);
}
protected void onPostExecute(Long result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
}
....
}
You may want to pass in the TextView to the constructor of the AsyncTask and store it as a WeakReference.
private final WeakReference textViewReference;
public MyAsyncTask(TextView txt) {
textViewReference = new WeakReference<TextView>(txt);
}
And then in onPostExecute you would make sure that the TextView reference still exists.
protected void onPostExecute(Long result) {
TextView txt = textViewReference.get();
if (txt != null)
txt.setText("Executed");
}
If you want to notify the user that the task is executing I would put that before invoking the AsyncTask.
myTextView.setText("Update in progress...");
new MyAsyncTask().execute();
then in onPostExecute set the TextView to say "Update complete."
Have you tried it the second way?
From what you've posted it seems like it should work fine how you have it in the second example.
However (perhaps unrelated to your question?) in your first example I think it will fail because you are trying to change the UI from a background thread. You'd want to put the parts that manipulate the TextView inside of onPostExecute() rather than doInBackground()
Yes you can, the call to your postToServer method (that's the name in java) will run off the main thread.
Everything inside the doInBackground method of an AsyncTask is run on a pooled thread, but be sure to NOT invoke it directly! Call execute on your asynktask instead, the android framework will do the work for you and run doInBackground on another thread.
try doing something like this:
new AsyncTask<Void, Void, Void>() {
#Override
// this runs on another thread
protected Void doInBackground(Void... params) {
// assuming x, y, z, h are visible here
postToServer(x, y, z, h);
return null;
}
#Override
// this runs on main thread
protected void onPostExecute(Void result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
}
}.execute(); // call execute, NOT doInBackGround
Also, notice that every other method of AsyncTask, such as onPostExecute runs on the main thread, so avoid heavy loading them.
Basically The Bottom Line Is the doInBackground() method is Can't interact with The Ui Thread Or The Main thread. that's Why When You are Try To Interact With The TextView in doInBackground () it Will Crash the UI Thread Cuz It's Illegal.
so if anytime You want to Interact with the UI Thread,When You are Working on doInBackground You need to Override
OnPostExecute() //this Function is Called when The doInBackground Function job is Done.
So You can Update The UI Thread Content By this When You're Job is Done In doInBackground () or You are In doInBackground ()
I have a View that recieves swipe gestures.
this should trigger the creation and setContentView(layout) for the main activity.
(the View is within a Layout on the Main activity)
When i try to do this within an asynctask it leaves out all the images for the layout?
as if it hasn't finished yet .
There is prolly something i'm not fully understanding .
this the partial code from the view.java
Main.mHandler.post(new Runnable()
{
public void run()
{
new Main.GetNavigationLayout().execute(url);
}
});
the url is a location of an .xml file I use to create a new layout.
GetNavigationLayout is the AsyncTask
code that is the AsyncTask in Main.java :
public static class GetNavigationLayout extends AsyncTask<String, Void, CustomLayout> {
boolean isDone = false;
#Override
protected EvadoLayout doInBackground(String... url)
{
CustomLayout layout = new CustomLayout(Main);
Looper.prepare();
try
{
String newpage = url[0];
Document doc = XmlImporter.GetNewPlayer(newpage);
if (doc == null)
{
layout = null;
}
layout.LoadXml(doc); // cause drawing of objects etc.
}
catch (Exception e)
{
e.printStackTrace();
}
isDone = true;
//Looper.loop(); // causes it to never return...
return layout;
}
#Override
protected void onPostExecute(CustomLayout layout)
{
if (isDone)
Main.setContentView(layout);
}
}
now this shows everythig besides images , whereas if i run this without AsyncTask it displays everything in the layout.
what am i missing?
layout.LoadXml(doc); // cause drawing of objects etc.
I reckon you are drawing the images here? I think this is the problem. Trying to draw from the AsyncTask's doInBackground() is wrong, since it is not the UI thread. You should do the drawing in onPostExecute() which runs in the UI thread
You can post to the UI in the onProgressUpdate(). Using this method will allow you to stay inside the doInBackground and post an update to the UI when you get a layout and continue inside the doInBackground. Use publishProgress() to send information from the doInBackground to the UI thread.
I wanted to use AsyncTask to load images to the ListView.
private class LoadImageTask extends AsyncTask<HashMap<String,Bitmap>,Void,Bitmap>{
#SuppressWarnings("unchecked")
#Override
protected void onPostExecute(Bitmap result) {
if(model.getIconCache().get(cellIcon)!=null){
icon.setImageBitmap(model.getIconCache().get(cellIcon));
}else{
new LoadImageTask().execute(model.getIconCache());
}
}
#Override
protected Bitmap doInBackground(HashMap<String, Bitmap>... params) {
//return model.getIconCache().get(cellIcon);
return null;
}
}
Ok, I know that this not an affective code. However it works well but with a lot of memory allocation. When reading the documentation about AsyncTask it said that Asynctask can be called only from UI thread, how could it let to use inside itself? And of course I want to make my code work inside a single AsyncTask. "model" in the code is an object that is updated at runtime through another thread. So I need to find a way to use a single Asynctask with periodically control the state of an object. How do I do that? Thanks
only do in backGround runs on backGround thread and postExecute and preExecute run on UI thread itself.. For the same reason u can show and dismiss dialogs in it..
if u want to use single Asynctask for multiple purpose u can play around by passing Different constants.. in .execute() method..
I mean something like this.
Integer Constant1 = 1;
int Constant2 = 2;
and while calling,,
new Background.execute(Constan1 or 2)
and in AsyncTask
doInBackground(Object.. arg0)
{
if(arg0.equals())
{
}
else if(arg0.equals())
{
}
}
Take a look at the asynctask documentation: http://developer.android.com/reference/android/os/AsyncTask.html
private class MyTask extends AsyncTask<Void, Integer, Boolean> {
protected Boolean doInBackground(Void...) {
int i = 0;
while(true){
publishProgress(i++);
}
}
protected void onProgressUpdate(Integer... progress) {
myObject.setState(progress[0]);
}
}
You do your background stuff in the doInBackground method (which runs in the background thread).
You control the state of your object in the onProgressUpdate (which runs on the ui thread)
You send messages between the background thread and the ui thread using publishProgress. These messages could contain the state of your object.