編輯:關於Android編程
Service的主要作用是,讓系統可以在後台干一些不與用戶交互的操作,這些操作可能會比較耗時,比如去下載一些網絡資源等;也可能是一項長期運行的工作,比如說監聽電話來電、播放音樂等。初聽起來,Service與線程Thread很像,但Service和Thread完全是兩個不同的東西啊。
(1)Service不是運行在一個獨立的進程中,它和我們的應用程序在同一個進程中;
(2)Service也不是一個線程,相反,Service是運行在主線程的,因此我們不能直接在Service中干上面那些耗時操作,因為它會很耗CPU,阻塞主線程,很容易出現ANR錯誤(Application Not Responding),合適的做法是,在Service中開啟一個Thread,進行上面的耗時操作。
如果我們要在Service中開啟線程進行工作,我們也可以使用Service的一個子類IntentService,IntentService類中已經開啟了線程,我們只需要實現一個方法即可,後面具體介紹。
和Activity類似,我們要使用Service,只需要通過Intent發出請求即可。當然,在使用Service前,記得在AndroidManifest.xml中進行聲明。啟動方式有兩種:
我們先在Activity中加入兩組四個按鈕,一組為startService的啟動按鈕和相應的停止按鈕;一組為bindService的啟動按鈕和相應的停止按鈕。如圖:

