當前位置:首頁 » 安卓系統 » android進程哪些

android進程哪些

發布時間: 2023-01-28 21:15:42

1. android.process.media是什麼進程

android.process.media是安卓媒體儲存系統進程,屬於安卓系統進程。

2. Android 如何進行進程保活

每一個 Android 應用啟動後至少對應一個進程,有的是多個進程,而且主流應用中多個

進程的應用比例較大

對於任何一個進程,我們都可以通過 adb shell ps|grep <package_name>的方式來查看

它的基本信息

Android 中的進程跟封建社會一樣,分了三流九等,Android 系統把進程的劃為了如下

幾種(重要性從高到低),網上多位大神都詳細總結過(備註:嚴格來說是劃分了 6 種)。

場景:

1.某個進程持有一個正在與用戶交互的 Activity 並且該 Activity 正處於 resume 的

狀態。

2.某個進程持有一個 Service,並且該 Service 與用戶正在交互的 Activity 綁定。

3.某個進程持有一個 Service,並且該 Service 調用 startForeground()方法使之位於前台運行。

4.某個進程持有一個 Service,並且該 Service 正在執行它的某個生命周期回調方法,比如 onCreate()、 onStart()或 onDestroy()。

5.某個進程持有一個 BroadcastReceiver,並且該 BroadcastReceiver 正在執行其onReceive()方法。用戶正在使用的程序,一般系統是不會殺死前台進程的,除非用戶強制停止應用或者系統內存不足等極端情況會殺死。

場景:

1.擁有不在前台、但仍對用戶可見的 Activity(已調用 onPause())。

2.擁有綁定到可見(或前台)Activity 的 Service

用戶正在使用,看得到,但是摸不著,沒有覆蓋到整個屏幕,只有屏幕的一部分可見進程

不包含任何前台組件,一般系統也是不會殺死可見進程的,除非要在資源吃緊的情況下,

要保持某個或多個前台進程存活

場景

1.某個進程中運行著一個 Service 且該 Service 是通過 startService()啟動的,與用戶看見的界面沒有直接關聯。

在內存不足以維持所有前台進程和可見進程同時運行的情況下,服務進程會被殺死

場景:

在用戶按了"back"或者"home"後,程序本身看不到了,但是其實還在運行的程序,

比如 Activity 調用了 onPause 方法系統可能隨時終止它們,回收內存

場景:

某個進程不包含任何活躍的組件時該進程就會被置為空進程,完全沒用,殺了它只有好處沒壞處,第一個干它!

上面是進程的分類,進程是怎麼被殺的呢?系統出於體驗和性能上的考慮,app 在退到

後台時系統並不會真正的 kill 掉這個進程,而是將其緩存起來。打開的應用越多,後台緩存的進程也越多。在系統內存不足的情況下,系統開始依據自身的一套進程回收機制

來判斷要 kill 掉哪些進程,以騰出內存來供給需要的 app, 這套殺進程回收內存的機制

就叫 Low Memory Killer。那這個不足怎麼來規定呢,那就是內存閾值,我們可以使用

cat /sys/mole/lowmemorykiller/parameters/minfree 來查看某個手機的內存閾值。

其實系統在進程回收跟內存回收類似也是有一套嚴格的策略,可以

自己去了解,大概是這個樣子的,oom_adj 越大,佔用物理內存越多會被最先 kill 掉,OK,那麼現在對於進程如何保活這個問題就轉化成,如何降低 oom_adj 的值,以及如

何使得我們應用占的內存最少。

據說這個是手 Q 的進程保活方案,基本思想,系統一般是不會殺死前台進程的。所以要

使得進程常駐,我們只需要在鎖屏的時候在本進程開啟一個 Activity,為了欺騙用戶,

讓這個 Activity 的大小是 1 像素,並且透明無切換動畫,在開屏幕的時候,把這個 Activity

關閉掉,所以這個就需要監聽系統鎖屏廣播,我試過了,的確好使,如下。

如果直接啟動一個 Activity,當我們按下 back 鍵返回桌面的時候,oom_adj 的值是 8,

上面已經提到過,這個進程在資源不夠的情況下是容易被回收的。現在造一個一個像素

的 Activity。

為了做的更隱藏,最好設置一下這個 Activity 的主題,當然也無所謂了

在屏幕關閉的時候把 LiveActivity 啟動起來,在開屏的時候把 LiveActivity 關閉掉,所以

要監聽系統鎖屏廣播,以介面的形式通知 MainActivity 啟動或者關閉 LiveActivity。

現在 MainActivity 改成如下

按下 back 之後,進行鎖屏,現在測試一下 oom_adj 的值

果然將進程的優先順序提高了。

但是還有一個問題,內存也是一個考慮的因素,內存越多會被最先 kill 掉,所以把上面

的業務邏輯放到 Service 中,而 Service 是在另外一個 進程中,在 MainActivity 開啟這

個服務就行了,這樣這個進程就更加的輕量,

OK,通過上面的操作,我們的應用就始終和前台進程是一樣的優先順序了,為了省電,

系統檢測到鎖屏事件後一段時間內會殺死後台進程,如果採取這種方案,就可以避免了

這個問題。但是還是有被殺掉的可能,所以我們還需要做雙進程守護,關於雙進程守護,

比較適合的就是 aidl 的那種方式,但是這個不是完全的靠譜,原理是 A 進程死的時候,

B 還在活著,B 可以將 A 進程拉起來,反之,B 進程死的時候,A 還活著,A 可以將 B

拉起來。所以雙進程守護的前提是,系統殺進程只能一個個的去殺,如果一次性殺兩個,

這種方法也是不 OK 的。

事實上

那麼我們先來看看 Android5.0 以下的源碼,ActivityManagerService 是如何關閉在應用

退出後清理內存的

在應用退出後,ActivityManagerService 不僅把主進程給殺死,另外把主進程所屬的進

程組一並殺死,這樣一來,由於子進程和主進程在同一進程組,子進程在做的事情,也

就停止了。所以在 Android5.0 以後的手機應用在進程被殺死後,要採用其他方案。

