Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> API翻譯 --- Progresses and Threads

API翻譯 --- Progresses and Threads

編輯:關於Android編程

When an application component starts and the application does not have any other components running, the Android system starts a new Linux process for the application with a single thread of execution. By default, all components of the same application run in the same process and thread (called the "main" thread). If an application component starts and there already exists a process for that application (because another component from the application exists), then the component is started within that process and uses the same thread of execution. However, you can arrange for different components in your application to run in separate processes, and you can create additional threads for any process.

This document discusses how processes and threads work in an Android application.

當一個應用程序組件啟動和運行的應用程序沒有任何其他組件,Android系統啟動一個新的Linux應用程序的過程用單個線程的執行。默認情況下,同一應用程序的所有組件運行在相同的進程和線程(稱為“主要”線程)。如果一個應用程序組件開始,已經存在一個過程,應用程序(因為存在另一個組件從應用程序),那麼組件啟動過程和使用相同的線程內執行。然而,您可以安排不同的組件在應用程序運行在單獨的進程,你可以為任何過程創建額外的線程。
本文討論了如何在Android應用程序進程和線程的工作。

 

Processes


By default, all components of the same application run in the same process and most applications should not change this. However, if you find that you need to control which process a certain component belongs to, you can do so in the manifest file.

默認情況下,同一應用程序的所有組件運行在相同的進程,大多數應用程序不應該改變這種情況。然而,如果你發現你需要控制哪些過程屬於某一組件,你可以在manifest文件中。

 

The manifest entry for each type of component element—,,, and—supports anandroid:processattribute that can specify a process in which that component should run. You can set this attribute so that each component runs in its own process or so that some components share a process while others do not. You can also setandroid:processso that components of different applications run in the same process—provided that the applications share the same Linux user ID and are signed with the same certificates.

為每種類型的組件清單條目元素——<活動>,<服務>、<接收機>,和<供應商>支持android:過程屬性,可以指定一個組件應該運行的過程。您可以設置該屬性,這樣每個組件運行在它自己的進程或這樣一些組件共享一個過程而其他人沒有。設置此屬性還可以讓不同的應用程序的組件運行在相同的進程中——實現多個應用程序共享相同的Linux用戶ID和賦予同樣的證書。

 

Theelement also supports anandroid:processattribute, to set a default value that applies to all components.

<應用>元素也支持android:過程屬性,設置一個默認值,適用於所有組件。

 

Android might decide to shut down a process at some point, when memory is low and required by other processes that are more immediately serving the user. Application components running in the process that's killed are consequently destroyed. A process is started again for those components when there's again work for them to do.

Android可能決定關閉一個過程在某種程度上,當內存低,要求其他進程,更直接的服務於用戶。應用程序組件運行的過程,也會因此被銷毀。為這些組件重新開始一個過程的時候再為他們工作。

 

When deciding which processes to kill, the Android system weighs their relative importance to the user. For example, it more readily shuts down a process hosting activities that are no longer visible on screen, compared to a process hosting visible activities. The decision whether to terminate a process, therefore, depends on the state of the components running in that process. The rules used to decide which processes to terminate is discussed below.

在決定哪個進程時,Android系統向用戶重他們的相對重要性。例如,它更容易關閉托管活動過程不再顯示在屏幕上,相比一個托管可見的活動過程。因此,決定是否終止一個進程取決於組件的狀態在這一進程運行。終止進程的判定規則用於下面討論。

 

Process lifecycle 進程生命周期

The Android system tries to maintain an application process for as long as possible, but eventually needs to remove old processes to reclaim memory for new or more important processes. To determine which processes to keep and which to kill, the system places each process into an "importance hierarchy" based on the components running in the process and the state of those components. Processes with the lowest importance are eliminated first, then those with the next lowest importance, and so on, as necessary to recover system resources.

Android系統試圖盡可能保持應用程序進程,但最終需要刪除舊的進程來回收內存新的或更重要的是過程。來確定哪個進程保留或終止,系統把每個進程都劃入一個“重要性層次結構”基於組件中運行過程和這些組件的狀態。重要性最低的進程首先是消除,那麼下一個最低的重要性,等等,必要時恢復系統資源。

 

