Android bind service講解以及跨程序通訊
**
Android bind service講解以及Messenger跨程序通訊
**
android service是執行在後臺的程式,說白了,就是沒有介面,這裡我想強調的一點是,執行在後臺不等於執行在非主執行緒,除了IntentService外,普通的service如果你沒有開啟新的執行緒,那麼預設是執行在主執行緒中的。
service有兩種啟動方式,一個是bind,一個是start,兩種啟動方式,有挺多區別。需要注意的是,bind繫結service的時候,直到最後一個bind到service的程式呼叫unbind,否則service一直會執行。而對於startservice這種啟動方式來說,一旦啟動,需要自己stopService或者呼叫service內部的stopSelf,否則該service是不會關閉的。
還需要注意的是service的宣告週期,這個附張圖就全明白了。
需要注意的是,onCreate方法只在你啟動service的時候呼叫一次,之後再啟動service的時候就直接走到onStartCommand()或者onBind()裡了。
好了,廢話不多說了,上個demo吧:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private boolean hasBound;
private Button intent_Service;
private Button start_Service;
private Button bind_Service;
private Button messenger_Service;
//下面的handler和Messenger使用來進行跨程序通訊的
private Handler handler=new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==322)
{
Toast.makeText(getApplicationContext(),"receive message from server" ,Toast.LENGTH_SHORT).show();
}
}
};
private Messenger clientMessenger=new Messenger(handler);
private Messenger serverMessenger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView()
{
intent_Service= (Button) findViewById(R.id.intent_service);
intent_Service.setOnClickListener(this);
start_Service= (Button) findViewById(R.id.start_service);
start_Service.setOnClickListener(this);
bind_Service=(Button)findViewById(R.id.bind_service);
bind_Service.setOnClickListener(this);
messenger_Service=(Button)findViewById(R.id.messenger_service);
messenger_Service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch(v.getId())
{
case R.id.intent_service:
//啟動IntentService
Intent intentService=new Intent(this,IntentTestService.class);
startService(intentService);
break;
case R.id.start_service:
//start呼叫普通Sservice
Intent startService=new Intent(this,NormalService.class);
startService(startService);
break;
case R.id.bind_service:
//bind呼叫service
Intent bindService=new Intent(this,NormalService.class);
if(bindService.resolveActivity(getPackageManager())!=null)
bindService(bindService,connection,BIND_AUTO_CREATE);
break;
//利用Messenger進行跨程序通訊
case R.id.messenger_service:
if(!hasBound) {
Intent intent = new Intent("com.skateboard.serverservice.service.BIND");
intent.setPackage("com.skateboard.serverservice");
bindService(intent, messengerConnection, Context.BIND_AUTO_CREATE);
}
else
{
sendMessageToServier();
}
break;
}
}
private ServiceConnection messengerConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
hasBound=true;
serverMessenger=new Messenger(service);
Message message=new Message();
message.what=233;
message.replyTo=clientMessenger;
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
hasBound=false;
}
};
private ServiceConnection connection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
NormalService.NormalBinder binder= (NormalService.NormalBinder) service;
binder.bindMethod();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private void sendMessageToServier()
{
Message message=new Message();
message.what=233;
message.replyTo=clientMessenger;
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
IntentTestService繼承子IntentService,它的功能很簡單,就是列印一行話 “intent service start”。比較特別的就是就如如上所說的,這個service是執行在非主執行緒的。
public class IntentTestService extends IntentService {
public IntentTestService()
{
super("IntentTestService");
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentTestService(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
System.out.println("intent service start");
}
}
NormalService可以接受startService()的方式啟動同時也可以接受bindService的方式啟動,這裡主要講講bindService的啟動方式,當我們bind到一個service的時候,回撥用的onBind方法,這時會返回一個IBinder類,這個類個人覺得很想代理模式,通過它來呼叫service中的方法,在我們bindservice的時候,需要傳入一個引數,ServiceConnection,在這個物件裡面有兩個回撥方法,一個是ublic void onServiceConnected(ComponentName name, IBinder service)一個是public void onServiceDisconnected(ComponentName name),在onServiceConnected中的引數service就是我們在onBind方法中返回的IBinder,通過對它的轉型,我們就可以呼叫相應的service中的方法了。所以這裡我寫了一個內部類NormalBinder,用它來列印“”bind method”並在onBind方法中返回他,這樣我在MainActivity中就可以得到這個NormalBinder,並呼叫它內部的方法。
public class NormalService extends Service {
public NormalService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("service start");
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
NormalBinder normalBinder=new NormalBinder();
return normalBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("stop service");
}
public class NormalBinder extends Binder
{
public void bindMethod()
{
System.out.println("bind method");
}
}
}
對於這個流程,我本想畫個示意圖,但是太懶了,我決定還是文字吧。
跨程序通訊的方式有兩種,一種是AIDL,一種就是利用Messenger了,這兩種方式的區別就在魚AIDL是多執行緒的,而Messenger是單執行緒的,也就是說利用Messenger的跨程序通訊在訊息佇列中每次只有一個請求。需要注意的是如果你需要伺服器回傳資料給客戶端,你需要在handler的public void handleMessage(Message msg)方法中得到客戶端的Messenger,這個messenger就是Messenger clientMessenger=msg.replyTo;這是在客戶端在向服務端傳送Message的時候傳遞給message的引數。
public class ServerService extends Service {
private Handler handler=new Handler()
{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==233)
{
Messenger clientMessenger=msg.replyTo;
Message message=new Message();
message.what=322;
try {
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
};
private Messenger messenger=new Messenger(handler);
public ServerService()
{
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("service create");
}
@Override
public IBinder onBind(Intent intent) {
System.out.println("bind service");
return messenger.getBinder();
}
}
接著MainAcitivyt發起bindService的請求,(這裡要注意的是,5.0以後,開啟service的Intent必需是顯示的Intent,所以你的Intent裡必須要包含另一個程式的包名的資訊。)在ServiceConnection中的onServiceConnected的方法裡,通過返回的IBinder來得到相應的Messenger
private ServiceConnection messengerConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
hasBound=true;
serverMessenger=new Messenger(service);
Message message=new Message();
message.what=233;
message.replyTo=clientMessenger;
try {
serverMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
hasBound=false;
}
};