@Overridepublicvoidcomplete(Bitmap result, Picasso.LoadedFrom from){ if (result == null) { thrownew AssertionError( String.format("Attempted to complete action with no result!\n%s", this)); }
An ExecutorService that executes each submitted task using one of possibly several pooled threads, normally configured using Executors factory methods.
Thread pools address two different problems: they usually provide improved performance when executing large numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks. Each ThreadPoolExecutor also maintains some basic statistics, such as the number of completed tasks.
To be useful across a wide range of contexts, this class provides many adjustable parameters and extensibility hooks. However, programmers are urged to use the more convenient Executors factory methods Executors.newCachedThreadPool() (unbounded thread pool, with automatic thread reclamation), Executors.newFixedThreadPool(int) (fixed size thread pool) and Executors.newSingleThreadExecutor() (single background thread), that preconfigure settings for the most common usage scenarios. Otherwise, use the following guide when manually configuring and tuning this class:
Core and maximum pool sizes
A ThreadPoolExecutor will automatically adjust the pool size (see getPoolSize()) according to the bounds set by corePoolSize (see getCorePoolSize()) and maximumPoolSize (see getMaximumPoolSize()). When a new task is submitted in method execute(Runnable), and fewer than corePoolSize threads are running, a new thread is created to handle the request, even if other worker threads are idle. If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full. By setting corePoolSize and maximumPoolSize the same, you create a fixed-size thread pool. By setting maximumPoolSize to an essentially unbounded value such as Integer.MAX_VALUE, you allow the pool to accommodate an arbitrary number of concurrent tasks. Most typically, core and maximum pool sizes are set only upon construction, but they may also be changed dynamically using setCorePoolSize(int) and setMaximumPoolSize(int).
On-demand construction
By default, even core threads are initially created and started only when new tasks arrive, but this can be overridden dynamically using method prestartCoreThread() or prestartAllCoreThreads(). You probably want to prestart threads if you construct the pool with a non-empty queue.
Creating new threads
New threads are created using a ThreadFactory. If not otherwise specified, a Executors.defaultThreadFactory() is used, that creates threads to all be in the same ThreadGroup and with the same NORM_PRIORITY priority and non-daemon status. By supplying a different ThreadFactory, you can alter the thread’s name, thread group, priority, daemon status, etc. If a ThreadFactory fails to create a thread when asked by returning null from newThread, the executor will continue, but might not be able to execute any tasks. Threads should possess the “modifyThread” RuntimePermission. If worker threads or other threads using the pool do not possess this permission, service may be degraded: configuration changes may not take effect in a timely manner, and a shutdown pool may remain in a state in which termination is possible but not completed.
Keep-alive times
If the pool currently has more than corePoolSize threads, excess threads will be terminated if they have been idle for more than the keepAliveTime (see getKeepAliveTime(TimeUnit)). This provides a means of reducing resource consumption when the pool is not being actively used. If the pool becomes more active later, new threads will be constructed. This parameter can also be changed dynamically using method setKeepAliveTime(long, TimeUnit). Using a value of Long.MAX_VALUETimeUnit.NANOSECONDS effectively disables idle threads from ever terminating prior to shut down. By default, the keep-alive policy applies only when there are more than corePoolSize threads. But method allowCoreThreadTimeOut(boolean) can be used to apply this time-out policy to core threads as well, so long as the keepAliveTime value is non-zero.
Queuing
Any BlockingQueue may be used to transfer and hold submitted tasks. The use of this queue interacts with pool sizing: If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing. If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread. If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected. There are three general strategies for queuing: Direct handoffs. A good default choice for a work queue is a SynchronousQueue that hands off tasks to threads without otherwise holding them. Here, an attempt to queue a task will fail if no threads are immediately available to run it, so a new thread will be constructed. This policy avoids lockups when handling sets of requests that might have internal dependencies. Direct handoffs generally require unbounded maximumPoolSizes to avoid rejection of new submitted tasks. This in turn admits the possibility of unbounded thread growth when commands continue to arrive on average faster than they can be processed. Unbounded queues. Using an unbounded queue (for example a LinkedBlockingQueue without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn’t have any effect.) This may be appropriate when each task is completely independent of others, so tasks cannot affect each others execution; for example, in a web page server. While this style of queuing can be useful in smoothing out transient bursts of requests, it admits the possibility of unbounded work queue growth when commands continue to arrive on average faster than they can be processed. Bounded queues. A bounded queue (for example, an ArrayBlockingQueue) helps prevent resource exhaustion when used with finite maximumPoolSizes, but can be more difficult to tune and control. Queue sizes and maximum pool sizes may be traded off for each other: Using large queues and small pools minimizes CPU usage, OS resources, and context-switching overhead, but can lead to artificially low throughput. If tasks frequently block (for example if they are I/O bound), a system may be able to schedule time for more threads than you otherwise allow. Use of small queues generally requires larger pool sizes, which keeps CPUs busier but may encounter unacceptable scheduling overhead, which also decreases throughput.
Rejected tasks
New tasks submitted in method execute(Runnable) will be rejected when the Executor has been shut down, and also when the Executor uses finite bounds for both maximum threads and work queue capacity, and is saturated. In either case, the execute method invokes the RejectedExecutionHandler.rejectedExecution(Runnable, ThreadPoolExecutor) method of its RejectedExecutionHandler. Four predefined handler policies are provided: In the default ThreadPoolExecutor.AbortPolicy, the handler throws a runtime RejectedExecutionException upon rejection. In ThreadPoolExecutor.CallerRunsPolicy, the thread that invokes execute itself runs the task. This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted. In ThreadPoolExecutor.DiscardPolicy, a task that cannot be executed is simply dropped. In ThreadPoolExecutor.DiscardOldestPolicy, if the executor is not shut down, the task at the head of the work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.) It is possible to define and use other kinds of RejectedExecutionHandler classes. Doing so requires some care especially when policies are designed to work only under particular capacity or queuing policies.
Hook methods
This class provides protected overridable beforeExecute(Thread, Runnable) and afterExecute(Runnable, Throwable) methods that are called before and after execution of each task. These can be used to manipulate the execution environment; for example, reinitializing ThreadLocals, gathering statistics, or adding log entries. Additionally, method terminated() can be overridden to perform any special processing that needs to be done once the Executor has fully terminated. If hook or callback methods throw exceptions, internal worker threads may in turn fail and abruptly terminate.
Queue maintenance
Method getQueue() allows access to the work queue for purposes of monitoring and debugging. Use of this method for any other purpose is strongly discouraged. Two supplied methods, remove(Runnable) and purge() are available to assist in storage reclamation when large numbers of queued tasks become cancelled.
Finalization
A pool that is no longer referenced in a program AND has no remaining threads will be shutdown automatically. If you would like to ensure that unreferenced pools are reclaimed even if users forget to call shutdown(), then you must arrange that unused threads eventually die, by setting appropriate keep-alive times, using a lower bound of zero core threads and/or setting allowCoreThreadTimeOut(boolean).
Extension example. Most extensions of this class override one or more of the protected hook methods. For example, here is a subclass that adds a simple pause/resume feature:
/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor * creates a new thread * @param handler the handler to use when execution is blocked * because the thread bounds and queue capacities are reached * @throws IllegalArgumentException if one of the following holds:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} * or {@code threadFactory} or {@code handler} is null */ publicThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler){
publicvoidexecute(Runnable command){ if (command == null) thrownew NullPointerException(); /* * Proceed in 3 steps: * * 1. If fewer than corePoolSize threads are running, try to * start a new thread with the given command as its first * task. The call to addWorker atomically checks runState and * workerCount, and so prevents false alarms that would add * threads when it shouldn't, by returning false. * * 2. If a task can be successfully queued, then we still need * to double-check whether we should have added a thread * (because existing ones died since last checking) or that * the pool shut down since entry into this method. So we * recheck state and if necessary roll back the enqueuing if * stopped, or start a new thread if there are none. * * 3. If we cannot queue task, then we try to add a new * thread. If it fails, we know we are shut down or saturated * and so reject the task. */ int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); elseif (workerCountOf(recheck) == 0) addWorker(null, false); } elseif (!addWorker(command, false)) reject(command); }
AsyncTask was intended to enable proper and easy use of the UI thread. However, the most common use case was for integrating into UI, and that would cause Context leaks, missed callbacks, or crashes on configuration changes. It also has inconsistent behavior on different versions of the platform, swallows exceptions from doInBackground, and does not provide much utility over using Executors directly. AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask. 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. Developer Guides For more information about using tasks and threads, read the Processes and Threads developer guide. Usage AsyncTask must be subclassed to be used. The subclass will override at least one method (doInBackground), and most often will override a second one (onPostExecute.) Here is an example of subclassing:
privateclassDownloadFilesTaskextendsAsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls){ int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; }
new DownloadFilesTask().execute(url1, url2, url3);
AsyncTask’s generic types The three types used by an asynchronous task are the following: Params, the type of the parameters sent to the task upon execution. Progress, the type of the progress units published during the background computation. Result, the type of the result of the background computation. Not all types are always used by an asynchronous task. To mark a type as unused, simply use the type Void:
The 4 steps When an asynchronous task is executed, the task goes through 4 steps: onPreExecute(), invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface. doInBackground, invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate step. onProgressUpdate, invoked on the UI thread after a call to publishProgress. The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field. onPostExecute, invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter. Cancelling a task A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.) Threading rules There are a few threading rules that must be followed for this class to work properly: The AsyncTask class must be loaded on the UI thread. This is done automatically as of Build.VERSION_CODES.JELLY_BEAN. The task instance must be created on the UI thread. execute must be invoked on the UI thread. Do not call onPreExecute(), onPostExecute, doInBackground, onProgressUpdate manually. The task can be executed only once (an exception will be thrown if a second execution is attempted.) Memory observability AsyncTask guarantees that all callback calls are synchronized to ensure the following without explicit synchronizations. The memory effects of onPreExecute, and anything else executed before the call to execute, including the construction of the AsyncTask object, are visible to doInBackground. The memory effects of doInBackground are visible to onPostExecute. Any memory effects of doInBackground preceding a call to publishProgress are visible to the corresponding onProgressUpdate call. (But doInBackground continues to run, and care needs to be taken that later updates in doInBackground do not interfere with an in-progress onProgressUpdate call.) Any memory effects preceding a call to cancel are visible after a call to isCancelled that returns true as a result, or during and after a resulting call to onCancelled. Order of execution When first introduced, AsyncTasks were executed serially on a single background thread. Starting with Build.VERSION_CODES.DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with Build.VERSION_CODES.HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can invoke executeOnExecutor(Executor, Object[]) with THREAD_POOL_EXECUTOR. Deprecated Use the standard java.util.concurrent or Kotlin concurrency utilities instead.
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override publicvoidhandleMessage(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; } } }
public E poll(long timeout, TimeUnit unit)throws InterruptedException { E x = null; int c = -1; long nanos = unit.toNanos(timeout); final AtomicInteger count = this.count; final ReentrantLock takeLock = this.takeLock; takeLock.lockInterruptibly(); try { while (count.get() == 0) { if (nanos <= 0L) returnnull; nanos = notEmpty.awaitNanos(nanos); } x = dequeue(); c = count.getAndDecrement(); if (c > 1) notEmpty.signal(); } finally { takeLock.unlock(); } if (c == capacity) signalNotFull(); return x; }
/** Prepares the [request] to be executed at some point in the future. */ overridefunnewCall(request: Request): Call = RealCall(this, request, forWebSocket = false)
// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to // the same host. if (!call.call.forWebSocket) { val existingCall = findExistingCallWithHost(call.host) if (existingCall != null) call.reuseCallsPerHostFrom(existingCall) } } promoteAndExecute() }
/** * Promotes eligible calls from [readyAsyncCalls] to [runningAsyncCalls] and runs them on the * executor service. Must not be called with synchronization because executing calls can call * into user code. * * @return true if the dispatcher is currently running calls. */ privatefunpromoteAndExecute(): Boolean { this.assertThreadDoesntHoldLock()
val executableCalls = mutableListOf<AsyncCall>() val isRunning: Boolean synchronized(this) { val i = readyAsyncCalls.iterator() while (i.hasNext()) { val asyncCall = i.next()
if (runningAsyncCalls.size >= this.maxRequests) break// Max capacity. if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue// Host max capacity.
/** * Attempt to enqueue this async call on [executorService]. This will attempt to clean up * if the executor has been shut down by reporting the call as failed. */ funexecuteOn(executorService: ExecutorService) { client.dispatcher.assertThreadDoesntHoldLock()
var success = false try { executorService.execute(this) success = true } catch (e: RejectedExecutionException) { val ioException = InterruptedIOException("executor rejected") ioException.initCause(e) noMoreExchanges(ioException) responseCallback.onFailure(this@RealCall, ioException) } finally { if (!success) { client.dispatcher.finished(this) // This call is no longer running! } } }
@Throws(IOException::class) overridefunintercept(chain: Interceptor.Chain): Response { val realChain = chain as RealInterceptorChain var request = chain.request val call = realChain.call var followUpCount = 0 var priorResponse: Response? = null var newExchangeFinder = true var recoveredFailures = listOf<IOException>() while (true) { call.enterNetworkInterceptorExchange(request, newExchangeFinder)
var response: Response var closeActiveExchange = true try { if (call.isCanceled()) { throw IOException("Canceled") }
try { response = realChain.proceed(request) newExchangeFinder = true } catch (e: RouteException) { // The attempt to connect via a route failed. The request will not have been sent. if (!recover(e.lastConnectException, call, request, requestSendStarted = false)) { throw e.firstConnectException.withSuppressed(recoveredFailures) } else { recoveredFailures += e.firstConnectException } newExchangeFinder = false continue } catch (e: IOException) { // An attempt to communicate with a server failed. The request may have been sent. if (!recover(e, call, request, requestSendStarted = e !is ConnectionShutdownException)) { throw e.withSuppressed(recoveredFailures) } else { recoveredFailures += e } newExchangeFinder = false continue }
// Attach the prior response if it exists. Such responses never have a body. if (priorResponse != null) { response = response.newBuilder() .priorResponse(priorResponse.newBuilder() .body(null) .build()) .build() }
val exchange = call.interceptorScopedExchange val followUp = followUpRequest(response, exchange)
if (followUp == null) { if (exchange != null && exchange.isDuplex) { call.timeoutEarlyExit() } closeActiveExchange = false return response }
val followUpBody = followUp.body if (followUpBody != null && followUpBody.isOneShot()) { closeActiveExchange = false return response }
response.body?.closeQuietly()
if (++followUpCount > MAX_FOLLOW_UPS) { throw ProtocolException("Too many follow-up requests: $followUpCount") }
@Throws(IOException::class) overridefunintercept(chain: Interceptor.Chain): Response { val realChain = chain as RealInterceptorChain val exchange = realChain.exchange!! val request = realChain.request val requestBody = request.body val sentRequestMillis = System.currentTimeMillis()
exchange.writeRequestHeaders(request)
var invokeStartEvent = true var responseBuilder: Response.Builder? = null if (HttpMethod.permitsRequestBody(request.method) && requestBody != null) { // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100 // Continue" response before transmitting the request body. If we don't get that, return // what we did get (such as a 4xx response) without ever transmitting the request body. if ("100-continue".equals(request.header("Expect"), ignoreCase = true)) { exchange.flushRequest() responseBuilder = exchange.readResponseHeaders(expectContinue = true) exchange.responseHeadersStart() invokeStartEvent = false } if (responseBuilder == null) { if (requestBody.isDuplex()) { // Prepare a duplex body so that the application can send a request body later. exchange.flushRequest() val bufferedRequestBody = exchange.createRequestBody(request, true).buffer() requestBody.writeTo(bufferedRequestBody) } else { // Write the request body if the "Expect: 100-continue" expectation was met. val bufferedRequestBody = exchange.createRequestBody(request, false).buffer() requestBody.writeTo(bufferedRequestBody) bufferedRequestBody.close() } } else { exchange.noRequestBody() if (!exchange.connection.isMultiplexed) { // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection // from being reused. Otherwise we're still obligated to transmit the request body to // leave the connection in a consistent state. exchange.noNewExchangesOnConnection() } } } else { exchange.noRequestBody() }
if (requestBody == null || !requestBody.isDuplex()) { exchange.finishRequest() } if (responseBuilder == null) { responseBuilder = exchange.readResponseHeaders(expectContinue = false)!! if (invokeStartEvent) { exchange.responseHeadersStart() invokeStartEvent = false } } var response = responseBuilder .request(request) .handshake(exchange.connection.handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build() var code = response.code if (code == 100) { // Server sent a 100-continue even though we did not request one. Try again to read the actual // response status. responseBuilder = exchange.readResponseHeaders(expectContinue = false)!! if (invokeStartEvent) { exchange.responseHeadersStart() } response = responseBuilder .request(request) .handshake(exchange.connection.handshake()) .sentRequestAtMillis(sentRequestMillis) .receivedResponseAtMillis(System.currentTimeMillis()) .build() code = response.code }
exchange.responseHeadersEnd(response)
response = if (forWebSocket && code == 101) { // Connection is upgrading, but we need to ensure interceptors see a non-null response body. response.newBuilder() .body(EMPTY_RESPONSE) .build() } else { response.newBuilder() .body(exchange.openResponseBody(response)) .build() } if ("close".equals(response.request.header("Connection"), ignoreCase = true) || "close".equals(response.header("Connection"), ignoreCase = true)) { exchange.noNewExchangesOnConnection() } if ((code == 204 || code == 205) && response.body?.contentLength() ?: -1L > 0L) { throw ProtocolException( "HTTP $code had non-zero Content-Length: ${response.body?.contentLength()}") } return response }
客户端(如浏览器)先发起一个 HTTPS 请求,服务端响应这个请求,并发送给客户端 TA 的证书。浏览器检查 CA 证书是否合法。不合法浏览器会给出警告。合法则浏览器生成一个随机数,并用 CA 证书里服务端的公钥加密随机数。接着浏览器将用公钥加密后的随机数传给服务端,服务端用对应的私钥解密加密后的随机数,得到随机数。接下来服务端通过随机数对接下来的通信数据进行对称加密,并把加密后的数据传给客户端。之后的内容都通过对称加密传输。客户端有随机数,得到加密后的数据可以解密。
细节
建立连接具体细节
先是 TCP 3 次握手,然后客户端再发一个 Client Hello 的包,然后服务端响应一个 Server Hello,接着再给客户端发送证书。
Defines an object that has an Android Lifecycle. Fragment and FragmentActivity classes implement LifecycleOwner interface which has the getLifecycle method to access the Lifecycle. You can also implement LifecycleOwner in your own classes.
Event#ON_CREATE, Event#ON_START, Event#ON_RESUME events in this class are dispatched after the LifecycleOwner‘s related method returns. Event#ON_PAUSE, Event#ON_STOP, Event#ON_DESTROY events in this class are dispatched before the LifecycleOwner‘s related method is called. For instance, Event#ON_START will be dispatched after onStart returns, Event#ON_STOP will be dispatched before onStop is called. This gives you certain guarantees on which state the owner is in.
If you use Java 8 Language, then observe events with DefaultLifecycleObserver. To include it you should add "androidx.lifecycle:lifecycle-common-java8:<version>" to your build.gradle file.
1 2 3 4 5 6
classTestObserverimplementsDefaultLifecycleObserver{ @Override publicvoidonCreate(LifecycleOwner owner){ // your code } }
If you use Java 7 Language, Lifecycle events are observed using annotations. Once Java 8 Language becomes mainstream on Android, annotations will be deprecated, so between DefaultLifecycleObserver and annotations, you must always prefer DefaultLifecycleObserver.
Observer methods can receive zero or one argument. If used, the first argument must be of type LifecycleOwner. Methods annotated with Event#ON_ANY can receive the second argument, which must be of type Event.
publicstaticvoidinjectIfNeededIn(Activity activity){ if (Build.VERSION.SDK_INT >= 29) { // On API 29+, we can register for the correct Lifecycle callbacks directly activity.registerActivityLifecycleCallbacks( new LifecycleCallbacks()); } // Prior to API 29 and to maintain compatibility with older versions of // ProcessLifecycleOwner (which may not be updated when lifecycle-runtime is updated and // need to support activities that don't extend from FragmentActivity from support lib), // use a framework fragment to get the correct timing of Lifecycle events android.app.FragmentManager manager = activity.getFragmentManager(); if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) { manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit(); // Hopefully, we are the first to make a transaction. manager.executePendingTransactions(); } }
@Override publicvoidonDestroy(){ super.onDestroy(); dispatch(Lifecycle.Event.ON_DESTROY); // just want to be sure that we won't leak reference to an activity mProcessListener = null; }
在 ReportFragment 中调用这些生命周期方法来 dispatch 生命周期事件。
1 2 3 4 5 6 7 8
privatevoiddispatch(@NonNull Lifecycle.Event event){ if (Build.VERSION.SDK_INT < 29) { // Only dispatch events from ReportFragment on API levels prior // to API 29. On API 29+, this is handled by the ActivityLifecycleCallbacks // added in ReportFragment.injectIfNeededIn dispatch(getActivity(), event); } }
if (activity instanceof LifecycleOwner) { Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle(); if (lifecycle instanceof LifecycleRegistry) { ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event); } } }
LifecycleRegistry#handleLifecycleEvent
1 2 3 4
publicvoidhandleLifecycleEvent(@NonNull Lifecycle.Event event){ State next = getStateAfter(event); moveToState(next); }
getStateAfter 获得 event 后的 state:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
static State getStateAfter(Event event){ switch (event) { case ON_CREATE: case ON_STOP: return CREATED; case ON_START: case ON_PAUSE: return STARTED; case ON_RESUME: return RESUMED; case ON_DESTROY: return DESTROYED; case ON_ANY: break; } thrownew IllegalArgumentException("Unexpected event value " + event); }
LifecycleRegistry#moveToState
1 2 3 4 5 6 7 8 9 10 11 12 13 14
privatevoidmoveToState(State next){ if (mState == next) { return; } mState = next; if (mHandlingEvent || mAddingObserverCounter != 0) { mNewEventOccurred = true; // we will figure out what to do on upper level. return; } mHandlingEvent = true; sync(); mHandlingEvent = false; }
// happens only on the top of stack (never in reentrance), // so it doesn't have to take in account parents privatevoidsync(){ LifecycleOwner lifecycleOwner = mLifecycleOwner.get(); if (lifecycleOwner == null) { thrownew IllegalStateException("LifecycleOwner of this LifecycleRegistry is already" + "garbage collected. It is too late to change lifecycle state."); } while (!isSynced()) { mNewEventOccurred = false; // no need to check eldest for nullability, because isSynced does it for us. if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) { backwardPass(lifecycleOwner); } Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest(); if (!mNewEventOccurred && newest != null && mState.compareTo(newest.getValue().mState) > 0) { forwardPass(lifecycleOwner); } } mNewEventOccurred = false; }
ViewModel is a class that is responsible for preparing and managing the data for an Activity or a Fragment. It also handles the communication of the Activity / Fragment with the rest of the application (e.g. calling the business logic classes).
ViewModel 是一个负责准备和管理 Activity 或 Fragment 的数据的类。TA 也处理与应用中其它 Activity / Fragment 的交流。
A ViewModel is always created in association with a scope (a fragment or an activity) and will be retained as long as the scope is alive. E.g. if it is an Activity, until it is finished.
In other words, this means that a ViewModel will not be destroyed if its owner is destroyed for a configuration change (e.g. rotation). The new owner instance just re-connects to the existing model.
The purpose of the ViewModel is to acquire and keep the information that is necessary for an Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the ViewModel. ViewModels usually expose this information via LiveData or Android Data Binding. You can also use any observability construct from you favorite framework.
ViewModel 的目的是获得和保存 Activity / Fragment 的必要信息。Activity / Fragment 应该能 observe ViewModel 中的改变。ViewModel 经常通过 LiveData 或 Data Binding 暴露这些信息。你也可以使用任何你喜欢的框架中的 observability construct。
ViewModel’s only responsibility is to manage the data for the UI. It should never access your view hierarchy or hold a reference back to the Activity or the Fragment.
ViewModel 只负责管理 UI 的数据。TA 绝不应该访问 view,或保存 Activity / Fragment 的引用。
Typical usage from an Activity standpoint would be:
@Override protectedvoidonCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.user_activity_layout); final UserModel viewModel = new ViewModelProvider(this).get(UserModel.class); viewModel.getUser().observe(this, new Observer<User>() { @Override publicvoidonChanged(@Nullable User data){ // update ui. } }); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override publicvoidonClick(View v){ viewModel.doAction(); } }); } }
ViewModel would be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
publicclassUserModelextendsViewModel{ privatefinal MutableLiveData<User> userLiveData = new MutableLiveData<>();
public LiveData<User> getUser(){ return userLiveData; }
publicUserModel(){ // trigger user load. }
voiddoAction(){ // depending on the action, do necessary business logic calls and update the // userLiveData. } }
ViewModels can also be used as a communication layer between different Fragments of an Activity. Each Fragment can acquire the ViewModel using the same key via their Activity. This allows communication between Fragments in a de-coupled fashion such that they never need to talk to the other Fragment directly.
1 2 3 4 5 6
publicclassMyFragmentextendsFragment{ publicvoidonStart(){ UserModel userModel = new ViewModelProvider(requireActivity()).get(UserModel.class); } }
/** * Returns an existing ViewModel or creates a new one in the scope (usually, a fragment or * an activity), associated with this {@code ViewModelProvider}. * <p> * The created ViewModel is associated with the given scope and will be retained * as long as the scope is alive (e.g. if it is an activity, until it is * finished or process is killed). * * @param key The key to use to identify the ViewModel. * @param modelClass The class of the ViewModel to create an instance of it if it is not * present. * @param <T> The type parameter for the ViewModel. * @return A ViewModel that is an instance of the given type {@code T}. */ @SuppressWarnings("unchecked") @NonNull @MainThread public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass){ ViewModel viewModel = mViewModelStore.get(key);
/** * Clears internal storage and notifies ViewModels that they are no longer used. */ publicfinalvoidclear(){ for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); }
调用 clear 来通知 ViewModel 不再使用 TA 们了,且 clear HashMap<String, ViewModel>。
A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler it is bound to a Looper. It will deliver messages and runnables to that Looper’s message queue and execute them on that Looper’s thread.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
Scheduling messages is accomplished with the post(Runnable), postAtTime(java.lang.Runnable, long), postDelayed(Runnable, Object, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long) methods. The post versions allow you to enqueue Runnable objects to be called by the message queue when they are received; the sendMessage versions allow you to enqueue a Message object containing a bundle of data that will be processed by the Handler’s handleMessage(Message) method (requiring that you implement a subclass of Handler).
When posting or sending to a Handler, you can either allow the item to be processed as soon as the message queue is ready to do so, or specify a delay before it gets processed or absolute time for it to be processed. The latter two allow you to implement timeouts, ticks, and other timing-based behavior.
当 Handler post 或 send 时,你可以让 TA 们在 message queue 准备好去做时处理或是指定一个被处理前的延迟或指定处理的绝对时间。后两者允许你实现 timeout,ticks 等基于时间的行为。
When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing the top-level application objects (activities, broadcast receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will then be scheduled in the Handler’s message queue and processed when appropriate.
publicfinalbooleansendMessageDelayed(@NonNull Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
这里会把 time 设置为开机时间加上设置的 delayMillis。
1 2 3 4 5 6 7 8 9 10
publicbooleansendMessageAtTime(@NonNull Message msg, long uptimeMillis){ MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); returnfalse; } return enqueueMessage(queue, msg, uptimeMillis); }
booleanenqueueMessage(Message msg, long when){ if (msg.target == null) { thrownew IllegalArgumentException("Message must have a target."); }
synchronized (this) { if (msg.isInUse()) { thrownew IllegalStateException(msg + " This message is already in use."); }
if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); returnfalse; }
msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; }
// We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } returntrue; }
这个方法,将 msg 按照 uptimeMillis 组成一个链表,小的在前。mMessage 是头,如果为 null,将 msg 设为头。将 msg 按照 time 插到链表主要在 for 循环中,在 for 循环中找到 msg 的位置, msg.when >= p.when 的时候继续向后遍历。下面两行将 msg 插入链表。
Handler#dispatchMessage
handleCallback(msg) message.callback.run()
mCallback.handleMessage(msg)
handleMessage(msg) 默认是空方法,如果想要处理 msg 需要实现这个方法。
Looper
源码注释
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
Most interaction with a message loop is through the Handler class.
大多数与 message loop 的交互通过 Handler 类。
This is a typical example of the implementation of a Looper thread, using the separation of prepare() and loop() to create an initial Handler to communicate with the Looper.
Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.
定义一个包含描述和任意数据对象的 message,可以被 Handler 发送。这个对象包含两个额外的 int 域,和一个额外的对象域,允许你在大多数情况下不分配。
While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.
private static final int MAX_POOL_SIZE = 50; Message 缓存池最大 50 个。
MessageQueue
源码注释
Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper.
@UnsupportedAppUsage Message next(){ // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. finallong ptr = mPtr; if (ptr == 0) { returnnull; }
int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); }
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) { // Try to retrieve the next message. Return if found. finallong now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { ...
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID). For example, the class below generates unique identifiers local to each thread. A thread’s id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls.
这个类提供 thread-local 变量。这些变量与普通的对应变量不同的是,每个访问变量的线程(通过其 get 或 set 方法)都有自己的、独立初始化的变量副本。ThreadLocal 实例通常是希望将状态与线程关联起来的类中的私有静态字段(例如,用户 ID 或事务 ID )。 例如,下面的类为每个线程生成本地的唯一标识符。线程的 id 在它第一次调用 ThreadId.get() 时就会被分配,并在后续调用中保持不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import java.util.concurrent.atomic.AtomicInteger;
publicclassThreadId{ // Atomic integer containing the next thread ID to be assigned privatestaticfinal AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID privatestaticfinal ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Overrideprotected Integer initialValue(){ return nextId.getAndIncrement(); } }; // Returns the current thread's unique ID, assigning it if necessary publicstaticintget(){ return threadId.get(); } }
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
public T get(){ Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
e.get() : e 是一个 WeakReference,get 返回 ThreadLocal。
ThreadLocalMap#getEntry
1 2 3 4 5 6 7 8
private Entry getEntry(ThreadLocal<?> key){ int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }
// CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir);
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line. // It will be in the format "seq=114" long startSeq = 0; if (args != null) { for (int i = args.length - 1; i >= 0; --i) { if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) { startSeq = Long.parseLong( args[i].substring(PROC_START_SEQ_IDENT.length())); } } } ActivityThread thread = new ActivityThread(); // 建立Binder通道 (创建新线程) thread.attach(false, startSeq);
if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); }
if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); }
// End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop();
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View). While activities are often presented to the user as full-screen windows, they can also be used in other ways: as floating windows (via a theme with R.attr.windowIsFloating set), Multi-Window mode or embedded into other windows. There are two methods almost all subclasses of Activity will implement:
onCreate(Bundle) is where you initialize your activity. Most importantly, here you will usually call setContentView(int) with a layout resource defining your UI, and using findViewById(int) to retrieve the widgets in that UI that you need to interact with programmatically.
onPause() is where you deal with the user pausing active interaction with the activity. Any changes made by the user should at this point be committed (usually to the ContentProvider holding the data). In this state the activity is still visible on screen.
To be of use with Context.startActivity(), all activity classes must have a corresponding <activity> declaration in their package’s AndroidManifest.xml.
The Activity class is an important part of an application’s overall lifecycle, and the way activities are launched and put together is a fundamental part of the platform’s application model. For a detailed perspective on the structure of an Android application and how activities behave, please read the Application Fundamentals and Tasks and Back Stack developer guides.
You can also find a detailed discussion about how to create activities in the Activities developer guide.
Fragments
The FragmentActivity subclass can make use of the Fragment class to better modularize their code, build more sophisticated user interfaces for larger screens, and help scale their application between small and large screens.
FragmentActivity 子类可以让 Fragment 类更好地模块化 TA 们的代码,为大屏构建更复杂的 UI,帮助 TA 们的应用在大的小的屏幕上缩放。
For more information about using fragments, read the Fragments developer guide.
Activity Lifecycle
Activities in the system are managed as activity stacks. When a new activity is started, it is usually placed on the top of the current stack and becomes the running activity – the previous activity always remains below it in the stack, and will not come to the foreground again until the new activity exits. There can be one or multiple activity stacks visible on screen.
可以有一个或多个 Activity 栈在屏幕上可见。
An activity has essentially four states:
If an activity is in the foreground of the screen (at the highest position of the topmost stack), it is active or running. This is usually the activity that the user is currently interacting with.
If an activity has lost focus but is still presented to the user, it is visible. It is possible if a new non-full-sized or transparent activity has focus on top of your activity, another activity has higher position in multi-window mode, or the activity itself is not focusable in current windowing mode. Such activity is completely alive (it maintains all state and member information and remains attached to the window manager).
If an activity is completely obscured by another activity, it is stopped or hidden. It still retains all state and member information, however, it is no longer visible to the user so its window is hidden and it will often be killed by the system when memory is needed elsewhere.
The system can drop the activity from memory by either asking it to finish, or simply killing its process, making it destroyed. When it is displayed again to the user, it must be completely restarted and restored to its previous state.
The following diagram shows the important state paths of an Activity. The square rectangles represent callback methods you can implement to perform operations when the Activity moves between states. The colored ovals are major states the Activity can be in.
There are three key loops you may be interested in monitoring within your activity:
The entire lifetime of an activity happens between the first call to onCreate(Bundle) through to a single final call to onDestroy(). An activity will do all setup of “global” state in onCreate(), and release all remaining resources in onDestroy(). For example, if it has a thread running in the background to download data from the network, it may create that thread in onCreate() and then stop the thread in onDestroy().
The visible lifetime of an activity happens between a call to onStart() until a corresponding call to onStop(). During this time the user can see the activity on-screen, though it may not be in the foreground and interacting with the user. Between these two methods you can maintain resources that are needed to show the activity to the user. For example, you can register a BroadcastReceiver in onStart() to monitor for changes that impact your UI, and unregister it in onStop() when the user no longer sees what you are displaying. The onStart() and onStop() methods can be called multiple times, as the activity becomes visible and hidden to the user.
The foreground lifetime of an activity happens between a call to onResume() until a corresponding call to onPause(). During this time the activity is in visible, active and interacting with the user. An activity can frequently go between the resumed and paused states – for example when the device goes to sleep, when an activity result is delivered, when a new intent is delivered – so the code in these methods should be fairly lightweight.
The entire lifecycle of an activity is defined by the following Activity methods. All of these are hooks that you can override to do appropriate work when the activity changes state. All activities will implement onCreate(Bundle) to do their initial setup; many will also implement onPause() to commit changes to data and prepare to pause interacting with the user, and onStop() to handle no longer being visible on screen. You should always call up to your superclass when implementing these methods.
In general the movement through an activity’s lifecycle looks like this:
Method
Description
Killable?
Next
onCreate()
Called when the activity is first created. This is where you should do all of your normal static set up: create views, bind data to lists, etc. This method also provides you with a Bundle containing the activity’s previously frozen state, if there was one. Always followed by onStart().这个方法也提供一个 Bundle,包含 Activity 之前冻结的动态,如果有。
No
onStart()
onRestart()
Called after your activity has been stopped, prior to it being started again. Always followed by onStart()在 Activity 停止后,在 TA 重启之前。总是在 onStart() 之后。
No
onStart()
onStart()
Called when the activity is becoming visible to the user. Followed by onResume() if the activity comes to the foreground, or onStop() if it becomes hidden. 当 Activity 变得可见之时调用。如果 Activity 从后台返回前台, onResume() 会在之后调用。如果变得隐藏,onStop() 会接着调用。
onResume() or onStop()
onResume()
Called when the activity will start interacting with the user. At this point your activity is at the top of its activity stack, with user input going to it. Always followed by onPause().当 Activity 将开始与用户交互时调用。在这个时刻,你的 Activity 在栈顶,用户正在输入。后面总是跟着 onPause()。
No
onPause()
onPause()
Called when the activity loses foreground state, is no longer focusable or before transition to stopped/hidden or destroyed state. The activity is still visible to user, so it’s recommended to keep it visually active and continue updating the UI. Implementations of this method must be very quick because the next activity will not be resumed until this method returns. Followed by either onResume() if the activity returns back to the front, or onStop() if it becomes invisible to the user.
Pre-Build.VERSION_CODES.HONEYCOMB
onResume() or onStop()
onStop()
Called when the activity is no longer visible to the user. This may happen either because a new activity is being started on top, an existing one is being brought in front of this one, or this one is being destroyed. This is typically used to stop animations and refreshing the UI, etc. Followed by either onRestart() if this activity is coming back to interact with the user, or onDestroy() if this activity is going away.
Yes
onRestart() or onDestroy()
onDestroy()
The final call you receive before your activity is destroyed. This can happen either because the activity is finishing (someone called Activity#finish on it), or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
Yes
nothing
Note the “Killable” column in the above table – for those methods that are marked as being killable, after that method returns the process hosting the activity may be killed by the system at any time without another line of its code being executed. Because of this, you should use the onPause() method to write any persistent data (such as user edits) to storage. In addition, the method onSaveInstanceState(android.os.Bundle) is called before placing the activity in such a background state, allowing you to save away any dynamic instance state in your activity into the given Bundle, to be later received in onCreate(Bundle) if the activity needs to be re-created. See the Process Lifecycle section for more information on how the lifecycle of a process is tied to the activities it is hosting. Note that it is important to save persistent data in onPause() instead of onSaveInstanceState(Bundle) because the latter is not part of the lifecycle callbacks, so will not be called in every situation as described in its documentation.
Be aware that these semantics will change slightly between applications targeting platforms starting with Build.VERSION_CODES.HONEYCOMB vs. those targeting prior platforms. Starting with Honeycomb, an application is not in the killable state until its onStop() has returned. This impacts when onSaveInstanceState(android.os.Bundle) may be called (it may be safely called after onPause()) and allows an application to safely wait until onStop() to save persistent state.
For applications targeting platforms starting with Build.VERSION_CODES.PonSaveInstanceState(android.os.Bundle) will always be called after onStop(), so an application may safely perform fragment transactions in onStop() and will be able to save persistent state later.
For those methods that are not marked as being killable, the activity’s process will not be killed by the system starting from the time the method is called and continuing after it returns. Thus an activity is in the killable state, for example, between after onStop() to the start of onResume(). Keep in mind that under extreme memory pressure the system can kill the application process at any time.
Configuration Changes
If the configuration of the device (as defined by the Resources.Configuration class) changes, then anything displaying a user interface will need to update to match that configuration. Because Activity is the primary mechanism for interacting with the user, it includes special support for handling configuration changes.
Unless you specify otherwise, a configuration change (such as a change in screen orientation, language, input devices, etc) will cause your current activity to be destroyed, going through the normal activity lifecycle process of onPause(), onStop(), and onDestroy() as appropriate. If the activity had been in the foreground or visible to the user, once onDestroy() is called in that instance then a new instance of the activity will be created, with whatever savedInstanceState the previous instance had generated from onSaveInstanceState(Bundle).
This is done because any application resource, including layout files, can change based on any configuration value. Thus the only safe way to handle a configuration change is to re-retrieve all resources, including layouts, drawables, and strings. Because activities must already know how to save their state and re-create themselves from that state, this is a convenient way to have an activity restart itself with a new configuration.
In some special cases, you may want to bypass restarting of your activity based on one or more types of configuration changes. This is done with the android:configChanges attribute in its manifest. For any types of configuration changes you say that you handle there, you will receive a call to your current activity’s onConfigurationChanged(Configuration) method instead of being restarted. If a configuration change involves any that you do not handle, however, the activity will still be restarted and onConfigurationChanged(Configuration) will not be called.
The startActivity(Intent) method is used to start a new activity, which will be placed at the top of the activity stack. It takes a single argument, an Intent, which describes the activity to be executed.
Sometimes you want to get a result back from an activity when it ends. For example, you may start an activity that lets the user pick a person in a list of contacts; when it ends, it returns the person that was selected. To do this, you call the startActivityForResult(Intent, int) version with a second integer parameter identifying the call. The result will come back through your onActivityResult(int, int, Intent) method.
When an activity exits, it can call setResult(int) to return data back to its parent. It must always supply a result code, which can be the standard results RESULT_CANCELED, RESULT_OK, or any custom values starting at RESULT_FIRST_USER. In addition, it can optionally return back an Intent containing any additional data it wants. All of this information appears back on the parent’s Activity.onActivityResult(), along with the integer identifier it originally supplied.
当一个 Activity 退出时,TA 可以调用 setResult(int) 来返回数据给父 Activity。方法必须提供一个 result code,可以是标准的结果 RESULT_CANCELED,RESULT_OK,或是任何自定义的值,从 RESULT_FIREST_USER 开始。此外,还可以选择返回一个包含任何想要的数据的 Intent。所有的这些信息在父 Activity.onActivityResut() 方法中出现,随着 TA 初始提供的 integer 标识符。
If a child activity fails for any reason (such as crashing), the parent activity will receive a result with the code RESULT_CANCELED.
publicbooleanonKeyDown(int keyCode, KeyEvent event){ if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { // When the user center presses, let them pick a contact. startActivityForResult( new Intent(Intent.ACTION_PICK, new Uri("content://contacts")), PICK_CONTACT_REQUEST); returntrue; } returnfalse; }
protectedvoidonActivityResult(int requestCode, int resultCode, Intent data){ if (requestCode == PICK_CONTACT_REQUEST) { if (resultCode == RESULT_OK) { // A contact was picked. Here we will just display it // to the user. startActivity(new Intent(Intent.ACTION_VIEW, data)); } } } }
Saving Persistent State
There are generally two kinds of persistent state that an activity will deal with: shared document-like data (typically stored in a SQLite database using a content provider) and internal state such as user preferences.
For content provider data, we suggest that activities use an “edit in place” user model. That is, any edits a user makes are effectively made immediately without requiring an additional confirmation step. Supporting this model is generally a simple matter of following two rules:
When creating a new document, the backing database entry or file for it is created immediately. For example, if the user chooses to write a new email, a new entry for that email is created as soon as they start entering data, so that if they go to any other activity after that point this email will now appear in the list of drafts.
When an activity’s onPause() method is called, it should commit to the backing content provider or file any changes the user has made. This ensures that those changes will be seen by any other activity that is about to run. You will probably want to commit your data even more aggressively at key times during your activity’s lifecycle: for example before starting a new activity, before finishing your own activity, when the user switches between input fields, etc.
This model is designed to prevent data loss when a user is navigating between activities, and allows the system to safely kill an activity (because system resources are needed somewhere else) at any time after it has been stopped (or paused on platform versions before Build.VERSION_CODES.HONEYCOMB). Note this implies that the user pressing BACK from your activity does not mean “cancel” – it means to leave the activity with its current contents saved away. Canceling edits in an activity must be provided through some other mechanism, such as an explicit “revert” or “undo” option.
See the content package for more information about content providers. These are a key aspect of how different activities invoke and propagate data between themselves.
The Activity class also provides an API for managing internal persistent state associated with an activity. This can be used, for example, to remember the user’s preferred initial display in a calendar (day view or week view) or the user’s default home page in a web browser.
Activity persistent state is managed with the method getPreferences(int), allowing you to retrieve and modify a set of name/value pairs associated with the activity. To use preferences that are shared across multiple application components (activities, receivers, services, providers), you can use the underlying Context.getSharedPreferences() method to retrieve a preferences object stored under a specific name. (Note that it is not possible to share settings data across application packages – for that you will need a content provider.)
Here is an excerpt from a calendar activity that stores the user’s preferred view mode in its persistent settings:
SharedPreferences.Editor ed = mPrefs.edit(); ed.putInt("view_mode", mCurViewMode); ed.commit(); } }
Permissions
The ability to start a particular Activity can be enforced when it is declared in its manifest’s <activity> tag. By doing so, other applications will need to declare a corresponding <uses-permission> element in their own manifest to be able to start that activity.
When starting an Activity you can set Intent.FLAG_GRANT_READ_URI_PERMISSION and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION on the Intent. This will grant the Activity access to the specific URIs in the Intent. Access will remain until the Activity has finished (it will remain across the hosting process being killed and other temporary destruction). As of Build.VERSION_CODES.GINGERBREAD, if the Activity was already created and a new Intent is being delivered to onNewIntent(android.content.Intent), any newly granted URI permissions will be added to the existing ones it holds.
See the Security and Permissions document for more information on permissions and security in general.
Process Lifecycle
The Android system attempts to keep an application process around for as long as possible, but eventually will need to remove old processes when memory runs low. As described in Activity Lifecycle, the decision about which process to remove is intimately tied to the state of the user’s interaction with it. In general, there are four states a process can be in based on the activities running in it, listed here in order of importance. The system will kill less important processes (the last ones) before it resorts to killing more important processes (the first ones).
The foreground activity (the activity at the top of the screen that the user is currently interacting with) is considered the most important. Its process will only be killed as a last resort, if it uses more memory than is available on the device. Generally at this point the device has reached a memory paging state, so this is required in order to keep the user interface responsive.
A visible activity (an activity that is visible to the user but not in the foreground, such as one sitting behind a foreground dialog or next to other activities in multi-window mode) is considered extremely important and will not be killed unless that is required to keep the foreground activity running.
A background activity (an activity that is not visible to the user and has been stopped) is no longer critical, so the system may safely kill its process to reclaim memory for other foreground or visible processes. If its process needs to be killed, when the user navigates back to the activity (making it visible on the screen again), its onCreate(Bundle) method will be called with the savedInstanceState it had previously supplied in onSaveInstanceState(Bundle) so that it can restart itself in the same state as the user last left it.
An empty process is one hosting no activities or other application components (such as Service or BroadcastReceiver classes). These are killed very quickly by the system as memory becomes low. For this reason, any background operation you do outside of an activity must be executed in the context of an activity BroadcastReceiver or Service to ensure that the system knows it needs to keep your process around.
Sometimes an Activity may need to do a long-running operation that exists independently of the activity lifecycle itself. An example may be a camera application that allows you to upload a picture to a web site. The upload may take a long time, and the application should allow the user to leave the application while it is executing. To accomplish this, your Activity should start a Service in which the upload takes place. This allows the system to properly prioritize your process (considering it to be more important than other non-visible applications) for the duration of the upload, independent of whether the original activity is paused, stopped, or finished.
voidscheduleTransaction(ClientTransaction transaction)throws RemoteException { final IApplicationThread client = transaction.getClient(); transaction.schedule(); if (!(client instanceof Binder)) { // If client is not an instance of Binder - it's a remote call and at this point it is // safe to recycle the object. All objects used for local calls will be recycled after // the transaction is executed on client in ActivityThread. transaction.recycle(); } }
/** Prepare and schedule transaction for execution. */ voidscheduleTransaction(ClientTransaction transaction){ transaction.preExecute(this); sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction); }
sendMessage 在父类中是抽象方法。
ActivityThread#H#handleMessage
1 2 3 4 5 6 7 8 9 10 11
case EXECUTE_TRANSACTION: final ClientTransaction transaction = (ClientTransaction) msg.obj; mTransactionExecutor.execute(transaction); if (isSystem()) { // Client transactions inside system process are recycled on the client side // instead of ClientLifecycleManager to avoid being cleared before this // message is handled. transaction.recycle(); } // TODO(lifecycler): Recycle locally scheduled transactions. break;
if (low_thresh > 0) low_thresh *= low_thresh; if (high_thresh > 0) high_thresh *= high_thresh; } int low = cvFloor(low_thresh); int high = cvFloor(high_thresh);
// If Scharr filter: aperture size is 3, ksize2 is 1 int ksize2 = aperture_size < 0 ? 1 : aperture_size / 2; // 计算可用的线程数 int numOfThreads = std::max(1, std::min(getNumThreads(), getNumberOfCPUs())); // Make a fallback for pictures with too few rows. int grainSize = src.rows / numOfThreads; int minGrainSize = 2 * (ksize2 + 1); if (grainSize < minGrainSize) numOfThreads = std::max(1, src.rows / minGrainSize);
while (!stack.empty()) { uchar* m = stack.back(); stack.pop_back();
if (!m[-mapstep-1]) CANNY_PUSH((m-mapstep-1), stack); if (!m[-mapstep]) CANNY_PUSH((m-mapstep), stack); if (!m[-mapstep+1]) CANNY_PUSH((m-mapstep+1), stack); if (!m[-1]) CANNY_PUSH((m-1), stack); if (!m[1]) CANNY_PUSH((m+1), stack); if (!m[mapstep-1]) CANNY_PUSH((m+mapstep-1), stack); if (!m[mapstep]) CANNY_PUSH((m+mapstep), stack); if (!m[mapstep+1]) CANNY_PUSH((m+mapstep+1), stack); }
// now track the edges (hysteresis thresholding) CV_TRACE_REGION_NEXT("hysteresis"); while (!stack.empty()) { uchar *m = stack.back(); stack.pop_back(); // 如果不是位于边界 if((unsigned)(m - pmapLower) < pmapDiff) { if (!m[-mapstep-1]) CANNY_PUSH((m-mapstep-1), stack); if (!m[-mapstep]) CANNY_PUSH((m-mapstep), stack); if (!m[-mapstep+1]) CANNY_PUSH((m-mapstep+1), stack); if (!m[-1]) CANNY_PUSH((m-1), stack); if (!m[1]) CANNY_PUSH((m+1), stack); if (!m[mapstep-1]) CANNY_PUSH((m+mapstep-1), stack); if (!m[mapstep]) CANNY_PUSH((m+mapstep), stack); if (!m[mapstep+1]) CANNY_PUSH((m+mapstep+1), stack); } else { // 处理边界 borderPeaksLocal.push_back(m); ptrdiff_t mapstep2 = m < pmapLower ? mapstep : -mapstep;
if (!m[-1]) CANNY_PUSH((m-1), stack); if (!m[1]) CANNY_PUSH((m+1), stack); if (!m[mapstep2-1]) CANNY_PUSH((m+mapstep2-1), stack); if (!m[mapstep2]) CANNY_PUSH((m+mapstep2), stack); if (!m[mapstep2+1]) CANNY_PUSH((m+mapstep2+1), stack); } }
最后一步,将边缘点(标记为2)映射为灰度值 255(白),其它映射到 0:
1 2 3 4 5 6 7 8 9 10 11 12
// the final pass, form the final image for (int i = boundaries.start; i < boundaries.end; i++) { int j = 0; uchar *pdst = dst.ptr<uchar>(i); const uchar *pmap = map.ptr<uchar>(i + 1); pmap += 1; for (; j < dst.cols; j++) { pdst[j] = (uchar)-(pmap[j] >> 1); } }
所以现在剩下的就是这些包装文件的编译了,这给了我们cv2模块。因此,当你调用函数时,例如在Python中说res = equalizeHist(img1,img2),你将传递两个numpy数组,并期望另一个numpy数组作为输出。因此,将这些numpy数组转换为cv::Mat,然后在C++中调用equalizeHist()函数。最终结果将res转换回Numpy数组。简而言之,几乎所有操作都是在 C++ 中完成的,这给了我们几乎与C++相同的速度。
static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h;
cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; }
publicstaticvoidmain(String[] args){ Integer a = new Integer(127); Integer b = new Integer(127); int c = new Integer(127); int d = new Integer(127); int e = new Integer(128); int f = new Integer(128); Integer g = 128; Integer h = 128; Integer i = 127; Integer j = 127; Integer k = 1; Integer l = 2; Integer m = 3; Long n = 3L; System.out.println(a == b); // false System.out.println(a.equals(b)); // true System.out.println(c == d); // true System.out.println(e == f); // true System.out.println(g == h); // false System.out.println(i == j); // true System.out.println(m == (k + l)); // true System.out.println(m.equals(k + l)); // true System.out.println(n == (k + l)); // true System.out.println(n.equals(a + b)); // false }
Compiled from "Test.java" public class stack.Test { public stack.Test(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return
对于加减法的控制,你是采取何种⽅式实现的? 由于补码的性质,减法可以通过加法实现,只需将减数的补码再次求补送入加法器即可实现减法运算。因此,引入 Sub 信号,当 Sub 为 0 时,送入加法器的是 Y 本身,此时实现加法操作;当 Sub 为 1 时,通过异或门取反,送入加法器的是 Y 的反码,且 Sub 也送入了加法器最低位的进位输入,即对 Y 实现了取反加一的操作,完成了求补过程,从而完成减法操作。
“Failed to commit transaction (conflicting files)” 错误
error: could not prepare transaction error: failed to commit transaction (conflicting files) package: /usr/lib/node_modules/node-gyp/.github/workflows/tests.yml exists in filesystem Errors occurred, no packages were upgraded.
再 sudo pacman -Syu 但遇到了 wanring warning: could not get file information for usr/lib/node_modules/node-gyp/node_modules/safer-buffer/Porting-Buffer.md warning: could not get file information for usr/lib/node_modules/node-gyp/node_modules/safer-buffer/Readme.md warning: could not get file information for usr/lib/node_modules/node-gyp/node_modules/safer-buffer/dangerous.js warning: could not get file information for usr/lib/node_modules/node-gyp/node_modules/safer-buffer/package.json … … warning: could not get file information for usr/lib/node_modules/node-gyp/test/test-install.js warning: could not get file information for usr/lib/node_modules/node-gyp/test/test-options.js warning: could not get file information for usr/lib/node_modules/node-gyp/test/test-process-release.js warning: could not get file information for usr/lib/node_modules/node-gyp/update-gyp.py
Plasma can autostart applications and run scripts on startup and shutdown. To autostart an application, navigate to System Settings > Startup and Shutdown > Autostart and add the program or shell script of your choice.
Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
This implementation provides constant-time performance for the basic operations (get and put), assuming the hash function disperses the elements properly among the buckets. Iteration over collection views requires time proportional to the “capacity” of the HashMap instance (the number of buckets) plus its size (the number of key-value mappings). Thus, it’s very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.
这个实现提供基本操作( get 和 put )的常数时间,假设 hash 函数将元素合适地分配到 bucket 中。遍历集合需要和 HashMap 实例的容量( capacity )( bucket 的数量)+ TA 的大小( size )( key-value 映射的数量)成比例的时间。因此,如果遍历的性能很重要,不要设置太大的 capacity (或太小的 load factor)。
An instance of HashMap has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets.
As a general rule, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup cost (reflected in most of the operations of the HashMap class, including get and put). The expected number of entries in the map and its load factor should be taken into account when setting its initial capacity, so as to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of entries divided by the load factor, no rehash operations will ever occur.
If many mappings are to be stored in a HashMap instance, creating it with a sufficiently large capacity will allow the mappings to be stored more efficiently than letting it perform automatic rehashing as needed to grow the table. Note that using many keys with the same hashCode() is a sure way to slow down performance of any hash table. To ameliorate impact, when keys are Comparable, this class may use comparison order among keys to help break ties.
Note that this implementation is not synchronized. If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more mappings; merely changing the value associated with a key that an instance already contains is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be “wrapped” using the Collections.synchronizedMap method. This is best done at creation time, to prevent accidental unsynchronized access to the map:
1
Map m = Collections.synchronizedMap(new HashMap(...));
The iterators returned by all of this class’s “collection view methods” are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator’s own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
/** * Splits nodes in a tree bin into lower and upper tree bins, * or untreeifies if now too small. Called only from resize; * see above discussion about split bits and indices. * * @param map the map * @param tab the table for recording bin heads * @param index the index of the table being split * @param bit the bit of hash to split on */ finalvoidsplit(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit){ TreeNode<K,V> b = this; // Relink into lo and hi lists, preserving order // 将节点分为两个 list TreeNode<K,V> loHead = null, loTail = null; TreeNode<K,V> hiHead = null, hiTail = null; int lc = 0, hc = 0; for (TreeNode<K,V> e = b, next; e != null; e = next) { next = (TreeNode<K,V>)e.next; e.next = null; // (e.hash & bit) == 0 划分根据 // bit 是 oldCap if ((e.hash & bit) == 0) { if ((e.prev = loTail) == null) loHead = e; else loTail.next = e; loTail = e; // 记录节点数量 ++lc; } else { if ((e.prev = hiTail) == null) hiHead = e; else hiTail.next = e; hiTail = e; ++hc; } }
if (loHead != null) { // 根据节点数量是否 treeify if (lc <= UNTREEIFY_THRESHOLD) tab[index] = loHead.untreeify(map); else { tab[index] = loHead; if (hiHead != null) // (else is already treeified) loHead.treeify(tab); // else hiHead == null 时,表明扩容后, // 所有节点仍在原位置,树结构不变,无需重新树化 } } if (hiHead != null) { if (hc <= UNTREEIFY_THRESHOLD) tab[index + bit] = hiHead.untreeify(map); else { tab[index + bit] = hiHead; if (loHead != null) hiHead.treeify(tab); } } }