There are five levels in the importance hierarchy. The following list presents the different types of processes in order of importance (the first process ismost importantand iskilled last):

有五個層次重要性水平。下面的列表給出了不同類型的流程按照重要性(第一個過程是最重要的和被殺最後):

 

Foreground process

前台進程

A process that is required for what the user is currently doing. A process is considered to be in the foreground if any of the following conditions are true:

這一過程需要用戶目前正在做什麼。一個過程被認為是前景如果下列條件:

  • It hosts anActivitythat the user is interacting with (theActivity'sonResume()method has been called).舉辦一個活動,用戶交互(活動的onResume()方法被調用)。
  • It hosts aServicethat's bound to the activity that the user is interacting with. 它所承載的服務綁定到用戶交互的活動。
  • It hosts aServicethat's running "in the foreground"—the service has calledstartForeground(). 它的主機服務運行“前景”——服務稱為startForeground()。
  • It hosts aServicethat's executing one of its lifecycle callbacks (onCreate(),onStart(), oronDestroy()). 主機的服務的執行它的生命周期回調(onCreate(),onStart(),或onDestroy())。
  • It hosts aBroadcastReceiverthat's executing itsonReceive()method. 它執行主機的BroadcastReceiver onReceive()方法。

Generally, only a few foreground processes exist at any given time. They are killed only as a last resort—if memory is so low that they cannot all continue to run. Generally, at that point, the device has reached a memory paging state, so killing some foreground processes is required to keep the user interface responsive.

一般來說,只有少數前台進程存在在任何給定的時間。它們被殺死只作為最後的策略——當內存不足以維持,他們不能繼續運行。一般來說,在這一點上,設備已經達到了一個內存分頁狀態,所以殺死一些前台進程需要保持用戶界面的響應性。

 

Visible process 可視進程

A process that doesn't have any foreground components, but still can affect what the user sees on screen. A process is considered to be visible if either of the following conditions are true:

這一過程沒有前台組件,但仍然會影響用戶在屏幕上看到的內容。一個過程被認為是可見如果下列條件是真實的:

 

It hosts anActivitythat is not in the foreground, but is still visible to the user (itsonPause()method has been called). This might occur, for example, if the foreground activity started a dialog, which allows the previous activity to be seen behind it. 舉辦一個活動,不在前台,但仍可見到用戶(onPause()方法被調用)。這可能發生,例如,如果前台活動開始一個對話框,允許它後面看到前面的活動。
It hosts aServicethat's bound to a visible (or foreground) activity. 主機的服務的綁定到一個可見(或前台)活動。

A visible process is considered extremely important and will not be killed unless doing so is required to keep all foreground processes running.

可見進程被認為是極其重要的,不會被殺死,除非這樣做需要維持所有前台進程運行。

Service process 服務進程

A process that is running a service that has been started with thestartService()method and does not fall into either of the two higher categories. Although service processes are not directly tied to anything the user sees, they are generally doing things that the user cares about (such as playing music in the background or downloading data on the network), so the system keeps them running unless there's not enough memory to retain them along with all foreground and visible processes.

這一過程是運行一個服務,由startService()方法已經啟動,不落入兩個中的哪一個更高的類別。盡管服務過程不直接與用戶看到的任何東西,他們通常做用戶關心的事情(比如在後台播放音樂或下載數據網絡上的),所以系統讓他們運行,除非沒有足夠的內存來留住他們連同所有前景和可見的進程。

 

Background process 後台處理

A process holding an activity that's not currently visible to the user (the activity'sonStop()method has been called). These processes have no direct impact on the user experience, and the system can kill them at any time to reclaim memory for a foreground, visible, or service process. Usually there are many background processes running, so they are kept in an LRU (least recently used) list to ensure that the process with the activity that was most recently seen by the user is the last to be killed. If an activity implements its lifecycle methods correctly, and saves its current state, killing its process will not have a visible effect on the user experience, because when the user navigates back to the activity, the activity restores all of its visible state. See theActivitiesdocument for information about saving and restoring state.