啟動方式一:startService()
在Activity中,我們如下使用:
public class MainActivity extends Activity implements OnClickListener{
private Button startServiceBtn,stopServiceBtn,bindServiceBtn,unBindServiceBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startServiceBtn = (Button) findViewById(R.id.startService);
stopServiceBtn = (Button) findViewById(R.id.stopService);
bindServiceBtn = (Button) findViewById(R.id.bindService);
unBindServiceBtn = (Button) findViewById(R.id.unBindService);
startServiceBtn.setOnClickListener(this);
stopServiceBtn.setOnClickListener(this);
bindServiceBtn.setOnClickListener(this);
unBindServiceBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.startService://通過startService()的方式啟動Service
Intent intent = new Intent(this,ServiceTest.class);
startService(intent);
break;
case R.id.stopService:
stopService(new Intent(this,ServiceTest.class));
break;
case R.id.bindService://通過bindService()的方式啟動Service
//TODO
break;
case R.id.unBindService:
//TODO
break;
}
}
}
我們的Service如下:
public class ServiceTest extends Service {
@Override
public void onCreate() {
super.onCreate();
Log.d("TAG", "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("TAG", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent arg0) {
Log.d("TAG", "onBind");
return null;
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("TAG", "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.d("TAG", "onDestroy");
super.onDestroy();
}
}
上面,點擊“startService”按鈕,我們就通過startService()將傳進來的Intent中的Service啟動了,啟動時Log打印如下:

說明,我們第一次啟動Service的時候,會執行onCreate()和onStartCommand()方法,如果我們這個時候,再次點擊“startService”按鈕,此時打印如下:

可以看到,如果一個Service已經運行了,再次啟動這個Service,只會進入onStartCommand(),onCreate()方法只會在第一次啟動的時候進行初始化。Service不像我們的Activity,Activity每次通過Intent啟動時都會創建一個新的Activity(默認模式下),然後放入到棧中。而同一個Service,應用中只會存在一個實例,在第一次創建時通過onCreate初始化,運行後,再次啟動只是進入onStartCommand。
點擊“stopService”後,Service被銷毀,進入onDestroy()方法。不管前面我們啟動了多少次Service,只要在外部調用一次Context.stopService()或者在Service內部自己調用一次stopSelf(),Service就會被銷毀。
上面這種啟動方式,在啟動完Service後,這個Service就開始在後台運行了,同時,也與啟動它的Activity失去了聯系,因為不能通過ServiceTest service = new ServiceTest()的方式啟動Service,因而我們的Activity中不能獲取到ServiceTest的實例,也就不能夠控制啟動後的Service干Activity想干的事,這個Service只能干它內部定義好的功能,沒有與啟動它的Activity交互能力。
為了解決與啟動Service的組件的通信能力,有一種解決方案就是通過廣播的形式,我們在Activity中發出一些想用操作廣播,在Service中注冊該廣播,Service接收到該廣播信息後,完成相應的功能。但是這種方案不夠優雅,頻繁發送廣播比較消耗性能,同時,由於廣播接受者中的onReceive()中,不能執行長時間的工作,時間超過後,可能就直接跳出了方法。因此,這種方案不是首選。
啟動方式二:bindService()
如上面所述,如果要求我們的Service能夠和啟動Service的組件進行通信,我們可以使用bindService的啟動方式。
首先改造Service,Service和組件之間是通過管道IBinder接口進行通信的。
public class ServiceTest extends Service {
private MyLocalBinder mBinder = new MyLocalBinder();
@Override
public void onCreate() {
super.onCreate();
Log.d("TAG", "onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("TAG", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
class MyLocalBinder extends Binder{
public ServiceTest getServiceInstance(){
return ServiceTest.this;
}
//...這裡也可以繼續寫方法對外提供
}
@Override
public IBinder onBind(Intent arg0) {
Log.d("TAG", "onBind");
return mBinder;
}
//對外提供的訪問方法
public void downLoad(){
System.out.println("下載操作方法...");
}
@Override
public boolean onUnbind(Intent intent) {
Log.d("TAG", "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.d("TAG", "onDestroy");
super.onDestroy();
}
}
繼續改造我們的Activity,在ASctivity中獲得連接通道,如下:
private ServiceTest mService;
private boolean isConnected = false;//我們在使用bindService時,最好定義一個是否連接的標志,方便我們在組件中通信前的判斷操作
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder binder) {
ServiceTest.MyLocalBinder localBinder = (MyLocalBinder)binder; //先獲得管道
mService = localBinder.getServiceInstance(); //通過管道,拿到Service的實例
isConnected = true;
mService.downLoad();//拿到Service實例後想干嘛干嘛
}
//注意:這個方法當Service意外運行失敗時調用,如系統殺死這個Service或者運行Service時遇到崩潰,調用unBinderService並不會調用該方法
@Override
public void onServiceDisconnected(ComponentName arg0) {
isConnected = false;
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.startService://通過startService()的方式啟動Service
Intent intent = new Intent(this,ServiceTest.class);
startService(intent);
break;
case R.id.stopService:
stopService(new Intent(this,ServiceTest.class));
break;
case R.id.bindService://通過bindService()的方式啟動Service
Intent intent2 = new Intent(this,ServiceTest.class);
bindService(intent2, connection, BIND_AUTO_CREATE);
break;
case R.id.unBindService:
unbindService(connection);
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(isConnected){
unbindService(connection);
isConnected = false;
}
}
點擊“bindService”按鈕,我們就通過bindService()將傳進來的Intent中的Service啟動了,啟動時Log打印如下:

我們通過bindService()方法第一次啟動後,會進入Service的onCreate()和onBind()方法。如果我們另一個組件(如Activity),又對同一個Service發起了bindService()操作(也就是說在bindService()中傳入了不同的ServiceConnection),此時只會進入onBind()方法。也就是說onCreate也只是在Service第一次創建時執行。
我們點擊“unBindService”時,打印如下:

此時走的是onUnbind和onDestroy方法。
可以看到,不管通過哪種方式啟動Service,同一個Service在整個應用程序中只會有一個實例存在,如果通過bindService啟動,獲得的IBinder實例也都是同一個。四大組件中,只有在Activity、Service、Content Providers中通過bindService啟動Service。
從上面的打印日志可以看到,兩種啟動Service方式走的生命周期方法是不同的,官方的生命周期圖很好地描述了兩種啟動方式下的回調:

兩種方式都是只有在第一次啟動沒有運行的Service時,才會進入onCreate()方法。當Service啟動後,後面多次繼;啟動該Service,只會進入onStartCommand()或者onBind()。
(1)當我們通過startService()方法啟動Service時,不管前面我們啟動了多少次Service,只要在外部調用一次stopService()或者在Service內部自己調用一次stopSelf(),Service就會被銷毀;
(2)當我們通過bindService()啟動Service時,前面我們多次啟動Service後,當沒有客戶端連接後(即所有客戶端發出了unBindService),這個Service將會被系統銷毀;
(3)當這個Service既被startService啟動,又被bindService啟動時,即使當所有連接的客戶端斷開連接後,Service也不會被銷毀,除非再調用一次stopService或內部使用stopSelf(),這個時候Service才會被銷毀。或者說如果我們調用了stopService或內部使用stopSelf(),Service也不會被銷毀,只有當所有的客戶端斷開連接後,Service才會被銷毀。也就是說,在這種情況下,Service必須在既沒有任何Activity關聯又停止的情況下,Service才會被銷毀。這種情況下的生命周期如下:

注意:為了讓我們的Service不過多的浪費CPU資源、內存資源,我們需要在必要的時候進行解除綁定。
(1)當我們的Service只在Activity被用戶可見的時候,才與Activity進行交互,那我們應該在Activity的onStart()中bindService,在onStop()中unBindService();
(2)如果我們希望Activity即使在後台時也能夠與Service交互,那我們應該在onCreate()中bindService(),在onDestroy()中unBindService(),即Activity在整個生命周期中要與Service交互。
正如我們第一部分所談到的,Service和Thread完全是兩個不同的東西,Service主要功能是可以在後台執行一些操作,這些操作不需要與用戶交互。Service是直接運行在主線中中的,如果我們需要在Service中執行耗時操作,為了避免ANR錯誤,是需要在Service中開啟線程來執行的。對於使用Service有開啟線程需求的開發者來說,Android提供了IntentService給用戶,IntentService內部已經幫我們開啟了線程,我們只需要實現它定義的onHandleIntent()方法,在裡面實現我們的功能即可。注意,IntentService不能處理多個線程的請求,但是可以處理多個啟動Service的請求。
IntentService提供的功能:
(1)內部創建了一個工作線程,來處理每個啟動Service的請求傳來的Intent;
(2)內部有一個工作隊列,來分別處理多個啟動Service的請求,因此避免了多線程的問題;
(3)在所有的請求處理完後,自動停止服務,因此我們不必在Service內部自己寫stopSelf();
(4)提供了默認onBind()的實現,直接返回null,意味著IntentService只能通過startService()的方式啟動;
(5)提供了默認onStartCommand()的實現,它將我們的Intent放入到了工作隊列中,然後執行我們具體實現的onHandleIntent()方法。
一個簡單實例如下:
public class IntentServiceTest extends IntentService {
public IntentServiceTest(){//提供一個默認的構造方法,調用父類構造方法,傳入工作線程的名字
super("IntentServiceTest");
}
@Override
protected void onHandleIntent(Intent arg0) {
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
使用IntentService主要是注意兩點,一是定義一個默認構造方法,裡面調用父類構造方法,並定義工作線程的名字;第二是實現onHandleIntent()方法,我們在這裡面具體實現我們Service需要做的事情。可以看到,如果我們要使用開啟線程的Service,IntentService提供了一種非常好的方案,讓用戶只需要關注與onHandleIntent接口的具體實現即可,同時用工作隊列的形式支持多啟動服務的訪問。
我們的Service默認都是在後台默默運行的,用戶基本察覺不到有Service在運行。此時,Service的優先級是比較低的,當系統資源不足的時候,很容易被銷毀。因此,如果我們想讓用戶知道有Service在後台運行,如音樂播放器,或者想讓Service一直保持運行狀態,不容易被系統回收,此時,就可以考慮使用前台Service。前台Service必須要設置有一個Notification到手機的狀態欄,類似360一樣,因此前台Service能夠與用戶進行交互,它的優先級也就提高了,在內存不足的時候,不會優先去回收前台Service。
創建前台Service也很簡單,就是設置一個Notification到狀態欄,如下:
@Override
public void onCreate() {
super.onCreate();
Log.d("TAG", "onCreate");
Notification notification = new Notification(R.drawable.ic_launcher,"前台Service通知來了",System.currentTimeMillis());
Intent notificationIntent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, "通知標題", "前台Service內容", pendingIntent);
//設置到前台運行,第一個參數為通知notification的唯一ID
startForeground(1, notification);
}
運行效果如下:

如果,我們要移除掉這個前台Service,只需要調用stopService()即可。這個方法並不會停止Service,只是移除掉Notification。
(本部分內容來自:http://www.cnblogs.com/rossoneri/p/4530216.html)
這個倒是有點流氓軟件的意思,但有些特定情況還是需要服務能保持開啟不被殺死,當然這樣做我還是在程序裡添加了關閉服務的按鈕,也就是開啟了就殺不死,除非在軟件裡關閉。
服務不被殺死分3種來討論
1.系統根據資源分配情況殺死服務
2.用戶通過settings->Apps->Running->Stop方式殺死服務
3.用戶通過settings->Apps->Downloaded->Force Stop方式殺死服務
第一種情況:
用戶不干預,完全靠系統來控制,辦法有很多。比如onStartCommand()方法的返回值設為START_STICKY,服務就會在資源緊張的時候被殺掉,然後在資源足夠的時候再恢復。當然也可設置為前台服務,使其有高的優先級,在資源緊張的時候也不會被殺掉。
第二種情況:
用戶干預,主動殺掉運行中的服務。這個過程殺死服務會通過服務的生命周期,也就是會調用onDestory()方法,這時候一個方案就是在onDestory()中發送廣播開啟自己。這樣殺死服務後會立即啟動。如下:
@Overridepublicvoid onCreate() {
// TODO Auto-generated method stubsuper.onCreate();
mBR = new BroadcastReceiver() {
@Overridepublicvoid onReceive(Context context, Intent intent) {
// TODO Auto-generated method stubIntent a = new Intent(ServiceA.this, ServiceA.class);
startService(a);
}
};
mIF = new IntentFilter();
mIF.addAction("listener");
registerReceiver(mBR, mIF);
}
@Overridepublicvoid onDestroy() {
// TODO Auto-generated method stubsuper.onDestroy();
Intent intent = new Intent();
intent.setAction("listener");
sendBroadcast(intent);
unregisterReceiver(mBR);
}
當然,從理論上來講這個方案是可行的,實驗一下也可以。但有些情況下,發送的廣播在消息隊列中排的靠後,就有可能服務還沒接收到廣播就銷毀了(這是我對實驗結果的猜想,具體執行步驟暫時還不了解)。所以為了能讓這個機制完美運行,可以開啟兩個服務,相互監聽,相互啟動。服務A監聽B的廣播來啟動B,服務B監聽A的廣播來啟動A。經過實驗,這個方案可行,並且用360殺掉後幾秒後服務也還是能自啟的。到這裡再說一句,如果不是某些功能需要的服務,不建議這麼做,會降低用戶體驗。
也就是如下操作,啟動服務時,我們開啟兩個Service,A和B,在A的onCreate中注冊B的廣播事件,在B的onCreate中注冊A的廣播事件,當A被銷毀時,進入onDestroy()方法時,發送A的廣播,B接收到廣播之後再啟動A;同理,如果B被銷毀,發送廣播給A,讓A啟動B。
第三種情況:
強制關閉就沒有辦法。這個好像是從包的level去關的,並不走完整的生命周期。所以在服務裡加代碼是無法被調用的。處理這個情況的唯一方法是屏蔽掉force stop和uninstall按鈕,讓其不可用。方法自己去找吧。當然有些手機自帶的清理功能就是從這個地方清理的,比如華為的清理。所以第三種情況我也沒有什麼更好的辦法了。
Android 仿UC浏覽器三點加載效果
1.import android.app.Activity;import android.content.Context;import android.content.r
android豎向顯示新特性界面
騰訊手機管家,初始界面有個小飛機動啊動啊,還挺好玩的,而且顯示新特征為豎向展示,不知道這種東西該如何實現呢?給自己留下比較深的印象,然後樓主就是探索這種是如何實現的。
Activity數據保存探索
在開發中發現一個問題:當一個我通過Intent開啟一個前面已經打開的activty的界面時,新打開的activity的狀態會丟失。當時,當我直接按home減將acitvi
Android控件之CheckBox、RadioButton用法實例分析
本文實例講述了Android控件之CheckBox、RadioButton用法。分享給大家供大家參考。具體如下:CheckBox和RadioButton控件都只有選中和未