In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-05 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article will give you a detailed explanation on how to get started with Picasso quickly. The editor thinks it is very practical, so I share it with you as a reference. I hope you can get something after reading this article.
Picasso is a very excellent open source picture loading library produced by Square, and it is one of the super popular picture loading libraries in Android development.
The introduction of Picasso basic usage Project
Add dependencies to the Picasso framework in our project build.gradle:
Compile 'com.squareup.picasso:picasso:2.5.2'
I am using the latest version of 2.5.2 here.
Second, add permissions
Because we need to access the network to load the picture, we add access to the network in Manifest:
"android.permission.INTERNET" / > three create and load picture layout files
Add a Button and an ImageView to our MainActivity layout file:
Picasso basic usage and source code complete parsing Picasso basic usage and source code complete parsing four MainActivity click to load pictures
Here we click Button to load a picture of Baidu Logo and display it in the ImageView control.
Picasso basic usage and source code complete parsing Picasso basic usage and source code complete parsing five loaded pictures
Click Button to load the image to see the result:
Ok, it's loaded, but what exactly have we done?
Basic loading
In fact, what we do is very simple, and that is to add a line of code to the click event of Button:
Picasso.with (MainActivity.this) .load (url) .into (headerImage)
Yes, it's just one line of code to load the picture. As for how it is implemented, we will analyze it in detail in the later source code analysis, and now take a look at its other uses.
Occupancy map
Not only is it very easy to load a picture, but you can also use a placemap during the loading process, which is a friendly way to tell the user that it is loading instead of displaying a blank interface during loading.
Picasso.with (MainActivity.this) .load (url) .placeholder (R.mipmap.ic_launcher) .into (headerImage)
Use placeholder to set up a bitmap for ImageView that has not yet been loaded into the picture, which is a friendly way to display.
Picasso basic usage and source code complete parsing Picasso basic usage and source code complete parsing exception diagram
Not only can you set a bitmap, but you can also set an exception map for ImageView after the image cannot be loaded or the image to be loaded can not be found:
Picasso.with (MainActivity.this) .load (url) .placeholder (R.mipmap.ic_launcher) .error (R.drawable.error) .into (headerImage)
Using error, you can set the exception graph for the loaded ImageView, modify our image URL so that it cannot get the correct image address, and then look at the result:
Converter
Not only that, we can also re-adjust the loaded image, such as changing the size of the picture, display shape, etc., we can use the transform method, such as:
First, let's customize a Transformation:
Private class customTransformer implements Transformation {@ Override public Bitmap transform (Bitmap source) {/ / here you can operate on Bitmap, such as changing size, shape, etc. Return source;} @ Override public String key () {return null;}}
Then deal with it in the transform method:
Picasso.with (MainActivity.this) .load (url) .placeholder (R.mipmap.ic_launcher) .transform (new customTransformer ()) .error (R.drawable.error) .into (headerImage)
In this way, the picture can be controlled and selected in a certain sense to make it more in line with our needs.
Of course, there are many other applications in Picasso, such as setting the load size, using the resizeDimen method, using centerCrop,fit for filling, and so on. You can try to use it yourself if necessary. There is no need to introduce them one by one here.
Picasso source code parsing
Ok, the above introduced some of the basic use of Picasso, very simple, one line of code to get what you need, so let's analyze how it implements our image loading and use from the point of view of source code.
Take the following simplest load as an example:
Picasso.with (MainActivity.this) .load (url) .into (headerImage); with
First, Picasso calls the static with method, so let's take a look at how the with method is implemented:
From the above source code, we can see that one of the main things done in the with method is to return an Picasso instance, of course, this instance is not so simple to create, in order to prevent multiple creation of Picasso, the singleton mode of double locking is used to create it, the main purpose is to ensure thread safety. However, it is not created directly using singleton mode. Builder mode is used in the process of creating instances, which enables Picasso to initialize many objects for later use, so let's take a look at how this Builder works:
The construction method of Builder only obtains the context of the current application level, which means that Picasso is for application-level use and will not change with the life cycle of Activity or Fragment. Picasso will stop its behavior only when the current application is exited or destroyed.
So let's take a look at what is done in the build method:
Picasso basic usage and complete source code parsing Picasso basic usage and complete source code parsing
The code here is also very simple, mainly initializing several instance variables such as downloader,cache,service,dispatcher, and the values of these variables are also set, such as source code:
Public Builder downloader (Downloader downloader) {if (downloader = = null) {throw new IllegalArgumentException ("Downloader must not be null.");} if (this.downloader! = null) {throw new IllegalStateException ("Downloader already set.");} this.downloader = downloader; return this;} public Builder executor (ExecutorService executorService) {if (executorService = = null) {throw new IllegalArgumentException ("Executor service must not be null.") } if (this.service! = null) {throw new IllegalStateException ("Executor service already set.");} this.service = executorService; return this;} public Builder memoryCache (Cache memoryCache) {if (memoryCache = = null) {throw new IllegalArgumentException ("Memory cache must not be null.");} if (this.cache! = null) {throw new IllegalStateException ("Memory cache already set.") } this.cache = memoryCache; return this;}.
These settings are the same as we usually use AlertDialog, which can be set separately after arrival at Builder.
Ok, let's now look at several very important variables in initialization: the downloader downloader first looks at downloader,builder to determine whether downloader is null, and initializes the default value for it when it is null, such as:
If (downloader = = null) {downloader = Utils.createDefaultDownloader (context);}
Let's take a look at how downloader initialization is implemented in Utils:
In the createDefaultDownloader method, the Java reflection mechanism is first used to find out whether the OKHttp network loading framework is used in the project. If so, okhttp is used as the image loading method, and if not, the built-in wrapper loader UrlConnectionDownloader is used.
Note: since the package name of okhttp3 has been changed, the built-in wrapper downloader is used here, and this is a small bug waiting to be improved. When fixed, Picasso+okhttp3 is the best way to load. The service thread pool is also used, first determining whether it is empty, and initializing the default object if it is empty:
If (service = = null) {service = new PicassoExecutorService ();}
Let's take a look at the PicassoExecutorService source code:
PicassoExecutorService directly inherits from ThreadPoolExecutor thread pool and initializes the main thread size, maximum thread and so on in the constructor, such as:
Public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) {if (corePoolSize if (workQueue = = null | | threadFactory = = null | | handler = = null) throw new NullPointerException () This.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos (keepAliveTime); this.threadFactory = threadFactory; this.handler = handler
Of course, the number of main threads and the maximum number of threads in the Picaso thread pool is variable, and the number of threads is set according to the type of network the user uses, and its application will be described in detail later. Dispatcher, the dispatcher transaction dispenser, plays a very important role in Picasso. It can be said that it is the transit station in the whole picture loading process. The switching between main thread and child thread mainly depends on its existence. Next, we will carefully study its design in the future, and understand its existence. The understanding of the whole Picasso framework is basically clear.
First, let's take a look at its debut:
Dispatcher dispatcher = new Dispatcher (context, service, HANDLER, downloader, cache, stats)
Its debut is to create a Dispatcher object instance. Of course, it passes the object instance initialized in the Builder, such as the downloader downloader, for downloading images. Of course, the download cannot be carried out by the main thread, so the service thread pool is also passed here, and the downloaded resources cannot be updated directly in the child thread, so the HANDLER in the main thread is also passed in at the same time. At the same time, application-level context context, cache cache, state change stats, etc., are also passed in for corresponding business operations.
After the above analysis, we can clearly see that such a simple creation example has clearly expressed the meaning of the existence of Dispatcher, and we have also made clear its approximate responsibilities.
So let's go on to see what specific actions are done in the constructor of Dispatcher:
In the construction method of Dispatcher, I divided it into five parts. Let's analyze it in detail:
①: dispatcherThread, which is a Handler thread, such as:
Static class DispatcherThread extends HandlerThread {DispatcherThread () {super (Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);}}
It is mainly created to open a child thread for Dispatcher to call, in order to perform time-consuming image download operations in the child thread.
②: the acceptance of object instances is mainly about accepting object instances initialized in Picasso. There is nothing to say about this.
③: create collections to hold data or objects, that is, to save objects or states.
④: DispatcherHandler, which is a Handler and a Handler in the dispatcherThread thread, is used to transfer the operation in the thread thread to the Dispatcher, and its constructor accepts the Dispatcher object:
Picasso basic usage and complete source code parsing Picasso basic usage and complete source code parsing
Let's take a look at how his handleMessage method handles messages:
@ Override public void handleMessage (final Message msg) {switch (msg.what) {case REQUEST_SUBMIT: {Action action = (Action) msg.obj; dispatcher.performSubmit (action); break;} case REQUEST_CANCEL: {Action action = (Action) msg.obj; dispatcher.performCancel (action); break } case TAG_PAUSE: {Object tag = msg.obj; dispatcher.performPauseTag (tag); break;} case TAG_RESUME: {Object tag = msg.obj; dispatcher.performResumeTag (tag); break;} case HUNTER_COMPLETE: {BitmapHunter hunter = (BitmapHunter) msg.obj; dispatcher.performComplete (hunter); break } case HUNTER_RETRY: {BitmapHunter hunter = (BitmapHunter) msg.obj; dispatcher.performRetry (hunter); break;} case HUNTER_DECODE_FAILED: {BitmapHunter hunter = (BitmapHunter) msg.obj; dispatcher.performError (hunter, false); break;} case HUNTER_DELAY_NEXT_BATCH: {dispatcher.performBatchComplete (); break } case NETWORK_STATE_CHANGE: {NetworkInfo info = (NetworkInfo) msg.obj; dispatcher.performNetworkStateChange (info); break;} case AIRPLANE_MODE_CHANGE: {dispatcher.performAirplaneModeChange (msg.arg1 = = AIRPLANE_MODE_ON); break } default: Picasso.HANDLER.post (new Runnable () {@ Override public void run () {throw new AssertionError ("Unknown handler message received:" + msg.what);}});}}
From its message processing handleMessage, we can see that all messages are not processed directly but transferred to the dispatcher, where the corresponding processing is carried out in the dispatcher.
⑤: monitor the operation of network changes, which is used to monitor users' mobile phone network changes. Let's take a look at the NetworkBroadcastReceiver class:
The Dispatcher object is also received in the construction parameters, and there are methods to register broadcasts and destroy broadcasts, of course, it is not handled directly, but is passed to the Dispatcher for digestion.
Let's look at what it does when a user's network changes:
We can see that it is mainly divided into two categories, one is the flight mode, and the other is the normal change of the network state. Let's ignore the flight mode and mainly look at the operation of the network change:
Void dispatchNetworkStateChange (NetworkInfo info) {handler.sendMessage (handler.obtainMessage (NETWORK_STATE_CHANGE, info));}
The handler here is DispatcherHandler, so let's see how it works:
Case NETWORK_STATE_CHANGE: {NetworkInfo info = (NetworkInfo) msg.obj; dispatcher.performNetworkStateChange (info); break;}
Call the performNetworkStateChange method of dispatcher to handle:
When the network changes, it passes to our PicassoExecutor Service thread pool, determines in the adjustThreadCount method the type of network the user is using, such as wifi,4G, and then sets the appropriate number of threads for the thread pool. Let's see:
Void adjustThreadCount (NetworkInfo info) {if (info = = null | |! info.isConnectedOrConnecting ()) {setThreadCount (DEFAULT_THREAD_COUNT); return;} switch (info.getType ()) {case ConnectivityManager.TYPE_WIFI: case ConnectivityManager.TYPE_WIMAX: case ConnectivityManager.TYPE_ETHERNET: setThreadCount (4); break Case ConnectivityManager.TYPE_MOBILE: switch (info.getSubtype ()) {case TelephonyManager.NETWORK_TYPE_LTE: / / 4G case TelephonyManager.NETWORK_TYPE_HSPAP: case TelephonyManager.NETWORK_TYPE_EHRPD: setThreadCount (3); break Case TelephonyManager.NETWORK_TYPE_UMTS: / / 3G case TelephonyManager.NETWORK_TYPE_CDMA: case TelephonyManager.NETWORK_TYPE_EVDO_0: case TelephonyManager.NETWORK_TYPE_EVDO_A: case TelephonyManager.NETWORK_TYPE_EVDO_B: setThreadCount (2); break; case TelephonyManager.NETWORK_TYPE_GPRS: / / 2G case TelephonyManager.NETWORK_TYPE_EDGE: setThreadCount (1) Break; default: setThreadCount (DEFAULT_THREAD_COUNT);} break; default: setThreadCount (DEFAULT_THREAD_COUNT);}} private void setThreadCount (int threadCount) {setCorePoolSize (threadCount); setMaximumPoolSize (threadCount);}
As mentioned above, set the number of threads according to different network types used by users, for example, when users are using wifi, the number of threads will be set to 4, 4G to 3, etc., so it is very humane to design the number of threads according to the specific situation of users, and it is worth us to emulate.
Ok, at this point, the construction method of our important Dispatcher object has been completely parsed. From the above parsing, we can clearly see the significance of Dispatcher as a transit station. Almost all thread conversion operations are controlled by Dispatcher. Of course, there may be friends who do not know how it works and how to enter the child thread, because our explanation has not yet reached the step of entering the child thread. I will explain it further below.
Summarize the important object examples contained in Dispatcher:
①: PicassoExecutorService thread pool
②: downloader downloader
③: DispatcherHandler processor for DispatcherThread threads
④: NetworkBroadcastReceiver network listener
⑤: the Handler of the mainThreadHandler main thread
⑥: a collection of saved data: hunterMap,pausedActions,batch, etc.
A good understanding of the meaning of the existence of the above objects will be of great benefit to the following understanding. Please read it carefully if you do not fully understand it.
Ok, now that we know which important object instances Dispatcher contains, let's go back to Picasso's Builder, and finally return an object instance of Picasso in the build method:
Return new Picasso (context, dispatcher, cache, listener, transformer, requestHandlers, stats,defaultBitmapConfig, indicatorsEnabled, loggingEnabled)
In the construction method of Picasso, we only need to pay attention to the application of requestHandlers:
Here, all the RequestHandler that can be applied by Picasso are added to the collection, and then the corresponding Handler is used for business processing according to the specific requirements.
Ok, what is done in the with method in this Picasso has been fully demonstrated in front of you, and I believe you have a thorough understanding of this step of the application.
So to sum up, Picasso now has those important object instances:
①: dispatcher, the importance is self-evident, the above has fully demonstrated its importance, as a transit station, Picasso is a must. The ultimate goal is to get our main thread into the child thread for time-consuming download operations.
②: requestHandlers, which exists to choose a way to handle requests. For example, you need to use NetworkRequestHandler as a requestor to download web images.
③: HANDLER, which is the Handler of the main thread, which is used to process the returned results. For example, it is used to update UI after a successful image download. This will be explained in detail later when the UI is updated after the picture has been downloaded.
④: some configuration object instances, such as: cache cache, image loading default configuration defaultBitmapConfig, state storage stats, etc.
Load
With method is mainly to do a basic configuration work, such as Picasso configuration, Dispatcher configuration, these are very important premise work, only to do a good job of these configurations, we can use it effortlessly.
Let's take a look at its application. In the load method, we need to pass a parameter, which can be Url, a path path, a file, a resource layout, and so on:
①: urlpublic RequestCreator load (Uri uri) {return new RequestCreator (this, uri, 0);} ②: pathpublic RequestCreator load (String path) {if (path = = null) {return new RequestCreator (this, null, 0);} if (path.trim (). Length () = = 0) {throw new IllegalArgumentException ("Path must not be empty.");} return load (Uri.parse (path)) } ③: filepublic RequestCreator load (File file) {if (file = = null) {return new RequestCreator (this, null, 0);} return load (Uri.fromFile (file));} ④: resourceIdpublic RequestCreator load (int resourceId) {if (resourceId = = 0) {throw new IllegalArgumentException ("Resource ID must not be zero.");} return new RequestCreator (this, null, resourceId);}
No matter what parameter is passed, it returns a request constructor RequestCreator, and let's see what its constructor does:
Picasso basic usage and complete source code parsing Picasso basic usage and complete source code parsing
The main thing is to get the Picasso object and Builder the schema to build a Builder object in Request:
Builder (Uri uri, int resourceId, Bitmap.Config bitmapConfig) {this.uri = uri; this.resourceId = resourceId; this.config = bitmapConfig;}
This Builder object mainly accepts some parameter information, including url, resource layout, and default image configuration.
Because the load method returns a RequestCreator object, we can call other methods in the form of a method chain, such as adding a placeholder, exception map, converter, etc., depending on the source code.
Ok, in general, a RequestCreator object is created in the load method, and a Request.Builder object is constructed in RequestCreator.
So let's take a look at what important object instances RequestCreator has:
①: picasso object
②: Request.Builder object instance data, which includes the URL address we requested, the resource file, and the default image configuration.
Into
In the load method, you mainly create a RequestCreator object and get the path of the url/path/file/resourceId resource address to load, so the next thing to do is to load the picture in the into method. Let's take a look at what is done in the into method:
Public void into (ImageView target, Callback callback) {long started = System.nanoTime (); checkMain (); if (target = = null) {throw new IllegalArgumentException ("Target must not be null.");} if (! data.hasImage ()) {picasso.cancelRequest (target); if (setPlaceholder) {setPlaceholder (target, getPlaceholderDrawable ());} return } if (deferred) {if (data.hasSize ()) {throw new IllegalStateException ("Fit cannot be used with resize.");} int width = target.getWidth (); int height = target.getHeight (); if (width = = 0 | | height = = 0) {if (setPlaceholder) {setPlaceholder (target, getPlaceholderDrawable ();} picasso.defer (target, new DeferredRequestCreator (this, target, callback)); return } data.resize (width, height);} Request request = createRequest (started); String requestKey = createKey (request); if (shouldReadFromMemoryCache (memoryPolicy)) {Bitmap bitmap = picasso.quickMemoryCacheCheck (requestKey); if (bitmap! = null) {picasso.cancelRequest (target); setBitmap (target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled) If (picasso.loggingEnabled) {log (OWNER_MAIN, VERB_COMPLETED, request.plainId (), "from" + MEMORY);} if (callback! = null) {callback.onSuccess ();} return;}} if (setPlaceholder) {setPlaceholder (target, getPlaceholderDrawable ()) } Action action = new ImageViewAction (picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade); picasso.enqueueAndSubmit (action);}
As you can see from the above source code, into has done a lot of things, which is explained one by one below:
* * ①: checkMain (): * * first check whether the main thread is running in the main thread. If it is not in the main thread, it will throw an exception that should run in the main thread: throw new IllegalStateException ("Method call should happen from the main thread."). This means that this step is still running in the main thread.
* * ②: data.hasImage (): * * the data here is the Request.Builder object initialized earlier, which contains the url address, resourceId and default configuration. Here is to determine whether uri or resourceId is empty and 0, and if so, cancel the imageview request: picasso.cancelRequest (target)
* * ③: deferred:** delay, which will be executed if delay is needed, and then get the size of the image in data. If not, get the width and height of target to resize the image to be loaded: data.resize (width, height)
* * ④: createRequest:** creates a Request object in the data.build () method, which will contain uri or resourceId and the default image config. The transformation is then performed after the Request object is obtained, which is mainly related to whether we customized the RequestTransformer when we built the Picasso.
* * ⑤: createKey:** it mainly returns the String type key, and the main purpose is to establish the association between ImageView and key. You can obtain the status of the Imageview through key, such as whether it has been cached.
* * ⑥: shouldReadFromMemoryCache:** can also know whether to read data from the in-memory cache by looking at the method name. If so, it will read the data from the cache. If it gets the data, it will cancel the current ImageView request and directly set the Bitmap to it. And if there is a callback, it will call back the successful method.
* * ⑦: ImageViewAction:** builds an ImageViewAction object. It is worth noting that when building an object, ImageView is added to RequestWeakReference for storage to facilitate lookup when using it. RequestWeakReference is a weak reference of WeakReference type. At the same time, ImageViewAction is also the key class to actually update UI when the image is loaded, which will be explained in more detail later.
* * ⑧: enqueueAndSubmit:** calls the enqueueAndSubmit method of picasso to submit the task.
Let's look at the operation of the submit method:
Void submit (Action action) {dispatcher.dispatchSubmit (action);}
Here we use the dispatcher task dispenser again to submit our task action to Dispatcher for processing. Then let's take a look at the dispatchSubmit method:
Void dispatchSubmit (Action action) {handler.sendMessage (handler.obtainMessage (REQUEST_SUBMIT, action));}
From the construction method of Dispatcher, we can know that the handler at this time is DispatcherHandler, so let's take a look at how it handles our task behavior:
Case REQUEST_SUBMIT: {Action action = (Action) msg.obj; dispatcher.performSubmit (action); break
Get our task behavior Action directly in handleMessage, and then call the performSubmit method:
Void performSubmit (Action action, boolean dismissFailed) {if (pausedTags.contains (action.getTag () {pausedActions.put (action.getTarget (), action); if (action.getPicasso (). LoggingEnabled) {log (OWNER_DISPATCHER, VERB_PAUSED, action.request.logId (), "because tag'" + action.getTag () + "is paused");} return;} BitmapHunter hunter = hunterMap.get (action.getKey ()) If (hunter! = null) {hunter.attach (action); return;} if (service.isShutdown ()) {if (action.getPicasso (). LoggingEnabled) {log (OWNER_DISPATCHER, VERB_IGNORED, action.request.logId (), "because shut down");} return;} hunter = forRequest (action.getPicasso (), this, cache, stats, action); hunter.future = service.submit (hunter) HunterMap.put (action.getKey (), hunter); if (dismissFailed) {failedActions.remove (action.getTarget ());} if (action.getPicasso (). LoggingEnabled) {log (OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId ());}}
The performSubmit method is the real place where the task is submitted, so let's interpret its source code:
①: first, query whether it already exists in the pause list according to the flag of action. If so, the action will be stored in the collection list of pausedActions to wait for the wake-up request.
②: then query whether the request item of the hunterMap already exists from the hunterMap collection through the key of action. If so, the attach method of hunter will be called to make a merge request to avoid repeated requests from an ImageView:
Store the action in ArrayList, and if there is the same action, an action will be saved according to the non-repeatability of the ArrayList, and the new attribute value priority will be updated.
③: when the thread pool service is not closed, get a BitmapHunter thread of type Runnable through the forRequest method to take a look at the source code of forRequest:
Get the Request request and all the requestHandlers request handlers from action and picasso respectively, then traverse all the requesters to get each request handler, call canHandleRequest to try to see if the processor can handle it, take a look at the canHandleRequest method:
Public abstract boolean canHandleRequest (Request data)
It is an abstract method that needs to be found in a subclass, and the subclass that implements it determines whether the request can be processed according to the following constraints:
String scheme = data.uri.getScheme ()
That is to say, it is judged by the Scheme constraint of the url address, and the url obtained by the Request in our current action starts with http/https, so take a look at the canHandleRequest source code in NetworkRequestHandler:
Private static final String SCHEME_HTTP = "http"; private static final String SCHEME_HTTPS = "https"; @ Overridepublic boolean canHandleRequest (Request data) {String scheme = data.uri.getScheme (); return (SCHEME_HTTP.equals (scheme) | | SCHEME_HTTPS.equals (scheme));}
You can see the exact match, which leads to the conclusion that it is the NetworkRequestHandler processor that will process our request data.
Once the request processor is matched, a BitmapHunter thread will be returned. After getting the thread, the thread will be opened in the thread pool and stored in the huntermap thread collection so that multiple requests can merge the same thread:
Hunter.future = service.submit (hunter); hunterMap.put (action.getKey (), hunter)
Ok, you can go to our thread pool PicassoExecutorService to see how it is executed. The submit source code is as follows:
@ Override public Future > submit (Runnable task) {PicassoFutureTask ftask = new PicassoFutureTask ((BitmapHunter) task); execute (ftask); return ftask;}
By encapsulating our thread task into PicassoFutureTask, PicassoFutureTask is a thread that makes it easier for us to control processing, then call execute to start the thread, and then transfer our thread to the addWorker method and open the thread start in addWorker:
Boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try {w = new Worker (firstTask); final Thread t = w. Thread; if (t! = null) {final ReentrantLock mainLock = this.mainLock; mainLock.lock (); try {/ / Recheck while holding lock. / / Back out on ThreadFactory failure or if / / shut down before lock acquired. Int rs = runStateOf (ctl.get ()); if (rs if (t.isAlive ()) / / precheck that t is startable throw new IllegalThreadStateException (); workers.add (w); int s = workers.size (); if (s > largestPoolSize) largestPoolSize = s WorkerAdded = true;}} finally {mainLock.unlock ();} if (workerAdded) {t.start (); workerStarted = true;}
The above source code is executed in the ThreadPoolExecutor thread pool, you can look at the source code.
When the thread is turned on, where does it execute?
We know that our encapsulated thread is initially BitmapHunter, so let's go into it and see how it is executed:
In the run method of BitmapHunter, first modify the name of the thread, then execute the hunt method, and store the execution result in result. Let's first look at how the hunt method is executed:
Bitmap hunt () throws IOException {Bitmap bitmap = null; if (shouldReadFromMemoryCache (memoryPolicy)) {bitmap = cache.get (key); if (bitmap! = null) {stats.dispatchCacheHit (); loadedFrom = MEMORY; if (picasso.loggingEnabled) {log (OWNER_HUNTER, VERB_DECODED, data.logId (), "from cache");} return bitmap;}} data.networkPolicy = retryCount = = 0? NetworkPolicy.OFFLINE.index: networkPolicy; RequestHandler.Result result = requestHandler.load (data, networkPolicy); if (result! = null) {loadedFrom = result.getLoadedFrom (); exifRotation = result.getExifOrientation (); bitmap = result.getBitmap (); / / If there was no Bitmap then we need to decode it from the stream. If (bitmap = = null) {InputStream is = result.getStream (); try {bitmap = decodeStream (is, data);} finally {Utils.closeQuietly (is);} if (bitmap! = null) {if (picasso.loggingEnabled) {log (OWNER_HUNTER, VERB_DECODED, data.logId ());} stats.dispatchBitmapDecoded (bitmap) If (data.needsTransformation () | | exifRotation! = 0) {synchronized (DECODE_LOCK) {if (data.needsMatrixTransform () | | exifRotation! = 0) {bitmap = transformResult (data, bitmap, exifRotation); if (picasso.loggingEnabled) {log (OWNER_HUNTER, VERB_TRANSFORMED, data.logId ()) } if (data.hasCustomTransformations ()) {bitmap = applyCustomTransformations (data.transformations, bitmap); if (picasso.loggingEnabled) {log (OWNER_HUNTER, VERB_TRANSFORMED, data.logId (), "from custom transformations");} if (bitmap! = null) {stats.dispatchBitmapTransformed (bitmap) } return bitmap;}
Parse the source code:
①: first get the key corresponding to the action behavior, and use key to find out whether the bitmap exists in the cache cache. If so, modify the stats status and return the bitmap directly.
②: if it does not exist in the cache, you need to load requestHandler.load () on the network. From the above analysis, we know that the requestHandler that can handle the current requesr is NetworkRequestHandler, so let's check the load method of NetworkRequestHandler:
Here you can clearly see that the load method of downloader is called directly, and our downloader also clearly states that it is UrlConnectionDownloader when Picasso builds Builder, so take a look at the load method of UrlConnectionDownloader:
Protected HttpURLConnection openConnection (Uri path) throws IOException {HttpURLConnection connection = (HttpURLConnection) new URL (path.toString ()). OpenConnection (); connection.setConnectTimeout (Utils.DEFAULT_CONNECT_TIMEOUT_MILLIS); connection.setReadTimeout (Utils.DEFAULT_READ_TIMEOUT_MILLIS); return connection;} @ Override public Response load (Uri uri, int networkPolicy) throws IOException {if (Build.VERSION.SDK_INT > = Build.VERSION_CODES.ICE_CREAM_SANDWICH) {installCacheIfNeeded (context) } HttpURLConnection connection = openConnection (uri); connection.setUseCaches (true); if (networkPolicy! = 0) {String headerValue; if (NetworkPolicy.isOfflineOnly (networkPolicy)) {headerValue = FORCE_CACHE;} else {StringBuilder builder = CACHE_HEADER_BUILDER.get (); builder.setLength (0); if (! NetworkPolicy.shouldReadFromDiskCache (networkPolicy)) {builder.append ("no-cache") } if (! NetworkPolicy.shouldWriteToDiskCache (networkPolicy)) {if (builder.length () > 0) {builder.append (',');} builder.append ("no-store");} headerValue = builder.toString ();} connection.setRequestProperty ("Cache-Control", headerValue);} int responseCode = connection.getResponseCode () If (responseCode > = 300) {connection.disconnect (); throw new ResponseException (responseCode + "" + connection.getResponseMessage (), networkPolicy, responseCode);} long contentLength = connection.getHeaderFieldInt ("Content-Length",-1); boolean fromCache = parseResponseSourceHeader (connection.getHeaderField (RESPONSE_SOURCE)); return new Response (connection.getInputStream (), fromCache, contentLength);}
I believe we are all familiar with the code here, which is to build a HttpURLConnection to make network requests and, of course, to carry out some network caching strategies according to networkPolicy. Finally, the result is stored in the Response object.
The load method of NetworkRequestHandler then takes the data from the Response object and stores it in the Result object. Then return it to the RequestHandler.Result result of the hunt method in BitmapHunter, get the input stream from result, parse the input stream into Bitmap, and return it.
At this point, we have downloaded the data from the network and converted it to bitmap, and then in the run method returned to our BitmapHunter, we got the bitmap in the run method, and then called dispatcher for transaction distribution. If we successfully get it, we call dispatchComplete, otherwise we call dispatchFailed.
Let's take a look at the dispatchComplete method in dispatcher:
Void dispatchComplete (BitmapHunter hunter) {handler.sendMessage (handler.obtainMessage (HUNTER_COMPLETE, hunter));}
By the same token, the handler here is still DispatcherHandler, so let's take a look at what it does:
Case HUNTER_COMPLETE: {BitmapHunter hunter = (BitmapHunter) msg.obj; dispatcher.performComplete (hunter); break;}
Go to the performComplete method of dispatcher:
Here we first save our results in the cache cache, and then remove the key corresponding to BitmapHunter from the hunterMap collection because the request has been completed. Then call batch (hunter); method:
First determine whether the BitmapHunter has been cancelled, then store the BitmapHunter in an List batch collection, and finally send an empty delay message through DispatcherHandler to delay the next network load in order to handle the current bitmap work. Let's see how it handles it:
Case HUNTER_DELAY_NEXT_BATCH: {dispatcher.performBatchComplete (); break;}
Enter the performBatchComplete to view:
Void performBatchComplete () {List copy = new ArrayList (batch); batch.clear (); mainThreadHandler.sendMessage (mainThreadHandler.obtainMessage (HUNTER_BATCH_COMPLETE, copy)); logBatch (copy);}
In performBatchComplete, you first get the collection that holds the BitmapHunter, and then call mainThreadHandler to send the message.
Remember how mainThreadHandler was?
In the build method of building Builder in the Picasso class, create a Dispatcher object and pass in a parameter of HANDLER, as shown below:
Dispatcher dispatcher = new Dispatcher (context, service, HANDLER, downloader, cache, stats)
This HANDLER is mainThreadHandler.
So how do you handle the messages it sends?
Get the BitmapHunter collection for traversal, and then directly call the complete method in Picasso:
Void complete (BitmapHunter hunter) {Action single = hunter.getAction (); List joined = hunter.getActions (); boolean hasMultiple = joined! = null & &! joined.isEmpty (); boolean shouldDeliver = single! = null | | hasMultiple; if (! shouldDeliver) {return;} Uri uri = hunter.getData (). Uri; Exception exception = hunter.getException (); Bitmap result = hunter.getResult (); LoadedFrom from = hunter.getLoadedFrom () If (single! = null) {deliverAction (result, from, single);} if (hasMultiple) {/ / noinspection ForLoopReplaceableByForEach for (int I = 0, n = joined.size (); I if (listener! = null & & exception! = null) {listener.onImageLoadFailed (this, uri, exception);}}
In the complete method, get the original encapsulated Action,Uri,Bitmap and other data information directly from the BitmapHunter, and then call the deliverAction method:
A series of judgments are made here. When action is not cancelled and Bitmap is not empty, action.complete (result, from); will be called to complete the operation.
Remember what we did when we created action?
Action action = new ImageViewAction (picasso, target, request, memoryPolicy, networkPolicy, errorResId, errorDrawable, requestKey, tag, callback, noFade)
Then it is clear that our Action is actually ImageViewAction.
So let's take a look at how ImageViewAction's complete works.
Target is the ImageView we passed when we created the ImageViewAction. Now get it, and then call the PicassoDrawable.setBitmap method to set the image:
When we saw target.setImageDrawable (drawable);, did we finally breathe a sigh of relief and finally saw the message of setting the image for ImageView.
This is the end of the article on "how to get started with Picasso". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it out for more people to see.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.