這種大部分人都了解,據說這個微信也用過的進程保活方案,移步微信 Android 客戶端

後台保活經驗分享,這方案實際利用了 Android 前台 service 的漏洞。

原理如下

對於 API level < 18 :調用 startForeground(ID, new Notification()),發送空的

Notification ,圖標則不會顯示。

對於 API level >= 18:在需要提優先順序的 service A 啟動一個 InnerService,兩個服務

同時 startForeground,且綁定同樣的 ID。Stop 掉 InnerService ,這樣通知欄圖標即

被移除。

public class KeepLiveService extends Service{

public static final int NOTIFICATION_ID=0x11; 

public KeepLiveService() {

}

@Override

public IBinder onBind(Intent intent){

throw new UnsupportedOperationException("Not yet implemented");

 }

 @Override 

public void onCreate() {

 super.onCreate(); //API 18 以下,直 接發 送 Notification 並 將 其 置 為 前 台

if(Build.VERSION.SDK_INT<Build.VERSION_CODES.JELLY_BEAN_MR2){

startForeground(NOTIFICATION_ID,new Notification()); 

} else { //API 18 以上,發送 Notification 並將其置為前台後,啟動 InnerService

Notification.Builder builder=new Notification.Builder(this);

builder.setSmallIcon(R.mipmap.ic_launcher);

startForeground(NOTIFICATION_ID, builder.build());

 startService(new Intent(this, InnerService.class));

 }

 }

 public class InnerService extends Service{

 @Override public IBinder onBind(Intent intent) {

 return null;

 } 

@Override public void onCreate() {

 super.onCreate(); //發送與 KeepLiveService中 ID 相同的 Notification,然後將其取消並取消自己的前台顯示

 Notification.Builder builder = new Notification.Builder(this);

builder.setSmallIcon(R.mipmap.ic_launcher);startForeground(NOTIFICATION_ID,

builder.build());

new Handler().postDelayed(new Runnable() { 

@Override public void run() { 

stopForeground(true);

 NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

manager.cancel(NOTIFICATION_ID); 

stopSelf();

 }

 },

100);

 }

 }

 }

在沒有採取前台服務之前,啟動應用,oom_adj 值是 0,按下返回鍵之後,變成 9(不

同 ROM 可能不一樣)

相互喚醒的意思就是,假如你手機里裝了支付寶、淘寶、天貓、UC 等阿里系的 app,

那麼你打開任意一個阿里系的 app 後,有可能就順便把其他阿里系的 app 給喚醒了。

這個完全有可能的。此外,開機,網路切換、拍照、拍視頻時候,利用系統產生的廣播

也能喚醒 app,不過 Android N 已經將這三種廣播取消了。

如果應用想保活,要是 QQ,微信願意救你也行,有多少手機上沒有 QQ,微信呢?或

者像友盟,信鴿這種推送 SDK,也存在喚醒 app 的功能。

拉活方法

JobSheler是作為進程死後復活的一種手段,

native進程方式最大缺點是費電,Native

進程費電的原因是感知主進程是否存活有兩種實現方式,在 Native 進程中通過死循環

或定時器,輪訓判斷主進程是否存活,當主進程不存活時進行拉活。其次 5.0 以上系統

不支持。 但是 JobSheler 可以替代在 Android5.0 以上 native 進程方式,這種方式即

使用戶強制關閉,也能被拉起來,親測可行。

JobSheler@TargetApi(Build.VERSION_CODES.LOLLIPOP) 

public class MyJobService extends JobService {

 @Override

 public void onCreate() {

 super.onCreate();

 startJobSheler();

 }

public void startJobSheler() { 

try {

 JobInfo.Builder builder = new JobInfo.Builder(1,new ComponentName(getPackageName(), MyJobService.class.getName()));

builder.setPeriodic(5); builder.setPersisted(true); JobScheler jobScheler =(JobScheler)

this.getSystemService(Context.JOB_SCHEDULER_SERVICE);

jobScheler.schele(builder.build());

}

catch

(Exception ex)

{ ex.printStackTrace(); } }

 @Override 

public boolean onStartJob(JobParameters jobParameters) {

 return false; 

} @Override public boolean onStopJob(JobParameters jobParameters) { 

return false; 

}

 }

這個是系統自帶的,onStartCommand 方法必須具有一個整形的返回值,這個整形的返

回值用來告訴系統在服務啟動完畢後,如果被 Kill,系統將如何操作,這種方案雖然可

以,但是在某些情況 or 某些定製 ROM 上可能失效,我認為可以多做一種保保守方案。

1.START_STICKY

如果系統在 onStartCommand 返回後被銷毀,系統將會重新創建服務並依次調用

onCreate 和 onStartCommand(注意:根據測試 Android2.3.3 以下版本只會調用

onCreate 根本不會調用 onStartCommand,Android4.0 可以辦到),這種相當於服務

又重新啟動恢復到之前的狀態了)。

2.START_NOT_STICKY

如果系統在 onStartCommand 返回後被銷毀,如果返回該值,則在執行完

onStartCommand 方法後如果 Service 被殺掉系統將不會重啟該服務

3.START_REDELIVER_INTENT

START_STICKY 的兼容版本,不同的是其不保證服務被殺後一定能重啟。

4.相比與粘性服務與系統服務捆綁更厲害一點,這個來自愛哥的研究,這里說的系統服務

很好理解,比如 NotificationListenerService,NotificationListenerService 就是一個監聽

通知的服務,只要手機收到了通知,NotificationListenerService 都能監聽到,即時用戶

把進程殺死,也能重啟,所以說要是把這個服務放到我們的進程之中,那麼就可以呵呵



所以你的應用要是有消息推送的話,那麼可以用這種方式去欺騙用戶。

3. 人人都應該了解的 Android 進程管理機制

打開設置--應用與服務(不同機型進入方式可能不同),你就會看到當前正在運行的進程和服務,也就是目前正在「後台運行」的任務。列表中有你剛剛使用過的 APP ,也有一兩小時前打開過的 APP。還有一些軟體你甚至不知道自己什麼時候打開過(其實根本就不是自己打開的),或者記得自己已經「關閉」了,但它們也在列表中,消耗著你的手機資源。列表中有一些條目名字很奇怪,一般人看不懂,但還是覺得「它很重要」,不敢輕易強制關閉。這個列表展示的內容和普通的後台管理界面不太一樣,感覺稍稍有些神秘,然而這又是我們日常使用所迴避不了的一部分。

