Welcome to join the Learning wheels (Android) communication group

Group Name: Wheel Learning (Android) Group Number: 539175620





thread

In Android, threads are usually divided into two types, one of which is called Main Thread. All threads other than Main Thread can be called Worker Thread.

When an application is running, the Android operating system will give the application start a Thread, the Thread is our Main Thread, the Thread is very important, it is mainly used to load our UI interface, complete the interaction between the system and our users, and to show the results of interaction and to our users, So Main threads are also called UI threads.

Android doesn’t create an extra thread for our application components by default; all of them run in the same thread by default. However, sometimes when our application needs to perform a time-consuming operation, such as accessing the network or querying a database, our UI Thread will be blocked. For example, if we click on a Button and expect it to retrieve some data from the network, if this is done in the UI Thread, when we click on the Button, the UI Thread will be blocked, and our system will not schedule any other events. Worse, When our entire field is blocked for more than five seconds (officially), an ANR (Application Not Responding) occurs, and the Application pops up with a box that lets the user choose whether to quit the program. ANR is absolutely unacceptable for Android development.

In addition, since our Android UI controls are thread-safe, we cannot operate on our UI controls in threads other than the UI Thread. So there are two very important principles that we must follow in Android multithreaded programming:

  1. Do not perform time-consuming operations on the UI Thread or block our UI Thread
  2. We cannot manipulate our UI elements in threads other than UI Thread

Interthread communication

Now that there are two important principles to follow in Android, we might be wondering, right? We can’t handle time-consuming operations in the main thread, and we can’t access our UI controls in the worker thread, so how can we, say, download an image from the network and update it to the UI control? This concerns the communication between our main thread and the worker thread. In Android, there are two ways to handle direct thread communication. One is through the Handler mechanism, and the other is the AsyncTask mechanism, which will be explained in detail today.

AsyncTask

AsyncTask AsyncTask AsyncTask AsyncTask AsyncTask AsyncTask

AsyncTask enables proper and easy use of the UI thread. This class allows you to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate and onPostExecute.

It makes it much easier to create asynchronous tasks, eliminating the need to write task threads and Handler instances to accomplish the same tasks. An asynchronous task is usually a calculation that runs in the background and then sends the results to the UI main thread. Typically, asynchronous tasks are defined as three generic types: parameters, procedure, and result, and four steps: onPreExecute, doInBackground, onProgressUpdate, and onPostExecute.

How to understand AsyncTask? Generally speaking, AsyncTask is equivalent to Android providing us with a framework for multithreaded programming, which is between Thread and Handler. If we want to define an AsyncTask, we need to define a class to inherit the abstract class AsyncTask. And implement its only doInBackgroud abstract method. To master AsyncTask, we need a concept that can be summed up as three generics and four steps.

Three generic

What do three generics mean? When we define a class that inherits AsyncTask, we need to specify three generic parameters:

public abstract class AsyncTask<Params, Progress, Result>Copy the code

Params: This generic specifies the type of parameter we pass to the asynchronous task to execute. Progress: This generic specifies the type of parameter that our asynchronous task returns to the UI thread when it executes. Result: This generic specifies the type of results returned to the UI thread after the asynchronous task completes execution

Four steps

4 Steps: When we execute an asynchronous task, we need to follow the following 4 steps:

OnPreExecute (): This method is executed before the asynchronous task is executed, and is executed in the UI Thread. It is used to initialize UI controls, such as popups to ProgressDialog.

2, the doInBackground (Params… params): The onPreExecute() method is executed immediately after the onPreExecute() method is executed. This method is used to handle asynchronous tasks. The Android operating system starts a worker thread in the background thread pool to execute our method. Therefore, this method is executed in worker thread. After this method is executed, we can send our execution result to our last onPostExecute method. In this method, we can obtain data from the network and other time-consuming operations.

3, onProgressUpdate (Progess… Values): This method is also executed in UI Thread. When executing asynchronous tasks, we sometimes need to send the execution progress back to our UI interface. For example, when downloading a network picture, we need to display its download progress at any time, so that we can use this method to update our progress. Before calling this method, we need to call a publishProgress(Progress) method in the doInBackground method to pass our Progress to the onProgressUpdate method to update it from time to time.

