Why MainActivity is Freezing even if i am using AsyncTask? - android

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)

Related

Android asynchronously fetch data from db

I need a safe practise to load data from the Database.
I need to load the data from the Sql db and put it into a list or recylerview.
So what i tryied, i created a class which call the method load more everytime when user reaches the end of the list. And than it load the data limited.
Like 'Select * From a Where 1 Limit listItemCount, 20'.
I run into problems with this because, the thread which was loading, started 2 times with the same count value. Than i have changed it to synch the global run method from the loading thread. And accessing the count from the background worker of the list. This helped me a lot, and the data was loaded correctly, but i got still troubles with this. Because i need to insert the data into listview from the background thread, because of the synchronisation and calling notify in main thread after. If i am not doing it like this, i will get still problem.
Thread A: Started work.
Thread B: Waiting for finishing thread A (synchronized)
Thread A: runOnUiThread() trying to fill data.
Thread B: Allowed to run the code, getting count from listView.
Thread B: Got the same count as A, because A not finished the insert
statement.
Thread A: Added data.
Thread B: Added same data.
After this i added a atomicboolean, that if the thread is running he is just not executed. With this it was working perfect, but sometimes you have to scroll up a bit and down to trigger the load more.
And i think this solution is a bit dirty for me. Anyone now how to load data in pieces depending on list size of the listview, in a background thread without this issues?
This method is getting called when reaching the end of list:
private ExecutorService executorService;
private void loadMore()
{
if(!isAdded())
return;
if(executorService == null)
executorService = Executors.newSingleThreadExecutor();
executorService.execute(new AsyncLoadMoreHashTags(getContext(), this, esaphTagAdapterVertical));
}
Background Thread:
public class AsyncLoadMoreHashTags extends MyDataLoaderClass
{
private static final Object obLock = new Object();
public AsyncLoadMoreHashTags(Context context, DataBaseLoadWaiter dataBaseLoadWaiter, SpecialRecylerView spRecylerView)
{
super(context, dataBaseLoadWaiter, spRecylerView);
}
#Override
public void run()
{
super.run();
synchronized (AsyncLoadMoreHashTags.obLock)
{
List<Object> objectList = null;
try
{
int startFrom = super.getStartFrom()[0];
SQLHashtags sqlHashtags = new SQLHashtags(super.getSoftReferenceContext().get());
if(startFrom <= 0)
{
objectList = sqlHashtags.getMostUsedHashtags();
}
else
{
objectList = sqlHashtags.getAllHashtagLimited(startFrom);
}
sqlHashtags.close();
}
catch (Exception ec)
{
Log.i(getClass().getName(), "AsyncLoadMoreHashTags run() failed: " + ec);
}
finally
{
publish(objectList);
}
}
}
}
What i need is to, just start one thread a time.
Anyone a idea?
First of all use You should have to use PagedList library provided by google, for implementing paging facility, and if you want to use background thread for database operations you can use Anko commons to do this. Here is an example,
internal val pagedList by lazy {
runBlocking { // Coroutine Context
withContext(Dispatchers.IO) {
// Read from database, and it will be initialised in background
}
}
}
and if you want something to be dispatched on UI but performed in Background Thread, then go with this, doAsync operation and then return data in uiThread by using a custom interface.
doAsync {
// LONG OPERATION
Thread.sleep(2000)
uiThread {
callback.onActionsDone(dataList)
}
}
Let me know if you want more help on this.
Fixed it with like this, working fine.
public class AsyncLoadMoreHashTags extends OwnDataLoader
{
private static AtomicBoolean obLock = new AtomicBoolean(false);
public AsyncLoadMoreHashTags(Context context, DataBaseLoadWaiter dataBaseLoadWaiter, MomentsRecylerView momentsRecylerView)
{
super(context, dataBaseLoadWaiter, momentsRecylerView);
}
#Override
public void run()
{
super.run();
if(!AsyncLoadMoreHashTags.obLock.compareAndSet(false, true)) //when any thread loading data, its return and skips.
return;
List<Object> objectList = null;
try
{
int startFrom = super.getStartFrom()[0];
SQLHashtags sqlHashtags = new SQLHashtags(super.getSoftReferenceContext().get());
if(startFrom <= 0)
{
objectList = sqlHashtags.getMostUsedHashtags();
}
else
{
objectList = sqlHashtags.getAllHashtagLimited(startFrom);
}
sqlHashtags.close();
System.out.println("TEST: " + startFrom + " LOAD: " + objectList.size());
}
catch (Exception ec)
{
Log.i(getClass().getName(), "AsyncLoadMoreHashTags run() failed: " + ec);
}
finally
{
publish(objectList, AsyncLoadMoreHashTags.obLock); //Passing the lock object, when data has been added, obLock.set(false); is called.
}
}
}

Trouble implementing a Slide Show from an ArrayList of Bitmap