作為一名資深的手機用戶(我相信人人都是),是時候該解決這類疑問了。這一切都要從人與宇宙的關系。。。咳咳。。手機進程的概念開始說起。

在開發文檔中是這么說的:當一個應用程序啟動時(僅僅只是「啟動時」,並不一定有組件運行),就會產生一個進程。在這個進程中同時會創建一個主線程,使應用內的任務開始執行。Android系統總是盡可能地保留進程。舉個例子,當你打開qq時,進程創建(同時創建主線程),隨後各種內容載入(首先是活動,然後是各種控制項什麼的)。當你完成操作時,一般都會按後退鍵(back),直至退出程序。

這里需要注意,一般情況下我們一直按後退是希望應用程序關閉的。然而事實上這樣做只是關閉了界面(活動),大多數app的「進程」仍會保留(少數良心app可以設置在退出時「完全關閉」),佔用內存以進行後台任務。進程隨應用啟動而產生,但往往並不隨著應用的「關閉」而關閉

所以很多時候我們看上去關閉了程序,但其實它仍在後台運行!(此處請自行回憶那些困擾你的流氓軟體們)。不過不必擔心,Android 系統自有一套進程管理機制來幫你管理後台任務。系統會根據應用的重要程度把所有進程劃歸為幾個等級,最不重要的進程將會被優先關閉,相對重要的進程將獲得資源來保留。

那麼問題來了----到底如何分辨哪些進程重要而哪些不重要呢?

系統當然要保證用戶體驗,所以重要等級的劃分原則就是要首先滿足用戶當前的需求:用戶正在使用的當然不能關閉,而用戶暫時不需要的,相對的就沒那麼重要了。

1.Foreground process 前台進程:也就是用戶正在進行操作的進程。這樣的進程優先順序(優先保留)最高,最不容易銷毀,因為它表現在屏幕上,直接同用戶進行交互,所以只有當內存資源極度緊張等一些其他極端情況才會關閉,表現為「閃退」。我用的第一台 Android 手機運行內存(RAM)只有 290M,多任務時經常內存不足導致程序「閃退」。這手機我竟然用了兩年,現在想想都佩服我自己hhhh。

不只是界面交互,如果應用程序中的服務(service)組件正在進行一些操作或者廣播接收者(BroadcastReceiver)在執行接收廣播的操作(onReceive)時,該進程仍被視為前台進程。

2.Visible process 可視進程:顧名思義,就是仍然在屏幕上有顯示,但用戶不再能直接與它交互的程序。比如當在應用中打開下滑菜單時(有些下滑菜單是透明的),用戶能「看得到」,但是「摸不著」。優先順序僅次於前台進程。

3.Service process 服務進程:該進程中開啟了一個服務(通過startService方法)。注意這里強調的是服務的「開啟」,區別於第一類中的「服務正在執行一些操作」。大多數音樂軟體都是通過這種方法來保留其播放音樂的進程。

4.Background process 後台進程:當你按下 HOME 鍵或 BACK 鍵時,手機退回主界面,此時應用程序不再可見,轉入後台運行。如果如果不滿足前幾類的條件,這個進程就會被判定為後台進程。

5.Empty process 空進程:A process that doesn't hold any active application components.沒有任何組件在運行,包括活動界面(Activity)。事實上用戶已經不再需要這個進程了,但出於 Android 系統「盡可能保留進程」的原則,這樣的進程出現後不會被立即銷毀。保留進程的唯一理由,就是為了下次開啟這個應用時能快一些。其實現在的手機硬體性能足夠好,這樣的緩存對於用戶體驗的提升效果不怎麼明顯。這樣的進程最不重要,將首先被銷毀。

也許你已經注意到了,在屏幕上正在顯示的或者正在服務於用戶的進程的重要等級是比較高的,這是出於對用戶體驗的考慮-----誰會接受在自己打王者榮耀的時候游戲突然閃退呢?大多數情況下,一個應用程序的組件成分都會比較復雜,這個進程可能同時滿足多個級別的劃分條件。在這種情況下,它會被盡可能地劃為能夠達到的最重要等級。

如果你的手機上安裝了好幾個同一家公司推出的 APP(比如企鵝系、頭條系等),那麼當你啟動其中之一時,剩下的幾款 APP 大概率也會被喚醒(視軟體的流氓程度而定)。聯動開啟的 APP 會大大佔用內存,讓手機變得卡頓。並且它們許多都需要聯網服務,佔用網速,有些還會在你不知情(因為你並沒有直接開啟或使用它們)的情況下監控你的數據並上傳

不過,你以為這就完了?

