📜 ⬆️ ⬇️

Processes and threads in Android: we write AsyncTask correctly

I continue my stories about Android. And this time I want to share valuable information about processes and threads, which should be well understood and always be on hand to avoid mistakes and misunderstandings when writing applications. At the end of the article I will give an example of the implementation of AsyncTask, which loads the image into ImageView by pressing a button.

First of all, I’ll note that more details on this topic can be found in this tutorial - developer.android.com/guide/topics/fundamentals/processes-and-threads.html

A note about processes and threads in Android

When an application component starts and the application has no other components running, Android creates a new process for an application with a single thread of execution. By default, all components of a single application run in the same process, in a thread called the “main” one. If the application component starts and a process already exists for the application (some component from the application exists), then the component is started in this process and uses its execution flow. You can change this behavior by specifying different processes for different components of your application. In addition, you can add threads to any process.
')
You can define a separate process for a component using the manifest file. Each component tag (activity, service, receiver and provider) supports the android: process attribute. This attribute allows you to specify the process in which the component will run. You can also specify the process in which components of different applications will run. This attribute is also supported by the application tag, which allows you to define a specific process for all application components.

Android tries to maintain the application process for as long as possible, but when resources are required, old processes will be superseded by importance hierarchy.

There are 5 levels of importance hierarchy: (the processes of the first level from the list will be deleted last)

1. Process with which the user interacts (Foreground process)
Such processes include for example: activit with which it interacts to use; the service (Service instance) with which the user interacts; service started by startForeground () method; a service that performs one of the methods of its life cycle; BroadcastReceiver which executes the onReceive () method.

2. Visible process
A process in which the conditions from clause 1 are not met, but which influences what the user sees on the screen. For example, the onPause () method is activated.

3.Service process
Service started by startService () method

4. Background process
A process running in the background that is invisible to the user.

5. Empty process

I note that there is an onLowMemory () method in the application components, but it’s impossible to rely on this method to be called, it’s also impossible to rely on the onDestroy () method 100%, so the logic of storing data or any settings can be implemented in onStop (), which (as they assure) is exactly called.

When an application is launched, the system creates a “main” thread of execution for this application, also called a UI thread. This stream is very important, since it is in it that the widgets (buttons, lists) are rendered and the events of your application are processed. The system does not create a separate thread for each component instance. All components that are running in the same process will be created in the UI thread. The Android UI library is not thread safe, so two important rules must be followed:

1) Do not block the UI thread
2) Do not access components of the user interface from outside the UI thread.

Now, suppose that we had a task - to upload a picture to the ImageView from the network and immediately display it. How will we proceed? Logically, we will create a separate thread that will do all our work, like this:
public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork("http://example.com/image.png"); mImageView.setImageBitmap(b); } }).start(); } 

It looks believable, since we rendered the operation of loading the image into a separate stream. The problem is that we have violated rule number 2. You can fix this problem using the following methods:

Activity.runOnUiThread (Runnable)
View.post (Runnable)
View.postDelayed (Runnable, long)

For example, we will use the first of them:
 public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(bitmap); } }); } }).start(); } 

Now the implementation is thread-safe: the network operation is performed in a separate thread, and the ImageView is accessed from the UI stream.
Fortunately, these operations can be combined with the help of inheriting the Handler class and implementing the necessary logic, but the best solution is to inherit the AsyncTask class.

AsyncTask allows you to perform asynchronous work and make user interface updates.
To update, implement the onPostExecute () method, and enclose all background work in the doInBackground () method. After you implement your own task, you need to run it with the execute () method.

