View related

View drawing process

Custom control: 1, combination control. This custom control does not require us to draw ourselves, but instead uses the native control combined into a new control. For example, the title bar. 2. Inherit the original control. This custom control can add its own methods in addition to those provided by the native control. Such as making rounded corners, round pictures. 3. Fully custom controls: The contents of this View are all drawn by ourselves. For example, make a water ripple progress bar.

OnMeasure() — >OnLayout() — >OnDraw()

Step 1: OnMeasure() : Measures view size. We recursively call the measure method from the parent View to the child View, and the measure method calls back to OnMeasure.

Step 2: OnLayout() : Determine the location of the View, the page layout. The process of recursively calling the view.layout method from the parent View to the child View. That is, the parent View places the child View in the appropriate position according to the layout size and layout parameters obtained by measuring the child View in the previous step.

Step 3: OnDraw() : Draw the view. ViewRoot creates a Canvas object and then calls OnDraw().

View, ViewGroup Event distribution

There are only two main actors in Touch event distribution :ViewGroup and View. ViewGroup contains onInterceptTouchEvent, dispatchTouchEvent, and onTouchEvent. View contains dispatchTouchEvent and onTouchEvent. ViewGroup inherits from View.

2.ViewGroup and View form a tree structure. The root node is a ViwGroup contained within the Activity.

3. Touch events consist of Action_Down, Action_Move, and Aciton_UP. In a complete touch event, there is only one Down and one Up, and the number of Move events can be 0.

4. When Acitivty receives the Touch event, it traverses the child View to distribute the Down event. The traversal of the ViewGroup can be viewed as recursive. The purpose of this distribution is to find the View that will actually handle the full touch event, which will return true in the onTouchuEvent result.

5. When a subview returns true, the Down event is aborted and the subview is recorded in the ViewGroup. Subsequent Move and Up events are handled directly by the child View. Since the child View is saved in the ViewGroup, in the node structure of the multi-layer ViewGroup, the upper ViewGroup will save the ViewGroup object where the View that actually handles the event is: for example, in the viewGroup0-ViewGroup1-textView structure, TextView returns true, which will be saved in ViewGroup1, and ViewGroup1 will return true, which will be saved in ViewGroup0. When the Move and UP events come, they are passed from ViewGroup0 to ViewGroup1, and from ViewGroup1 to TextView.

6. When no child View in the ViewGroup captures the Down event, the onTouch event of the ViewGroup itself is triggered. This is triggered by calling the Super.DispatchTouchEvent function, which is the dispatchTouchEvent method of the superclass View. Acitivity’s onTouchEvent method fires if none of the subviews are processed.

7. OnInterceptTouchEvent has two functions: 1. Intercept the distribution of the Down event. 2. Stop the Up and Move events from being passed to the target View, so that the ViewGroup where the target View resides captures the Up and Move events.

View Event distribution process

ViewGroup Time distribution process

Overall Activity-viewGroup-View distribution process

View event distribution and source explanation

MeasureSpec relevant knowledge

MeasureSpec is a 32-bit int value, with the high 2 bits representing SpecMode(the measurement mode) and the low 30 bits representing SpecSize(the specification size in a certain measurement mode). Determine the size of the View by measuring widthMeasureSpec and heightMeasureSpec. The three measurement modes represented by SpecMode are:

  1. UNSPECIFIED: The parent container has no restrictions on the View, as large as it may be. Often used within systems.

  2. EXACTLY: The parent view specifies an exact size for the child view, SpecSize. Corresponds to match_parent or a specific value in LyaoutParams.

  3. AT_MOST(maximum mode) : The parent container specifies a SpecSize for the child View. The size of the View cannot be greater than this value. Corresponds to wrap_content in LayoutParams.

Determining factor: The value is determined by the child View’s layout parameter LayoutParams and the parent container’s MeasureSpec value. See below:

Refer to pictures and address

The difference between SurfaceView and View