I'm trying to implement a slide show for my App and I seem to be getting stuck on what seems like a small detail.
I'm new to Android and haven't been programming long so I'm not great with threading yet.
Anyways I have an ArrayList which is great. When I try to loop through the ArrayList replacing the images in my ImageView with images in the ArrayList however, I only end up seeing the final image in the List.
I can see in my LogCat that the images are being set (at least that section of the code is running. It is sleeping as I asked (I can notice the 1000ms in the LogCat entries.
I'm doing this in an AsyncTask and trying to set the results from the onPostExecute(). Here is that code...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_slide_show);
screenSize = MediaHelper.getScreenSize(this);
Bundle extras = getIntent().getExtras();
Long projectId;
imageList = new ArrayList<Bitmap>();
ivSlide = (ImageView)findViewById(R.id.iv_slide);
tvLoading = (TextView)findViewById(R.id.tv_loading);
tvLoading.setVisibility(View.INVISIBLE);
if(extras != null){
projectId = extras.getLong("projectId");
new GetImagesTask().execute(projectId);
}
}
class GetImagesTask extends AsyncTask<Long, Void, ArrayList<Bitmap>>{
#Override
protected ArrayList<Bitmap> doInBackground(Long... id) {
long projectId = id[0];
Project project = getProject(projectId);
ArrayList<Bitmap> i = new ArrayList<Bitmap>();
File[] files = MediaHelper.getProjectFilenames(project.getImagePath());
if(files != null && files.length > 0){
for(File aFile : files){
Bitmap bitmap = MediaHelper.getScaledImage(aFile, screenSize.y / 2, screenSize.x / 2);
i.add(bitmap);
Log.d(TAG, "image added...size = " + bitmap.getByteCount());
}
}
return i;
}
#Override
protected void onPostExecute(ArrayList<Bitmap> result) {
if(result != null && result.size() > 0){
imageList = result;
if(imageList.size() > 0){
ivSlide.post(new Runnable(){
#Override
public void run(){
try{
for(Bitmap bm : imageList){
ivSlide.setImageBitmap(bm);
Thread.sleep(1000);
Log.d(TAG, "Setting ImageView image");
}
}catch (InterruptedException e){
Log.d(TAG, "Thread interrupted unexpectedly.");
}
}
});
}
}
Any help is much appreciated!!
I haven't used View.post(Runnable) before so I'm not sure about its behaviour but if it's not running on the UI Thread that "setImageBitmap" is not applied instantly, I think it's only applied when some other thread asks your view to reload its layout.
If it runs on the UI Thread, the Thread.sleep(1000) is asking the UI to stop working for a whole second, making the setImageBitmap effect impossible to notice to the user.
While facing a similar problem some time ago I used a Thread with a while in it that calls to runOnUiThread(Runnable) each time it needed to swipe the pictures. That way, the loading task was ran on the UI Thread and the waiting on a background one.
Hope it helps.
EDIT:
Could you try replacing your AsyncTask with this one, as suggested on my comment? gist.github.com/Arasthel/9788601
Your problem is likely Thread.sleep(). This will sleep the main thread and likely prevent the ui from updating till the last image when you don't sleep the thread.
Use Handler.postDelayed(Runnable, long) instead of sleeping the thread.
Obviously only the last item will be updated, Because you are running the loop and updating the same variable "ivSlide". For each iteration the same variable is getting updated with the different Bitmap at last it stops with the last Bitmap updated.
To overcome this you have to update each ivslide view variable with the arraylist index.
Please update full code so I can answer better.

Creating bitmaps from view in the background

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).

execute the runnable in a continuous loop

My code mainly decodes a series of bitmap objects to be displayed in an ImageView later. To get more control over the duration to display each image. So i implemented the code in a Runnable. Here is a snippet of that runnable.
final File file[]=dir.listFiles();
iv.postDelayed(
new Runnable() {
#Override
public void run() {
i++;
Bitmap bitmap = decodeFile(file[i]);
iv.setImageBitmap(bitmap);
iv.postDelayed(this, 5000);
}
}, 5000);
However, I don't seem to have a good control over the looping here. I am not able to use a for loop since "i" cant be assigned to zero in this case. This code works as long as the "file[i]" has a File reference to it. As soon as it displays all the images once, the app just force closes do to arrayindexoutofbounds exception. I just want to continue the loop infinitely. Any pointers?
Here decodeFile() calls another function to rescale the size of the bitmap.
I'm not really sure what you are doing, but why not use a while loop instead? Something like while (true) { } would cause the infinite loop.
sense you want to do some work that might take a while and you want do repeat it, try looking at an android component called service and send it the images by intent.
http://developer.android.com/guide/topics/fundamentals/services.html
probably an IntentService would be fine, and help alot
http://developer.android.com/reference/android/app/IntentService.html
I suggest an AsyncTask
File means a file array is given the task to be processed in the background.
Bitmap means you call publishProgress with your bitmap, you fetch it on the UI thread in onProgressUpdate
Void means you simply stop once it is done.
The good thing here is that the decoding is done on a background thread, but the iv.setBitmap is still called on the UI thread, which is mandatory.
Just reset to zero once you reach the end:
final File file[]=dir.listFiles();
iv.postDelayed(
new Runnable() {
#Override
public void run() {
if (i == file.length) {
i = 0;
}
else {
i++;
}
Bitmap bitmap = decodeFile(file[i]);
iv.setImageBitmap(bitmap);
iv.postDelayed(this, 5000);
}
}, 5000);

AsyncTask result differs from regular function

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.

Categories

Resources