4, onPostExecute (Result… Result: When our asynchronous task completes, we return the result to this method, which is also called in the UI Thread. We can display the result on the UI control.

Why does our AsyncTask abstract class only have a doInBackground abstract method? The reason is that if I want to do an asynchronous task, I have to create a new Thread for it to complete some operations, and to complete this asynchronous task, I probably don’t need to pop up a ProgressDialog, I don’t need to update my ProgressDialog progress bar, I also don’t need to update the results to our UI, so none of the three methods, other than the doInBackground method, are required, so the method we have to implement is the doInBackground method.

Examples demonstrate

Next, we will demonstrate the use of AsyncTask by downloading a network picture. First, the effect:





Second, let’s look at some code. In fact, the principle of the downloaded code is very simple, is to stream into a byte array, and then into a Bitmap.

// The progress box is displayed

        progressDialog = new ProgressDialog(MainActivity.this);
        progressDialog.setTitle("Warning message");
        progressDialog.setMessage("Downloading, please wait......");
        / / set setCancelable (false); It means we can't cancel the popup and make it disappear after the download is complete
        progressDialog.setCancelable(false);
        // Set the ProgressDialog style to horizontal
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);



/ / download classes
public class MyAsyncTask extends AsyncTask<String.Integer.Bitmap> {
        @Override
        protected void onPreExecute(a) {
            super.onPreExecute();
            // In onPreExecute() we make ProgressDialog visible
            progressDialog.show();
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            Bitmap bitmap = null;

            try {
                URL url = new URL(params[0]);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(5 * 1000);
                conn.setRequestMethod("GET");
                InputStream inputStream = conn.getInputStream();
                if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    int fileLength = conn.getContentLength();
                    ByteArrayOutputStream outStread = new ByteArrayOutputStream();
                    byte[] buffer = new byte[1024];
                    int length = 0;
                    long total = 0;
                    while((length = inputStream.read(buffer)) ! = -1) {
                        outStread.write(buffer, 0, length);
                        total += length;
                        if (fileLength > 0) {
                            publishProgress((int) (total * 100 / fileLength));
                        }
                    }

                    outStread.close();
                    inputStream.close();
                    byte[] data = outStread.toByteArray();
                    if(data ! =null) {
                        bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                    } else {
                        Toast.makeText(MainActivity.this."Image error!", Toast.LENGTH_LONG).show();
                    }
                    returnbitmap; }}catch (Exception e) {
                e.printStackTrace();
            }

            return null;

        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            // Update the ProgressDialog progress bar
            progressDialog.setProgress(values[0]);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            imageView.setImageBitmap(bitmap);
            try {
                saveFile(bitmap, "netpic.jpg");
            } catch(IOException e) { e.printStackTrace(); } progressDialog.dismiss(); }}// Execute the download in the UI main thread
String picUrl = "Http://img3.imgtn.bdimg.com/it/u=2437337628, & FM = 214 & 1430863508 gp = 0. JPG";

new MyAsyncTask().execute(picUrl);Copy the code

See github- Easy – Net package to learn the basic network request library for detailed code


And that’s basically the end of it. This is the basic way that the UI thread communicates with the Work thread using simple AsyncTask. Let’s take a closer look at the source code for AsyncTask.

Source code interpretation (based on API25)

Start with execute, the starting point of an asynchronous task:

//<p>This method must be invoked on the UI thread.
// This method must be called in the UI main thread.
@MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

// Jump to executeOnExecutor
@MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if(mStatus ! = Status.PENDING) {switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)"); }}// Set the current AsyncTask state to RUNNING
        mStatus = Status.RUNNING;

        // Still in the UI main thread, some initialization can be done at this time
        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }Copy the code

The code is relatively simple, and there are mWork and mFuture variables, which we will trace to investigate.

1. For mWork variables

private final WorkerRunnable<Params, Result> mWorker;

private static abstract class WorkerRunnable<Params.Result> implements Callable<Result> {
        Params[] mParams;
    }
// It is a subclass of Callable and contains an mParams to hold the parameters we passed in
// This is initialized in the AsyncTask constructor
mWorker = new WorkerRunnable<Params, Result>() {
            public Result call(a) throws Exception {
                // Set it to true, which is used next
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked 
                    // This is one of the four methods we use to get the result of the process
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {

                    // Send the execution result
                    postResult(result);
                }
                returnresult; }};Copy the code

MWork initializes in the constructor of AsyncTask, then implements the call method of CallBack, performs some Settings, then calls the doInBackground method, and finally executes postResult(result) for result processing. Let’s move on to the postResult(result) method.

private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }Copy the code

We saw the familiar asynchronous Message processing, Handler and Message, sending a Message,

msg.what=MESSAGE_POST_RESULT;
msg.obj=new AsyncTaskResult<Result>(this, result);Copy the code

If the handler has already sent the message, there must be a handler somewhere to process the message.

// Find the relevant Handler
private static Handler getHandler(a) {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            returnsHandler; }}// Message processing
private static class InternalHandler extends Handler {
        public InternalHandler(a) {
            super(Looper.getMainLooper());
        }