事實上,如果不手動清除,這樣的進程很難被系統關閉,它們會一直長期運行。這些進程大多屬於第三或第四等級,然而如果不同 APP 中的組件構成「相互依賴」的關系,它們所屬進程的保留優先順序就會提高,也就越不容易被關閉。(我等流氓軟體可不是浪得虛名的ε=ε=ε=(  ̄▽ ̄)

盡管 Android 想要盡可能的保存所有的進程,但是並非所有的內存都會被用於維持進程。比如系統運行會佔用相當的內存,系統也需要留出一部分閑置內存用以處理新事件。Android 的管理讓內存的分配處於一種「動態平衡」中,以保障各項任務都能盡可能的穩定、高效地執行。

好了,關於進程的管理就暫時說到這了。眾所周知,Android 系統是一個復雜的機體,它管理著手機硬體和軟體,讓它們盡可能的配合,提供給用戶最好的服務。這次只是簡單介紹了進程管理機制,今後我也會盡量用通俗的語言從系統上去解釋那些平常看上去似是而非的問題,期待你的關注!

4. android進程分為哪5種優先順序順序是怎樣的

Andrid
5個進程及重要優先順序前台進程>可見進程>服務進程>後台進程>空進程,
它們的回收優先順序則反之

5. Android 進程管理篇(四)-cpu限制

梳理Process進程相關知識點,再繼續補充點內容。

Linux系統中對進程的管理無非是從調度策略、優先順序以及CPU限制三個角度進行配置與管理,那麼Android中主要是通過AMS來管理應用程序進程的,是不是也是從這三個方面進行管理的呢?答案是肯定的,那麼本篇文章先來看看cpuset負載均衡在AMS中是如何應用的。

cpuset是Linux cgroup子系統,它為cgroup任務分配單獨的CPU和內存。單獨分配CPU即表明進程可調度cpu范圍。cpu按不同的晶元,大小核數目和頻率都有差別,大核頻率高處理速度相對比小核快,而Android系統實際上還是響應優先於吞吐的交互型系統,因此Android AMS對進程管理於不同優先順序的進程在調度cpu限制上會做有一些策略,以保證更好的交互響應。

還是回到AMS中與adj相關的有三個方法,這三個方法值得看一萬遍,每一遍都會有新收獲:

聚焦到computeOomAdjLocked方法,該方法主要是根據進程的四大組件狀態決定當前進程的adj優先順序。

以TOP_APP為例,這里ProcessRecord 的curSchedGroup屬性對應的是cup調度組,而在後續applyOomAdjLocked中會執行Process的setProcessGroup方法。

調用Process的setProcessGroup方法

setProcessGroup是個native方法,並且這里分了若干類型的group,這里看top app優先順序是最高的。接著jni到native

這里直接調用sched_policy.cpp的set_cpuset_policy,並傳入對應的pid和SchedPolicy

這里主要就是通過policy對應具體的fd句柄,然後通過add_tid_to_cgroup()寫cpuset對應節點。這里要注意,如果cpusets_enabled為false的話,會走set_sched_policy,這部分下篇會講到。
看看對應的fd是什麼:

那我們來看看對應節點是什麼內容:

然後看看對應的cpuset配置:

顯然,top app 滿核隨便跑,foreground跑在除了3這個核以外的所有核上, 而background只能跑在小核上。

不同晶元平台配置會有差別。

6. android進程管理機制

Android系統與其他操作系統有個很不一樣的地方,就是其他操作系統盡可能移除不再活動的進程,從而盡可能保證多的內存空間,而Android系統卻是反其道而行之,盡可能保留進程。Android這樣設計有什麼優勢呢?又是通過怎樣的方法來管理這些被保留的進程的呢?Android用戶又該如何正確使用手機從而更好發揮Android系統所特有的優勢呢?本文將一一為您解開這些謎團。

      本文的主要內容如下:

一、Android進程管理的特殊設計

       Linux系統對進程的管理方式是一旦進程活動停止,系統就會結束該進程。盡管Android基於Linux Kernel,但在進程管理上,卻採取了另外一種獨特的設計:當進程活動停止時,系統並不會立刻結束它,而是會盡可能地將該進程保存在內存中,在以後的某個時間,一旦需要該進程,系統就會立即打開它,而不用再做一些初始化操作。只有當剩餘內存不夠用了,為了維持新開啟的進程或者比較重要的進程的正常運行,系統才會選擇性地殺掉一些不重要的內存,騰出內存空間來,所以Android系統永遠不會有內存不足的提示。

二、Android獨特進程管理設計的好處

      Android這種獨特的設計,也正是Android標榜的優勢之一,這有兩個好處:

  1、最大限度地提高內存的使用率。

       比如,你的內存是8G,如果每次使用完某個進程就殺掉,那麼被使用的內存基本上會始終保持在某個值,比如4G以內,那麼內存的使用率就總是保存在50%以內,剩餘的4G內存形同虛設,發揮用處的機會非常少。而Android的這種設計,就可以做到有多少內存就用多少內存,盡可能大地提高內存使用率。同樣比如有8G內存,使用完的進程仍保留在內存中,累積下來,被使用的內存就盡可能地會接近8G。

  2、提高再次啟動時的啟動速度

       被駐留在內存中不再活動的進程(後台進程或空進程,後面會再講到),很多是經常需要使用的,當再次使用該進程的時候,系統立即打開它,而不需要再重新初始化。例如,我們常用的瀏覽器,當暫時不再使用時,按下Home鍵或Back鍵,瀏覽器進程就變成了不再活動的進程。如果下次又要使用了,點擊多任務鍵,在最近使用應用列表中點擊瀏覽器即可,瀏覽器界面仍然保持著退出前的界面。但如果退出時把該進程移除了,那麼再次使用時,就需要重新初始化,然後進入該應用,這往往會花費不少的時間。

三、Android進程的五個等級

        Android系統將盡量長時間地保持應用進程,但為了新建進程或運行更重要的進程,最終需要移除舊進程來回收內存。為了確定保留或終止哪些進程,系統會根據進程中正在運行的組件以及這些組件的狀態,將每個進程放入「重要性層次結構」中。必要時,系統會首先消除重要性最低的進程,然後是重要性略遜的進程,以此類推,以回收系統資源。該「重要性層級結構」將進程分為了五個等級:

  1、前台進程(foreground)

       前台進程是指那些有組件正和用戶進行交互的應用程序的進程,也稱為Active進程。這些都是Android嘗試通過回收其他應用程序來使其保持相應的進程。這些進程的數量非常少,只有等到最後關頭才會終止這些進程,是用戶最不希望終止的進程。例如:而當你運行瀏覽器這類應用時,它們的界面就會顯示在前台,它們就屬於前台進程,當你按home鍵回到主界面,他們就變成了後台程序。

       如果一個進程滿足以下任一條件,即視為前台進程:

     (1)託管處於活動狀態的Activity,也就是說,它們位於前台並對用戶事件進行響應,此時的情形為響應了Activity中的onResume()生命周期方法,但沒有響應onPause()。

     (2)託管正在執行onReceive()方法處理事件程序的BroadcastReceiver。

     (3)託管正在執行onStart()、onCreate()或onDestroy()事件處理程序的Service。

     (4)託管正在運行且被標記為在前台運行的Service,即調用了該Service的startForeground()方法。

     (5)託管某個Service,且該Service正綁定在用戶正在交互的Activity的Service,即該Activity正處於活動狀態。

  2、可見進程(visible)

        沒有任何前台組件、但仍然會影響用戶在屏幕上所見內容的進程。如果一個進程滿足以下任一條件,即視為可見進程:

    (1)託管不在前台、但仍對用戶可見的Activity(已調用其onPause()方法)。例如:如果前台Acitivty啟動了一個對話框,或者啟動了一個非全屏,亦或是一個透明的Activity,允許在其後顯示上一個Activity,則可能會發生這種情況,這類Activity不在前台運行,也不能對用戶事件作出反應。

    (2)託管綁定到可見Activity的Service。(官網上說是綁定到可見或前台Activity,但筆者有一點疑問,這個和「前台進程」中第(5)點相矛盾嗎,綁定到前台Activity,那就是前台進程了)

        可見進程被視為是極其重要的進程,這類進程的數量也很少,只有在資源極度匱乏的環境下,為保證前台進程繼續執行時才會終止。

  3、服務進程(Service)

        正在運行已使用startService()方法啟動的Serice且不屬於上述兩個更高類別進程的進程。盡管服務進程與用戶所見內容沒有直接關聯,但是它們通常在執行一些用戶關心的操作。因此,除非內存不足以維持所有前台進程和可見進程同時運行,否則系統會讓服務進程保持運行狀態。

       有些資料上面也稱這種進程為次要服務(Secondary Service),而屬於上述兩個更高類別的進程則被稱為主要服務,主要服務往往屬於系統進程,如撥號進程等,不可能被進程管理輕易終止。這里我們以Android開發者官網的稱呼為標准,稱為服務進程。

  4、後台進程(hidden)

       包含目前對用戶不可見的Activity,即該Activity調用了onStop()方法。這些進程對用戶體驗沒有直接影響,系統可能隨時終止它們,以回收內存供上述三個更高級別的進程使用。通常會有很多後台進程在運行,它們會保存在LRU(Least Recently Used,最近最少使用)列表中,以確保包含用戶最近查看的Activity的進程最後一個被終止。如果某個Activity正確實現了生命周期方法,並保存了其當前狀態,則終止其進程不會對用戶體驗產生明顯影響,因為當用戶導航回該Activity時,Activity會恢復其所有可見狀態。

       這里讀者可以做個試驗,先開啟微信,進入到朋友圈界面, 然後點擊手機屏幕下方的導航欄中的Home按鍵進入到後台,再點擊最近使用應用列表顯示按鈕(不同的手機位置不一樣,有的在Home鍵左邊,有的則在Home鍵右邊),在顯示的最近使用應用的列表中清理掉微信應用,最後再點擊桌面的微信圖標啟動微信,會發現顯示的界面仍然是朋友圈界面。

       後台進程,我們可以簡單理解為,應用(只考慮只有Activity組件的情況)啟動後按Home鍵後被切換到後台的進程。如瀏覽器、閱讀器等,當程序顯示在屏幕上時,它們所運行的進程即為前台進程(foreground),一旦按home鍵(注意不是back鍵)返回到桌面,程序就停留在後台,成為後台進程。

  5、空進程(empty)

       不含任何活動應用組件的進程。保留這種進程的唯一目的是用作緩存,以縮短下次再其中運行組件所需要的啟動時間。一般來說,當應用按back按鍵退出後應用後,就變成了一個空進程。比如BTE,在程序退出後,依然會在進程中駐留一個空進程,這個進程里沒有任何數據在運行,作用往往是提高該程序下次的啟動速度或者記錄程序的一些歷史信息。當系統內存不夠用時,無疑,該進程是應該最先終止的。在最近使用應用列表中,可以看到按back鍵退出的應用。

       根據進程中當前活動組件的重要程度,Android會將進程評定為它可能達到的最高級別。通俗地說,就是如果一個進程同時擁有多個對應上述不同等級進程的組件時,會以最高的那個等級作為該進程的等級。例如,如果某進程託管著服務和可見Activity,則會將此進程評定為可見進程,而不是服務進程。

       此外,一個進程的級別可能會因為其他進程對它的依賴而有所提高,即服務於另一進程的進程其級別永遠不會低於其所服務的進程。例如,如果進程A中的內容提供程序為進程B中的客戶端提供服務,或者如果進程A中的服務綁定到進程B中的組件,則進程A始終被視為至少與進程B同樣重要。

       由於運行服務的進程其級別高於託管後台Activity的進程,因此啟動長時間運行操作的Activity最好為該操作啟動Service,而不是簡單地創建工作線程,當操作有可能比Activity更加持久時更應該如此。例如,正在將圖片上傳到網站的Activity應該啟動服務來執行上傳,這樣一來,即使用戶退出Activity,仍可在後台繼續執行上傳操作。使用服務可以保證,無論Activity發生什麼情況,該操作至少具備「服務進程」優先順序。如果某個Activity開啟了線程執行耗時操作,當Activity退出時,該Activity的實例將不會釋放內存資源,直到線程執行完,這樣容易導致內存泄漏。同理,廣播接收器也應該使用服務,而不是簡單地將耗時冗長的操作放入線程中。

四、進程移除順序的依據——閾(yu,第四聲)值

        前面講到,內存不夠用時,會根據進程的等級來決定優先回收哪類進程。那麼系統是根據什麼來判斷需要移除這些進程的時機的呢?答案是閾值。

  1、查看閾值

        我們可以採用如下方法查看手機中各個等級進程的閾值(需要root許可權),如第二排數據所示(其單位為頁):

       以第一個數據44032為例,計算方法為:

       1page=4KB=4*1024B=4096B

       44032page* 4048B/page =  180355072B

       180355072B/1024/1024 = 172M

       即第一個等級的進程的閾值為172M。依次類推,閾值依次為:172M,190M,208M,226M,316M,415M。

       有必要說明一下,在Android開發者官方文檔中,是將Android應用進程分為了5個等級,但很多資料卻是分的6個等級,在後台進程和空進程之間還有一個「內容提供節點(content provider)進程」。內容提供節點,沒有實體程序,僅提供內容供別的程序去用 ,比如日歷供應節點,郵件供應節點等,在終止進程時,這類進程有比較高的優先權。手機中應該是採用的6個等級的方式,如上六個數據,正好對應著六個等級的進程,等級越高,閾值越低,即前台進程閾值為172M,空進程為415M。當系統的剩餘內存只剩餘不到415M的時候,系統首先會回收空進程,依次類推,只有剩餘內存不到172M了,才會去回收前台進程,這樣就起到了優化保護重要進程的作用。

五、Home鍵、Back鍵和多任務鍵

       Home鍵、Back鍵和多任務鍵,在手機屏幕的下方,這三個按鍵一般稱為導航欄,中間的按鈕為Home鍵,多任務鍵和Back鍵分別在其左右,一般根據手機品牌不同,左右位置也有所差異。

       在運行App的時候,如果按一下Home鍵或者Back鍵,都可以退到桌面,那麼這兩者有什麼區別呢?

Home鍵。按Home鍵的時候,App如果沒有Service開啟,會從一個前台進程轉變為一個後台進程;如果有前台service運行,就仍然是前台進程,比如QQ音樂播放器等;如果是只有普通service運行,那麼就轉變為服務進程(參照前文中講的Android進程的5個級別)。

Back鍵。按Back鍵的時候,App如果沒有Service開啟,會從一個前台進程轉變為一個空進程;對於有Service運行的情況,和按Home鍵一樣。

        後台進程和空進程,都是駐留在後台,處於暫停狀態,也都是除了佔用一部分內存外,不佔用其他如cpu等資源的,那麼問題來了,為什麼要設計後台進程和空進程這兩種空進程呢?它們的區別到底在哪裡呢?我們在前文講Android進程的5個等級的時候講到過,當剩餘內存不足的時候,系統會按照等級順序,優先移除不太重要進程,以收回內存供更重要的進程運行。那麼,它們的區別就是,在剩餘內存不足時,會優先移除空進程,再不足,才會移除空進程。所以,如果確實要退出某個應用一段時間內不大使用了,如果這款應用有退出按鈕,就用應用自帶的退出功能;如果沒有,則最好按系統的Back鍵,這樣可以變成空進程,當系統要回收內存時,就會優先被回收,從而釋放的所佔的資源。如果只是暫時退出去做點別的,過一會還要切換回來,或者對這款應用使用比較頻繁,那就使用Home鍵,因為相比於按Back鍵,這樣可以盡可能保住後台進程,方便下次使用的時候快速啟動。

       當然,按Home鍵或Back鍵,對用戶來說,其實感覺不到差異,使用起來沒什麼兩樣,但是,對於Android開發者來說,卻有必要作為常識來了解其中的道理和差異。無論是按Home鍵還是按Back鍵,在按多任務鍵的時候,都可以看到這些進程,如下圖所示。最下面的按鍵為清理按鍵,點擊後可以清除掉這些進程,回收內存了,當然,前面也講了很多遍了,不建議這樣做。

  2、修改閾值。

       可以採用命令:echo "44032,48640,53248,57856,80896,106241" > /sys/mole/lowmemorykiller/parameters/minfree來修改閾值,如下所示:

       重啟後,會恢復為原來的值。至於如何永久性修改該閾值,這里不深入探討,有興趣的童鞋可以自行研究,一般來說,就按照系統給定的默認值使用就可以了,沒特殊用途的話,沒必要修改。

       對於這一節閾值的內容,暫時先講到這里,如果要更深入,可以自行多研究研究。筆者也沒有看到比較好的更深入的文章,所以也不好推薦,如果讀者看到比較好的,可以推薦給筆者,感激不盡。

六、開發者選項中的進程管理功能

        Android手機都帶有開發者選項,隱藏了很多功能,顧名思義,這些功能主要用於輔助開發者調試程序用的。其中有一些就是關於進程管理功能的,筆者這里簡單介紹一下其中兩款,如下圖紅框部分所示:

不保留活動。用戶離開以後即銷毀每個活動(Activity),這樣做使得後台進程都被銷毀了。筆者試驗過幾款app,比如微信,瀏覽器,開啟/關閉「不保留活動」前後,按Home鍵後,再打開應用,有明顯的差別。當然,也試用了簡訊,DD打車,就沒看出起了什麼作用。讀者若是感興趣可以深入研究研究,到時候在指導指導筆者!

後台進程限制。如下圖所示,給出了後台進程個數限制的選項。

七、進程管理軟體的使用

       Windows操作系統用戶往往總想著保留更多的內存,在使用Android手機的時候,喜歡經常清理後台進程或空進程,而且清理完後,心裡有一種特別爽的感覺,就像給家裡做了一次大掃除一樣,筆者最初使用Android手機的時候也是這樣的心態-_-!基於這樣的心態,一些進程清理軟體,很受普通用戶的青睞。其實這樣做卻正好抹殺了Android系統所標榜的優勢,如前文所講到的。

       那麼進程管理軟體有無必要呢?當然有的,只是需要注意使用場合。當需要運行大型程序的時候,可以手動關閉掉一些進程,騰出足夠的空間供大型程序使用,這樣就可以有效避免系統調用進程調度策略而引起的卡頓,這一點,第八大點第3小節中會有說明。而且由於開發者的原因,可能是程序寫得太爛,或程序容易出錯,或做不該做的動作,或是惡意程序,對於這類程序進程,手動移除也是有好處的。

       但如果是運行一些小程序,就完全沒有必要去預先殺進程了,完全可以交給系統自己管理。讀者可能會疑惑,因為小程序啟動的時候,也有可能會因為內存不足而導致需要移除部分進程的情況。筆者認為,即便是內存不足,小程序運行引起的調用進程調度策略測的次數非常少,要移除的進程也非常少,產生的影響不大。同時,我們也要意識到另外一點就是,無論是手動殺死進程還是自動殺進程,都需要cpu去執行這些任務,所以也會拖慢手機和消耗電量。所以從這一點看,頻繁殺進程,也是一個不好的習慣。

八、答疑解惑

        在以前沒有專門去了解Android進程管理機制的時候,甚至是在研究的過程中,筆者心裡都經常存在很多疑惑,以下整理了其中5個,不知道讀者您是否有也類似的困惑呢?

  1、這么多駐留在內存的進程,不會耗電嗎?

       大多數用慣了Windows操作系統的童鞋,看到Android系統盡可能保留不在活動的進程的設計,可能第一反應就是質疑,難道這樣不會增加耗電量嗎?其實,但一個程序按home鍵變成後台進程或者按back鍵退出變成空進程後,其實已經被暫停了,只保留了運行狀態,不會消耗cpu,一個程序會耗電,是因為它需要調用cpu來運算,現在不消耗cpu了,當然就不會耗電了。當然,開了service的應用就另當別論了,比如QQ音樂播放器,當按home鍵或back鍵後,音樂仍然播放,是因為它開啟了服務,而且是一個前台服務,在後面我們會繼續講到,此時它是一個前台進程,而不是後台進程或空進程。

  2、為什麼一個不太app,運行時會佔用很大的內存呢?

我們經常會碰到這樣一種現象,一個只有20M的App,運行起來的時候,卻會耗掉100M以上的內存。一方面是,程序運行時為對象分配內存,另一方面,是Android虛擬機的原因。Android中的應用啟動的時候,系統都會給它開啟一個獨立的虛擬機,這樣做的好處是可以避免虛擬機崩潰導致整個系統崩潰,代價就是耗用更多的內存。

  3、為什麼內存少的時候,運行大型程序會卡頓呢?

        當剩餘內存不多時,打開大型程序,系統會觸發自身的進程調度策略,去移除一些等級比較低的進程來回收內存,以供大型程序運行。而這個進程調度策略在決定哪些進程需要被移除的過程,是一個十分消耗資源的操作,特別是一個程序頻繁像系統申內存的時候,這樣就導致了系統的卡頓。

 4、應用開得太多了,手機變慢,是因為內存被佔用太多嗎?

        其實手機變慢的根本原因是cpu被耗用太多,而不是內存佔用太多,因為真正執行程序所要完成的任務的最終執行者是CPU,而不是內存(RAM)。在內存足夠的情況下,如果系統中佔用cpu的進程太多,那無疑cpu總有忙不過來的時候,那肯定就會變慢了。這就好比,在一條道路上駕車,道路就像內存,車的引擎就像cpu,如果車的引擎的動力不夠,或者承載的貨物太多,車都跑不快,即便是道路上一路暢通無阻,也無濟於事。所以,內存佔用多少並不重要,只要道路提供給車輛前行的空間是足夠的,手機變慢的責任,就和內存無關了。這個比喻用來解釋第三點也很恰當,道路提供的車輛前進的空間無法滿足車輛所必需的空間時,就需要交通機制花時間來調節交通,給這輛車提供足夠的空間,而在此期間,這輛車只能乖乖候著。

  5、Android手機越用越慢,是什麼原因呢?

Android手機常常是越用越慢,即使是恢復出廠設置,也無法改變這個現象。手機越用越慢,主要由如下幾個原因:(1)虛擬機機制問題。這一點在上一個問題中也提到了,在Android4.4以前的系統,使用的是Dalvik虛擬機,它的設計機制有缺陷,就是越用越慢;在Android4.4系統中有切換按鈕,可以在Art虛擬機和Dalvik虛擬機之間切換;在Android4.4以後的系統就徹底拋棄了Dalvik而全面使用Art。(2)開啟了太多的服務,導致耗用太多的CPU。隨著手機開機使用時間的增長,應用使用越來越多,很多應用看似退出了,而其實後台可能開了不少的服務,而他們可能還沒有關閉。這些服務正在執行一些操作,會消耗CPU,而CPU才是手機變慢的根本原因。 而且Android app比較開放的,有很多不良應用充斥其中,可能對服務處理不當,濫用服務等,增加系統中的服務。(3)系統頻繁調用自身的進程調度演算法。這一點在前面已經說明了,這里不再贅述。(4)手機硬體的自然老化

7. Android進程間通信

1. 簡要說說進程與線程的區別和聯系。

2. 應用內使用多進程可能導致哪些問題?

當一個APP啟用了多進程後,系統會為不同的進程分配不同的內存空間,因此所有需要通過內存共享的行為都會失敗。另外,還會導致以下幾個問題:

3. Android中有哪些進程間通信方式?

由於不同的進程擁有不同的數據空間,所以無論是應用內還是應用間,均無法通過共享內存來實現進程間通信。

進程和線程的主要區別(總結) - CSDN

線程和進程的區別是什麼? - 知乎

Android 多進程通信之幾個基本問題

面試題:IPC(跨進程通信)

8. g700中android os進程包含哪些

我們通過ActivityManager獲取了系統的可用內存信息以及正在運行在系統里的進程具體信息,當然你也可以選擇手動殺死這些進程,不過前提是用戶進程,因為系統進程是殺不死的。
資源文件什麼的就不再貼代碼了,直接列出邏輯文件。


獲取系統可用內存的代碼:MainActivity.java

package com.qin.ammp;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Debug;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

private static String TAG = "AM_MEMORYIPROCESS" ;

private ActivityManager mActivityManager = null ;

private TextView tvAvailMem ;
private Button btProcessInfo ;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

tvAvailMem = (TextView)findViewById(R.id.tvAvailMemory) ;
btProcessInfo =(Button)findViewById(R.id.btProcessInfo);
//跳轉到顯示進程信息界面
btProcessInfo.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(MainActivity.this,BrowseProcessInfoActivity.class);
startActivity(intent);
}
});

