1. 概念
開始,先稍稍講一點(diǎn)android中Service的概念和用途吧~
Service分為本地服務(wù)(LocalService)和遠(yuǎn)程服務(wù)(RemoteService):
1治力、本地服務(wù)依附在主進(jìn)程上而不是獨(dú)立的進(jìn)程萤晴,這樣在一定程度上節(jié)約了資源,另外Local服務(wù)因?yàn)槭窃谕贿M(jìn)程因此不需要IPC析蝴,
也不需要AIDL禁漓。相應(yīng)bindService會(huì)方便很多是目。主進(jìn)程被Kill后妹萨,服務(wù)便會(huì)終止年枕。
2、遠(yuǎn)程服務(wù)為獨(dú)立的進(jìn)程乎完,對(duì)應(yīng)進(jìn)程名格式為所在包名加上你指定的android:process字符串熏兄。由于是獨(dú)立的進(jìn)程,因此在Activity所在進(jìn)程被Kill的時(shí)候树姨,該服務(wù)依然在運(yùn)行霍弹,
不受其他進(jìn)程影響,有利于為多個(gè)進(jìn)程提供服務(wù)具有較高的靈活性娃弓。該服務(wù)是獨(dú)立的進(jìn)程,會(huì)占用一定資源岛宦,并且使用AIDL進(jìn)行IPC稍微麻煩一點(diǎn)台丛。
按使用方式可以分為以下三種:
1、startService 啟動(dòng)的服務(wù):主要用于啟動(dòng)一個(gè)服務(wù)執(zhí)行后臺(tái)任務(wù)砾肺,不進(jìn)行通信挽霉。停止服務(wù)使用stopService;
2变汪、bindService 啟動(dòng)的服務(wù):該方法啟動(dòng)的服務(wù)可以進(jìn)行通信侠坎。停止服務(wù)使用unbindService;
3裙盾、startService 同時(shí)也 bindService 啟動(dòng)的服務(wù):停止服務(wù)應(yīng)同時(shí)使用stepService與unbindService
2. Service 與 Thread 的區(qū)別
很多時(shí)候实胸,你可能會(huì)問他嫡,為什么要用 Service,而不用 Thread 呢庐完,因?yàn)橛?Thread 是很方便的钢属,比起 Service 也方便多了,下面我詳細(xì)的來解釋一下门躯。
1). Thread:Thread 是程序執(zhí)行的最小單元淆党,它是分配CPU的基本單位⊙攘梗可以用 Thread 來執(zhí)行一些異步的操作染乌。
2). Service:Service 是android的一種機(jī)制,當(dāng)它運(yùn)行的時(shí)候如果是Local Service懂讯,那么對(duì)應(yīng)的 Service 是運(yùn)行在主進(jìn)程的 main 線程上的荷憋。如:onCreate,onStart 這些函數(shù)在被系統(tǒng)調(diào)用的時(shí)候都是在主進(jìn)程的 main 線程上運(yùn)行的域醇。如果是RemoteService台谊,那么對(duì)應(yīng)的 Service 則是運(yùn)行在獨(dú)立進(jìn)程的 main 線程上。
既然這樣,那么我們?yōu)槭裁匆?Service 呢减宣?其實(shí)這跟 android 的系統(tǒng)機(jī)制有關(guān)盐须,我們先拿 Thread 來說。Thread 的運(yùn)行是獨(dú)立于 Activity 的漆腌,也就是說當(dāng)一個(gè) Activity 被 finish 之后贼邓,如果你沒有主動(dòng)停止 Thread 或者 Thread 里的 run 方法沒有執(zhí)行完畢的話,Thread 也會(huì)一直執(zhí)行闷尿。因此這里會(huì)出現(xiàn)一個(gè)問題:當(dāng) Activity 被 finish 之后塑径,你不再持有該 Thread 的引用。另一方面填具,你沒有辦法在不同的 Activity 中對(duì)同一 Thread 進(jìn)行控制统舀。
舉個(gè)例子:如果你的 Thread 需要不停地隔一段時(shí)間就要連接服務(wù)器做某種同步的話,該 Thread 需要在 Activity 沒有start的時(shí)候也在運(yùn)行劳景。這個(gè)時(shí)候當(dāng)你 start 一個(gè) Activity 就沒有辦法在該 Activity 里面控制之前創(chuàng)建的 Thread誉简。因此你便需要?jiǎng)?chuàng)建并啟動(dòng)一個(gè) Service ,在 Service 里面創(chuàng)建盟广、運(yùn)行并控制該 Thread闷串,這樣便解決了該問題(因?yàn)槿魏?Activity 都可以控制同一 Service,而系統(tǒng)也只會(huì)創(chuàng)建一個(gè)對(duì)應(yīng) Service 的實(shí)例)筋量。
因此你可以把 Service 想象成一種消息服務(wù)烹吵,而你可以在任何有 Context 的地方調(diào)用 Context.startService碉熄、Context.stopService、Context.bindService年叮,Context.unbindService具被,來控制它,你也可以在 Service 里注冊(cè) BroadcastReceiver只损,在其他地方通過發(fā)送 broadcast 來控制它一姿,當(dāng)然這些都是 Thread 做不到的。
3. Service和Activity通信
需要用到bindService跃惫,通過onBind()方法來實(shí)現(xiàn)叮叹,看下面bindService的例子
4. Service的生命周期
第一種方式:通過StartService啟動(dòng)Service
通過startService啟動(dòng)后,service會(huì)一直無限期運(yùn)行下去爆存,只有外部調(diào)用了stopService()或stopSelf()方法時(shí)蛉顽,該Service才會(huì)停止運(yùn)行并銷毀。
要?jiǎng)?chuàng)建一個(gè)這樣的Service先较,你需要讓該類繼承Service類携冤,然后重寫以下方法:
onCreate()
1.如果service沒被創(chuàng)建過,調(diào)用startService()后會(huì)執(zhí)行onCreate()回調(diào)闲勺;
2.如果service已處于運(yùn)行中曾棕,調(diào)用startService()不會(huì)執(zhí)行onCreate()方法。
也就是說菜循,onCreate()只會(huì)在第一次創(chuàng)建service時(shí)候調(diào)用翘地,多次執(zhí)行startService()不會(huì)重復(fù)調(diào)用onCreate(),此方法適合完成一些初始化工作癌幕。onStartCommand()
如果多次執(zhí)行了Context的startService()方法衙耕,那么Service的onStartCommand()方法也會(huì)相應(yīng)的多次調(diào)用。onStartCommand()方法很重要勺远,我們?cè)谠摲椒ㄖ懈鶕?jù)傳入的Intent參數(shù)進(jìn)行實(shí)際的操作橙喘,比如會(huì)在此處創(chuàng)建一個(gè)線程用于下載數(shù)據(jù)或播放音樂等。onBind()
Service中的onBind()方法是抽象方法胶逢,Service類本身就是抽象類厅瞎,所以onBind()方法是必須重寫的,即使我們用不到宪塔。onDestory()
在銷毀的時(shí)候會(huì)執(zhí)行Service該方法。
這幾個(gè)方法都是回調(diào)方法囊拜,且在主線程中執(zhí)行某筐,由android操作系統(tǒng)在合適的時(shí)機(jī)調(diào)用。
startService代碼實(shí)例
創(chuàng)建TestOneService冠跷,并在manifest里注冊(cè)南誊。
需要注意身诺,項(xiàng)目中的每一個(gè)Service都必須在AndroidManifest.xml中注冊(cè)才行,所以還需要編輯AndroidManifest.xml文件抄囚,代碼如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.servicetest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="17" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
……
<service android:name="com.example.servicetest.TestOneService" >
</service>
</application>
</manifest>
在MainActivty中操作TestOneService霉赡,code如下:
/**
* Created by Kathy on 17-2-6.
*/
public class TestOneService extends Service{
@Override
public void onCreate() {
Log.i("Kathy","onCreate - Thread ID = " + Thread.currentThread().getId());
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Kathy", "onStartCommand - startId = " + startId + ", Thread ID = " + Thread.currentThread().getId());
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("Kathy", "onBind - Thread ID = " + Thread.currentThread().getId());
return null;
}
@Override
public void onDestroy() {
Log.i("Kathy", "onDestroy - Thread ID = " + Thread.currentThread().getId());
super.onDestroy();
}
}
在MainActivity中三次startService,之后stopService幔托。
/**
* Created by Kathy on 17-2-6.
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("Kathy", "Thread ID = " + Thread.currentThread().getId());
Log.i("Kathy", "before StartService");
//連續(xù)啟動(dòng)Service
Intent intentOne = new Intent(this, TestOneService.class);
startService(intentOne);
Intent intentTwo = new Intent(this, TestOneService.class);
startService(intentTwo);
Intent intentThree = new Intent(this, TestOneService.class);
startService(intentThree);
//停止Service
Intent intentFour = new Intent(this, TestOneService.class);
stopService(intentFour);
//再次啟動(dòng)Service
Intent intentFive = new Intent(this, TestOneService.class);
startService(intentFive);
Log.i("Kathy", "after StartService");
}
}
打印出的Log如下:
02-06 15:19:45.090 8938-8938/? I/Kathy: Thread ID = 1
02-06 15:19:45.090 8938-8938/? I/Kathy: before StartService
02-06 15:19:45.233 8938-8938/? I/Kathy: onCreate - Thread ID = 1
02-06 15:19:45.234 8938-8938/? I/Kathy: onStartCommand - startId = 1, Thread ID = 1
02-06 15:19:45.234 8938-8938/? I/Kathy: onStartCommand - startId = 2, Thread ID = 1
02-06 15:19:45.235 8938-8938/? I/Kathy: onStartCommand - startId = 3, Thread ID = 1
02-06 15:19:45.236 8938-8938/? I/Kathy: onDestroy - Thread ID = 1
02-06 15:19:45.237 8938-8938/? I/Kathy: onCreate - Thread ID = 1
02-06 15:19:45.237 8938-8938/? I/Kathy: onStartCommand - startId = 1, Thread ID = 1
02-06 15:19:45.238 8938-8938/? I/Kathy: after StartService
分析:
1.主線程打印出是1穴亏,所有回調(diào)方法中打印出的執(zhí)行線程ID都是1,證明回調(diào)方法都是在主線程中執(zhí)行的重挑。
2.三次調(diào)用startService嗓化,只觸發(fā)一次onCreate回調(diào),觸發(fā)了三次onStartCommand回調(diào)谬哀,且startId分別為1刺覆,2,3史煎。證明 多次startService不會(huì)重復(fù)執(zhí)行onCreate回調(diào)谦屑,但每次都會(huì)執(zhí)行onStartCommand回調(diào)。
第二種方式:通過bindService啟動(dòng)Service
bindService啟動(dòng)服務(wù)特點(diǎn):
1.bindService啟動(dòng)的服務(wù)和調(diào)用者之間是典型的client-server模式篇梭。調(diào)用者是client氢橙,service則是server端。service只有一個(gè)很洋,但綁定到service上面的client可以有一個(gè)或很多個(gè)充蓝。這里所提到的client指的是組件,比如某個(gè)Activity喉磁。
2.client可以通過IBinder接口獲取Service實(shí)例谓苟,從而實(shí)現(xiàn)在client端直接調(diào)用Service中的方法以實(shí)現(xiàn)靈活交互,這在通過startService方法啟動(dòng)中是無法實(shí)現(xiàn)的协怒。
3.bindService啟動(dòng)服務(wù)的生命周期與其綁定的client息息相關(guān)涝焙。當(dāng)client銷毀時(shí),client會(huì)自動(dòng)與Service解除綁定孕暇。當(dāng)然仑撞,client也可以明確調(diào)用Context的unbindService()方法與Service解除綁定。當(dāng)沒有任何client與Service綁定時(shí)妖滔,Service會(huì)自行銷毀隧哮。
bindService代碼實(shí)例
交互界面設(shè)計(jì)如下:
1.創(chuàng)建一個(gè)TestTwoService繼承Service(Server)
2.創(chuàng)建ActivityA,可以通過bindService綁定服務(wù)(client)
3.創(chuàng)建ActivityB座舍,可以通過bindService綁定服務(wù)(client)
4.ActivityA可以跳轉(zhuǎn)到ActivityB
TestTwoService創(chuàng)建如下:
要想讓Service支持bindService調(diào)用方式沮翔,需要做以下事情:
1.在Service的onBind()方法中返回IBinder類型的實(shí)例。
2.onBInd()方法返回的IBinder的實(shí)例需要能夠返回Service實(shí)例本身曲秉。通常采蚀,最簡單的方法就是在service中創(chuàng)建binder的內(nèi)部類疲牵,加入類似getService()的方法返回Service,這樣綁定的client就可以通過getService()方法獲得Service實(shí)例了榆鼠。
/**
* Created by Kathy on 17-2-6.
*/
public class TestTwoService extends Service{
//client 可以通過Binder獲取Service實(shí)例
public class MyBinder extends Binder {
public TestTwoService getService() {
return TestTwoService.this;
}
}
//通過binder實(shí)現(xiàn)調(diào)用者client與Service之間的通信
private MyBinder binder = new MyBinder();
private final Random generator = new Random();
@Override
public void onCreate() {
Log.i("Kathy","TestTwoService - onCreate - Thread = " + Thread.currentThread().getName());
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Kathy", "TestTwoService - onStartCommand - startId = " + startId + ", Thread = " + Thread.currentThread().getName());
return START_NOT_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("Kathy", "TestTwoService - onBind - Thread = " + Thread.currentThread().getName());
return binder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("Kathy", "TestTwoService - onUnbind - from = " + intent.getStringExtra("from"));
return false;
}
@Override
public void onDestroy() {
Log.i("Kathy", "TestTwoService - onDestroy - Thread = " + Thread.currentThread().getName());
super.onDestroy();
}
//getRandomNumber是Service暴露出去供client調(diào)用的公共方法
public int getRandomNumber() {
return generator.nextInt();
}
}
client端要做的事情:
1.創(chuàng)建ServiceConnection類型實(shí)例纲爸,并重寫onServiceConnected()方法和onServiceDisconnected()方法。
2.當(dāng)執(zhí)行到onServiceConnected回調(diào)時(shí)妆够,可通過IBinder實(shí)例得到Service實(shí)例對(duì)象识啦,這樣可實(shí)現(xiàn)client與Service的連接。
3.onServiceDisconnected回調(diào)被執(zhí)行時(shí)责静,表示client與Service斷開連接袁滥,在此可以寫一些斷開連接后需要做的處理。
創(chuàng)建ActivityA灾螃,代碼如下:
/**
* Created by Kathy on 17-2-6.
*/
public class ActivityA extends Activity implements Button.OnClickListener {
private TestTwoService service = null;
private boolean isBind = false;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
isBind = true;
TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder) binder;
service = myBinder.getService();
Log.i("Kathy", "ActivityA - onServiceConnected");
int num = service.getRandomNumber();
Log.i("Kathy", "ActivityA - getRandomNumber = " + num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBind = false;
Log.i("Kathy", "ActivityA - onServiceDisconnected");
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_a);
Log.i("Kathy", "ActivityA - onCreate - Thread = " + Thread.currentThread().getName());
findViewById(R.id.btnBindService).setOnClickListener(this);
findViewById(R.id.btnUnbindService).setOnClickListener(this);
findViewById(R.id.btnStartActivityB).setOnClickListener(this);
findViewById(R.id.btnFinish).setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btnBindService) {
//單擊了“bindService”按鈕
Intent intent = new Intent(this, TestTwoService.class);
intent.putExtra("from", "ActivityA");
Log.i("Kathy", "----------------------------------------------------------------------");
Log.i("Kathy", "ActivityA 執(zhí)行 bindService");
bindService(intent, conn, BIND_AUTO_CREATE);
} else if (v.getId() == R.id.btnUnbindService) {
//單擊了“unbindService”按鈕
if (isBind) {
Log.i("Kathy",
"----------------------------------------------------------------------");
Log.i("Kathy", "ActivityA 執(zhí)行 unbindService");
unbindService(conn);
}
} else if (v.getId() == R.id.btnStartActivityB) {
//單擊了“start ActivityB”按鈕
Intent intent = new Intent(this, ActivityB.class);
Log.i("Kathy",
"----------------------------------------------------------------------");
Log.i("Kathy", "ActivityA 啟動(dòng) ActivityB");
startActivity(intent);
} else if (v.getId() == R.id.btnFinish) {
//單擊了“Finish”按鈕
Log.i("Kathy",
"----------------------------------------------------------------------");
Log.i("Kathy", "ActivityA 執(zhí)行 finish");
this.finish();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i("Kathy", "ActivityA - onDestroy");
}
}
創(chuàng)建ActivityB题翻,代碼如下:
/**
* Created by Kathy on 17-2-6.
*/
public class ActivityB extends Activity implements Button.OnClickListener {
private TestTwoService service = null;
private boolean isBind = false;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
isBind = true;
TestTwoService.MyBinder myBinder = (TestTwoService.MyBinder)binder;
service = myBinder.getService();
Log.i("Kathy", "ActivityB - onServiceConnected");
int num = service.getRandomNumber();
Log.i("Kathy", "ActivityB - getRandomNumber = " + num);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBind = false;
Log.i("Kathy", "ActivityB - onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
findViewById(R.id.btnBindService).setOnClickListener(this);
findViewById(R.id.btnUnbindService).setOnClickListener(this);
findViewById(R.id.btnFinish).setOnClickListener(this);
}
@Override
public void onClick(View v) {
if(v.getId() == R.id.btnBindService){
//單擊了“bindService”按鈕
Intent intent = new Intent(this, TestTwoService.class);
intent.putExtra("from", "ActivityB");
Log.i("Kathy", "----------------------------------------------------------------------");
Log.i("Kathy", "ActivityB 執(zhí)行 bindService");
bindService(intent, conn, BIND_AUTO_CREATE);
}else if(v.getId() == R.id.btnUnbindService){
//單擊了“unbindService”按鈕
if(isBind){
Log.i("Kathy", "----------------------------------------------------------------------");
Log.i("Kathy", "ActivityB 執(zhí)行 unbindService");
unbindService(conn);
}
}else if(v.getId() == R.id.btnFinish){
//單擊了“Finish”按鈕
Log.i("Kathy", "----------------------------------------------------------------------");
Log.i("Kathy", "ActivityB 執(zhí)行 finish");
this.finish();
}
}
@Override
public void onDestroy(){
super.onDestroy();
Log.i("Kathy", "ActivityB - onDestroy");
}
}
測(cè)試步驟1
step1: 點(diǎn)擊ActivityA的bindService按鈕
step2: 再點(diǎn)擊ActivityA的unbindService按鈕
Log輸出:
02-07 14:09:38.031 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main
02-07 14:09:39.488 1738-1738/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:09:39.488 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA 執(zhí)行 bindService
02-07 14:09:39.496 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main
02-07 14:09:39.497 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main
02-07 14:09:39.500 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected
02-07 14:09:39.500 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -1046987376
02-07 14:09:50.866 1738-1738/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:09:50.866 1738-1738/com.demo.kathy.demo I/Kathy: ActivityA 執(zhí)行 unbindService
02-07 14:09:50.870 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA
02-07 14:09:50.871 1738-1738/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main
總結(jié)調(diào)用bindService之后發(fā)生的事情:
1.client執(zhí)行bindService()
2.如果Service不存在,則Service執(zhí)行onCreate(),onBind()
3.client實(shí)例ServiceConnection執(zhí)行onServiceConnected()方法
總結(jié)調(diào)用unbindService之后發(fā)生的事情:
1.client執(zhí)行unbindService()
2.client與Service解除綁定連接狀態(tài)
3.Service檢測(cè)是否還有其他client與其連接腰鬼,如果沒有Service執(zhí)行onUnbind()和onDestroy()
測(cè)試步驟2
step1: 點(diǎn)擊ActivityA的bindService按鈕
step2: 再點(diǎn)擊ActivityA的Finish按鈕
Log 輸出:
02-07 14:49:16.626 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main
02-07 14:49:18.102 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:49:18.102 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 執(zhí)行 bindService
02-07 14:49:18.105 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main
02-07 14:49:18.110 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main
02-07 14:49:18.112 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected
02-07 14:49:18.112 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -318399886
02-07 14:49:19.540 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:49:19.540 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 執(zhí)行 finish
02-07 14:49:19.789 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onDestroy
02-07 14:49:19.798 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA
02-07 14:49:19.798 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main
總結(jié):如果client銷毀嵌赠,那么client會(huì)自動(dòng)與Service解除綁定。
測(cè)試步驟3
step1: 點(diǎn)擊ActivityA的bindService按鈕
step2: 點(diǎn)擊ActivityA的startActivity B按鈕熄赡,切換到ActivityB
step3: 點(diǎn)擊ActivityB中的bindService按鈕
step4: 點(diǎn)擊ActivityB中的unbindService按鈕
step5: 點(diǎn)擊ActivityB中的Finish按鈕
step6: 點(diǎn)擊ActivityA中的unbindService按鈕
得到Log:
02-07 14:55:04.390 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onCreate - Thread = main
02-07 14:55:08.191 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:08.191 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 執(zhí)行 bindService
02-07 14:55:08.197 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onCreate - Thread = main
02-07 14:55:08.198 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onBind - Thread = main
02-07 14:55:08.205 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - onServiceConnected
02-07 14:55:08.205 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA - getRandomNumber = -706215542
02-07 14:55:23.261 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:23.261 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 啟動(dòng) ActivityB
02-07 14:55:29.239 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:29.239 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 執(zhí)行 bindService
02-07 14:55:29.241 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - onServiceConnected
02-07 14:55:29.241 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - getRandomNumber = 1827572726
02-07 14:55:33.951 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:33.951 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 執(zhí)行 unbindService
02-07 14:55:36.645 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:36.645 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB 執(zhí)行 finish
02-07 14:55:36.852 12566-12566/com.demo.kathy.demo I/Kathy: ActivityB - onDestroy
02-07 14:55:43.137 12566-12566/com.demo.kathy.demo I/Kathy: ----------------------------------------------------------------------
02-07 14:55:43.137 12566-12566/com.demo.kathy.demo I/Kathy: ActivityA 執(zhí)行 unbindService
02-07 14:55:43.143 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onUnbind - from = ActivityA
02-07 14:55:43.143 12566-12566/com.demo.kathy.demo I/Kathy: TestTwoService - onDestroy - Thread = main
總結(jié)bindService的生命周期:
1.點(diǎn)擊ActivityA的bindService按鈕
第一次調(diào)用bindService會(huì)實(shí)例化TestTwoService姜挺,然后執(zhí)行其onBind()方法,得到IBinder類型的實(shí)例彼硫,將其作為參數(shù)傳入ActivityA的ServiceConnection的onServiceConnected方法中炊豪,標(biāo)志著ActivityA與TestTwoService建立了綁定。
2.點(diǎn)擊ActivityB中的bindService按鈕
由于TestTwoService已處于運(yùn)行狀態(tài)拧篮,所以再次調(diào)用bindService不會(huì)重新創(chuàng)建它的實(shí)例词渤,所以也不會(huì)執(zhí)行TestTwoService的onCreate()方法和onBind()方法。ActivityB與ActivityA共享IBinder實(shí)例串绩。此時(shí)有兩個(gè)client與TestTwoService綁定缺虐。
3.點(diǎn)擊ActivityB中的unbindService按鈕
ActivityB與TestTwoService解除了綁定,當(dāng)沒有任何client與Service綁定時(shí)礁凡,才會(huì)執(zhí)行Service的onUnbind()方法高氮。此時(shí),ActivityA還在綁定連接中顷牌,所以不會(huì)執(zhí)行Service的解綁方法剪芍。
4.點(diǎn)擊ActivityA中的unbindService按鈕
ActivityA執(zhí)行unbindService之后,ActivityA與TestTwoService就解除綁定了窟蓝,這樣就沒有client與TestTwoService綁定罪裹,這時(shí)候Android會(huì)銷毀TestTwoService,在銷毀前會(huì)先執(zhí)行TestTwoService的onUnbind()方法,然后才會(huì)執(zhí)行其onDestroy()方法坊谁,這樣TestService就銷毀了。
如何保證Service不被殺死滑臊?
1. onStartCommand方式中口芍,返回START_STICKY
首先我們來看看onStartCommand都可以返回哪些值:
調(diào)用Context.startService方式啟動(dòng)Service時(shí),如果Android面臨內(nèi)存匱乏雇卷,可能會(huì)銷毀當(dāng)前運(yùn)行的Service鬓椭,待內(nèi)存充足時(shí)可以重建Service。而Service被Android系統(tǒng)強(qiáng)制銷毀并再次重建的行為依賴于Service的onStartCommand()方法的返回值关划。
START_NOT_STICKY
如果返回START_NOT_STICKY小染,表示當(dāng)Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后,不會(huì)重新創(chuàng)建該Service贮折。當(dāng)然如果在其被殺掉之后一段時(shí)間又調(diào)用了startService裤翩,那么該Service又將被實(shí)例化。那什么情境下返回該值比較恰當(dāng)呢调榄?
如果我們某個(gè)Service執(zhí)行的工作被中斷幾次無關(guān)緊要或者對(duì)Android內(nèi)存緊張的情況下需要被殺掉且不會(huì)立即重新創(chuàng)建這種行為也可接受踊赠,那么我們便可將 onStartCommand的返回值設(shè)置為START_NOT_STICKY。
舉個(gè)例子每庆,某個(gè)Service需要定時(shí)從服務(wù)器獲取最新數(shù)據(jù):通過一個(gè)定時(shí)器每隔指定的N分鐘讓定時(shí)器啟動(dòng)Service去獲取服務(wù)端的最新數(shù)據(jù)筐带。當(dāng)執(zhí)行到Service的onStartCommand時(shí),在該方法內(nèi)再規(guī)劃一個(gè)N分鐘后的定時(shí)器用于再次啟動(dòng)該Service并開辟一個(gè)新的線程去執(zhí)行網(wǎng)絡(luò)操作缤灵。假設(shè)Service在從服務(wù)器獲取最新數(shù)據(jù)的過程中被Android系統(tǒng)強(qiáng)制殺掉伦籍,Service不會(huì)再重新創(chuàng)建,這也沒關(guān)系腮出,因?yàn)樵龠^N分鐘定時(shí)器就會(huì)再次啟動(dòng)該Service并重新獲取數(shù)據(jù)帖鸦。START_STICKY
如果返回START_STICKY,表示Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后利诺,Android系統(tǒng)會(huì)將該Service依然設(shè)置為started狀態(tài)(即運(yùn)行狀態(tài))富蓄,但是不再保存onStartCommand方法傳入的intent對(duì)象,然后Android系統(tǒng)會(huì)嘗試再次重新創(chuàng)建該Service慢逾,并執(zhí)行onStartCommand回調(diào)方法立倍,但是onStartCommand回調(diào)方法的Intent參數(shù)為null,也就是onStartCommand方法雖然會(huì)執(zhí)行但是獲取不到intent信息侣滩。如果你的Service可以在任意時(shí)刻運(yùn)行或結(jié)束都沒什么問題口注,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY君珠,比如一個(gè)用來播放背景音樂功能的Service就適合返回該值寝志。START_REDELIVER_INTENT
如果返回START_REDELIVER_INTENT,表示Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后,與返回START_STICKY的情況類似材部,Android系統(tǒng)會(huì)將再次重新創(chuàng)建該Service毫缆,并執(zhí)行onStartCommand回調(diào)方法,但是不同的是乐导,Android系統(tǒng)會(huì)再次將Service在被殺掉之前最后一次傳入onStartCommand方法中的Intent再次保留下來并再次傳入到重新創(chuàng)建后的Service的onStartCommand方法中苦丁,這樣我們就能讀取到intent參數(shù)。只要返回START_REDELIVER_INTENT物臂,那么onStartCommand重的intent一定不是null旺拉。如果我們的Service需要依賴具體的Intent才能運(yùn)行(需要從Intent中讀取相關(guān)數(shù)據(jù)信息等),并且在強(qiáng)制銷毀后有必要重新創(chuàng)建運(yùn)行棵磷,那么這樣的Service就適合返回START_REDELIVER_INTENT蛾狗。
2.提高Service的優(yōu)先級(jí)
在AndroidManifest.xml文件中對(duì)于intent-filter可以通過android:priority = "1000"這個(gè)屬性設(shè)置最高優(yōu)先級(jí),1000是最高值仪媒,如果數(shù)字越小則優(yōu)先級(jí)越低沉桌,同時(shí)適用于廣播。
3.提升Service進(jìn)程的優(yōu)先級(jí)
當(dāng)系統(tǒng)進(jìn)程空間緊張時(shí)算吩,會(huì)依照優(yōu)先級(jí)自動(dòng)進(jìn)行進(jìn)程的回收蒲牧。
Android將進(jìn)程分為6個(gè)等級(jí),按照優(yōu)先級(jí)由高到低依次為:
- 前臺(tái)進(jìn)程foreground_app
- 可視進(jìn)程visible_app
- 次要服務(wù)進(jìn)程secondary_server
- 后臺(tái)進(jìn)程hiddena_app
- 內(nèi)容供應(yīng)節(jié)點(diǎn)content_provider
- 空進(jìn)程empty_app
可以使用startForeground將service放到前臺(tái)狀態(tài)赌莺,這樣低內(nèi)存時(shí)冰抢,被殺死的概率會(huì)低一些。
4.在onDestroy方法里重啟Service
當(dāng)service走到onDestroy()時(shí)艘狭,發(fā)送一個(gè)自定義廣播挎扰,當(dāng)收到廣播時(shí),重新啟動(dòng)service巢音。
5.系統(tǒng)廣播監(jiān)聽Service狀態(tài)
6.將APK安裝到/system/app遵倦,變身為系統(tǒng)級(jí)應(yīng)用
FROM:
Android Service兩種啟動(dòng)方式詳解(總結(jié)版)
http://www.reibang.com/p/4c798c91a613
Android Service完全解析,關(guān)于服務(wù)你所需知道的一切(上)
http://blog.csdn.net/guolin_blog/article/details/11952435/
Android中Service的使用詳解和注意點(diǎn)(LocalService)
http://www.cnblogs.com/linlf03/p/3296323.html
Android四大組件:Service服務(wù)史上最全面解析