Android-遠程Service

http://blog.csdn.net/guolin_blog/article/details/9797169java

http://www.jianshu.com/p/eeb2bd59853fandroid

 

將一個普通的Service轉換成遠程Service其實很是簡單,只須要在註冊Service的時候將它的android:process屬性指定成:remote就能夠了,代碼以下所示:app

<service android:name=".AIDLService"
        android:process=":remote"></service>

遠程service可讓service在另外一個進程運行,因此能夠執行阻塞進程的操做ide

 

遠程Service這麼好用,乾脆之後咱們把全部的Service都轉換成遠程Service吧,還免得再開啓線程了。其實否則,遠程Service非但很差用,甚至能夠稱得上是較爲難用。通常狀況下若是能夠不使用遠程Service,就儘可能不要使用它。ui

 

下面就來看一下它的弊端吧,首先將MyService的onCreate()方法中讓線程睡眠的代碼去除掉,而後從新運行程序,並點擊一下Bind Service按鈕,你會發現程序崩潰了!爲何點擊Start Service按鈕程序就不會崩潰,而點擊Bind Service按鈕就會崩潰呢?這是因爲在Bind Service按鈕的點擊事件裏面咱們會讓MainActivity和MyService創建關聯,可是目前MyService已是一個遠程Service了,Activity和Service運行在兩個不一樣的進程當中,這時就不能再使用傳統的創建關聯的方式,程序也就崩潰了。this

 

那麼如何才能讓Activity與一個遠程Service創建關聯呢?這就要使用AIDL來進行跨進程通訊了(IPC)。spa

 

調用者和Service若是不在一個進程內, 就須要使用android中的遠程Service調用機制.
android使用AIDL定義進程間的通訊接口. AIDL的語法與java接口相似, 須要注意如下幾點:.net

    1. AIDL文件必須以.aidl做爲後綴名.
    2. AIDL接口中用到的數據類型, 除了基本類型, String, List, Map, CharSequence以外, 其餘類型都須要導包, 即便兩種在同一個包內. List和Map中的元素類型必須是AIDL支持的類型.
    3. 接口名須要和文件名相同.
    4. 方法的參數或返回值是自定義類型時, 該自定義的類型必須實現了Parcelable接口.
    5. 全部非java基本類型參數都須要加上in, out, inout標記, 以代表參數是輸入參數, 輸出參數, 仍是輸入輸出參數.
    6. 接口和方法前不能使用訪問修飾符和static, final等修飾.

AIDL實現
1.首先我創建2個app工程,經過aidl實現一個app調用另外一個app的service
目錄結構以下:
service提供端app線程

利用aidl調用service的appcode

2.在兩個app中都創建一個文件 IPerson.aidl注意 包名 要相同
IPerson.aidl只是一個接口文件,用來aidl交互的,創建好以後在Studio中點Build-->Rebuild會自動建立須要的java文件。

IPerson.aidl代碼

package mangues.com.aidl;
interface IPerson {
  String greet(String someone);
}

3.在aidl_service 中創建AIDLService
這個IPerson.Stub 就是經過IPerson.aidl 自動生成的binder 文件,你實現下,而後在onBind()中 return出去就行了,就和Android Service實現和activity交互同樣。
代碼:

public class AIDLService extends Service {
  private static final String TAG = "AIDLService";

  IPerson.Stub stub = new IPerson.Stub() {
      @Override
      public String greet(String someone) throws RemoteException {
          Log.i(TAG, "greet() called");
          return "hello, " + someone;
      }
  };

  @Override
  public void onCreate() {
      super.onCreate();
      Log.i(TAG, "onCreate() called");
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Log.i(TAG, "onBind() onStartCommand");
      return super.onStartCommand(intent, flags, startId);

  }

  @Override
  public IBinder onBind(Intent intent) {
      Log.i(TAG, "onBind() called");
      return stub;
  }

  @Override
  public boolean onUnbind(Intent intent) {
      Log.i(TAG, "onUnbind() called");
      return true;
  }

  @Override
  public void onDestroy() {
      super.onDestroy();
      Log.i(TAG, "onDestroy() called");
  }
}

這裏爲何能夠這樣寫呢?由於Stub其實就是Binder的子類,因此在onBind()方法中能夠直接返回Stub的實現。

4.aidl_service MainActivity 中啓動這個service
簡單點就不寫關閉什麼的了;

@Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      Intent startIntent = new Intent(this, AIDLService.class);
      startService(startIntent);
  }

在AndroidManifest.xml註冊

<service android:name=".AIDLService"
               android:process=":remote">
          <intent-filter>
              <action android:name="android.intent.action.AIDLService" />
              <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
      </service>

做用就是把這個service暴露出去,讓別的APP能夠利用
android.intent.action.AIDLService 字段隱形綁定這個service,獲取數據。

5.aidl_client 中綁定aidl_service service 獲取數據
代碼:

public class MainActivity extends AppCompatActivity {
    private IPerson person;
    private ServiceConnection conn = new ServiceConnection() {
       @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
          Log.i("ServiceConnection", "onServiceConnected() called");
          person = IPerson.Stub.asInterface(service);
          String retVal = null;
          try {
              retVal = person.greet("scott");
          } catch (RemoteException e) {
              e.printStackTrace();
          }
          Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show();
      }

      @Override
      public void onServiceDisconnected(ComponentName name) {
          //This is called when the connection with the service has been unexpectedly disconnected,
          //that is, its process crashed. Because it is running in our same process, we should never see this happen.
          Log.i("ServiceConnection", "onServiceDisconnected() called");
      }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      Intent mIntent = new Intent();
      mIntent.setAction("android.intent.action.AIDLService");
      Intent eintent = new Intent(getExplicitIntent(this,mIntent));
      bindService(eintent, conn, Context.BIND_AUTO_CREATE);
  }
  public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
      // Retrieve all services that can match the given intent
      PackageManager pm = context.getPackageManager();
      List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
      // Make sure only one match was found
      if (resolveInfo == null || resolveInfo.size() != 1) {
          return null;
      }
      // Get component info and create ComponentName
      ResolveInfo serviceInfo = resolveInfo.get(0);
      String packageName = serviceInfo.serviceInfo.packageName;
      String className = serviceInfo.serviceInfo.name;
      ComponentName component = new ComponentName(packageName, className);
      // Create a new intent. Use the old one for extras and such reuse
      Intent explicitIntent = new Intent(implicitIntent);
      // Set the component to be explicit
      explicitIntent.setComponent(component);
      return explicitIntent;
  }
}

 

在上一篇文章中咱們已經知道,若是想要讓Activity與Service之間創建關聯,須要調用bindService()方法,並將Intent做爲參數傳遞進去,在Intent裏指定好要綁定的Service,示例代碼以下:

Intent bindIntent = new Intent(this, MyService.class);  
bindService(bindIntent, connection, BIND_AUTO_CREATE);

這裏在構建Intent的時候是使用MyService.class來指定要綁定哪個Service的,可是在另外一個應用程序中去綁定Service的時候並無MyService這個類,這時就必須使用到隱式Intent了

 <intent-filter>  
            <action android:name="com.example.servicetest.MyAIDLService"/>  
        </intent-filter>

這就說明,MyService能夠響應帶有com.example.servicetest.MyAIDLService這個action的Intent。

相關文章
相關標籤/搜索