SurfaceView is a display class derived from the base View class. It is different from the View class.

  • View needs to refresh the screen in the UI thread, while SurfaceView can refresh the page in the child thread. View is suitable for active update. Frequent refresh of View will block the main thread, resulting in interface lag

  • SurfaceView has implemented double bufferization mechanism in the bottom layer, while View does not, so SurfaceView is more suitable for passive update, need to refresh frequently, refresh the page with a lot of data processing, while SurfaceView is suitable for

The difference between invalidate() and postInvalidate(

Invalidate() and postInvalidate() are both used to refresh the View. The main difference is that invalidate() is called on the main thread, and if it is used on a subthread, it needs to work with the handler. PostInvalidate () can be called directly from a child thread. How do we update in a child thread with postInvalidate

	// System code
	public void postInvalidateDelayed(long delayMilliseconds) {
        // We try only with the AttachInfo because there's no point in invalidating
        // if we are not attached to our window
        final AttachInfo attachInfo = mAttachInfo;
        if(attachInfo ! =null) {
            attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); }}Copy the code

So let’s see

	// System code
	 public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
        Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
        mHandler.sendMessageDelayed(msg, delayMilliseconds);
    }

Copy the code

We can see that postInvalidate is sending a Message to the main thread, and then invalidate() is called when the handleMessage is sent. (The system has written the Handle for us)

Android animation

Several animations in Android

Frame animation: By specifying the picture and playback time of each frame, orderly play to form an animation effect, such as the rhythm bar that you want to hear.

Tween animation: By specifying the initial state, changing time, and changing mode of the View, the image is transformed through a series of algorithms to form animation effects, including Alpha, Scale, Translate, and Rotate. Note: We only animate the View layer, and we don’t really change the properties of the View, such as sliding the list or changing the opacity of the title bar.

Attribute animation: supported only in Android3.0, by constantly changing the attributes of the View, constantly redraw to form the animation effect. The properties of the View are really changed compared to the View animation. For example, rotate the view, zoom in, zoom out.

Attribute animation and tween animation difference

  • The tween animation is just the Parents View manipulating the canvas inside the child View. The new position does not respond to the click event, the original position responds.
  • Property animation is animated by modifying the View property, and the new position responds to the click event

Why does property animation respond to events in new locations

ViewGroup in getTransformedMotionEvent () method will pass the child View hasIdentityMatrix () method to judge the child View ever applications such as displacement, zoom, rotate the properties of the animation. If so, it will also call the child View’s getInverseMatrix() to “reverse pan” and determine whether the processed touch point is within the boundaries of the child View.

Property animation click decrypt

Principle of property animation

Attributes of the object of the animation for animation role provides the attribute set method, attribute animation delivery according to you the familiar initial value and final value for the animation effect many times to call set method, each time passed to the value of the set method is different, it is exactly as time goes on, the passed value is more and more close to the final value. If the initial value is not passed during the animation, then the get method is also provided because the system is going to get the initial value of the property.

// System code
void setAnimatedValue(Object target) {
        if(mProperty ! =null) {
            mProperty.set(target, getAnimatedValue());
        }
        if(mSetter ! =null) {
            try {
                mTmpValueArray[0] = getAnimatedValue();
                mSetter.invoke(target, mTmpValueArray);
            } catch (InvocationTargetException e) {
                Log.e("PropertyValuesHolder", e.toString());
            } catch (IllegalAccessException e) {
                Log.e("PropertyValuesHolder", e.toString()); }}}Copy the code

Attribute animation source parsing

Handler,

The principle of the Handler

In Android, the main thread cannot do time-consuming operations, and the child thread cannot update the UI. So you have a handler, which is used to communicate between threads. Handler the whole process, there are mainly four objects, handler, Message, MessageQueue, stars. When the application is created, the handler object is created in the main thread.

For the Message:

Messages passed between threads, which internally hold references to the Handler and Runnable, as well as the message type. You can use the what, arg1, and arg2 fields to carry some integer data, and the obj field to carry an Object. There is an obtain () method that, internally, retrieves messages from the message pool without creating them, enabling reuse of message objects. There is an internal target reference, which is a reference to the Handler object. In looper.loop, the message is handled by calling the Handler’s dispatchMessage() method using the target reference to the message.

