Deep source parsing Handler,message,messagequeue,looper in Android

Source: Internet
Author: User
<span id="Label3"></p><p><p>This article is mainly to handler and message loop implementation principle of source analysis, if not familiar with handler can see the blog "android handler use", which is why Android introduced handler mechanism and how to use handler to Explain.</p></p><p><p>In summary, handler is a mechanism introduced in Android that allows developers to participate in the processing of message loops in THREADS. We have the most dealings with the message when using handler, and the message is the related class that the Hanlder mechanism exposes to the developer, which can do most of the operations handler through the message class. But as a programmer, I can not only know how to use handler, but also know how the internal Implementation. The internal implementation of handler mainly involves the following classes: Thread, messagequeue, and Looper. The relationships between these classes can be illustrated in the following diagram:</p></p><p><p></p></p><p><p>Thread is the most basic, Looper and MessageQueue are built on the thread, handler is built on Looper and messagequeue, we are indirectly through the handler with the following a few relatively low-level classes dealing with.</p></p>MessageQueue<p><p>MessageQueue Source Link</p></p><p><p>The Bottom-most Foundation is thread, which maintains a message queuing--messagequeue inside each thread. Message Queuing messagequeue, As the name implies, is the queue that holds messages (as if it were nonsense ...). )。 What is the message stored in that queue? Let's say we clicked on a button on the UI and the program just received a broadcast event, so what do we do with these two things? Because a thread can only handle one thing at a time, cannot handle many things at the same time, so we can't handle the Button's click event and broadcast event at the same time. For this reason, Android encapsulates a Button-click event in the UI interface into a message, puts it into the messagequeue, and then wraps the messages of the button event into the queue, then encapsulates the broadcast event as a message, It is also stacked into the message Queue. This means that a message object represents one thing that a thread needs to handle, and a queue is a pool of messages to be Processed. Thread threads take the messages in the message queue in turn, processing them sequentially. There are two more important methods in messagequeue, one is the Enqueuemessage method and the next Method. The Enqueuemessage method is used to put a message into the messages queue messagequeue, and the next method is to block out a message from the messages queue Messagequeue. In android, Message Queuing is responsible for managing top-level program objects (Activity, broadcastreceiver, and so On) and all windows created by it. It is important to note that Message Queuing is not unique to the Android platform, and that other platform frameworks also use message queuing, such as Microsoft's MFC Framework.</p></p>Looper<p><p>Looper Source Link</p></p><p><p>Message Queuing MessageQueue is just the place where the message is stored, the real thing is looper, which is like Message Queuing MessageQueue is a water truck, so Looper is the river that makes the water wheel turn up, if there is no river, Then the water wheel is a static equipment, no use, looper let MessageQueue move up, have a Vitality.</p></p><p><p>Looper is used to loop the messages in a Thread. By default, When we create a new thread, there is no message queue MessageQueue in this Thread. To enable a thread to bind a message queue, we need to use Looper: first we call the prepare method of Looper and then call the Looper loop method. The typical code is as Follows:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs ">class LooperThread extends Thread { <span class="hljs-keyword">public</span> Handler mHandler; <span class="hljs-keyword">public</span><span class="hljs-keyword">void</span><span class="hljs-title">run</span>() { Looper.prepare(); <span class="hljs-keyword">new</span> Handler() { <span class="hljs-keyword">public</span><span class="hljs-keyword">void</span><span class="hljs-title">handleMessage</span>(Message msg) { <span class="hljs-comment">// process incoming messages here</span> } }; Looper.loop(); } }</code></pre></pre><p><p>It is important to note that both Looper.prepare () and Looper.loop () are called within the new Thread's run method, both of which are static METHODS. By looking at the source code of Looper, we can find that the Looper constructor is private, that is, the outside of the class cannot get a Looper object in the form of new Looper (). According to our description above, we know that thread and looper are one-on-one binding, that is, there is only a single Looper object in a thread, which can explain why the Looper constructor is Private. We can only get the looper that is bound by the current thread by using the factory method Looper.mylooper () this static method.</p></p><p><p>Looper saves a reference to the current thread by using the following code:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">static</span><span class="hljs-keyword">final</span><span class="hljs-keyword">new</span> ThreadLocal<Looper>();</code></pre></pre><p><p>So it is possible to find its bound thread in the Looper object through Sthreadlocal. There is a set method and a Get method in threadlocal that can be used to store an object in the Threadlocal by the set method, and then the object can be fetched by the get Method. Threadlocal used generics in new, and from the code above we can see that the generic type here is looper, which means that we can only write and read Looper object types through the threadlocal set and get METHODS. If we call its Threadlocal set method to pass in a looper, bind the looper to the thread, the corresponding get gets the Looper object that the thread is bound to.</p></p><p><p>Let's take a look at Looper.prepare (), the method is to get Looper ready, only Looper ready to call the Looper.loop () method, the code of Looper.prepare () is as Follows:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">private</span><span class="hljs-keyword">static</span><span class="hljs-keyword">void</span><span class="hljs-title">prepare</span>(<span class="hljs-keyword">boolean</span> quitAllowed) { <span class="hljs-keyword">if</span><span class="hljs-keyword">null</span>) { <span class="hljs-keyword">throw</span><span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Only one Looper may be created per thread"</span>); } sThreadLocal.set(<span class="hljs-keyword">new</span> Looper(quitAllowed));}</code></pre></pre><p><p>The above code first gets the Looper object bound by the thread sthreadlocal through Sthreadlocal.get (), because sthreadlocal is not bound looper in the initial case, so when the prepare method is called for the first time, Sthreadlocal.get () returns null and does not throw an Exception. The point is that the following code, <strong>sthreadlocal.set (new Looper (QUITALLOWED))</strong>, first creates an instance of the Looper object through a private Constructor. The looper is then bound to sthreadlocal through the sthreadlocal set Method.<br>This completes the two-way binding of the thread sthreadlocal to the Looper:<br>A. The thread that the Looper binds can be obtained through sthreadlocal within the looper;<br>B. Thread sthreadlocal through the Sthreadlocal.get () method, you can get the Looper object that the thread is bound to.</p></p><p><p>The code above executes the Looper constructor, so let's take a look at its code:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">private</span><span class="hljs-title">Looper</span>(<span class="hljs-keyword">boolean</span> quitAllowed) { <span class="hljs-keyword">new</span> MessageQueue(quitAllowed); mThread = Thread.currentThread();}</code></pre></pre><p><p>We can see that a Message Queuing MessageQueue is instantiated in its constructor and assigned to its member field mqueue, so Looper is associated with MessageQueue through the member field Mqueue.</p></p><p><p>After Looper.prepare () is executed, we can get the current Thread-bound Looper object externally by calling Looper.mylooper ().<br>The code for Mylooper is as Follows:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">public</span><span class="hljs-keyword">static</span><span class="hljs-title">myLooper</span>() { <span class="hljs-keyword">return</span> sThreadLocal.get();}</code></pre></pre><p><p>It is important to note that only one looper.prepare () can be called in a thread, because after the first call to Looper.prepare (), the current thread is already bound to looper, and the line range the second call to Looper.prepare () method, Sthreadlocal.get () Returns the first call to prepare at the time of the binding Looper, not null, so the following code will go to throw new RuntimeException ("only one Looper May is created per thread "), which throws an exception and tells the developer that a thread can only bind a looper object.</p></p><p><p>After the Looper.prepare () method is called, the current thread and the Looper are bound in two directions, and we can call the Looper.loop () method to loop the message Queue.<br><strong>It is important to note that Looper.loop () should be executed in the thread that the Looper is bound to.</strong></p></p><p><p>The code for Looper.loop () is as Follows:</p></p><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword"><span class="hljs-keyword"></span> public</span> <span class="hljs-keyword"><span class="hljs-keyword">Static</span></span> <span class="hljs-keyword"><span class="hljs-keyword">void</span></span> <span class="hljs-title"><span class="hljs-title">Loop</span></span>() {<span class="hljs-keyword"><span class="hljs-keyword">Final</span></span>Looper me = Mylooper ();<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(me = =<span class="hljs-keyword"><span class="hljs-keyword">NULL</span></span>) {<span class="hljs-keyword"><span class="hljs-keyword">Throw</span></span> <span class="hljs-keyword"><span class="hljs-keyword">New</span></span>RuntimeException (<span class="hljs-string"><span class="hljs-string">"No Looper; Looper.prepare () wasn ' t called on the This Thread. "</span></span>); }<span class="hljs-comment"><span class="hljs-comment">//note the following line</span></span> <span class="hljs-keyword"><span class="hljs-keyword">Final</span></span>MessageQueue queue = me.mqueue;<span class="hljs-comment"><span class="hljs-comment">//make sure the identity of this thread is the the local process,</span></span> <span class="hljs-comment"><span class="hljs-comment">//and Keep</span> track of the what, identity token actually is.</span>Binder.clearcallingidentity ();<span class="hljs-keyword"><span class="hljs-keyword">Final</span></span> <span class="hljs-keyword"><span class="hljs-keyword">Long</span></span>Ident = Binder.clearcallingidentity ();<span class="hljs-comment"><span class="hljs-comment">//note the following line</span></span> <span class="hljs-keyword"><span class="hljs-keyword"></span> for</span>(;;) {<span class="hljs-comment"><span class="hljs-comment">//note the following line</span></span>Message msg = Queue.next ();<span class="hljs-comment"><span class="hljs-comment">//might Block</span></span> <span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(msg = =<span class="hljs-keyword"><span class="hljs-keyword">NULL</span></span>) {<span class="hljs-comment"><span class="hljs-comment">//No message indicates that the message queue is Quitting.</span></span> <span class="hljs-keyword"><span class="hljs-keyword">return</span></span>; }<span class="hljs-comment"><span class="hljs-comment">//this must is in a local variable, with case a UI event sets the logger</span></span>Printer logging = me.mlogging;<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(logging! =<span class="hljs-keyword"><span class="hljs-keyword">NULL</span></span>) {logging.println (<span class="hljs-string"><span class="hljs-string">">>>>> dispatching to"</span></span>+ Msg.target +<span class="hljs-string"><span class="hljs-string">" "</span></span>+ Msg.callback +<span class="hljs-string"><span class="hljs-string">": "</span></span>+ msg.what); }<span class="hljs-comment"><span class="hljs-comment">//note the following line</span></span>Msg.target.dispatchMessage (msg);<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(logging! =<span class="hljs-keyword"><span class="hljs-keyword">NULL</span></span>) {logging.println (<span class="hljs-string"><span class="hljs-string">"<<<<< finished to"</span></span>+ Msg.target +<span class="hljs-string"><span class="hljs-string">" "</span></span>+ msg.callback); }<span class="hljs-comment"><span class="hljs-comment">//make sure that during the course of dispatching</span> the</span> <span class="hljs-comment"><span class="hljs-comment">//identity of the thread wasn ' t Corrupted.</span></span> <span class="hljs-keyword"><span class="hljs-keyword">Final</span></span> <span class="hljs-keyword"><span class="hljs-keyword">Long</span></span>Newident = binder.clearcallingidentity ();<span class="hljs-keyword"><span class="hljs-keyword">if</span></span>(ident! = Newident) {LOG.WTF (TAG,<span class="hljs-string"><span class="hljs-string">"Thread identity changed from 0x"</span></span>+ long.tohexstring (ident) +<span class="hljs-string"><span class="hljs-string">"to 0x"</span></span>+ long.tohexstring (newident) +<span class="hljs-string"><span class="hljs-string">"while dispatching to"</span></span>+ Msg.target.getClass (). getName () +<span class="hljs-string"><span class="hljs-string">" "</span></span>+ Msg.callback +<span class="hljs-string"><span class="hljs-string">"what="</span></span>+ msg.what); } msg.recycleunchecked (); }}</code></pre><p><p>There are several lines of code that are key code:<br>1. <strong>final MessageQueue queue = me.mqueue;</strong><br>The variable me is the looper,me.mqueue that is bound by the current thread that is obtained by the static method Mylooper () is the message queue that the current thread is associated With.<br>2. for <strong>(;;)</strong><br>We found that the for loop did not set the condition for loop termination, so this for loop is a dead loop.<br>3. <strong>Message msg = queue.next ();//might block</strong><br>We take a message from the message queue through the next method of the message queue messagequeue, and if there is a message in the queue of messages, the next method returns the message immediately, and if there is no message at this point in the queue, the next method Wait for the message to get <strong>blocked</strong> .<br>4. <strong>Msg.target.dispatchMessage (msg);</strong><br>The target property of MSG is handler, which means that the handler associated with the message is let handler process the message through the DispatchMessage method. The DispatchMessage method for handler will be described in detail below.</p></p>Handler<p><p>Handler Source Link</p></p><p><p>Handler is a class that is exposed to the top of the developer, built on thread, looper, and Messagequeue.<br>Handler has multiple constructors, and the signatures are as Follows:<br>1. Publichandler ()<br>2. Publichandler (callbackcallback)<br>3. Publichandler (looperlooper)<br>4. Publichandler (looperlooper, Callbackcallback)<br>Neither the 1th and 2nd Constructors Pass looper, both of which will get the current Thread-bound Looper object by calling Looper.mylooper (), and then save the Looper object to a member field named Mlooper.<br>The 3rd and 4th Constructors Pass the Looper object, which saves the looper to a member field named Mlooper.<br>The 2nd and 4th Constructors also pass the callback object, callback is the internal interface in handler and needs to implement its internal handlemessage method, the callback code is as Follows:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">public</span><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Callback</span> {</span> <span class="hljs-keyword">public</span><span class="hljs-keyword">boolean</span><span class="hljs-title">handleMessage</span>(Message msg);}</code></pre></pre><p><p>Handler.callback is a means of handling the message, and if it is not passed, then the Handler Handlemessage method should be rewritten, that is, in order for Handler to be able to handle the message, we have two ways:<br>1. Pass in a Handler.callback object to the Hanlder constructor and implement the Handler.callback Handlemessage method<br>2. You do not need to pass in the Handler.callback object to the Hanlder constructor, but you need to override the Handlemessage method of the handler itself<br>In other words, either way, we have to somehow implement the Handlemessage method, which is similar to the design of thread in Java.<br>In java, if we want to use multi-threading, there are two ways:<br>1. Pass in a Runnable object to the Thread's constructor and implement the Runnable Run method<br>2. You do not need to pass in the Runnable object to the Thread's constructor, but override the Run method of the thread itself<br>So as long as the use of multi-threaded thread, should be hanlder this need to implement handlemessage two ways to clear the Heart.</p></p><p><p>We know that through the Sendmessagexxx series method can add messages to the message queue, we can see through the source code of these methods call order,<br>SendMessage called the sendmessagedelayed,sendmessagedelayed and called the Sendmessageattime.<br>Handler also has a series of sendemptymessagexxx methods, and these sendemptymessagexxx methods call their corresponding sendmessagexxx methods within Them.</p></p><p><p>We can see more clearly through the following call Graph:<br><br>This shows that all sendmessagexxx methods and Sendemptymessagexxx eventually call the Sendmessageattime Method.</p></p><p><p>We look at the postxxx method, we will find the Postxxx method in its internal and call the corresponding sendmessagexxx method, we can view the source of the next sendmessage:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">public</span><span class="hljs-keyword">final</span><span class="hljs-keyword">boolean</span><span class="hljs-title">post</span>(Runnable r){ <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>);}</code></pre></pre><p><p>You can see the internal call <strong>getpostmessage</strong> method, The method passed in a runnable object, get a message object, getpostmessage the source code as Follows:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">private</span><span class="hljs-keyword">static</span><span class="hljs-title">getPostMessage</span>(Runnable r) { Message m = Message.obtain(); m.callback = r; <span class="hljs-keyword">return</span> m; }</code></pre></pre><p><p>From the above code we can see that in the Getpostmessage method, we create a Message object and assign the incoming Runnable object to the callback member field of the message, and then return the message, The message that carries the runnable message in the Post method is then passed into the sendmessagedelayed method. Thus we can see that all postxxx methods need to be implemented internally by means of the sendmessagexxx method, so postxxx and sendmessagexxx are not opposites, but postxxx depend on sendmessagexxx, So the Postxxx method can pass the message to the message queue through the sendmessagexxx method, except that the Postxxx method carries a Runnable object (message.callback) to the message Queue.</p></p><p><p>We can see the call relationship between the Postxxx series method and the Sendmessagexxx method by the following diagram:<br></p></p><p><p>By analysing the relationship between sendemptymessagexxx, Postxxx method and Sendmessagexxx method respectively, We can see that all of the methods in handler that can send messages directly or indirectly to Message Queuing eventually call the Sendmessageattime method, which has the following source Code:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs"><span class="hljs-keyword">public </span> <span class="hljs-keyword">boolean </span> <span class="hljs-title">sendmessageattime </span> (Message msg, <span class="hljs-keyword">long </span> uptimemillis) {MessageQueue queue = mqueue; <span class="hljs-keyword">if </span> (queue = = <span class="hljs-keyword">null </span>) {runtimeexception e = <span class="hljs-keyword">new </span> runtimeexception (<span class=" Hljs-keyword ">this </span> + <span class=" hljs-string ">" sendmessageattime () called with no mqueue "</span>); LOG.W (<span class="hljs-string"> "Looper" </span>, e.getmessage (), e); <span class="hljs-keyword">return </span> <span class="hljs-keyword">false </span>; } <span class="hljs-comment">//note The following line of code </span> <span class="hljs-keyword">return </span> enqueuemessage (queue , msg, uptimemillis);} </code> </pre></pre><p><p>The method internally calls the <strong>enqueuemessage</strong> method, The source code of the method is as Follows:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">private</span><span class="hljs-keyword">boolean</span><span class="hljs-title">enqueueMessage</span><span class="hljs-keyword">long</span> uptimeMillis) { <span class="hljs-comment">//注意下面这行代码</span> <span class="hljs-keyword">this</span>; <span class="hljs-keyword">if</span> (mAsynchronous) { msg.setAsynchronous(<span class="hljs-keyword">true</span>); } <span class="hljs-comment">//注意下面这行代码</span> <span class="hljs-keyword">return</span> queue.enqueueMessage(msg, uptimeMillis);}</code></pre></pre><p><p>There are two things to be aware of in this approach:<br>1. <strong>msg.target =</strong> this<br>The code binds the target of the message to the current handler<br>2. <strong>queue.enqueuemessage</strong><br>The variable queue represents the message queue MessageQueue that handler binds to, and by calling Queue.enqueuemessage (msg, Uptimemillis) We put the message into the messages Queue.</p></p><p><p>So we can see the complete method call order By:<br></p></p><p><p>When we analyzed the source code of Looper.loop (), we found that Looper had been continuously retrieving messages from the message queue through the MessageQueue next method, and then through the code <strong>msg.target.dispatchMessage (msg)</strong> Let the msg bind handler (message.target) Execute the DispatchMessage method to implement the processing of the Message.<br>The source code of handler DispatchMessage is as Follows:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">public</span><span class="hljs-keyword">void</span><span class="hljs-title">dispatchMessage</span>(Message msg) { <span class="hljs-comment">//注意下面这行代码</span> <span class="hljs-keyword">if</span><span class="hljs-keyword">null</span>) { handleCallback(msg); <span class="hljs-keyword">else</span> { <span class="hljs-comment">//注意下面这行代码</span> <span class="hljs-keyword">if</span><span class="hljs-keyword">null</span>) { <span class="hljs-keyword">if</span> (mCallback.handleMessage(msg)) { <span class="hljs-keyword">return</span>; } } <span class="hljs-comment">//注意下面这行代码</span> handleMessage(msg); }}</code></pre></pre><p><p>Let's analyze This code:</p></p><p><p>1. The first is to determine if the Msg.callback exists, Msg.callback is the runnable type, if the Msg.callback exists, then the message is indicated by the handler series method of Postxxx to put the message into the messages queue, this situation The following will execute Handlecallback (msg), the Handlecallback source code is as Follows:</p></p><pre class="prettyprint"><pre class="prettyprint"><code class="language-java hljs "><span class="hljs-keyword">private</span><span class="hljs-keyword">static</span><span class="hljs-keyword">void</span><span class="hljs-title">handleCallback</span>(Message message) { message.callback.run();}</code></pre></pre><p><p>So we can see clearly that we have executed the Msg.callback run method, that is, the Run method that executed the Runnable object passed by postxxx.</p></p><p><p>2. If we do not place a message into the queue of messages through the POSTXXX series method, then Msg.callback is null, the code continues to execute, and we then determine that the handler member field Mcallback not Exist. Mcallback is a hanlder.callback type, as we mentioned above, in Handler's constructor we can pass an object of the Hanlder.callback type, which needs to implement the Handlemessage method, if we pass the Cal in the constructor Lback object, Then we will let Callback's Handlemessage method handle the Message.</p></p><p><p>3. If we do not have an object of type callback in the constructor, then Mcallback is null, then we call Handler's own Hanldemessage method, which is an empty method by default, and we need to implement the method ourselves as a Rewrite.</p></p><p><p>In summary, we can see that handler provides three ways to process a message, and that there is a pre-and post-priority point: first trying to get the runnable executed in postxxx, Next try to let the handler constructor in the callback Handlemessage method of processing, the last is to let handler own Handlemessage method processing Message.</p></p>A picture wins thousands of words<p><p>In this article we discuss the relationship between thread, MessageQueue, looper, and hanlder, and we can understand them more visually through a graph of the following belts.</p></p><p><p></p></p><p><p>In real life production and life, there are a variety of conveyor belts, the conveyor is covered with a variety of goods, the conveyor belt in the engine wheel drive has been rolling forward, constantly have new goods placed at one end of the conveyor belt, the goods are driven by the conveyor belt to the other end for collection and processing.</p></p><p><p>We can think of the goods on the conveyor belt as one message, and the conveyor belt that carries the goods is the message queue MessageQueue that loads Messages. The conveyor belt is driven by the transmitter roller, we can think of the transmitter roller as looper, and the engine rotation is the need for power, we can think of the power supply as thread threads, all the message loop operation is based on a thread. Everything is ready, we just need to press the POWER switch engine will turn up, This switch is Looper loop method, when we press the switch, we are equivalent to execute the Looper loop method, at this time Looper will drive the message queue loop up.</p></p><p><p>What is the equivalent of the Hanlder in the belt model? We can see the handler as a conduit for loading goods and taking goods: the goods are drawn from one end to the conveyor belt, and the goods from the other end along the Pipe. The way we put the goods at one end of the conveyor is equivalent to the sendmessagexxx, sendemptymessagexxx, or Postxxx method we call the handler, This puts the message object in the queue Messagequeue. When the goods are drawn down the pipe from the other end of the conveyor, we are equivalent to invoking the Hanlder DispatchMessage method, in which we complete the processing of the Message.</p></p><p><p>It's a lot to talk about and hopefully this article will help you understand the handler and message loop mechanisms in ANDROID.</p></p> <p style="font-size:12px;"><p style="font-size:12px;">Copyright Notice: This article for Bo Master original article, without Bo Master permission not Reproduced.</p></p> <p><p>Deep source parsing Handler,message,messagequeue,looper in Android</p></p></span>

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.