//獲得ActivityManager服務的對象
mActivityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);

//獲得可用內存信息
String availMemStr = getSystemAvaialbeMemorySize();
Log.i(TAG, "The Availabel Memory Size is"+availMemStr);
//顯示
tvAvailMem.setText(availMemStr);

}
//獲得系統可用內存信息
private String getSystemAvaialbeMemorySize(){
//獲得MemoryInfo對象
MemoryInfo memoryInfo = new MemoryInfo() ;
//獲得系統可用內存,保存在MemoryInfo對象上
mActivityManager.getMemoryInfo(memoryInfo) ;
long memSize = memoryInfo.availMem ;

//字元類型轉換
String availMemStr = formateFileSize(memSize);

return availMemStr ;
}

//調用系統函數,字元串轉換 long -String KB/MB
private String formateFileSize(long size){
return Formatter.formatFileSize(MainActivity.this, size);
}

}

獲取系統進程信息的代碼 :BrowseProcessInfoActivity .java


package com.qin.ammp;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Debug;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;

public class BrowseProcessInfoActivity extends Activity implements OnItemClickListener{

private static String TAG = "ProcessInfo";
private static final int KILL_PORCESS = 1;
private static final int SEARCH_RUNNING_APP = 2;

private ActivityManager mActivityManager = null;
// ProcessInfo Model類 用來保存所有進程信息
private List<ProcessInfo> processInfoList = null;

private ListView listviewProcess;
private TextView tvTotalProcessNo ;

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.browse_process_list);

