Search code examples
androidandroid-asynctasklayout-inflater

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.


Solution

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