簡單音樂播放實例的實現,Android Service AIDL 遠程調用服務

Android Service是分爲兩種: html

  • 本地服務(Local Service): 同一個apk內被調用
  • 遠程服務(Remote Service):被另外一個apk調用

遠程服務須要藉助AIDL來完成。 java

AIDL 是什麼

AIDL (Android Interface Definition Language) 是一種IDL 語言,用於生成能夠在Android設備上兩個進程之間進行進程間通訊(interprocess communication, IPC)的代碼。若是在一個進程中(例如Activity)要調用另外一個進程中(例如Service)對象的操做,就可使用AIDL生成可序列化的參數。 android

AIDL IPC機制是面向接口的,像COM或Corba同樣,可是更加輕量級。它是使用代理類在客戶端和實現端傳遞數據。 多線程

AIDL 的做用

因爲每一個應用程序都運行在本身的進程空間,而且能夠從應用程序UI運行另外一個服務進程,並且常常會在不一樣的進程間傳遞對象。在Android平臺,一個進程一般不能訪問另外一個進程的內存空間,因此要想對話,須要將對象分解成操做系統能夠理解的基本單元,而且有序的經過進程邊界。 併發

  經過代碼來實現這個數據傳輸過程是冗長乏味的,Android提供了AIDL工具來處理這項工做。 app

選擇AIDL的使用場合

官方文檔特別提醒咱們什麼時候使用AIDL是必要的:只有你容許客戶端從不一樣的應用程序爲了進程間的通訊而去訪問你的service,以及想在你的service處理多線程。 eclipse

若是不須要進行不一樣應用程序間的併發通訊(IPC),you should create your interface by implementing a Binder;或者你想進行IPC,但不須要處理多線程的,則implement your interface using a Messenger。不管如何,在使用AIDL前,必需要理解如何綁定service——bindService。 ide

下面用一個客戶端Activity操做服務端Service播放音樂的實例演示AIDL的使用。 工具

開發工具: eclipse 3.7(indigo)+ android sdk 4.1+ adt 20.0.2 oop

服務端代碼結構

服務端代碼結構

客戶端代碼結構

客戶端代碼結構

被標記的就是須要動手的。

服務端

新建一個android application project,命名爲PlayerServer。 在res下的raw文件夾裏面放入一個音樂文件,我這裏放入的是Delta Goodrem的《Lost Without You》片斷。若是不存在raw這個文件夾就本身新建一個,命名爲raw。這個文件夾在raw文件夾下,與layout文件夾平級。raw中的文件遵照標識符的命名規則,不要出現中文和空格,多個單詞能夠用下劃線鏈接。

新建一個IRemoteServiice.aidl 文件,加入以下代碼:

package pandafang.demo.playerserver;
  interface IRemoteService {
      void play();
      void stop();
  }

可見aidl文件的代碼跟java的interface同樣,可是aidl中不能加public等修飾符。Ctrl + S 保存後 ADT 會根據這個IRemoteService.aidl文件自動生成IRemoteService.java文件。如同R.java文件同樣在「gen/包名」下,代碼是自動生成的,不要手動修改。

接下來就是bound service(參考:官方文檔)的知識了。IRemoteService.java 中有一個Stub靜態抽象類extends Binder implements IRemoteService。本身動手寫一個PlayerService 用來播放音樂,播放音樂須要使用android.media.MediaPlayer類。代碼以下:

package pandafang.demo.playerserver;
  
  import java.io.FileDescriptor;
  import java.io.IOException;
  import android.app.Service;
  import android.content.Intent;
  import android.media.MediaPlayer;
  import android.os.IBinder;
  import android.os.RemoteException;
  import android.util.Log;
  
  /**
   * 播放音樂的服務
   * @author Panda Fang
   * @date 2012-10-22 10:15:33
   */
  public class PlayerService extends Service {
      
      public static final String TAG = "PlayerService";
      
      private MediaPlayer mplayer;
      
      // 實現aidl文件中定義的接口
      private IBinder mBinder = new IRemoteService.Stub() {
          
          @Override
          public void stop() throws RemoteException {
              try {
                  if (mplayer.isPlaying()) {
                      mplayer.stop();
                  }
              } catch (Exception e) {
                  // TODO: handle exception
                  e.printStackTrace();
              }
          }
          
          @Override
          public void play() throws RemoteException {
              try {
                  if (mplayer.isPlaying()) {
                      return;
                  }
                  // start以前須要prepare。
                  // 若是前面實例化mplayer時使用方法一,則第一次play的時候直接start,不用prepare。
                  // 可是stop一次以後,再次play就須要在start以前prepare了。
                  // 前面使用方法二 這裏就簡便了, 不用判斷各類情況
                  mplayer.prepare();
                  mplayer.start();
              } catch (Exception e) {
                  // TODO: handle exception
                  e.printStackTrace();
              }
          }
      };
  
      @Override
      public IBinder onBind(Intent intent) {
          Log.i(TAG,"service onbind");
          if(mplayer==null){
              // 方法一說明
              // 此方法實例化播放器的同時指定音樂數據源 ,若用此方法在,mplayer.start() 以前不需再調用mplayer.prepare() 
              // 官方文檔有說明 :On success, prepare() will already have been called and must not be called again.
              // 譯文:一旦create成功,prepare已被調用,勿再調用 。查看源代碼可知create方法內部已經調用prepare方法。
              // 方法一開始
              // mplayer = MediaPlayer.create(this, R.raw.lost);
              // 方法一結束
              
              // 方法二說明
              // 若用此方法,在mplayer.start() 以前須要調用mplayer.prepare() 
              // 方法二開始
              mplayer = new MediaPlayer();
              try {
                  FileDescriptor fd = getResources().openRawResourceFd(R.raw.lost).getFileDescriptor(); // 獲取音樂數據源
                  mplayer.setDataSource(fd); // 設置數據源
                  mplayer.setLooping(true); // 設爲循環播放
              } catch (IOException e) {
                  // TODO Auto-generated catch block
                  e.printStackTrace();
              }
              // 方法二結束
              Log.i(TAG,"player created");
          }
          return mBinder;
      }
      
      @Override
      public boolean onUnbind(Intent intent) {
          if (mplayer != null) {
              mplayer.release();
          }
          Log.i(TAG,"service onUnbind");
          return super.onUnbind(intent);
      }
  }