舉行一個活動過程,目前不可見的用戶(活動的onStop()方法被調用)。這些過程沒有直接影響用戶體驗,並且系統可以在任何時間來回收內存殺死他們的前景,可見,或服務的過程。通常有很多後台進程運行,所以它們被關在一個LRU(最近最少使用)列表,以確保流程的活動,最近看到的用戶是最後被殺死。如果一個活動實現其生命周期方法正確,並保存其當前狀態,造成其過程不會有明顯影響用戶體驗,因為當用戶導航回活動,活動恢復所有可見的狀態。看到無恥文檔信息保存和恢復狀態。

 

Empty process 空進程

A process that doesn't hold any active application components. The only reason to keep this kind of process alive is for caching purposes, to improve startup time the next time a component needs to run in it. The system often kills these processes in order to balance overall system resources between process caches and the underlying kernel caches.

這一過程不含任何活動應用程序組件。保持這種過程還活著的唯一原因就是緩存的目的,提高啟動時間下次運行組件。系統經常會終止這些進程,以平衡系統整體資源緩存和底層內核緩存之間的過程。

 

Android ranks a process at the highest level it can, based upon the importance of the components currently active in the process. For example, if a process hosts a service and a visible activity, the process is ranked as a visible process, not a service process.

Android排名最高水平的過程,基於目前活躍組件的重要性。例如,如果一個過程主機一個服務和一個可見的活動,這個過程是列為一個可見的過程,不是一個服務進程。

 

In addition, a process's ranking might be increased because other processes are dependent on it—a process that is serving another process can never be ranked lower than the process it is serving. For example, if a content provider in process A is serving a client in process B, or if a service in process A is bound to a component in process B, process A is always considered at least as important as process B.

Because a process running a service is ranked higher than a process with background activities, an activity that initiates a long-running operation might do well to start aservicefor that operation, rather than simply create a worker thread—particularly if the operation will likely outlast the activity. For example, an activity that's uploading a picture to a web site should start a service to perform the upload so that the upload can continue in the background even if the user leaves the activity. Using a service guarantees that the operation will have at least "service process" priority, regardless of what happens to the activity. This is the same reason that broadcast receivers should employ services rather than simply put time-consuming operations in a thread.

此外,一個進程的級別可能會增加,因為其他進程依賴於這是另一個進程提供服務的過程永遠無法排名低於此服務的進程。例如,如果一個內容提供者在處理服務客戶機進程B,或如果一個服務進程a是綁定到組件的過程,過程總是認為至少進程B一樣重要。
因為流程運行一個服務排名高於與背景活動流程,活動,發起一個長時間運行的操作最好開始一個服務操作,而不是簡單地創建一個工人thread-particularly如果操作可能會比活動。例如,一個活動的將照片上傳到一個網站應該啟動一個服務來執行上傳,這樣在後台上傳可以繼續即使用戶離開了活動。使用服務保證操作至少都會有“服務流程”優先,不管發生了什麼活動。這是同樣的原因,廣播接收器應該雇傭服務而不是簡單地把耗時的操作在一個線程。

 

Threads 線程


When an application is launched, the system creates a thread of execution for the application, called "main." This thread is very important because it is in charge of dispatching events to the appropriate user interface widgets, including drawing events. It is also the thread in which your application interacts with components from the Android UI toolkit (components from theandroid.widgetandandroid.viewpackages). As such, the main thread is also sometimes called the UI thread.

此外,一個進程的級別可能會增加,因為其他進程依賴於這是另一個進程提供服務的過程永遠無法排名低於此服務的進程。例如,如果一個內容提供者在處理服務客戶機進程B,或如果一個服務過程是當應用程序啟動時,系統會創建一個線程執行的應用程序,稱為“主要。“這個線程是非常重要的,因為它負責把事件分發給相應的用戶界面小部件,包括屏幕繪圖事件。這也是您的應用程序與組件交互的線程從Android UI toolkit(從android.widget組件和android.view包)。因此,主線程有時也稱為UI線程。

 

The system doesnotcreate a separate thread for each instance of a component. All components that run in the same process are instantiated in the UI thread, and system calls to each component are dispatched from that thread. Consequently, methods that respond to system callbacks (such asonKeyDown()to report user actions or a lifecycle callback method) always run in the UI thread of the process.