For the Message Queue:

Message queues, which maintain a list of messages through a single linked list of data structures, have advantages in insertion and deletion. There are two main operations: insert and read, and the read operation itself is accompanied by the delete operation. The insert operation is the enqueueMessage () method, which inserts a message into the MessageQueue; The read operation is the next () method, which is an infinite loop that returns if there is a message and removes it from the single linked list; Block without messages (at which point the main thread releases the CPU into sleep).

For the stars:

Looper loops messages in the message mechanism, acting like a pump, constantly checking for new messages from the MessageQueue and extracting them for handler to handle. Looper.prepare() is used to create a Looper for the current thread, and looper.loop () is used to enable reading of messages. Why is Looper not used when the Activity main thread is normally used? For the main thread (UI thread), a Looper is automatically created to drive the message queue to get the message, so Looper can get the Looper of the main thread through getMainLooper. To quit the Looper, run the quit/quitSafely command. The difference was that quit would quit while quitSafely would finish processing all the messages already in the message queue before exiting the Looper.

The Handler

Handler can send and receive messages. MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue The sendMessageAtTime method is called, and the target of the MSG is assigned to the handler itself. Go to MessageQueue. To process a message, Looper calls the loop () method to enter an infinite loop. After getting the message, it calls the dispatchMessage () method of MSG. Target (itself), and then calls the handlerMessage () method to process the message.

Handler causes memory leaks

Usually we write Handler:

Handler mHandler = new Handler() {
	@Override
	public void handleMessage(Message msg) { mImageView.setImageBitmap(mBitmap); }}Copy the code

When an inner class (including anonymous classes) is used to create a Handler, the Handler object implicitly holds a reference to an external class object (usually an Activity), and there are often unfinished messages on the message queue after the Activity exits. The activity is still referenced by the handler, causing memory to fail to be reclaimed and memory leaks.

Add a WeakReference to the Activity in the Handler:

static class MyHandler extends Handler {
	WeakReference mActivityReference;

	MyHandler(Activity activity) {
		mActivityReference= new WeakReference(activity);
	}

	@Override
	public void handleMessage(Message msg) {
		final Activity activity = mActivityReference.get();
		if(activity ! =null) { mImageView.setImageBitmap(mBitmap); }}}Copy the code

In the case of non-custom handlers, the Activity lifecycle can also be used to clear messages in a timely manner, thus reclaiming the Activity in a timely manner

override fun onDestroy(a) {
        super.onDestroy()
        if(mHandler ! =null){
            mHandler.removeCallbacksAndMessages(null)}}Copy the code

Handler post method principle

mHandler.post(new Runnable()
        {
            @Override
            public void run(a)
            {the e (" TAG ", Thread. CurrentThread () getName ()); MTxt. SetText (" yoxi "); }});Copy the code

The Runnable does not create a thread. Instead, it sends a message.

public final boolean post(Runnable r)
   {
      return  sendMessageDelayed(getPostMessage(r), 0);
   }
Copy the code

Finally, just like handler.sendMessage, sendMessageAtTime is called, then enqueueMessage method is called, MSG. Target is assigned as handler, and finally MessagQueue is added.

