編輯:關於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
按鈕,讓其不可用。方法自己去找吧。當然有些手機自帶的清理功能就是從這個地方清理的,比如華為的清理。所以第三種情況我也沒有什麼更好的辦法了。
1.import android.app.Activity;import android.content.Context;import android.content.r
騰訊手機管家,初始界面有個小飛機動啊動啊,還挺好玩的,而且顯示新特征為豎向展示,不知道這種東西該如何實現呢?給自己留下比較深的印象,然後樓主就是探索這種是如何實現的。
在開發中發現一個問題:當一個我通過Intent開啟一個前面已經打開的activty的界面時,新打開的activity的狀態會丟失。當時,當我直接按home減將acitvi
本文實例講述了Android控件之CheckBox、RadioButton用法。分享給大家供大家參考。具體如下:CheckBox和RadioButton控件都只有選中和未