listviewProcess = (ListView) findViewById(R.id.listviewProcess);
listviewProcess.setOnItemClickListener(this);

tvTotalProcessNo =(TextView)findViewById(R.id.tvTotalProcessNo);

this.registerForContextMenu(listviewProcess);
// 獲得ActivityManager服務的對象
mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
// 獲得系統進程信息
getRunningAppProcessInfo();
// 為ListView構建適配器對象
BrowseProcessInfoAdapter mprocessInfoAdapter = new BrowseProcessInfoAdapter(
this, processInfoList);
listviewProcess.setAdapter(mprocessInfoAdapter);

tvTotalProcessNo.setText("當前系統進程共有:"+processInfoList.size());
}
//殺死該進程,並且刷新
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, final int position, long arg3) {
// TODO Auto-generated method stub
new AlertDialog.Builder(this).setMessage("是否殺死該進程")
.setPositiveButton("確定", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
//殺死該進程,釋放進程佔用的空間
mActivityManager.killBackgroundProcesses(processInfoList.get(position).getProcessName());
//刷新界面
getRunningAppProcessInfo() ;
BrowseProcessInfoAdapter mprocessInfoAdapter = new BrowseProcessInfoAdapter(
BrowseProcessInfoActivity.this, processInfoList);
listviewProcess.setAdapter(mprocessInfoAdapter);
tvTotalProcessNo.setText("當前系統進程共有:"+processInfoList.size());

}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
dialog.cancel() ;
}
}).create().show() ;
}
// 獲得系統進程信息
private void getRunningAppProcessInfo() {
// ProcessInfo Model類 用來保存所有進程信息
processInfoList = new ArrayList<ProcessInfo>();

// 通過調用ActivityManager的getRunningAppProcesses()方法獲得系統里所有正在運行的進程
List<ActivityManager.RunningAppProcessInfo> appProcessList = mActivityManager
.getRunningAppProcesses();

for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessList) {
// 進程ID號
int pid = appProcessInfo.pid;
// 用戶ID 類似於Linux的許可權不同,ID也就不同 比如 root等
int uid = appProcessInfo.uid;
// 進程名,默認是包名或者由屬性android:process=""指定
String processName = appProcessInfo.processName;
// 獲得該進程佔用的內存
int[] myMempid = new int[] { pid };
// 此MemoryInfo位於android.os.Debug.MemoryInfo包中,用來統計進程的內存信息
Debug.MemoryInfo[] memoryInfo = mActivityManager
.getProcessMemoryInfo(myMempid);
// 獲取進程占內存用信息 kb單位
int memSize = memoryInfo[0].dalvikPrivateDirty;

Log.i(TAG, "processName: " + processName + " pid: " + pid
+ " uid:" + uid + " memorySize is -->" + memSize + "kb");
// 構造一個ProcessInfo對象
ProcessInfo processInfo = new ProcessInfo();
processInfo.setPid(pid);
processInfo.setUid(uid);
processInfo.setMemSize(memSize);
processInfo.setPocessName(processName);
processInfoList.add(processInfo);

// 獲得每個進程里運行的應用程序(包),即每個應用程序的包名
String[] packageList = appProcessInfo.pkgList;
Log.i(TAG, "process id is " + pid + "has " + packageList.length);
for (String pkg : packageList) {
Log.i(TAG, "packageName " + pkg + " in process id is -->"+ pid);
}
}
}