系統不為每個組件的實例創建一個單獨的線程。所有組件運行在同一進程中實例化在UI線程,每個組件和系統調用是派遣的線程。因此,應對系統回調的方法(如onKeyDown()報告用戶操作或生命周期回調方法)總是在UI線程的運行過程。

 

For instance, when the user touches a button on the screen, your app's UI thread dispatches the touch event to the widget, which in turn sets its pressed state and posts an invalidate request to the event queue. The UI thread dequeues the request and notifies the widget that it should redraw itself.

例如,當用戶觸摸屏幕上的按鈕時,應用程序的UI線程分派觸摸事件的小部件,進而設置按下和帖子一個無效請求到事件隊列。UI線程出列請求和通知小部件應該改變自己。

 

When your app performs intensive work in response to user interaction, this single thread model can yield poor performance unless you implement your application properly. Specifically, if everything is happening in the UI thread, performing long operations such as network access or database queries will block the whole UI. When the thread is blocked, no events can be dispatched, including drawing events. From the user's perspective, the application appears to hang. Even worse, if the UI thread is blocked for more than a few seconds (about 5 seconds currently) the user is presented with the infamous "application not responding" (ANR) dialog. The user might then decide to quit your application and uninstall it if they are unhappy.

當你的應用程序在響應用戶交互執行密集的工作,這個單線程模型能產生正常表現不佳,除非您實現您的應用程序。具體地說,如果一切發生在UI線程,耗時很長的操作,如訪問網絡或數據庫查詢將會阻塞整個UI。當線程被阻塞,沒有事件可以派遣,包括屏幕繪圖事件。從用戶的角度來看,應用程序掛起。更糟糕的是,如果UI線程被阻塞超過幾秒鐘目前(大約5秒鐘)用戶提出了臭名昭著的“應用程序沒有響應”(ANR)對話框。用戶可能會決定退出你的應用程序和卸載它,如果他們不開心。

 

Additionally, the Andoid UI toolkit isnotthread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread. Thus, there are simply two rules to Android's single thread model:

此外,Andoid UI工具包不是線程安全的。所以,你不能控制你的UI允許從一個工人操作的用戶界面UI線程。因此,僅僅有兩個規則,Android的單線程模型:

  1. Do not block the UI thread 不阻塞UI線程
  2. Do not access the Android UI toolkit from outside the UI thread 不從外部訪問Android UI toolkit UI線程嗎

Worker threads 工作線程

Because of the single thread model described above, it's vital to the responsiveness of your application's UI that you do not block the UI thread. If you have operations to perform that are not instantaneous, you should make sure to do them in separate threads ("background" or "worker" threads).

由於上述單線程模型,應用程序的響應能力是至關重要的UI,你不能阻塞UI線程。如果你有操作執行,不是瞬間,你應該確保他們在單獨的線程(“背景”或“工人”線程)。

 

For example, below is some code for a click listener that downloads an image from a separate thread and displays it in anImageView:

例如,下面是一些代碼點擊監聽器,下載一個圖像從一個單獨的線程在ImageView並顯示:

publicvoid onClick(View v){
  newThread(newRunnable(){
    publicvoid run(){
      Bitmap b = loadImageFromNetwork("http://example.com/image.png");
      mImageView.setImageBitmap(b);
    }
  }).start();
}

At first, this seems to work fine, because it creates a new thread to handle the network operation. However, it violates the second rule of the single-threaded model:do not access the Android UI toolkit from outside the UI thread—this sample modifies theImageViewfrom the worker thread instead of the UI thread. This can result in undefined and unexpected behavior, which can be difficult and time-consuming to track down.

To fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:

起初,這似乎工作好,因為它創建了一個新線程來處理網絡操作。然而,它違反了單線程模型的第二條規則:不要從外部訪問Android UI toolkit UI片樣品修改的ImageView工作線程而不是UI線程。這可能導致未定義的和意想不到的行為,可以是困難和耗時的追蹤。
為了解決這個問題,Android提供了幾種方法來從其他線程訪問UI線程。這裡是一個列表的方法,可以幫助:

 

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

For example, you can fix the above code by using theView.post(Runnable)method:

例如,您可以解決上面的代碼通過使用View.post(Runnable)方法:

 