Handler other issues

  1. Looper.loop() and messagequeue.next () fetch delayed messages, why is the main thread using an infinite loop not stuck?

    A: When MessageQueue is fetching a message, if it is a delayed message, it will calculate how long the delayed message will need to be delayed. Then, if nextPollTimeoutMillis is not equal to zero, the nativePollOnce is executed to block the thread nextPollTimeoutMillis milliseconds, The time to wake up after blocking is when the blocking time is up or a new message is added to execute the enqueueMessage method call nativeWake to wake up the blocking thread, and then continue to execute the code to get the message, if there is a message will return, if there is still a delay will continue to block as above. In Android, all events that change in the main thread are sent through the Handler in the main thread, so it is completely guaranteed that the event will not get stuck.

    The location of nativePollOnce is also careful. It is just outside synchronized, so it can ensure that the addition message can be executed when blocking, while the addition message needs to wait when fetching the message.

  2. Is MessageQueue a queue? A: MessageQueue is not a queue. It uses a linked list of messages to store and fetch messages internally.

  3. Is Handler’s postDelay correct? Does it use System.CurrentTime? Answer: No, because the execution of the messages retrieved from the MessageQueue in Looper is also serial. If the previous message is time-consuming, the execution time of the delayed message may inevitably exceed the delayed time. PostDelay uses system. uptimeMillis, which is the boot time.

  4. How can Handler be used in the run method of a child thread? A: Use Lopper. Prepare, then use looper to create a Handler, and then call looper.loop. Looper.loop is an infinite loop until it exits, so the Handler must be created before the loop.

  5. How does a ThreadLocal implement one Looper per thread? A: The use of Looper ultimately requires the execution of loop method, and the Looper to be obtained in loop method is obtained from sThreadLocal, so Looper needs to establish a relationship with sThreadLocal. In the case of not considering reflection, In this case, the edge will introduce a threadLocalMap, which corresponds to thread one by one. The get method of threadLocal actually uses the get method of threadLocalMap. The key is the static variable sThreadLocal in Looper, and the value is the current Looper object. The prepare method can only be executed once, which ensures that a thread has only one Looper. ThreadLocalMap accesses keys and values similar to hashMap.

  6. Assuming a postDelay of 10ms and then a postDelay of 1ms, what will the two messages experience differently? A: a message with a delay of 10ms is first passed into the MessageQueue. Because of the delay, assuming that there is no message in the current MessageQueue, the message will be directly put into the queue because loop has been fetching messages. However, if there is a delay, the message will be blocked for 10ms, regardless of the code execution time. Delay 1 ms messages into, then compare and before the news of 10 ms, sorting inserts, according to the size of the delay time delay is small in the front, so this time I put 1 ms message in the front of the 10 ms, and then wake up, don’t block, continue to implement the operation of the news, found that there is delay 1 ms, so will also continue to block 1 ms, In loop, by calling the handler’s dispatchMessage method, If the message callback or the Handler callback is not null, then the corresponding callback is null. Otherwise, the Handler’s handleMessage method is executed and we can handle the message accordingly. The processing of 10ms delayed messages is also consistent. When the delayed time is up, it will be returned to looper and then processed by handler.

  7. HandlerThread? A: HandlerThread is a subclass of Thread, but it creates a Handler internally. This Handler is the Handler of the child Thread, and the looper of the child Thread is created and managed.

  8. What do you know about message.obtain ()? Answer: message. obtain actually takes the first Message out of the buffered Message pool for use, avoiding frequent creation and destruction of Message objects; The Message pool is actually implemented using the Message list structure. After the Message is distributed and consumed by the handler in the loop, the operation of recycling will be performed, and the internal data of the Message will be emptied and added to the front of the Message list.

Multithreading related Issues

How to create multithreading

  1. Override the run function method by inheriting Thread
  2. Implement the Runnable interface and override the run function method
  3. Implement the Callable interface and override the call function method
  4. HandlerThread
  5. AsyncTask is a very old = =

Synchronization between multiple threads

  1. The keyword volatile can be used in GET and SET scenarios because both get and SET scenarios have read/write memory barriers, ensuring data synchronization in terms of visibility. But for non-atomic operations like ++, the data is out of sync

  2. Synchronized synchronizes data by locking a code block or method and combining wait and notify scheduling

  3. ReentrantLock combines with Condition setting to ensure data synchronization in thread scheduling

  4. CountDownLatch is a simplified version of conditional locking to ensure data synchronization on thread scheduling

  5. Cas =compare and swap(set), which ensures data synchronization in the atomicity of operations

  6. Following the idea of UI thread updating UI, we use handler to concentrate data updates of multiple threads on one thread to avoid dirty reads of multiple threads

  7. Of course, if only part of the variables have the possibility of multithreading modification, it is recommended to use AtomicInteger, AtomicBoolean, etc., which is a little more convenient.

Android optimization

