編輯:關於Android編程
Service通常總是稱之為“後台服務”,其中“後台”一詞是相對於前台而言的,具體是指其本身的運行並不依賴於用戶可視的UI界面,因此,從實際業務需求上來理解,Service的適用場景應該具備以下條件:
1.並不依賴於用戶可視的UI界面(當然,這一條其實也不是絕對的,如前台Service就是與Notification界面結合使用的);2.具有較長時間的運行特性。
一般而言,從Service的啟動方式上,可以將Service分為Started Service和Bound Service。無論哪種具體的Service啟動類型,都是通過繼承Service基類自定義而來。在使用Service時,要想系統能夠找到此自定義Service,無論哪種類型,都需要在AndroidManifest.xml中聲明,語法格式如下:
其中,android:exported屬性上一篇博文中對此已進行詳盡描述,android:name對應Service類名,android:permission是權限聲明,android:process設置具體的進程名稱。需要注意的是Service能否單獨使用一個進程與其啟動方式有關,本後下面會給出具體說明。其他的屬性此處與其他組件基本相同,不再過多描述。. . .
Started Service相對比較簡單,通過context.startService(Intent serviceIntent)啟動Service,context.stopService(Intent serviceIntent)停止此Service。當然,在Service內部,也可以通過stopSelf(...)方式停止其本身。
下面代碼片段顯示的是一個最基本的Started Service的自定義方式:
package com.lyz.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; /** * @author liuyazhuang * */ public class MyService extends Service { public static final String TAG = MyService; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); Log.w(TAG, in onCreate); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.w(TAG, in onStartCommand); Log.w(TAG, MyService: + this); String name = intent.getStringExtra(name); Log.w(TAG, name: + name); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); Log.w(TAG, in onDestroy); } }其中,onBind(...)函數是Service基類中的唯一抽象方法,子類都必須重寫實現,此函數的返回值是針對Bound Service類型的Service才有用的,在Started Service類型中,此函數直接返回 null 即可。onCreate(...)、onStartCommand(...)和onDestroy()都是Started Service相應生命周期階段的回調函數。
package com.lyz.service; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import com.lyz.service.activity.R; /** * 程序入口 * @author liuyazhuang * */ public class MainActivity extends Activity { public static final String TAG = MainActivity; private Button startServiceBtn; private Button stopServideBtn; private Button goBtn; private Intent serviceIntent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startServiceBtn = (Button) findViewById(R.id.start_service); stopServideBtn = (Button) findViewById(R.id.stop_service); goBtn = (Button) findViewById(R.id.go); startServiceBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { serviceIntent = new Intent(MainActivity.this, MyService.class); startService(serviceIntent); } }); stopServideBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { stopService(serviceIntent); } }); goBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, BActivity.class); startActivity(intent); } }); } @Override protected void onDestroy() { super.onDestroy(); Log.w(TAG, in onDestroy); } }如上代碼片段,
1.onCreate(Client首次startService(..)) >> onStartCommand >> onStartCommand - optional ... >> onDestroy(Client調用stopService(..))
注:onStartCommand(..)可以多次被調用,onDestroy()與onCreate()想匹配,當用戶強制kill掉進程時,onDestroy()是不會執行的。
2.對於同一類型的Service,Service實例一次永遠只存在一個,而不管Client是否是相同的組件,也不管Client是否處於相同的進程中。
3.Service通過startService(..)啟動Service後,此時Service的生命周期與Client本身的什麼周期是沒有任何關系的,只有Client調用stopService(..)或Service本身調用stopSelf(..)才能停止此Service。當然,當用戶強制kill掉Service進程或系統因內存不足也可能kill掉此Service。
4.Client A 通過startService(..)啟動Service後,可以在其他Client(如Client B、Client C)通過調用stopService(..)結束此Service。
5.Client調用stopService(..)時,如果當前Service沒有啟動,也不會出現任何報錯或問題,也就是說,stopService(..)無需做當前Service是否有效的判斷。
6.startService(Intent serviceIntent),其中的intent既可以是顯式Intent,也可以是隱式Intent,當Client與Service同處於一個App時,一般推薦使用顯示Intent。當處於不同App時,只能使用隱式Intent。
當Service需要運行在單獨的進程中,AndroidManifest.xml聲明時需要通過android:process指明此進程名稱,當此Service需要對其他App開放時,android:exported屬性值需要設置為true(當然,在有intent-filter時默認值就是true)。
當Client調用startService(Intent serviceIntent)啟動Service時,Client可以將參數通過Intent直接傳遞給Service。Service執行過程中,如果需要將參數傳遞給Client,一般可以通過借助於發送廣播的方式(此時,Client需要注冊此廣播)。
相對於Started Service,Bound Service具有更多的知識點。Bound Service的主要特性在於Service的生命周期是依附於Client的生命周期的,當Client不存在時,Bound Service將執行onDestroy,同時通過Service中的Binder對象可以較為方便進行Client-Service通信。Bound Service一般使用過程如下:
1.自定義Service繼承基類Service,並重寫onBind(Intent intent)方法,此方法中需要返回具體的Binder對象;
2.Client通過實現ServiceConnection接口來自定義ServiceConnection,並通過bindService (Intent service, ServiceConnection sc, int flags)方法將Service綁定到此Client上;
3.自定義的ServiceConnection中實現onServiceConnected(ComponentName name, IBinder binder)方法,獲取Service端Binder實例;
4.通過獲取的Binder實例進行Service端其他公共方法的調用,以完成Client-Service通信;
5.當Client在恰當的生命周期(如onDestroy等)時,此時需要解綁之前已經綁定的Service,通過調用函數unbindService(ServiceConnection sc)。
在Bound Service具體使用過程中,根據onBind(Intent intent)方法放回的Binder對象的定義方式不同,又可以將其分為以下三種方式,且每種方式具有不同的特點和適用場景:
這是Bound Service中最常見的一種使用方式,也是Bound Service中最簡單的一種。
局限:Clinet與Service必須同屬於同一個進程,不能實現進程間通信(IPC)。否則則會出現類似於“android.os.BinderProxy cannot be cast to xxx”錯誤。
下面通過代碼片段看下具體的使用:
package com.lyz.service; /** * @author liuyazhuang * */ public class MyBindService extends Service { public static final String TAG = MyBindService; private MyBinder mBinder = new MyBinder(); public class MyBinder extends Binder { MyBindService getService() { return MyBindService.this; } } @Override public void onCreate() { super.onCreate(); Log.w(TAG, in onCreate); } @Override public IBinder onBind(Intent intent) { Log.w(TAG, in onBind); return mBinder; } @Override public boolean onUnbind(Intent intent) { Log.w(TAG, in onUnbind); return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); Log.w(TAG, in onDestroy); } }
package com.lyz.service; /** * @author liuyazhuang * */ public class BActivity extends Activity { public static final String TAG = BActivity; private Button bindServiceBtn; private Button unbindServiceBtn; private Button startIntentService; private Intent serviceIntent; private ServiceConnection sc = new MyServiceConnection(); private MyBinder mBinder; private MyBindService mBindService; private boolean mBound; private class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder binder) { Log.w(TAG, in MyServiceConnection onServiceConnected); mBinder = (MyBinder) binder; mBindService = mBinder.getService(); mBound = true; } @Override public void onServiceDisconnected(ComponentName name) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. Log.w(TAG, in MyServiceConnection onServiceDisconnected); mBound = false; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.b); bindServiceBtn = (Button) findViewById(R.id.bind_service); unbindServiceBtn = (Button) findViewById(R.id.unbind_service); startIntentService = (Button) findViewById(R.id.start_intentservice); bindServiceBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(BActivity.this, MyBindService.class); bindService(intent, sc, Context.BIND_AUTO_CREATE); } }); unbindServiceBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { excuteUnbindService(); } }); startIntentService.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(BActivity.this, MyIntentService.class); startService(intent); } }); } private void excuteUnbindService() { if (mBound) { unbindService(sc); mBound = false; } } @Override protected void onDestroy() { super.onDestroy(); Log.w(TAG, in onDestroy); excuteUnbindService(); } }首次點擊bindServiceBtn進行bindService(..)時,依次回調順序如下:
1 MyBindService(13457): in onCreate 2 MyBindService(13457): in onBind 3 BActivity(13457): in MyServiceConnection onServiceConnected再次點擊bindServiceBtn按鈕時,發現沒有任何輸出,說明MyBindService沒有進行任何回調。
1 MyBindService(13457): in onUnbind 2 MyBindService(13457): in onDestroy注:在四大基本組件中,需要注意的的是BroadcastReceiver不能作為Bound Service的Client,因為BroadcastReceiver的生命周期很短,當執行完onReceive(..)回調時,BroadcastReceiver生命周期完結。而Bound Service又與Client本身的生命周期相關,因此,Android中不允許BroadcastReceiver去bindService(..),當有此類需求時,可以考慮通過startService(..)替代。
Messenger,在此可以理解成”信使“,通過Messenger方式返回Binder對象可以不用考慮Clinet - Service是否屬於同一個進程的問題,並且,可以實現Client - Service之間的雙向通信。極大方便了此類業務需求的實現。
局限:不支持嚴格意義上的多線程並發處理,實際上是以隊列去處理
下面直接看下具體的使用:
package com.lyz.service; /** * @author liuyazhuang * */ public class MyMessengerService extends Service { public static final String TAG = MyMessengerService; public static final int MSG_FROM_CLIENT_TO_SERVER = 1; public static final int MSG_FROM_SERVER_TO_CLIENT = 2; private Messenger mClientMessenger; private Messenger mServerMessenger = new Messenger(new ServerHandler()); @Override public IBinder onBind(Intent intent) { Log.w(TAG, in onBind); return mServerMessenger.getBinder(); } class ServerHandler extends Handler { @Override public void handleMessage(Message msg) { Log.w(TAG, thread name: + Thread.currentThread().getName()); switch (msg.what) { case MSG_FROM_CLIENT_TO_SERVER: Log.w(TAG, receive msg from client); mClientMessenger = msg.replyTo; // service發送消息給client Message toClientMsg = Message.obtain(null, MSG_FROM_SERVER_TO_CLIENT); try { Log.w(TAG, server begin send msg to client); mClientMessenger.send(toClientMsg); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } @Override public boolean onUnbind(Intent intent) { Log.w(TAG, in onUnbind); return super.onUnbind(intent); } @Override public void onDestroy() { Log.w(TAG, in onDestroy); super.onDestroy(); } }
package com.lyz.service; /** * @author liuyazhuang * */ public class CActivity extends Activity { public static final String TAG = CActivity; private Button bindServiceBtn; private Button unbindServiceBtn; private Button sendMsgToServerBtn; private ServiceConnection sc = new MyServiceConnection(); private boolean mBound; private Messenger mServerMessenger; private Handler mClientHandler = new MyClientHandler(); private Messenger mClientMessenger = new Messenger(mClientHandler); private class MyClientHandler extends Handler { @Override public void handleMessage(Message msg) { if (msg.what == MyMessengerService.MSG_FROM_SERVER_TO_CLIENT) { Log.w(TAG, reveive msg from server); } } } private class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder binder) { Log.w(TAG, in MyServiceConnection onServiceConnected); mServerMessenger = new Messenger(binder); mBound = true; } @Override public void onServiceDisconnected(ComponentName name) { // This is called when the connection with the service has been // unexpectedly disconnected -- that is, its process crashed. Log.w(TAG, in MyServiceConnection onServiceDisconnected); mBound = false; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.c); bindServiceBtn = (Button) findViewById(R.id.bind_service); unbindServiceBtn = (Button) findViewById(R.id.unbind_service); sendMsgToServerBtn = (Button) findViewById(R.id.send_msg_to_server); bindServiceBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(CActivity.this, MyMessengerService.class); bindService(intent, sc, Context.BIND_AUTO_CREATE); } }); unbindServiceBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { excuteUnbindService(); } }); sendMsgToServerBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sayHello(); } }); new Handler().postDelayed(new Runnable() { @Override public void run() { Intent intent = new Intent(CActivity.this, MyAlarmBroadcastReceiver.class); sendBroadcast(intent); } }, 3 * 1000); } public void sayHello() { if (!mBound) return; // Create and send a message to the service, using a supported 'what' value Message msg = Message.obtain(null, MyMessengerService.MSG_FROM_CLIENT_TO_SERVER, 0, 0); // 通過replyTo把client端的Messenger(信使)傳遞給service msg.replyTo = mClientMessenger; try { mServerMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } private void excuteUnbindService() { if (mBound) { unbindService(sc); mBound = false; } } @Override protected void onDestroy() { super.onDestroy(); Log.w(TAG, in onDestroy); excuteUnbindService(); } }其中,需要注意的幾點是:
一般情況下,Messenger這種方式都是可以滿足需求的,當然,通過自定義AIDL方式相對更加靈活。
這種方式需要自己在項目中自定義xxx.aidl文件,然後系統會自動在gen目錄下生成相應的接口類文件,接下來整個的流程與Messenger方式差別不大,網上也有不少實例,在此不再具體給出。
注:無論哪種方式的Bound Service,在進行unbind(..)操作時,都需要注意當前Service是否處於已經綁定狀態,否則可能會因為當前Service已經解綁後繼續執行unbind(..)會導致崩潰。這點與Started Service區別很大(如前文所述:stopService(..)無需做當前Service是否有效的判斷)。
Local Service:不少人又稱之為”本地服務“,是指Client - Service同處於一個進程;
Remote Service:又稱之為”遠程服務“,一般是指Service處於單獨的一個進程中。
其他使用上上文中基本上都有所述。
1.Service本身都是運行在其所在進程的主線程(如果Service與Clinet同屬於一個進程,則是運行於UI線程),但Service一般都是需要進行”長期“操作,所以經常寫法是在自定義Service中處理”長期“操作時需要新建線程,以免阻塞UI線程或導致ANR;
2.Service一旦創建,需要停止時都需要顯示調用相應的方法(Started Service需要調用stopService(..)或Service本身調用stopSelf(..), Bound Service需要調用unbindService(..)),否則對於Started Service將處於一直運行狀態,對於Bound Service,當Client生命周期結束時也將因此問題。也就是說,Service執行完畢後,必須人為的去停止它。
IntentService是系統提供給我們的一個已經繼承自Service類的特殊類,IntentService特殊性是相對於Service本身的特性而言的:
1.默認直接實現了onBind(..)方法,直接返回null,並定義了抽象方法onHandlerIntent(..),用戶自定義子類時,需要實現此方法;
2.onHandlerIntent(..)主要就是用來處於相應的”長期“任務的,並且已經自動在新的線程中,用戶無語自定義新線程;
3.當”長期“任務執行完畢後(也就是onHandlerIntent(..)執行完畢後),此IntentService將自動結束,無需人為調用方法使其結束;
4.IntentService處於任務時,也是按照隊列的方式一個個去處理,而非真正意義上的多線程並發方式。
下面是一個基本的繼承自IntentService的自定義Service:
package com.lyz.service; /** * @author liuyazhuang * */ public class MyIntentService extends IntentService { public static final String TAG = MyIntentService; public MyIntentService() { super(TAG); } public MyIntentService(String name) { super(name); } @Override protected void onHandleIntent(Intent intent) { Log.w(TAG, in onHandleIntent); Log.w(TAG, thread name: + Thread.currentThread().getName()); } }
Android中Service接口中還提供了一個稱之為”前台Service“的概念。通過Service.startForeground (int id, Notification notification)方法可以將此Service設置為前台Service。在UI顯示上,notification將是一個處於onGoing狀態的通知,使得前台Service擁有更高的進程優先級,並且Service可以直接notification通信。
下面是一個簡單的前台Service使用實例:
package com.lyz.service; /** * @author liuyazhuang * */ public class MyService extends Service { public static final String TAG = MyService; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); Log.w(TAG, in onCreate); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.w(TAG, in onStartCommand); Log.w(TAG, MyService: + this); String name = intent.getStringExtra(name); Log.w(TAG, name: + name); Notification notification = new Notification(R.drawable.ic_launcher, test, System.currentTimeMillis()); Intent notificationIntent = new Intent(this, DActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntesnt, 0); notification.setLatestEventInfo(this, title, content, pendingIntent); startForeground(1, notification); return START_REDELIVER_INTENT; } @Override public void onDestroy() { super.onDestroy(); Log.w(TAG, in onDestroy); } }
先上運行效果圖首先我們要把一張自己喜歡的圖片放到sdcard中,總之,只要我們可以獲取這個圖片就可以了。我這裡是放在sdcard中的,可以在eclipse中用鼠標點擊導入
需求是在我按下按鈕時,該變按鈕顏色,使用戶感覺到自己按了按鈕,當松開的時候,變回原來的顏色。正常時:按下時:有人說,直接監聽按鈕的按下事件不得了嘛,其實這樣確實能實現同樣
這周繼續我的Blog,前面幾篇博文簡單的介紹了閱讀Android FW的源碼所需要的基礎知識,主要和C++相關。從這篇博文開始將會和大家一起學
手機qq可以截圖嗎?有時候想把與某人的聊天記錄截屏保存,或者想在qq浏覽器上保存重要的信息,該怎麼操作呢?下面就隨小編把這項技能gei起來!手機qq怎麼截圖