        @SuppressWarnings({"unchecked"."RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) { AsyncTaskResult<? > result = (AsyncTaskResult<? >) msg.obj;switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break; }}}// After the message is processed, set the state to FINISHED
  private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {

            // Perform the last of the four methods to process the result
            onPostExecute(result);
        }

        // Set the final state to Finished
        mStatus = Status.FINISHED;
    }Copy the code

2. For mFuture variables

// Declare variables
private final FutureTask<Result> mFuture;

// Initialize variables in the AsyncTask constructor
mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done(a) {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null); }}};// Look at the postResultIfNotInvoked method with the argument get(), and get() denotes the return value of the mWorker's call, Result.

    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

// Note that the variable value mTaskInvoked. Set (true) is set when mWork is initialized, so wasTaskInvoked=true is used in the judgment, so it is almost never invokedCopy the code

Having analyzed the mWork and mFuture variables, we proceed to examine the following code:

exec.execute(mFuture);

Exec is sDefaultExecutor. What is sDefaultExecutor?

/ / sDefaultExecutor definition
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

// Continue tracking SERIAL_EXECUTOR
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

/ / SerialExecutor definition
private static class SerialExecutor implements Executor {

        // Maintain an array queue
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        // Execute the content
        public synchronized void execute(final Runnable r) {
            // Insert a task at the end of the queue
            mTasks.offer(new Runnable() {
                public void run(a) {
                    try {
                        r.run();
                    } finally{ scheduleNext(); }}});if (mActive == null) { scheduleNext(); }}protected synchronized void scheduleNext(a) {

            // The task of removing the team head is executed
            if((mActive = mTasks.poll()) ! =null) {

                // Start the taskTHREAD_POOL_EXECUTOR.execute(mActive); }}}Copy the code

So what is THREAD_POOL_EXECUTOR? Then analyze this variable:

/** * An {@link Executor} that can be used to execute tasks in parallel. */
    public static final Executor THREAD_POOL_EXECUTOR;

    // Thread pool configuration
    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }


    // Variable Settings
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1.4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;Copy the code

That’s the process analysis. Now let’s summarize and explain the specific process.

We first set the current AsyncTask state to RUNNING and then execute onPreExecute(), which is still in the UI thread, so we can do some preparatory work there. MWorker is a subclass of Callable, and in the internal call() method, doInBackground(mParams) is called. The resulting return value is then executed as a parameter to postResult; PostResult sends messages through sHandler, and finally onPostExecute is called in handleMessage of sHandler. Exec. Execute (mFuture), which is the actual unit of task execution, encapsulates the mWorker, which is then handed over to the thread pool by sDefaultExecutor.

We’ve covered three of the four methods here, so there’s one more method:

// Update progress
@Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            progressDialog.setProgress(values[0]);
        }Copy the code

So when is this method called? The third AsyncTask method, doInBackground, calls a publishProgress(int Progress) method that passes progress. Let’s go inside and see:

// Execute the method in the worker thread
@WorkerThread
    protected final void publishProgress(Progress... values) {
        if(! isCancelled()) {// The UI thread communicates with the Work thread through the Handler and Message asynchronous messaging mechanisms
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget(); }}Copy the code

The publishProgress method is essentially sending a message,

msg.what=MESSAGE_POST_PROGRESS// Message type
msg.obj=new AsyncTaskResult<Progress> (this, values)/ / schedule


// Process the message
private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

        @SuppressWarnings({"unchecked"."RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<? > result = (AsyncTaskResult<? >) msg.obj; switch (msg.what) {case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:// Process progress messages

                    // Call onProgressUpdate to display progress
                    result.mTask.onProgressUpdate(result.mData);
                    break; }}}Copy the code

That’s pretty clear. All four methods are called. The above is all the execution process of AsyncTask. It can be seen from the source code analysis that the inner part of AsyncTask also uses Handler+Message for Message transmission and processing.

The lowdown on AsyncTask

1. In-depth understanding of AsyncTask and major problems caused by thread pools

Pay attention to

Android6.0 Google has removed the HttpClient related classes, so you need to add the related JAR packages if you continue to use them.

1. Add method to AndroidStudio:

In the correspondingmoduleUnder thebuild.gradleAdd:
android {
    useLibrary 'org.apache.http.legacy'
}Copy the code

2. Add Eclipse as follows:

libsaddorg.apache.http.legacy.jarThe abovejarWrapped in: \ * *android-sdk-windows\platforms\android-23\optionalBelow (need to downloadandroid 6. 0theSDK)Copy the code

Refer to the link

1, http://www.cnblogs.com/xiaoluo501395377/p/3430542.html

2, http://blog.csdn.net/liuhe688/article/details/6532519

3, http://blog.csdn.net/lmj623565791/article/details/38614699