public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
menu.add(0, 0, KILL_PORCESS, "殺死該進程");
menu.add(0, 0, SEARCH_RUNNING_APP, "運行在該進程的應用程序");
super.onCreateContextMenu(menu, v, menuInfo);

}

public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case KILL_PORCESS: // 殺死該進程 , 重新載入界面
new AlertDialog.Builder(this).setMessage("是否殺死該進程")
.setPositiveButton("確定", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub

}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
dialog.cancel() ;
}
}).create().show() ;
break;
case SEARCH_RUNNING_APP: // 查看運行在該進程的應用程序信息
break;
default:
break;
}
return super.onContextItemSelected(item);
}

}

我們可以通過進程佔用內存大小來進而獲取佔用cpu大小,直接換算還是很簡單的。

熱點內容
腳本宣傳片 發布:2024-05-02 05:56:26 瀏覽:568
有線投屏安卓手機如何設置 發布:2024-05-02 05:43:26 瀏覽:894
搶誠信紅包用什麼伺服器好 發布:2024-05-02 05:37:44 瀏覽:102
淘寶客源碼程序 發布:2024-05-02 05:34:46 瀏覽:812
大淘客cms源碼 發布:2024-05-02 05:33:12 瀏覽:445
matlab新建文件夾 發布:2024-05-02 05:14:19 瀏覽:717
看加密相冊 發布:2024-05-02 04:45:53 瀏覽:663
資源存儲在哪 發布:2024-05-02 04:23:28 瀏覽:169
如何猜對方qq密碼後幾位 發布:2024-05-02 03:46:59 瀏覽:403
php最後出現字元串 發布:2024-05-02 03:46:31 瀏覽:492