1. 程式人生 > >Android bind service講解以及跨程序通訊

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;
        }
    };