I give the promised example of AsyncTask, which implements the task of loading and displaying a picture (a variant with annotations and deviations from the use of the standard dialog protocol):
 @EActivity(R.layout.main) public class DownloadImageActivity extends Activity { ProgressDialog progress; @ViewById ImageView image; @ViewById Button runButt; String pictUrl = "http://www.bigfoto.com/sites/main/churfirsten_switzerland-xxx.JPG"; @BeforeCreate void getProgressDialog(){ progress = new ProgressDialog(this); progress.setMessage("Loading..."); } @Click(R.id.runButt) void runClick(){ new DownloadImageTask().execute(pictUrl); } class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { @Override protected Bitmap doInBackground(String... params) { publishProgress(new Void[]{}); //or null String url = ""; if( params.length > 0 ){ url = params[0]; } InputStream input = null; try { URL urlConn = new URL(url); input = urlConn.openStream(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return BitmapFactory.decodeStream(input); } @Override protected void onProgressUpdate(Void... values) { super.onProgressUpdate(values); progress.show(); } @Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); progress.dismiss(); image.setImageBitmap(result); } } } 


And now we will consider the most correct option from the point of view of working with dialogues:
 public class DownloadImageActivity extends Activity { ImageView image; Button runButt; final static String PICT_URL = "http://www.bigfoto.com/sites/main/churfirsten_switzerland-xxx.JPG"; final int PROGRESS_DLG_ID = 666; final static String DEBUG_TAG = "+++ImageDownloader+++"; @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); loadViews(); } private void loadViews(){ runButt = (Button)findViewById(R.id.runButt); image =(ImageView)findViewById(R.id.image); } @Override protected Dialog onCreateDialog(int dialogId){ ProgressDialog progress = null; switch (dialogId) { case PROGRESS_DLG_ID: progress = new ProgressDialog(this); progress.setMessage("Loading..."); break; } return progress; } public void runButtonHandler(View button){ if(button.getId() == R.id.runButt) new DownloadImageTask().execute(PICT_URL); } class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { @Override protected Bitmap doInBackground(String... params) { publishProgress(new Void[]{}); String url = ""; if( params.length > 0 ){ url = params[0]; } InputStream input = null; try { URL urlConn = new URL(url); input = urlConn.openStream(); } catch (MalformedURLException e) { Log.d(DEBUG_TAG,"Oops, Something wrong with URL..."); e.printStackTrace(); } catch (IOException e) { Log.d(DEBUG_TAG,"Oops, Something wrong with inpur stream..."); e.printStackTrace(); } return BitmapFactory.decodeStream(input); } @Override protected void onProgressUpdate(Void... values) { super.onProgressUpdate(values); showDialog(PROGRESS_DLG_ID); } @Override protected void onPostExecute(Bitmap result) { super.onPostExecute(result); dismissDialog(PROGRESS_DLG_ID); image.setImageBitmap(result); } } } 

The code has become bigger, but it is better to use the standard protocol for working with dialogs.
I also removed all the annotations to make it easier for newbies to try this code.

Do not forget to add an attribute with the specified value to your button markup: android: onClick = "runButtonHandler"

And I will add: in the official document ( tyts ) as well as in my case, preExecute () is not used, but if you need to perform some actions with your user interface before starting the task, then boldly use this method.

Parameters passed to AsyncTask:
1. Parameters (in our case, the URL).
2. Progress (units defining the course of the task change). In our case, not used.
3. The result of the task (in our case, the object Bitmap)

The code is pretty simple: we do all the background work in the doInBackGround () method, calling the publishProgress () method so that our ProgressDialog spins when the image is loaded when the onProgressUpdate () method is called. After performing the background work, the onPostExecute () method is called into which the result of the doInBackGround () method is transferred, in our case it is a Bitmap object and right there we load it into the ImageView.
I will note a couple of important points that need to be considered:

1) The doInBackGround () method runs in the background thread, so there is no access to the UI stream within this method.
2) The onPostExecute () and onProgressUpdate () methods are executed in the UI thread, so we can safely refer to our UI components.

Conclusion

Yes, I applied the android-annotations library again, so don't be scared by annotations.

I want to note the importance of understanding the process model in Android, in order to prevent mistakes when developing applications.

This article is the processing of available information + personal experience in the implementation of this task and work with streams.

As always, suggestions and comments about the material of the article in a personal. If you have something to add or supplement - feel free to write in the comments.

UPD The article is updated by adding a more accurate version in terms of working with dialogs.

Source: https://habr.com/ru/post/124484/


All Articles