OOM

  1. According to the Java memory model, memory overflow can occur in the heap memory, method memory, virtual machine stack memory, native method memory; In general, the OOM is mainly for heap memory;

  2. There are two fundamental causes of heap memory overflow: (1) app process memory reaches the upper limit; (2) the phone is out of available memory. This situation is not that our app consumes a lot of memory, but that the entire phone is out of memory

The main thing we need to deal with is that the app is running out of memory

  1. There are only two situations when app memory reaches its limit

(1) The memory allocation speed exceeds the memory release speed by GC. (2) The memory leaks. Gc cannot reclaim the leaked memory, resulting in less and less available memory

  1. There are two main cases where the rate at which memory is allocated exceeds the rate at which memory is freed by the GC

(1) load very large files into memory and (2) loop to create a large number of objects

  1. Generally, the speed of memory application exceeds that of GC to release memory, and memory leakage is the key problem

This results in a memory leak

The root cause of memory leaks is that long-lived objects hold references to short-lived objects

  1. Memory leak caused by resource objects not being closed (e.g. Cursor, File, etc.)

  2. Memory leak caused by global collection class strong references not cleaned up (static modified collection)

  3. Memory leak caused by uncancelled receiver/listener registration, e.g. broadcast, EventsBus

  4. For leaks caused by the Activity Context, you can use ApplicationContext

  5. Memory leaks caused by handlers (usually due to the Handler life cycle being longer than that of its external class)

Note 1: The ListView Adapter cache uses ConvertView, the main cache is to remove the screen View, even if not reuse, temporarily only memory overflow, and leakage is still different.

Note 2: Should the Bitmap object call recycle() to free memory? Conclusion Before Android 3.0, pixel data is stored separately from the object itself, and pixel data is stored in the native layer. Objects are stored in the Java layer. After 3.0 the pixel data is stored in the Dalvik heap along with the Bitmap object data. So, at this point, you can consider using GC for automatic collection. So we just set the Bitmap to Null when we’re not using it. Refer to the blog

We have listed most of the common memory leaks, so I will also briefly list common ways to avoid memory leaks (for reference only);

  1. To request more memory for your application, set largdgeHeap on the manifest to true

  2. Use optimized collection objects, such as SpaseArray;

② Use MMKV of wechat instead of SharedPreference; ③ For often play log place to use StringBuilder to group, instead of String splice ④ unified with the cache of the basic library, especially the image library, if two sets of different image loading library will appear 2 pictures each maintain a set of image cache ⑤ to the ImageView set the appropriate size of the picture, ⑥ Optimize the design of business architecture, such as provincial data load in batches, need to load province load province, need to load city load lost, avoid loading all data at once

  1. Avoiding memory leaks

    In terms of coding specification:

    If the resource object is used up, you must close it

② Clean up static collection objects when they are used up ③ Register and cancel receivers and listeners when they are used ④ Pay attention to the life cycle of context usage. If it is a static class reference, directly use ApplicationContext ⑤ Use static inner classes ⑥ Set soft references and weak references in combination with business scenarios. Make sure the object can be recycled at the right time

Building a memory monitoring system:

Offline monitoring:

① ArtHook is used to detect whether the image size is more than twice the width and height of imageView itself

(2) During the coding stage, Memery Profile will look at the app’s memory usage, whether there is memory jitter, memory leak, and analyze the memory leak with Mat

Online monitoring:

(1) Report standby memory, memory of key modules, and OOM rate during app usage

(2) Report the GC times and GC time of the whole and key modules

Use LeakCannery to automate memory leak analysis

ANR

In The Android system, AMS and WMS will detect the Response time of the App. If the App performs time-consuming operations on the main thread, and the user’s operation does Not receive timely Response, the problem of Application Not Response will be reported, that is, ANR.

  1. Activity, keyboard input events, and touch events over five seconds
  2. Foreground broadcast 10 seconds not complete background 60 seconds
  3. Service front 20 seconds background 200 seconds

The main circumvention

Try to use child threads to avoid deadlocks and use child threads to handle time-consuming operations or blocking tasks. Service content providers try not to perform tasks for too long.