publicvoid onClick(View v){
  newThread(newRunnable(){
    publicvoid run(){
      finalBitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
      mImageView.post(newRunnable(){
        publicvoid run(){
          mImageView.setImageBitmap(bitmap);
        }
      });
    }
  }).start();
}

Now this implementation is thread-safe: the network operation is done from a separate thread while theImageViewis manipulated from the UI thread.

現在這個實現線程安全:網絡操作完成從一個單獨的線程而theImageView操縱從UI線程。

 

However, as the complexity of the operation grows, this kind of code can get complicated and difficult to maintain. To handle more complex interactions with a worker thread, you might consider using aHandlerin your worker thread, to process messages delivered from the UI thread. Perhaps the best solution, though, is to extend theAsyncTaskclass, which simplifies the execution of worker thread tasks that need to interact with the UI.

然而,隨著操作的復雜性,這種代碼也會變得很復雜,很難維護。工作線程來處理更復雜的交互,您可能會考慮使用一個處理程序在工作線程從UI線程處理消息交付。也許最好的解決方案,是擴展AsyncTask類,它簡化了工作線程的執行任務,需要與用戶界面交互。

 

Using AsyncTask 使用AsyncTask

AsyncTaskallows you to perform asynchronous work on your user interface. It performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.

AsyncTask允許您執行異步工作在你的用戶界面。它執行阻塞操作在一個工作線程,然後在UI線程上公布結果,不需要你自己處理線程和/或處理程序。

 

To use it, you must subclassAsyncTaskand implement thedoInBackground()callback method, which runs in a pool of background threads. To update your UI, you should implementonPostExecute(), which delivers the result fromdoInBackground()and runs in the UI thread, so you can safely update your UI. You can then run the task by callingexecute()from the UI thread.

使用它,您必須子類AsyncTask,實現doInBackground()回調方法,它運行在一個後台線程池。更新你的UI,你應該實現onPostExecute(),提供來自doInBackground()和結果在UI線程中運行,所以您可以安全地更新UI。然後您可以運行任務從UI線程通過調用execute()。

 

For example, you can implement the previous example usingAsyncTaskthis way:

例如,您可以使用AsyncTask實現前面的示例:

publicvoid onClick(View v){
  newDownloadImageTask().execute("http://example.com/image.png");
}

privateclassDownloadImageTaskextendsAsyncTask{
  /** The system calls this to perform work in a worker thread and
   * delivers it the parameters given to AsyncTask.execute() */
  protectedBitmap doInBackground(String... urls){
    return loadImageFromNetwork(urls[0]);
  }
  
  /** The system calls this to perform work in the UI thread and delivers
   * the result from doInBackground() */
  protectedvoid onPostExecute(Bitmap result){
    mImageView.setImageBitmap(result);
  }
}

Now the UI is safe and the code is simpler, because it separates the work into the part that should be done on a worker thread and the part that should be done on the UI thread.

現在UI是安全的,代碼比較簡單,因為它分離的部分應該完成的工作在一個工作者線程和UI線程上應該做的部分。

 

You should read theAsyncTaskreference for a full understanding on how to use this class, but here is a quick overview of how it works:

你應該閱讀AsyncTask參考充分理解如何使用這個類,但這裡是一個快速概述工作原理:

 

  • You can specify the type of the parameters, the progress values, and the final value of the task, using generics 您可以指定的類型參數,進步的價值觀,和任務的最終值,使用泛型
  • The methoddoInBackground()executes automatically on a worker thread doInBackground()方法執行自動工作線程
  • onPreExecute(),onPostExecute(), andonProgressUpdate()are all invoked on the UI threadonPostExecute onPreExecute(),()和onProgressUpdate()都是在UI線程上調用
  • The value returned bydoInBackground()is sent toonPostExecute() doInBackground()返回的值發送到onPostExecute()
  • You can callpublishProgress()at anytime indoInBackground()to executeonProgressUpdate()on the UI thread 你可以叫publishProgress()隨時doInBackground()來執行onProgressUpdate()在UI線程上
  • You can cancel the task at any time, from any thread 你可以在任何時候取消任務,從任何線程

