CPUs with multiple cores have almost become the standard in Android devices. This means multiple operations doesn’t have to wait to be executed on a single thread. They can rather run on multiple threads in parallel where each threads gets executed on a single processor.
For example imagine an operation where multiple images have to be downloaded. The downloads and any decoding required for different images could happen concurrently on multiple threads speeding up the entire operation, leading to a faster app experience.
Android has an Executor framework using which you can automatically manage a pool of threads with a task queue. Multiple threads will run in parellel executing tasks from the queue. This article mainly focuses on using the ThreadPoolExecutor class mainly.
ThreadPoolExecutor
Let’s see how most basic constructor looks like:
ThreadPoolExecutor executor = new ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue );
The parameters are
corePoolSize – Minimum number of threads to keep in the pool. Initially there are zero threads in the pool, but as tasks are added to the queue, new threads are created. If there are idle threads but the thread count is lower than the corePoolSize, then new threads will keep on being created.
maximumPoolSize – Maximum number of threads to allow in the pool. If this is more than the corePoolSize and the current number of threads is >= corePoolSize, then new worker threads will be created only if the queue is full.
keepAliveTime – When number of threads is greater than the core, the noncore threads (excess idle threads) will wait for time defined by this parameter for new tasks before terminating.
unit – Time unit for keepAliveTime.
workQueue – The task queue which’ll only hold Runnable tasks. It’ll have to be a .BlockingQueue.
Now Let’s see how the implementation goes.
Here I am downloading images from two URLs.
Just to make it simple I am repeating two URLs.
Here I have a Runnable Class that downloads the Image.
I am just using “InputStream” to download the image, You can modify it the way you want.
LongThread.java
package com.coderzheaven.multiplethread; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.os.Message; import android.util.Log; import java.io.InputStream; public class LongThread implements Runnable { int threadNo; Handler handler; String imageUrl; public static final String TAG = "LongThread"; public LongThread() { } public LongThread(int threadNo, String imageUrl, Handler handler) { this.threadNo = threadNo; this.handler = handler; this.imageUrl = imageUrl; } @Override public void run() { Log.i(TAG, "Starting Thread : " + threadNo); getBitmap(imageUrl); sendMessage(threadNo, "Thread Completed"); Log.i(TAG, "Thread Completed " + threadNo); } public void sendMessage(int what, String msg) { Message message = handler.obtainMessage(what, msg); message.sendToTarget(); } private Bitmap getBitmap(String url) { Bitmap bitmap = null; try { // Download Image from URL InputStream input = new java.net.URL(url).openStream(); // Decode Bitmap bitmap = BitmapFactory.decodeStream(input); // Do extra processing with the bitmap } catch (Exception e) { e.printStackTrace(); } return bitmap; } }
Now the MainActivity that implements this class using ThreadPoolExecutor
MainActivity.java
package com.coderzheaven.multiplethread; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.widget.ProgressBar; import android.widget.TextView; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class MainActivity extends AppCompatActivity implements Handler.Callback { private Handler handler; TextView tvStatus; int curCount = 0; ProgressBar progressBar; String url1 = "YOUR_IMAGE_URL1"; String url2 = "YOUR_IMAGE_URL2"; float totalCount = 50F; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvStatus = (TextView) findViewById(R.id.tvDownloadCount); progressBar = (ProgressBar) findViewById(R.id.progressBar); int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); ThreadPoolExecutor executor = new ThreadPoolExecutor( NUMBER_OF_CORES * 2, NUMBER_OF_CORES * 2, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() ); for (int i = 0; i < totalCount; i++) { String imageUrl = (i % 2 == 0) ? url1 : url2; executor.execute(new LongThread(i, imageUrl, new Handler(this))); } tvStatus.setText("Starting Download..."); } @Override public boolean handleMessage(Message msg) { curCount++; float per = (curCount / totalCount) * 100; progressBar.setProgress((int) per); if (per < 100) tvStatus.setText("Downloaded [" + curCount + "/" + (int)totalCount + "]"); else tvStatus.setText("All images downloaded."); return true; } }
My Simple Layout will look like this.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.coderzheaven.multiplethread.MainActivity"> <TextView android:id="@+id/tvDownloadCount" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_margin="20dp" android:gravity="center" android:textSize="20sp" android:textStyle="bold" android:textColor="@android:color/holo_green_dark"/> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/tvDownloadCount" android:layout_centerHorizontal="true" /> </RelativeLayout>
You can download the complete Android Studio Source Code from here
If I want to send the downloaded file instantly ,when it is completely downloaded from one fragment to another what I have to do.how I can do it??
Ishrat, You can create an Interface class and pass it to the fragment where you want the callback. When the download completes, you can trigger the event with the interface object to pass the information to the second fragment.