服務編寫好之後,按照慣例在AndroidManifest.xml中加入聲明,代碼以下:

 

須要加入的只是...那段,要注意的是 android:process=":remote" 和 intent-filter 。

運行服務端到設備上,準備給客戶端調用。

客戶端

新建一個android application project,命名爲PlayerClient。將服務端放有aidl文件的包直接copy到客戶端src目錄下,保留包中的aidl文件,其餘刪除。

編輯 layout 下的 activity_main.xml 佈局文件,加入兩個按鈕,代碼以下:

 

編寫MainActivity.java 代碼以下:

package pandafang.demo.playerclient;
  
  import pandafang.demo.playerserver.IRemoteService;
  import android.app.Activity;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.os.Bundle;
  import android.os.IBinder;
  import android.os.RemoteException;
  import android.util.Log;
  import android.view.Menu;
  import android.view.View;
  import android.view.View.OnClickListener;
  import android.widget.Button;
  
  /**
   * 客戶端控制界面
   * @author Panda Fang
   * @date 2012-10-22 10:36:44
   */
  public class MainActivity extends Activity {
      
      public static final String TAG = "MainActivity";
      
      // 服務端 AndroidManifest.xml中的intent-filter action聲明的字符串
      public static final String ACTION = "com.example.playerserver.PlayerService";
      
      private Button playbtn, stopbtn;
      
      private IRemoteService mService;
      
      private boolean isBinded = false;
      
      private ServiceConnection conn = new ServiceConnection() {
          
          @Override
          public void onServiceDisconnected(ComponentName name) {
              isBinded = false;
              mService = null;
          }
          
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
              mService = IRemoteService.Stub.asInterface(service);
              isBinded = true;
          }
      };
  
      @Override
      public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          
          doBind();
          initViews();
      }
      
      private void initViews() {
          playbtn = (Button) findViewById(R.id.button1);
          stopbtn = (Button) findViewById(R.id.button2);
          playbtn.setOnClickListener(clickListener);
          stopbtn.setOnClickListener(clickListener);
      }
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          getMenuInflater().inflate(R.menu.activity_main, menu);
          return true;
      }
      
      @Override
      protected void onDestroy() {
          doUnbind();
          super.onDestroy();
      }
      
      public void doBind() {
          Intent intent = new Intent(ACTION);
          bindService(intent, conn, Context.BIND_AUTO_CREATE);
      }
      
      public void doUnbind() {
          if (isBinded) {
              unbindService(conn);
              mService = null;
              isBinded = false;
          }
          
      }
      private OnClickListener clickListener = new OnClickListener() {
          
          @Override
          public void onClick(View v) {
              if (v.getId() == playbtn.getId()) {
                  // play
                  Log.i(TAG,"play button clicked");
                  try {
                      mService.play();
                  } catch (RemoteException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  }
              } else {
                  // stop
                  Log.i(TAG,"stop button clicked");
                  try {
                      mService.stop();
                  } catch (RemoteException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  }
              }
          }
      };
  
  }

MainActivity是根據嚮導自動生成的,不須要在AndroidManifest.xml中註冊聲明瞭。

運行客戶端到設備,按下按鈕能夠播放/中止 效果如圖:

Android Service AIDL 遠程調用服務 - 簡單音樂播放實例

源代碼下載:http://files.cnblogs.com/lonkiss/AIDLPlayer.zip

更多詳細請參考:http://www.baisoujs.com/detail_137434192972690.html

相關文章
相關標籤/搜索