Caution:Another problem you might encounter when using a worker thread is unexpected restarts in your activity due to aruntime configuration change(such as when the user changes the screen orientation), which may destroy your worker thread. To see how you can persist your task during one of these restarts and how to properly cancel the task when the activity is destroyed, see the source code for theShelvessample application.

警告:使用工作線程時您可能會遇到另一個問題是意外重啟你的活動由於運行時配置更改(例如,當用戶改變屏幕的方向),這可能會毀掉你的工作線程。看看你可以堅持你的任務在其中一個重啟和如何正確地取消任務活動被摧毀時,看到貨架上的示例應用程序的源代碼。

Thread-safe methods

In some situations, the methods you implement might be called from more than one thread, and therefore must be written to be thread-safe.

在某些情況下,這些方法實現可能從多個線程調用,因此必須是線程安全的。

 

This is primarily true for methods that can be called remotely—such as methods in abound service. When a call on a method implemented in anIBinderoriginates in the same process in which theIBinderis running, the method is executed in the caller's thread. However, when the call originates in another process, the method is executed in a thread chosen from a pool of threads that the system maintains in the same process as theIBinder(it's not executed in the UI thread of the process). For example, whereas a service'sonBind()method would be called from the UI thread of the service's process, methods implemented in the object thatonBind()returns (for example, a subclass that implements RPC methods) would be called from threads in the pool. Because a service can have more than one client, more than one pool thread can engage the sameIBindermethod at the same time.IBindermethods must, therefore, be implemented to be thread-safe.

主要是這樣的方法,可以被稱為remotely-such綁定服務。當在一個方法的調用實現的一個內部起源於同一進程的內部運行,該方法在調用者的線程中執行。然而,當調用源自另一個進程,一個線程中執行的方法從一個線程池,選擇系統保持在同一過程的內部(這不是UI線程的執行過程)。例如,而服務的onBind()方法會從UI線程調用服務的過程中,方法中實現onBind()返回的對象(例如,一個子類實現RPC方法)將從線程池中。因為一個服務可以有多個客戶端,可以多個線程池與相同的內部方法在同一時間。因此,IBindermethods必須實現線程安全的。

 

Similarly, a content provider can receive data requests that originate in other processes. Although theContentResolverandContentProviderclasses hide the details of how the interprocess communication is managed,ContentProvidermethods that respond to those requests—the methodsquery(),insert(),delete(),update(), andgetType()—are called from a pool of threads in the content provider's process, not the UI thread for the process. Because these methods might be called from any number of threads at the same time, they too must be implemented to be thread-safe.

同樣,一個內容提供者可以接收來自其他進程的數據請求。盡管theContentResolver和ContentProvider類隱藏進程間通信是如何管理的細節,ContentProvider方法應對那些請求查詢(),插入()、刪除()、update()和方法()——從一個線程池內容提供者的過程,不是UI線程的過程。因為這些方法可能被稱為從任意數量的線程同時,他們也必須實現線程安全的。

 

Interprocess Communication


Android offers a mechanism for interprocess communication (IPC) using remote procedure calls (RPCs), in which a method is called by an activity or other application component, but executed remotely (in another process), with any result returned back to the caller. This entails decomposing a method call and its data to a level the operating system can understand, transmitting it from the local process and address space to the remote process and address space, then reassembling and reenacting the call there. Return values are then transmitted in the opposite direction. Android provides all the code to perform these IPC transactions, so you can focus on defining and implementing the RPC programming interface.

Android提供了進程間通信(IPC)機制使用遠程過程調用(rpc),一個方法被調用的一個活動或其他應用程序組件,但執行遠程(在另一個進程),與任何結果返回給調用者。這需要分解方法調用和操作系統的數據水平可以理解,從當地的進程和地址空間傳輸到遠程進程和地址空間,然後再組裝和執行調用。然後返回值相反的方向傳播。Android提供了所有的代碼來執行這些IPC事務,這樣你就可以專注於定義和實現RPC編程接口。

 

To perform IPC, your application must bind to a service, usingbindService(). For more information, see theServicesdeveloper guide.

執行IPC,應用程序必須綁定到一個服務,使用bindService()。有關更多信息,請參見服務的開發者指南。


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved