Still a bit of a miscalculation, over the word limit, can only be divided into two to share.
The subsequent NDK, cross-platform, underlying source code and other technologies will also take time to sort out the knowledge points for you. If you like it, I hope you will pay attention to it and give me a thumbs up. If you have contact information about your home page, please find me to get the PDF version of the study guide.
The working principle of
ThreadLocal
ThreadLocal is an internal data store class that allows you to store data in a specified thread that cannot be retrieved by other threads. ThreadLocal is used in Looper, ActivityThread, and AMS. When different threads access the get method of the same ThreadLocal, an array will be extracted from each thread inside ThreadLocal, and then the corresponding value will be searched from the array according to the index of the current ThreadLcoal. ThreadLocal.java
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map ! = null) map.set(this, value); else createMap(t, value); } ··· · 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(); }Copy the code
MessageQueue
MessageQueue consists of two operations: insert and read. The read operation itself is accompanied by a delete operation, and the insert and read methods are enqueueMessage and Next, respectively. The internal implementation of MessageQueue is not a queue, but actually maintains a list of messages through a single linked list data structure. The next method is an infinite loop and blocks if there are no messages in the message queue. When a new message arrives, the next method puts it back and removes it from the singly linked list.
MessageQueue.java
Boolean enqueueMessage(Message MSG, long when) {··· synchronized (this) {··· 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); } } return true; } ·· 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. ··· for (;) {·· synchronized (this) {// Try to retrieve the next message. Return if found. Final long 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 { // Got a message. mBlocked = false; if (prevMsg ! = null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } ···} // Run the idle handlers. // We only ever reach this code block during the first iteration. i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (! keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; }}Copy the code
Looper
Looper will constantly check MessageQueue for new messages, and if there are new messages it will process them immediately, otherwise it will block forever. Looper.java
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Copy the code
Create a Looper for the current thread with looper.prepare () :
new Thread("Thread#2") {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();
Copy the code
In addition to the prepare method, Looper also provides a prepareMainLooper method, which is used to create loOpers for activityThreads. Since the main thread Looper is special, Looper provides a getMainLooper method to get the main thread Looper.
Looper provides quit and quitSafely to exit a Looper. The difference between quit and quitSafly is that quit exits the Looper directly, whereas quitSafly simply sets an exit flag and processes the existing message queue before exiting safely. After Looper exits, messages sent by Handler will fail, and Handler’s send method will return false. Therefore, Looper should be terminated when no longer needed.
Looper.java
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; ... the for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; }... try {MSG. Target. DispatchMessage (MSG); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag ! = 0) { Trace.traceEnd(traceTag); }}... MSG. RecycleUnchecked (); }}Copy the code
The loop method is an infinite loop, and the only way out of the loop is if MessageQueue’s next method returns NULL. When Looper’s quit method is called, Looper calls MessageQueue’s quit or qutiSafely method to notify the MessageQueue to quit, and its next method returns null when the MessageQueue is marked quit. The loop method calls MessageQueue’s Next method to get a new message, and next is a blocking operation. When there is no message, next will block forever, causing the loop method to block forever. Which deal with the news: MSG. Target. DispatchMessage (MSG), the MSG here. The target is to send the message Handler object.
Handler
The Handler sends and receives messages. Messages can be sent through a series of post/send methods, and post is ultimately implemented through send.
Thread asynchronous
A thread is the smallest unit in which an operating system can schedule operations. It is contained within the process and is the actual operating unit within the process.
When the application starts, the system creates a thread of execution (UI thread) named “main thread” for the application. This thread is important because it is responsible for dispatching events to the appropriate user interface widgets, including drawing events. In addition, it is the thread through which the application interacts with Android UI toolkit components (components from the Android.Widget and Android.View packages).
The system does not create a separate thread for each component instance. All components running in the same process are instantiated in the UI thread, and system calls to each component are dispatched by that thread. Therefore, methods that respond to system callbacks (for example, onKeyDown() or lifecycle callback methods that report user actions) always run in the UI thread of the process.
Android’s single-threaded mode must follow two rules:
- Don’t block the UI thread
- Do not access the Android UI toolkit outside of the UI thread
To solve this problem, Android provides several ways to access the UI thread from other threads:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
AsyncTask
AsyncTask encapsulates Thread and Handler. Therefore, it is not suitable for background tasks that are time-consuming. For such tasks, Thread pools are recommended.
The basic use
methods | instructions |
---|---|
onPreExecute() | Call before an asynchronous task is executed to make preparations |
doInBackground(Params… params) | For performing asynchronous tasks, the progress of the task can be updated with the publishProgress method, which calls the onProgressUpdate method |
onProgressUpdate | Called when the execution progress of background tasks changes in the main thread |
onPostExecute | Executed in the main thread, after the asynchronous task is executed |
import android.os.AsyncTask; public class DownloadTask extends AsyncTask<String, Integer, Boolean> { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Boolean doInBackground(String... strings) { return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } @Override protected void onPostExecute(Boolean aBoolean) { super.onPostExecute(aBoolean); }}Copy the code
- Instances of asynchronous tasks must be created in the UI thread, that is, AsyncTask objects must be created in the UI thread.
- execute(Params… The params method must be called in the UI thread.
- Do not call onPreExecute(), doInBackground(), onProgressUpdate(), and onPostExecute() manually.
- You cannot change UI component information in doInBackground().
- A task instance can only be executed once, and a second execution will throw an exception.
- The execute() method causes asyncTasks in the same process to be executed serially, and if parallelism is required, the executeOnExcutor method can be called.
The working principle of
AsyncTask.java
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } @MainThread public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) { if (mStatus ! = Status.PENDING) { switch (mStatus) { case RUNNING: throw new IllegalStateException("Cannot execute task:" + " the task is already running."); case FINISHED: throw new IllegalStateException("Cannot execute task:" + " the task has already been executed " + "(a task can be executed only once)"); } } mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); return this; }Copy the code
SDefaultExecutor is a serial thread pool in which all asynctasks in a process are executed. AysncTask has two thread pools (SerialExecutor and THREAD_POOL_EXECUTOR) and one Handler (InternalHandler). The thread pool SerialExecutor is used to queue tasks. THREAD_POOL_EXECUTOR is used to actually execute the task, and InternalHandler is used to switch the execution environment from the thread pool to the main thread.
AsyncTask.java
private static Handler getMainHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(Looper.getMainLooper()); } return sHandler; } } private static class InternalHandler extends Handler { public InternalHandler(Looper looper) { super(looper); } @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override public void handleMessage(Message msg) { AsyncTaskResult<? > result = (AsyncTaskResult<? >) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } } private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; }Copy the code
HandlerThread
HandlerThread integrates threads, but is significantly different from regular threads. A common Thread is used to execute a time-consuming task in the RUN method, while a HandlerThread creates a message queue internally. The external world needs to notify the HanderThread to execute a specific task through the Handler message.
HandlerThread.java
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
Copy the code
IntentService
IntentService can be used to execute time-consuming background tasks. After the task is executed, it automatically stops. Because it is a Service, its priority is higher than that of a simple thread. Implementatively, IntentService encapsulates HandlerThread and Handler.
IntentService.java
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
Copy the code
When IntentService is first started, it creates a HandlerThread in the onCreatea method and then uses Looper to construct a Handler object, mServiceHandler, Messages sent through the mServiceHandler will eventually be executed in the HandlerThread. Every time I start the IntentService, it onStartCommand method is called once, handle each background tasks in onStartCommand Intent, onStartCommand calls the onStart method: IntentService.java
private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1); }} ··· @override public void onStart(@override Intent Intent) int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); }Copy the code
As you can see, the IntentService simply sends a message through the mServiceHandler, which is processed in the HandlerThread. After receiving the message, the mServiceHandler passes the Intent object to the onHandlerIntent method for processing. After executing the Intent, the mServiceHandler attempts to stop the service by using stopSelf(int startId). StopSelf () stops the service immediately, while stopSelf(int startId) stops the service until all messages have been processed.
The thread pool
Thread pools have the following advantages:
- Reuse threads in the thread pool to avoid performance overhead due to thread creation and destruction.
- It can effectively control the maximum number of concurrent threads in the thread pool and avoid the blocking phenomenon caused by the mutual preemption of system resources between a large number of threads.
- It can manage threads and provide functions such as timed execution and spaced loop execution.
In Java, ThreadPoolExecutor is the real implementation of thread pools:
ThreadPoolExecutor.java
/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters @param maximumPoolSize Maximum number of threads * @param keepAliveTime Timeout for non-core threads * @param unit Unit used to specify keepAliveTime * @param task queue, The Runnable object submitted via the thread pool's execute method is stored in this parameter * @param threadFactory threadFactory, */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler Handler) {···}Copy the code
type | Create method | instructions |
---|---|---|
FixedThreadPool | Executors.newFixedThreadPool(int nThreads) | A thread pool with a fixed number of threads, only core threads and no collection, no timeout mechanism |
CachedThreadPool | Executors.newCachedThreadPool() | A thread pool with an indefinite number of threads. Only non-core threads are created to handle new tasks when all threads are active. Otherwise, idle threads are used, with a timeout of 60 seconds |
ScheduledThreadPool | Executors.newScheduledThreadPool(int corePoolSize) | The number of core threads is fixed, and there is no limit on the number of non-core threads. Non-core threads are recycled immediately when they are idle, and are mainly used to perform scheduled tasks and repetitive tasks in a fixed period |
SingleThreadExecutor | Executors.newSingleThreadExecutor() | There is only one core thread, ensuring that all tasks are executed sequentially in the same thread |
RecyclerView optimization
- Separation of data processing and view loading: The data processing logic is handled asynchronously whenever possible, and the onBindViewHolder method only handles data filling into the view.
- Data optimization: Pull remote data in pages and cache the pulled remote data to improve the secondary loading speed; For new or deleted data, DiffUtil refreshes data locally rather than globally.
The sample
public class AdapterDiffCallback extends DiffUtil.Callback {
private List<String> mOldList;
private List<String> mNewList;
public AdapterDiffCallback(List<String> oldList, List<String> newList) {
mOldList = oldList;
mNewList = newList;
DiffUtil.DiffResult
}
@Override
public int getOldListSize() {
return mOldList.size();
}
@Override
public int getNewListSize() {
return mNewList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).getClass().equals(mNewList.get(newItemPosition).getClass());
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition));
}
}
Copy the code
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new AdapterDiffCallback(oldList, newList));
diffResult.dispatchUpdatesTo(mAdapter);
Copy the code
- Layout optimization: Reduce layout levels and simplify ItemView
- Upgrade RecycleView to 25.1.0 and above to use Prefetch
- Through rewriting RecyclerView. OnViewRecycled (holder) to recycling of resources
- If the Item is fixed, you can use RecyclerView. SetHasFixedSize (true); To prevent requestLayout from wasting resources
- Set up a listener for ItemView. Instead of calling addXxListener for each Item, you should use a common XxListener to perform different operations based on the ID. This optimizes resource consumption caused by frequent object creation
- If multiple RecycledView Adapter is the same, such as nested RecyclerView exist in the same Adapter, you can set RecyclerView. SetRecycledViewPool (pool), To share a RecycledViewPool.
Webview
The basic use
WebView
Public String getUrl(); Public String getOriginalUrl(); public String getOriginalUrl(); Public String getTitle(); // Get the current page favicon public Bitmap getFavicon(); Public int getProgress(); / / inform WebView kernel network status / / used to set the JS properties ` window. The navigator. IsOnline ` events and generate HTML 5 ` online/offline ` public void SetNetworkAvailable (Boolean networkUp) public void setInitialScale(int scaleInPercent); setNetworkAvailable(Boolean networkUp) public void setInitialScale(int scaleInPercent);Copy the code
WebSettings
WebSettings settings = web.getSettings(); Storage (storage) / / / / to enable HTML DOM storage API, the default value of false Settings. SetDomStorageEnabled (true); // Enable the Web SQL Database API. This setting affects all webViews in the same process. The default value is false. https://www.w3.org/TR/webdatabase/ settings.setDatabaseEnabled(true); // Enable Application Caches API, a valid cache path is required to take effect, default is false // This API is deprecated, see: https://developer.mozilla.org/zh-CN/docs/Web/HTML/Using_the_application_cache settings.setAppCacheEnabled(true); settings.setAppCachePath(context.getCacheDir().getAbsolutePath()); / / location (location) Settings. SetGeolocationEnabled (true); // Whether to save the form data settings.setSaveFormData(true); If/when the webview called requestFocus set focus for an element of the page, the default value of true Settings. SetNeedInitialFocus (true); <meta name=" viewPort "... <meta name="viewport"... / > ` adaptive Settings. The phone's screen setUseWideViewPort (true); / / whether or not to use the overview page mode loading, the default value of false / / when the page width greater than the WebView width narrowing the page width is equal to the width Settings. The WebView setLoadWithOverviewMode (true); / / layout algorithm Settings. SetLayoutAlgorithm (WebSettings. LayoutAlgorithm. NORMAL); / / support Javascript, Settings. The default value false setJavaScriptEnabled (true); / / whether the support for multiple Windows, the default value of false Settings. SetSupportMultipleWindows (false); / / is available Javascript (window. The open) open the window, the default value of false Settings. SetJavaScriptCanOpenWindowsAutomatically (false); / / resource access Settings. SetAllowContentAccess (true); / / whether can access the resources of the Content Provider, the default value of true Settings. SetAllowFileAccess (true); / / whether can access the local file, the default value is true / / whether to allow the file url to load Javascript read a local file, the default value is false Settings. SetAllowFileAccessFromFileURLs (false); / / whether to allow the file url to load Javascript read all resources (including documents, HTTP, HTTPS), the default value of false Settings. SetAllowUniversalAccessFromFileURLs (false); / / resource load Settings. SetLoadsImagesAutomatically (true); / / is automatically loaded picture Settings. The setBlockNetworkImage (false); / / prohibited load network picture Settings. The setBlockNetworkLoads (false); // Disable loading of all network resources // Zoom(zoom) settings.setSupportZoom(true); / / support zoom Settings. SetBuiltInZoomControls (false); / / whether or not to use the built-in zoom mechanism Settings. SetDisplayZoomControls (true); / / display the built-in zoom control / / the default text encoding, the default value "utf-8" Settings. SetDefaultTextEncodingName (" utf-8 "); settings.setDefaultFontSize(16); / / the default text size, the default value is 16, range 1-72 Settings. SetDefaultFixedFontSize (16); / / the default monospaced font sizes, 16 Settings. The default value setMinimumFontSize (8); / / minimum font sizes, 8 Settings. The default value setMinimumLogicalFontSize (8); // Minimum text logical size, default 8 settings.setTextZoom(100); / / text zoom percentage, the default value of 100 / / font Settings. SetStandardFontFamily (" sans-serif "); / / the standard font, "sans-serif" Settings. The default value setSerifFontFamily (" serif "); / / serif font, "serif" Settings. The default value setSansSerifFontFamily (" sans-serif "); / / sans serif font, "sans-serif" Settings. The default value setFixedFontFamily (" monospace "); / / monospaced font, "monospace" Settings. The default value setCursiveFontFamily (" cursive "); / / script (script), the default value "cursive" Settings. SetFantasyFontFamily (" fantasy "); // If (build.version.sdk_int >= build.version_codes.kitkat) {// If (build.version.sdk_int >= build.version_codes.kitkat) { The default value of true Settings. SetMediaPlaybackRequiresUserGesture (true); } if (build.version.sdk_int >= build.version_codes.lollipop) {// HTTP/HTTPS hybrid pages are allowed to load above 5.0 (default is allowed below 5.0, 5.0 + banned by default) Settings. SetMixedContentMode (WebSettings. MIXED_CONTENT_ALWAYS_ALLOW); } if (build.version.sdk_int >= build.version_codes.m) {// Whether to raster when leaving the screen (which increases memory consumption), The default value is false Settings. SetOffscreenPreRaster (false); } if (isNetworkConnected(context)) {setsettings. setCacheMode(websettings. LOAD_DEFAULT); Setsettings. setCacheMode(websettings. LOAD_CACHE_ELSE_NETWORK); } // deprecated settings.setRenderPriority(WebSettings.RenderPriority.HIGH); settings.setDatabasePath(context.getDir("database", Context.MODE_PRIVATE).getPath()); settings.setGeolocationDatabasePath(context.getFilesDir().getPath());Copy the code
WebViewClient
// This method was deprecated in API24, Public Boolean shouldOverrideUrlLoading(WebView view, String URL) {return false; } // This method is added to API24 and does not handle POST requests, @targetAPI (build.version_codes.n) public Boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { return shouldOverrideUrlLoading(view, request.getUrl().toString()); } // This method is deprecated by API21 and called for non-UI threads to intercept resource requests and return response data, Public WebResourceResponse shouldInterceptRequest(WebView View, String URL) {return null; } // This method was added to API21 and is called for non-UI threads to intercept resource requests and return data, @targetAPI (build.version_codes.lollipop) Public WebResourceResponse shouldInterceptRequest(WebView) view, WebResourceRequest request) { return shouldInterceptRequest(view, request.getUrl().toString()); Public void onPageStarted(WebView view, String url, Public void onPageFinished(WebView view, favicon) {// Page (url) complete load Public void onLoadResource(WebView view, String URL) {} This callback is added to the API23, Mainframe-only navigation // notifies the application that its legacy WebView content is no longer drawn when it navigates to the previous page. // This callback can be used to determine which WebView visible content can be safely recycled to ensure that no stale content is displayed. // It is called first to ensure that webView.ondraw does not draw any previous page content, followed by background colors or new content that needs to be loaded. // This method is called when the HTTP response body has already started loading and will be visible in the DOM in subsequent draws. // This callback occurs early in the document load, so its resources (CSS, and images) may not be available. // If finer grained view updates are needed, Check the postVisualStateCallback (long, WebView. VisualStateCallback). / / please note that all of the above conditions also supports postVisualStateCallback (long, WebView. VisualStateCallback) public void onPageCommitVisible(WebView view, Public void onReceivedError(WebView view, int errorCode,) {} // This method is deprecated by API23 // The main framework failed to load resources. String description, String failingUrl) {} // This method is added to the API23 // error when loading resources, which usually means that the server cannot be connected // This method is called due to all resource loading errors, @targetAPI (build.version_coder.m) public void onReceivedError(WebView view, WebResourceRequest Request, WebResourceError error) { if (request.isForMainFrame()) { onReceivedError(view, error.getErrorCode(), error.getDescription().toString(), request.getUrl().toString()); Add}} / / this method API23 / / in the load resources (iframe, image, js, CSS, ajax...). Public void onReceivedHttpError(WebView View, WebResourceRequest Request, WebResourceResponse errorResponse) {} // Whether to resubmit the form, Public void onFormResubmission(WebView View, Message dontResend, Message resend) {dontresend.sendToTarget (); } // The notification application can store the current URL in the database, meaning that the current access URL is valid and logged in the kernel. // This method is called only once during the page loading process. It is not called when the page moves forward or backward. Public void doUpdateVisitedHistory(WebView view, String URL, Boolean isReload) {} Application required response (continue request or cancel request) // Processing decisions may be cached for subsequent requests, The default behavior is to cancel the request. Public void onReceivedSslError(WebView view, SslErrorHandler Handler, SslError Error) {handler.cancel(); } // This method is added to API21 and is called on the UI thread to process SSL client certificate requests, displaying a UI to supply the KEY if necessary. // There are three types of response: Proceed ()/cancel()/ignore(), the default behavior is to cancel the request // If proceed() or cancel() is called, Webview will save the response result in memory and the same "host: port" never called again onReceivedClientCertRequest / / in most cases, Through KeyChain. ChoosePrivateKeyAlias start an Activity for the user to choose a suitable private key @ TargetApi (Build) VERSION_CODES) LOLLIPOP) public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) { request.cancel(); } // Handle HTTP authentication requests, The default behavior is to cancel the request public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm) { handler.cancel(); Public void onReceivedLoginRequest(WebView view, String realm, String Account, String args) {} // Give the application a chance to process the keystroke event // If true is returned, the WebView will not process the event, otherwise the WebView will keep processing, Public Boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {return false; } // Handle button events that are not consumed by WebView. Public void onUnhandledKeyEvent(WebView View, shouldOverrideKeyEvent) public void onUnhandledKeyEvent(WebView View, KeyEvent event) { super.onUnhandledKeyEvent(view, event); Public void onScaleChanged(WebView view, float oldScale, float newScale) {}Copy the code
WebChromeClient
// Get a list of all access history items for link coloring. Public void getVisitedHistory(ValueCallback<String[]> callback) {} Specified in HTML via its 'poster' attribute. // If the 'poster' attribute is not specified, a default poster diagram is provided through this method. public Bitmap getDefaultVideoPoster() { return null; } // This method returns a placeholder view (such as a rotating chrysanthemum) while a full-screen video is buffering. public View getVideoLoadingProgressView() { return null; } public void onProgressChanged(WebView view, Int newProgress) {} public void onReceivedTitle(WebView view, String title) {// Favicon public void onReceivedIcon(WebView view, Bitmap icon) {} // Touch icon in Android // http://droidyue.com/blog/2015/01/18/deal-with-touch-icon-in-android/index.html public void OnReceivedTouchIconUrl (WebView view, String URL, Boolean precomposed) {} // Notify the application that the current page is in full-screen mode, Public void onShowCustomView(View View, CustomViewCallback callback) {} public void onShowCustomView(View View, CustomViewCallback callback) Public void onHideCustomView() {} public Boolean onJsAlert(WebView View, String url, String message, JsResult result) { return false; Public Boolean onJsConfirm(WebView view, String URL, String message, JsResult result) { return false; Public Boolean onJsPrompt(WebView view, String URL, String message, String defaultValue, JsPromptResult result) { return false; Public Boolean onJsBeforeUnload(WebView View, String URL, String Message, JsResult result) { return false; } // The web page content of the specified source is trying to use the geolocation API without setting permissions. // Starting with API24, this method is called only from secure sources (HTTPS), The security of the source will be automatically rejected public void onGeolocationPermissionsShowPrompt (String origin, GeolocationPermissions. Callback Callback) {} / / the current one call onGeolocationPermissionsShowPrompt (cancelled), hidden related UI. Public void onGeolocationPermissionsHidePrompt () {} / / notify the application to open a new window public Boolean onCreateWindow (WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { return false; Public void onCloseWindow(WebView window) {} public void onRequestFocus(WebView view) { @targetAPI (build.version_codes.lollipop) public void Notifies the application that the web content has requested permission to access the specified resource (the permission is not authorized or denied) onPermissionRequest(PermissionRequest request) { request.deny(); } // Notify that the application permission application has been cancelled and hide the relevant UI. @TargetApi(Build.VERSION_CODES.LOLLIPOP) public void onPermissionRequestCanceled(PermissionRequest request) { } // Display file selector for '<input type="file" />', @targetAPI (build.version_codes.lollipop) public Boolean onShowFileChooser(WebView WebView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { return false; } // Receive JavaScript console messages public Boolean onConsoleMessage(ConsoleMessage ConsoleMessage) {return false; }Copy the code
Webview load optimization
- Use local resources instead
Put some resource files in the local asset S directory and rewrite the WebViewClient’s shouldInterceptRequest method to intercept the url. If the URL matches the local url, use the local resource instead. Otherwise, use resources on the network.
MWebview. SetWebViewClient (new WebViewClient () {/ / browser Settings without system open, @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } @override public WebResourceResponse shouldInterceptRequest(WebView view, String URL) { The use of local resources to replace the if (mDataHelper hasLocalResource (url)) {WebResourceResponse response = mDataHelper.getReplacedWebResourceResponse(getApplicationContext(), url); if (response ! = � � null) {return response; } } return super.shouldInterceptRequest(view, url); } @TargetApi(VERSION_CODES.LOLLIPOP)@Override public WebResourceResponse shouldInterceptRequest(WebView view,WebResourceRequest request) { String url = request.getUrl().toString(); if (mDataHelper.hasLocalResource(url)) { WebResourceResponse response = mDataHelper.getReplacedWebResourceResponse(getApplicationContext(), url); if (response ! = � � null) {return response; } } return super.shouldInterceptRequest(view, request); }});Copy the code
- WebView initialization is slow, you can initialize at the same time the first request data, so that the back end and the network do not idle.
- If the back-end processing is slow, the server can be divided into trunk outputs. The front-end also loads static network resources while the back-end computations.
- Script execution is slow, so let the script run at the end without blocking page parsing.
- At the same time, reasonable preloading and precaching can reduce the bottleneck of loading speed.
- WebView initialization is slow, it is ready to initialize a good WebView ready to use.
- DNS and links are slow. Try to reuse domain names and links used by clients.
- Script execution is slow, so the framework code can be broken down and executed before the page is requested.
A memory leak
Using a new WebView and passing in an Application Context instead of declaring it in XML to prevent activity references from being abused can solve 90+% WebView memory leaks.
vWeb = new WebView(getContext().getApplicationContext());
container.addView(vWeb);
Copy the code
Destruction of the WebView
if (vWeb ! = null) { vWeb.setWebViewClient(null); vWeb.setWebChromeClient(null); vWeb.loadDataWithBaseURL(null, "", "text/html", "utf-8", null); vWeb.clearHistory(); ((ViewGroup) vWeb.getParent()).removeView(vWeb); vWeb.destroy(); vWeb = null; }Copy the code