The basic steps of Aidl are as follows:
The Client obtains the Server Binder through ServiceConnection and encapsulates it as a Proxy. Call IPC methods synchronously through Proxy. Parameters are also passed to Binder via Parcel, which eventually triggers Binder’s Transact method. Binder’s Transact method eventually triggers the Stub onTransact method on the Server. The Stub onTransact method on the Server parses the parameters from the Parcel, executes them in the real method, writes the results to the Parcel and returns them. In Client’s Ipc methods, Binder’s transact is blocked and waits. Execution does not continue until the Server logic is finished. When the Server returns the result, the Client fetches the return value from the Parcel and implements an IPC call.
App startup process
(1) The starting point of startup occurs in the Launcher Activity. To start an app is to start an Activity. As we said, AMS is responsible for the start, switch and scheduling of all components, so the first step is the Launcher response to the user’s click event, and then notify AMS
(2) AMS needs to respond to the notification of the Launcher by creating a new Task to start the Activity and telling the Launcher that you can rest (Paused).
(3) Launcher gets a message from AMS telling me to “rest”, so I just hang up and tell AMS I have Paused.
(4) After AMS knows the Launcher has been suspended, it can rest assured to prepare for the start of a new Activity. First, the APP must need a new process to run, so it needs to create a new process, which requires Zygote to participate in. AMS negotiates with Zygote through sockets. If a process needs to be created, AMS forks itself and creates a thread. The new process imports the ActivityThread class, which is why every application has an ActivityThread.
(5) The process is created by calling the main method of ActivityThread, which is the entry point to the application and opens the message loop queue. This is why the main thread is bound to Looper by default.
(6) At this time, the App has not been started. Always remember that all the four components need AMS to start. Register the above application process information in AMS, and THEN AMS obtains the Activity to be started from the top of the stack and completes the App startup through a series of chain calls.
The View of the load
. Indirectly call the setContentView () of the Phonewindow with the Activity’s setContentView method, and get the LayoutInflate object in the Phonewindow with getLayoutInflate ()
2. Load the View through the LayoutInflate object. The main steps are
(1) Use THE Pull method of XML to parse XML layout files, obtain XML information, and save cache information, because these data are static and unchanged
(2) Create View layer by layer based on XML tag
(3) Recursively build the child View, and add the child View to the parent ViewGroup;
RecycleView and ListView
And the difference is that the two cache levels are different, ListView has only two levels, RecycleView has four levels of cache.
1. MActiveViews and mAttachedScrap have similar functions. The purpose of mActiveViews is to quickly reuse visible items on the screen, ItemView, without the need to recreate view and bindView;
2. MScrapView and mCachedViews + mReyclerViewPool function is similar, the meaning is to cache the off screen ItemView, the purpose is to reuse the incoming ItemView;
3. RecyclerView advantage is: the use of A.cacheviews, can be done off-screen list items ItemView into the screen without bindView rapid reuse; B. RecyclerPool can be used for multiple RecyclerView, in specific scenarios, such as viewpaper+ multiple list pages have the advantage. Objectively speaking, RecyclerView has strengthened and improved the cache mechanism of ListView in specific scenarios.
The RecycleView cache is stored in the ViewHolder, which can be regarded as View + ViewHolder(avoid calling findViewById each time createView) + flag(flag state).
MRecycleView is used to obtain the cache of the target location by matching pos. This can be done without re-bindView. Therefore, the RecycleVIew is used to obtain the viewholder by POS, which is pos–> (view, holder, flag). Flag determines whether the view needs to be bindView again.
Level 1 cache: mAttachedScrap and mChangedScrap, used to cache ViewHolder while still in the screen
MAttachedScrap stores the ViewHolder currently on the screen; MChangedScrap Represents the list of Viewholers whose data has changed. The Viewholers that need to be changed when storing notifyXXX
Level 2 cache: mCachedViews, used to cache ViewHolder removed from the screen. By default, the cache capacity is 2. The setViewCacheSize method can be used to change the cache capacity. If mCachedViews is full, the old ViewHolder is removed according to the FIFO rules
Level 3 cache: ViewCacheExtension, a custom extended cache developed for users that requires users to manage View creation and caching. In my opinion, this extension is separated from the Adapter. CreateViewHolder. Using this extension will cause View creation and data binding and other code to be too scattered for maintenance
RecycledViewPool, ViewHolder cache pool. If no new ViewHolder can be stored in the limited mCachedViews, ViewHolder will be stored in RecyclerViewPool.
By default, each Type can cache up to 5 recyclerViews. Multiple recyclerViews can share RecycledViewPool
Class loading mechanism
2, The life cycle of a class includes Loading, Verification, Preparation, Resolution, Initialization, Using and Unloading.
- Loading is a phase of the class loading process, and the two concepts must not be confused. During the load phase, the virtual machine needs to do the following three things:
(1) Get the binary byte stream that defines a class by its fully qualified name.
(2) Convert the static storage structure represented by this byte stream into the runtime data structure of the method area.
(3) Read the class file into memory and create a java.lang. class object for it. That is, when any class is used in the program, the system will create a Java.lang. class object for it, as the method area of the class data access point.
Parents delegate
When a file such as Hello.class is about to be loaded. Regardless of our custom class loader, we will first check in the AppClassLoader to see if it has been loaded, and if so, we don’t need to load it again. If not, the parent loader is taken and its loadClass method is called. The parent class also checks to see if it’s already loaded, and if it hasn’t gone any further. Note that this recursive process checks to see if the Bootstrap classLoader has been loaded until it reaches the Bootstrap classLoader. Until the BootstrapClassLoader, there is no parent loader, at this time to consider whether they can load, if they cannot load, will sink to the child loader to load, all the way to the bottom, if no loader can load, will throw ClassNotFoundException.
Android startup process
🤔️ : When the power button is pressed to trigger the boot, the boot program BootLoader will be loaded into RAM from a predefined place in ROM, and the BootLoader program will be executed to start the Linux Kernel. Then the first user-level process: init process will be started.
The init process parses the init.rc script to initialize the file system, create a working directory, and start system service processes, such as Zygote, Service Manager, and Media. In Zygote, AMS, WMS, PMS and other services will be started. In AMS, the home Activity of the application Launcher will open, and you will finally see the “desktop” of your phone.
Interviewer: Why should system_server be started in Zygote instead of init directly? 🤔️ : Zygote acts as an incubator to pre-load resources so that other processes created On copy-on-write at fork() can use them directly without reloading. System_server, for example, can use JNI functions, shared libraries, common classes, and theme resources directly from Zygote.
Interviewer: Why use Zygote specifically to incubate application processes instead of System_server?
🤔️ : First, System_server runs more AMS, WMS and other services than Zygote, which are not needed for an application. In addition, the process fork() is not multithreaded friendly, and only copies the calling thread into the child process, which can cause a deadlock, and system_server must have many threads.
Interviewer: Can you explain exactly how the deadlock was caused?
Forking () copies the calling thread to the child process, and all other threads stop immediately. If a thread uses a mutex before forking (), the mutex will not be released, and further requests for the mutex will be deadlocked.
Interviewer: Why didn’t Zygote use Binder mechanisms for IPC communication?
🤔️ : Binder thread pool exists in Binder mechanism, which is multithreaded. If Zygote uses Binder, there are fork() and multithreading problems mentioned above. A Binder mechanism does not have to be multithreaded strictly. A Binder thread is simply a loop reading of messages driven by the Binder. A single Binder thread can work, as is the case with Service Manager.
Zygote is actually not single-threaded despite not having Binder mechanisms, but it actively stops other threads before fork() and restarts after fork().
Static and dynamic proxies
Static proxies in Java require that both the proxy class (ProxySubject) and the delegate class (RealSubject) implement the same interface (Subject). The static proxy class is determined at compile time, while the dynamic proxy is generated dynamically at JVM runtime. The efficiency of static proxy is relatively higher than that of dynamic proxy, but the code redundancy of static proxy is large. Once the interface needs to be modified, both the proxy class and the delegate class need to be modified.
Retrofit implementation principle: First, a Retrofit object is created with the Builder. In the Create method, the implementation class is generated as a JDK dynamic proxy. When the interface method is called, the Invoke method of the InvocationHandler is triggered. To convert the empty methods of the interface to ServiceMethd, then generate okhttp request, find the corresponding actuator, through callAdapterFactory RxJava2CallAdapterFactory, for example, Finally, the returned data is parsed into JavaBena using ConverterFactory. The user only needs to care about the request parameters. The internal implementation is wrapped by RetroFIT, and the underlying request is implemented based on OKHTTP.
Benefits: The benefits of dynamic generation are obvious. The proxy logic is independent of the business logic, there is no coupling, and there is no difference between what one class or 100 classes do
To sum up: the agent is divided into static agent and dynamic agent, static agent will agent class and agent class coupling together, implementation of enhancement is very inconvenient, need a lot of coding;
The purpose of AOP is to implement uncoupled enhancement, and dynamic proxy is one of the ways to implement AOP ideas. Uncoupled method is not intrusive to code, it is very convenient to implement the enhancement, if you need to add some general enhancement to your code, you should first think of dynamic proxy
Plug-in words
VirtualApp is more powerful, can completely simulate the app running environment, can realize the APP free installation and double open technology. Atlas is an app basic framework which combines componentization and hot repair technology, and is widely used in various apps of Ali department. It claims to be a container framework.
Principle of plug-in
DexClassLoader and PathClassLoader, both of which inherit from BaseDexClassLoader. The difference is that when the parent constructor is called, DexClassLoader passes an additional parameter, optimizedDirectory, which must be the internal storage path used to cache the Dex files created by the system. For PathClassLoader, the parameter is null and only the Dex file of the internal storage directory can be loaded. So we can use DexClassLoader to load external APK.
-
Here I want to focus on the DexPathList of DexClassLoader. The DexClassLoader overloads the findClass method and calls its internal DexPathList to load the class. The DexPathList is generated when the DexClassLoader is constructed and contains the DexFile.
-
The loadClass of DexPathList traverses the DexFile until it finds a class that needs to be loaded.
Tencent’s Qzone hot repair technology makes use of the loading mechanism of DexClassLoader by adding the classes that need to be replaced to the front of dexElements, so that the system will use the repaired classes found first.
Resource to load
The Android system loads resources through the Resource object. As long as the path of the plug-in APK is added to the AssetManager, the access to the plug-in resources can be realized.
What are the methods of Object?
GetClass () // Returns the running class of this Object, which can be obtained from the class.
HashCode () // Used to get the hash value of the object.
Equals (Object obj) // Compares whether two objects are “the same”.
Clone () // Create a copy of the same object.
ToString () // Returns a string representation of the object, usually rewritten toString() except for the class name + hashCode
Notify () // Wakes up a single thread waiting on this object’s monitor.
NotifyAll () // Wakes up all threads waiting on the monitor of this object.
Wait (long timeout) // Causes the current thread to wait until another thread calls notify() or notifyAll() of this object, or until the specified amount of time has elapsed.
Wait (long timeout, int nanos) // Causes the current thread to wait until another thread calls notify() or notifyAll(), or until another thread interrupts the current thread, or until a certain amount of time has elapsed.
Wait () // Is used to disable the current thread and enter the wait sequence
Finalize () // This method is called by the object’s garbage collector when the garbage collector determines that there are no more references to the object.
How many threads does an Android startup normally have?
In Java, at least two threads are started each time a program runs: a main thread and a garbage collection thread. Because every time a class is executed using a Java command, a JVM is actually started, and each JVM is actually starting a process in the operating system. Two binder threads.
App crash capture analysis
Java exceptions can be categorized into two categories: Checked Exception and UnChecked Exception. All instances of the RuntimeException class and its subclasses are called Runtime exceptions (UnCheckedException), and Exception instances that are not RuntimeException and its subclasses are called Checked Exceptions.
There is no try… Caught exceptions, known as Uncaught exceptions, cause the application to crash. So is there anything we can do about it? For example, before the application exits, a personalized dialog box will pop up instead of the default forcible closure dialog box, or a dialog box will pop up to comfort the user, or even restart the application.
Java provides an interface to do this, called UncaughtExceptionHandler, which contains a pure virtual function: Public abstract void uncaughtException (Thread Thread, Throwableex).
When an Uncaught exception occurs, the thread will be terminated. In this case, UncaughtExceptionHandler is notified of the terminated thread and the corresponding exception. UncaughtException is then called. If the handler is not explicitly set, the default handler for the corresponding thread group is called. If we want to catch this exception, we must implement our own handler and set it up with the following function: public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler)
To implement a custom handler, only need to inherit UncaughtExceptionHandler interface and implement uncaughtException method.
package com.scott.crash; import java.io.File; import java.io.FileOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.os.Environment; import android.os.Looper; import android.util.Log; import android.widget.Toast; ** @author user ** / public class CrashHandler; /** * public class CrashHandler; /** * public class CrashHandler implements UncaughtExceptionHandler { public static final String TAG = "CrashHandler"; / / the system default UncaughtException processing class private Thread. UncaughtExceptionHandler mDefaultHandler; Private static CrashHandler INSTANCE = new CrashHandler(); // Program Context object private Context mContext; Private Map<String, String> infos = new HashMap<String, String>(); Private DateFormat formatter = new SimpleDateFormat(" YYYY-MM-DD-HH-MM-SS "); private DateFormat format = new SimpleDateFormat(" YYYY-MM-DD-HH-MM-SS "); Private CrashHandler() {} private CrashHandler() {} public static CrashHandler getInstance() { return INSTANCE; } /** * public void init(context context) {mContext = context; / / get the default system UncaughtException processor mDefaultHandler = Thread. GetDefaultUncaughtExceptionHandler (); / / set the CrashHandler as program default CPU Thread. SetDefaultUncaughtExceptionHandler (this); } @override public void UncaughtException (Thread Thread, Throwable ex) {if (! handleException(ex) && mDefaultHandler ! = null) {/ / if the user does not deal with the system default exception handler to handle mDefaultHandler. UncaughtException (thread, the ex); } else { try { Thread.sleep(3000); } catch (InterruptedException e) { Log.e(TAG, "error : ", e); Exit} / / android. OS. Process. KillProcess (android. OS. Process. MyPid ()); System.exit(1); ** @param ex * @return true: If the error message is processed, the error message will be sent. */ private Boolean handleException(Throwable ex) {if (ex == null) {return false; New Thread() {@override public void run() {looper.prepare (); Toast.maketext (mContext, "sorry, there is an exception, we are going to exit.", toast.length_long).show(); Looper.loop(); } }.start(); // Collect device parameter information collectDeviceInfo(mContext); // Save the log file saveCrashInfo2File(ex); return true; } /** * Collect device parameters * @param CTX */ public void collectDeviceInfo(Context CTX) {try {PackageManager PM = ctx.getPackageManager(); PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES); if (pi ! = null) { String versionName = pi.versionName == null ? "null" : pi.versionName; String versionCode = pi.versionCode + ""; infos.put("versionName", versionName); infos.put("versionCode", versionCode); } } catch (NameNotFoundException e) { Log.e(TAG, "an error occured when collect package info", e); } Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true); infos.put(field.getName(), field.get(null).toString()); Log.d(TAG, field.getName() + " : " + field.get(null)); } catch (Exception e) { Log.e(TAG, "an error occured when collect crash info", e); Private String saveCrashInfo2File(Throwable ex) {private String saveCrashInfo2File(Throwable ex) { StringBuffer sb = new StringBuffer(); for (Map.Entry<String, String> entry : infos.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key + "=" + value + "\n"); } Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); while (cause ! = null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); String result = writer.toString(); sb.append(result); try { long timestamp = System.currentTimeMillis(); String time = formatter.format(new Date()); String fileName = "crash-" + time + "-" + timestamp + ".log"; if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { String path = "/sdcard/crash/"; File dir = new File(path); if (! dir.exists()) { dir.mkdirs(); } FileOutputStream fos = new FileOutputStream(path + fileName); fos.write(sb.toString().getBytes()); fos.close(); } return fileName; } catch (Exception e) { Log.e(TAG, "an error occured while writing file..." , e); } return null; }}Copy the code
Several process communication modes
Android provides the following process communication mechanisms (process communication apis for developers) :
- file
- AIDL (Based on Binder)
- Binder
- Messenger (Based on Binder)
- ContentProvider (Based on Binder)
- Socket
Write a producer/consumer pattern
/ * *
- Public cache queue
- Do only two things :(1) produce; 2) Consumption
*/ public class PublicQueue {
private int putIndex = 0; Private int maxCount = 50; Private Lock Lock; private Condition addCondition; private Condition removeCondition; public PublicQueue(){ lock = new ReentrantLock(); addCondition = lock.newCondition(); removeCondition =lock.newCondition(); } private LinkedHashMap<Integer, T> linkedHashMap = new LinkedHashMap<>(); Public void add(T MSG){try {lock.lock(); If (linkedhashmap.size () == maxCount){// Block the producer thread addcondition.await () if the cache reaches the maximum number; } linkedhashmap. put(putIndex, MSG); System.out.println(" putIndex+"=== "+ MSG +"=== "+ linkedhashmap.size ()); putIndex = (putIndex + 1 >= maxCount) ? (putIndex + 1) % maxCount : putIndex + 1; removeCondition.signalAll(); // Wake up all threads} catch (InterruptedException e) {e.printStackTrace(); } finally { lock.unlock(); } } public T remove(){ T t = null; try { lock.lock(); If (linkedhashmap.size () == 0){// Block consuming thread removecondition.await () if there is no data in cache; Iterator it = linkedhashmap.entryset ().iterator(); if(it.hasNext()){ Map.Entry<Integer, T> entry = (Map.Entry<Integer, T>) it.next(); t = entry.getValue(); int index = entry.getKey(); linkedHashMap.remove(index); System.out.println(" Consume a product, the current item is marked as: "+index+"=== text: "+ t +"=== Cache length is: "+ linkedhashmap.size ()); } addCondition.signalAll(); // Wake up all threads} catch (InterruptedException e) {e.printStackTrace(); } finally { lock.unlock(); } return t; }Copy the code
}
Why is it possible to update the UI on child threads?
In short, we can update the UI on the child thread when the ViewRootImp is not created.
The error message is thrown from the ViewRootImpl checkThread method
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
Copy the code
The thread currently updating the UI is compared to the thread where the ViewRootImpl was created.
The ViewRootImp method is created after the Activity’s onResume method. If a child thread is started in the onCreate method to update the UI, the ViewRootImp method is not executed before it is created, so there is no error
Window, DecorView, ViewRootImp relationship?
An Activity corresponds to a Window, which is created when activity.attach() is attached
Calling setContentView() in your Activity actually calls the Window setContentView()
A call to setContentView() initializes the DecorView and its internal TitleView and mContentParent, You can make only mContentParent exist inside a DecorView by setting window.feature_no_title
The layout_activity. XML we define in XML is actually a child View of mContentParent.
ScrollView and RecycleView sliding conflict resolution?
1. External interception events are first processed by the parent container. If this event is not needed, it is not intercepted, so that the sliding conflict problem can be solved. The external interceptor overwrites the parent onInterceptTouchEvent() method and intercepts it internally
switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: intercepted = false; break; Case motionEvent. ACTION_MOVE: {if (parent needs event) {intercepted = true; } else { intercepted = false; } break; } case MotionEvent.ACTION_UP: { intercepted = false; break; } } return intercepted;Copy the code
In ACTION_DOWN, the parent must return false, that is, do not intercept the ACTION_DOWN event, because once intercepted, the subsequent ACTION_MOVE and ACTION_UP will be handled by the parent, and the event will not be transmitted to the child view
ACTION_MOVE events can be intercepted or not intercepted as needed
ACTION_UP must return false, which will cause the child View to fail to receive the UP event and the onClick() event in the child element to fail to fire.
2. Internal interception method the parent container does not intercept any events, all events are passed to the child element, if the child element needs the event directly consumed, otherwise the parent container for processing. This method need to cooperate with requestDisallowInterceptTouchEvent () method to work properly.
The main thing is to modify the dispatchTouchEvent() method of the child view.
@Override public boolean dispatchTouchEvent(MotionEvent ev) { int x = (int) ev.getX(); int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { getParent().requestDisallowInterceptTouchEvent(true); break; } case MotionEvent. ACTION_MOVE: {the if (the parent container need such events) {getParent () requestDisallowInterceptTouchEvent (false); } break; } case MotionEvent.ACTION_UP: { break; } } return super.dispatchTouchEvent(ev); }Copy the code
The parent container needs to override the onInterceptTouchEvent() method
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); if(action == MotionEvent.ACTION_DOWN){ return false; }else { return true; }}Copy the code
The parent intercepts events other than ACTION_DOWN. Since ACTION_DOWN events are not controlled by the FLAG_DISALLOW_INTERCEPT flag, once the parent intercepts an ACTION_DOWN event, none of the events can be passed to the child view. So the internal interception doesn’t work.
Summary of HashMap PUT methods
In put(key, value), the internal putVal method is directly called and the hash operation on the key is performed first.
PutVal (); putVal (); putVal (); putVal ();
3. Subtract the length of the HashMap index array table by one and the hash value of key to calculate the index in the array. If the position value specified by the index is empty, create a new k-V node.
4. If the condition of 3 is not met, it indicates that the contents of the array position specified by the index already exist, which is called collision.
5. After the above judgment process is completed, calculate the global modCount value of HashMap so as to provide a basis for the modified Fail-fast judgment for external concurrent iteration operations. Meanwhile, increase the number of records in the map and determine whether the number of records reaches the threshold of capacity expansion.
6. When a collision occurs in Step 4, a new round of logical judgment and processing is launched from Step 7.
7. Determine whether the hash and key of the node to which the key is indexed (temporarily called the node to be collided) are consistent with the current node to be inserted (new node). If so, save and record the node first; If the content of the new node is inconsistent with that of the old one, check whether the node is a TreeNode. If the node is a tree, append the content of the new node according to the operation of the tree. If the node is not a tree type, it indicates that the current collision is in the linked list (at this point, the linked list has not been turned into a red-black tree), and then it enters a cycle of processing logic.
8. In the loop, first judge whether the successor node of the collided node is empty. If it is empty, take the new node as the successor node, and judge whether the length of the current linked list exceeds the maximum allowable linked list length 8. If the successor node is not empty at the beginning, it determines whether the successor node is the same as the new node. If so, it records and breaks out of the loop. If both conditions are satisfied, the loop continues until one condition is reached and then out of the loop;
If the map index table is empty or the current index table length is less than 64 (the maximum length of index array table in a red-black tree), resize the map. Otherwise, if the node is not empty, the new node will be added along the tree of the node to be hit.
10. Finally, return to the remembered colliding node. If it is not empty, by default, the value of the new node replaces the colliding node and returns the colliding node’s value (V).
ANR related issues
- The ANR problem is caused by the main thread’s tasks not finishing within the specified time. The reasons for this problem are as follows:
- The main thread is doing some time-consuming work
- The main thread is locked by another thread
- The CPU is occupied by another process. The process is not allocated enough CPU resources. Procedure
Determining which case an ANR belongs to is the key to analyzing ANR problems: how do you analyze an ANR log?
When ANR occurs, the system will collect anR-related information for developers: first, anR-related information will be collected in the Log, second, the CPU usage during ANR will be collected, and trace information will be collected, that is, the execution status of each thread at that time. The trace.txt file is stored in /data/anr/ testamp.txt. In addition, the logs printed by the process before and after anr are of some value.
Here’s the idea:
-
Find ANR inverse information from log: You can search “ANR in” or “am_anr” from the log, and you will find the log of ANR occurrence. This line will contain information such as ANR time, process, and what kind of ANR. If is a BroadcastReceiver ANR can doubt BroadcastReceiver. OnRecieve (), if the Service or the Provider of doubt whether its onCreate ().
-
After this log, there will be CPU usage information, indicating the CPU usage before and after ANR (log indicates the time when ANR is intercepted). The following points can be analyzed from various CPU usage information:
(1). If the CPU usage of some processes is high and occupies almost all THE CPU resources, and the CPU usage of the ANR process is 0% or very low, it is considered that the CPU resources are occupied and the process is not allocated enough resources, thus ANR occurs. Most of this can be thought of as a system state problem, not caused by the application.
(2) If the CPU usage of ANR process is high, such as 80% or more than 90%, it can be suspected that some codes in the application consume CPU resources unreasonably, such as the occurrence of an infinite loop or many threads executing tasks in the background, etc., which should be further analyzed in combination with the log before and after TRACE and ANR.
(3). If the total CPU usage is not high, this process and other processes occupy too much, this may be due to the main thread operation is too long, or the main process is locked.
- In addition to the above case (1), the problem identified after analyzing CPU usage required further analysis of the trace file. The trace file records the stack of each thread of the process before and after ANR occurs. The most valuable thing for us to analyze ANR is the stack of the main thread. Generally, the trace of the main thread may have the following situations:
(1). The main thread is running or native, and the corresponding stack corresponds to the function in our application, so it is likely that timeout occurs when executing this function.
(2). The main thread is blocked: it is very obvious that the thread is locked. In this case, you can see which thread is locked and consider optimizing the code. If it is a deadlock problem, it is even more important to resolve it in a timely manner.
(3). At the moment of trace capture, time-consuming operations may have finished (ANR -> Time-consuming operations completed -> system trace capture), so the trace is useless.
Trigger principle of ANR:
- Service Indicates the Service Timeout caused by Service
Service Timeout is triggered when the ams. MainHandler in the "ActivityManager" thread receives the SERVICE_TIMEOUT_MSG message.Copy the code
Attach to the system_server Service process in the process of calls realStartServiceLocked, followed by a mAm. MHandler. SendMessageAtTime () to send a delayed message, delay often is defined, For example, 20 seconds for the foreground Service. Generated when ams.mainHandler in the ActivityManager thread receives a SERVICE_TIMEOUT_MSG message.
- The BroadcastQueue Timeout caused by the BroadcastReceiver
BroadcastReceiver Timeout is located in the "ActivityManager" thread of BroadcastQueue. Trigger BROADCAST_TIMEOUT_MSG BroadcastHandler received the news.Copy the code
- TimeOut caused by the main thread
In the Android Input system, InputDispatcher is responsible for distributing Input events to the UI main thread. After the UI main thread receives an input event, it uses InputConsumer to process the event. After a series of Inputstages have completed the event distribution, the finishInputEvent() method is executed to inform InputDispatcher that the event has been handled. The handleReceiveCallback() method is used in InputDispatcher to process messages returned by the UI main thread, and finally remove the dispatchEntry event from the wait queue.
InputDispatching Timeout ANR is generated during the dispatching of input events. InputDispatcher detects the status of the last input event during event distribution. If the last input event is not distributed within the specified time, ANR is triggered. InputDispatching Timeout The default Timeout is 5s, which is defined in two ways.
- ContentProvider Timeout
ContentProvider Timeout occurs during application startup. ANR is triggered if the Provider publication exceeds the specified time when the application is started. Once the application process is created, attachApplicationLocked() is called to initialize it.
When the Provider publication is not completed within the specified time, the message CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG is sent and the Handler processes it accordingly.
ContentProvider Timeout occurs without calling the ams.appNotresponding () method, just killing the problem process and cleaning up the information. The Provider timeout message is cleared when the publication is successful.
The post () principle
View.post() Usage scenario
-
Updating UI operations
-
Gets the actual width and height of the View
In an Activity, the View drawing process starts in the ActivityThread’s handleResumeActivity method, which first completes the Activity lifecycle onResume callback, Then start the View drawing task. In other words, the View drawing process is after the onResume method, but most of our business is in the onCreate method, for example, to get the actual width and height of a View, because the View drawing task has not started, so it can not get the correct.
PerformTranversals () is called during the resume phase of the Activity, which is when mAttachInfo is assigned.
2. Sometimes we might use view.post() in the Activity onCreate() to get the view’s width and height. If mAttachInfo is null, post() will store the incoming task and assign to mAttachInfo with dispatchAttachToWindow() when it resumes. Android is message-driven, and the Activity lifecycle is distributed as a message to the Handler. Therefore, these tasks will not be triggered until the current message callback event is completed. That is, these tasks will be executed after the view is measured, laid out, and drawn, so the width and height of the view can be retrieved in post(), and must be executed in the UI thread.
SwipeBackLayout principle analysis
By using SwipeBackLayout as the Parent of our contentView, the operation of right SwipeBackLayout will be handled by our outermost container SwipeBackLayout. SwipeBackLayout moves its childView to the right by the corresponding distance. After moving the left gap, draw a transparent color in the Draw method to show the underlying interface (you must specify windowIsTranslucent to true in the theme so that you can see the underlying activity).
- How does SwipeBackLayout set the outermost container?
In the onPostCreate callback of SwipeBackActivity, you can see that the attachToActivity method of SwipeBackLayout is executed through onPostCreate of SwipeBackActivityHelper. In this method, take the child view of the decorView, replace it with the cat, and use SwipeBackLayout as the root view.
Android Apk packaging process
Step 1: Package the resource file and generate the R.Java file. Aapt tool
Step 2: Process the AIDL file and generate the corresponding Java file. Aidl tool
Step 3: compile the project source code and generate the corresponding class file. Javac tools
Step 4: Convert all the class files to generate the classes.dex file. Javac tools
Step 5: Package to generate APK. Apkbuilder tools
Step 6: Sign the APK file. Jarsigner tool
Step 7: Align the signed APK files. The zipalign tool
Annotations associated
- Yuan notes
@documented -- Whether an annotation will be included in JavaDoc @Retention -- when the annotation is used @target -- Where the annotation is used @Inherited -- Whether a child class is allowed to inherit the annotationCopy the code
- @Target
Elementtype. FIELD annotations apply to variables
ElementType.METHOD Annotations apply to methods
Elementtype. PARAMETER Annotations apply to parameters
The elementType. CONSTRUCTOR annotation applies to a CONSTRUCTOR
The elementType. LOCAL_VARIABLE annotation applies to local variables
ElementType.PACKAGE annotations apply to packages
- @Retention
Retentionpolicy. SOURCE: stored only in the SOURCE code, not in the class, and not loaded into the VM. Discarded at compile time. These annotations are no longer meaningful after compilation, so they are not written to bytecode.
Retentionpolicy. CLASS: retained in the source code as well as in the CLASS, but not loaded into the VM. Discarded when the class is loaded. Useful in the processing of bytecode files. This is the default for annotations
Retentionpolicy. RUNTIME: saved in the source code, saved in the class, and finally loaded into the VM. It is never dropped, and the annotation is retained at runtime, so it can be read using reflection. Our custom annotations usually use this approach.
Compile-time annotations must use APT (compile-time annotation parsing technology) APT technology is mainly used to parse annotations at compile time and generate Java code. It is generally combined with Javapoet technology to generate code.
1. Write notes:
2 implement the annotation parser Processor
The annotation parser needs to implement the abstract class AbstractProcessor, and the parser needs to be placed in a separate Java Library Module. Rely on this lib through other Android Modules when you use it.
Several common cases of memory leaks
Memory leaks caused by handlers.
Solution: If you declare Handler as a static inner class, you do not hold a reference to the outer class SecondActivity, and its lifetime is independent of the outer class SecondActivity. If you need a context inside the Handler, you can use weak references to the outer class
Memory leak caused by singleton pattern.
Context is an ApplicationContext. Since ApplicationContext has the same lifecycle as app, it will not leak memory
Memory leaks caused by static instances of non-static inner classes.
Solution: Make the inner class static to avoid memory leaks
Memory leaks caused by non-static anonymous inner classes.
Solution: Make the anonymous inner class static.
5. Memory leakage caused by unpaired use of register/unregister.
Register broadcast receivers, EventBus, etc., remember to unbind.
Memory leaks caused by resource objects not being closed.
When these resources are not available, call recycler (), close (), destroy (), recycler () or release ().
Memory leaks caused by collection objects not being cleaned up in time.
Some objects are usually loaded into the collection, and when not in use, it is important to clean up the collection so that related objects are no longer referenced.
Why can an inner class access a member of an outer class?
1 The compiler automatically adds a member variable to the inner class. This member variable is of the same type as the outer class. This member variable is a reference to the object of the outer class.
2 The compiler automatically adds a parameter to the constructor of the inner class. The type of the parameter is the type of the outer class. Inside the constructor, this parameter is used to assign values to the member variables added in 1.
When an inner class’s constructor is called to initialize an inner class object, a reference to the outer class is passed in by default.
The thread pool
1, if the number of running threads < coreSize, create a core thread to execute the task immediately without queuing; 2, If the number of running threads >= coreSize, put the task in the blocking queue;
If the queue is full && the number of running threads < maximumPoolSize, create a new non-core thread to execute the task;
4. If the queue is full && number of running threads >= maximumPoolSize, the pool calls handler’s Reject method to reject the commit.
Thread reuse of thread pools
This is where you dive into the source code addWorker() : it is the key to creating new threads and the key entry point for thread reuse. The result is runWoker, which takes the task in two ways:
FirstTask: This is the first specified runnable executable task that will run the execution task Run in the Woker worker thread. And empty indicates that the task has been executed. GetTask () : This first is an infinite loop, the worker thread loop until it can retrieve the Runnable object or return a timeout. In this case, the target of the fetch is the workQueue, corresponding to the operation that was just enqueued.
In fact, the task not only performs the firstTask specified at the time of creation, but also actively fetches the task execution from the task queue through the getTask() method, and blocks and waits with/without time limit to ensure the survival of the thread.
What kinds of work queues do thread pools have?
ArrayBlockingQueue is a bounded blocking queue based on an array structure that sorts elements according to FIFO (first-in, first-out).
LinkedBlockingQueue A LinkedBlockingQueue based on a linked list structure that sorts its elements in FIFO (first-in, first-out) order and has a higher throughput than ArrayBlockingQueue. Static factory methods Executors. NewFixedThreadPool () and Executors. NewSingleThreadExecutor using the queue.
SynchronousQueue A blocking queue that does not store elements. Each insert operation must wait until another thread calls to remove operation, otherwise the insert has been in the blocking state, the throughput is usually more than LinkedBlockingQueue, static factory methods Executors. NewCachedThreadPool using the queue.
4, PriorityBlockingQueue
An infinite blocking queue with priority.