史上最詳細建立 Android AIDL 遠程服務的步驟

項目介紹

  • 建立時間:2019年10月4日16:30:09
  • 實現功能:Android 遠程服務的製做與測試運行,AIDL服務。
  • 開發環境介紹:Android API = 29Android 10,開發IDE是 Android Studio

吐槽

網上搜了N多文章,要麼年代久遠,要麼開發IDE不一樣操做不懂(小白搞不懂。。),本文以最詳細的步驟實現最簡的 AIDL 遠程服務的製做和調用。java

實現步驟

  • 概述

    Android Studio 中建立了空的工程(其實後來沒用到,不過爲了配合源碼仍是要說下),建立模塊 rsserver - 因爲 Android StudioIDEA 一個德行的,這裏只能叫它模塊了 - 英文名是 module 嗎。本模塊用來製做一個服務能夠在本模塊的 activity 中調用,能夠提供給其餘 app 或者模塊使用,再建立一個客戶端模塊 rsclient - 這裏的 rs 表示 remote service 。那麼最終造成的結構以下圖:
    android

    後面的步驟爲先製做服務端模塊中的服務而且測試經過後再製做客戶端模塊,讓咱們開始吧。git

  • 製做 AIDL 接口文件,在服務端模塊的根節點上經過右鍵菜單建立一個AIDL 文件

    將其命名爲 IProcessInfo,其中只要定義一個方法不用實現,所有代碼以下(建立完畢後會有一個默認的方法,將其刪除掉,而後追加一個咱們本身的方法)github

    // IProcessInfo.aidl
    package com.ccsoft.rsserver;
    
    // Declare any non-default types here with import statements
    
    interface IProcessInfo {
        int getProcessId();
    }

    保存後 AS 會自動建立同名的接口文件,以後建立服務的時候要用到
    app

  • 實現 AIDL 文件中定義的方法
    在項目包 com.ccsoft.rsserver 下建立包 service 再在其下建立服務 IProcessInfoImpl 繼承自 IProcessInfo.Stub,其所有代碼以下ide

    package com.ccsoft.rsserver.service;
    
    import android.os.RemoteException;
    
    import com.ccsoft.rsserver.IProcessInfo;
    
    public class IProcessInfoImpl extends IProcessInfo.Stub {
        @Override
        public int getProcessId() throws RemoteException {
            return android.os.Process.myPid();
        }
    }

    調用本方法會打印進程ID,最終造成的結構以下圖
    佈局

  • 建立服務,用來返回實現接口的類的實例,其所有代碼以下:測試

    package com.ccsoft.rsserver.service;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.util.Log;
    
    import androidx.annotation.Nullable;
    
    public class MyRemoteService extends Service {
        private static final String TAG = "chanchaw";
        IProcessInfoImpl mProcessInfo = new IProcessInfoImpl();
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.e(TAG, "MyRemoteService thread id = " + Thread.currentThread().getId());
            return mProcessInfo;
        }
    }

    最終實現的結構以下
    this

  • 接下來爲服務端建立一個 Activity 用來測試服務是否可用,先建立佈局文件 activity_main,其所有代碼以下:spa

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="綁定本地服務"
        android:onClick="bindLocalService" />
    </LinearLayout>

    最終造成的結構以下圖

  • 建立一個 Activity 顯示該佈局而且測試服務,命名爲 MainActivity ,其所有代碼以下:

    package com.ccsoft.rsserver;
    
    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.View;
    
    import androidx.annotation.Nullable;
    
    import com.ccsoft.rsserver.service.MyRemoteService;
    
    public class MainActivity extends Activity {
        // 打印日誌時用來過濾,快速找到調試信息
        private static final String TAG = "chanchaw";
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 關聯到 res/layout/activity_main.xml,會顯示其中定義的控件
            setContentView(R.layout.activity_main);
        }
    
        // 建立 ServiceConnection 類型的對象實例,在後面綁定服務時會用到
        ServiceConnection myServiceConnection = new ServiceConnection() {
    
            /**
             * 服務綁定成功後會調用本方法
             * @param name
*/
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {

          Log.i(TAG, "MyRemoteService onServiceConnected");
          // 經過aidl取出數據
          IProcessInfo processInfo = IProcessInfo.Stub.asInterface(service);
          try {
              Log.i(TAG, "MyRemoteService process id = " + processInfo.getProcessId());
          } catch (RemoteException e) {
              e.printStackTrace();
          }
      }

      @Override
      public void onServiceDisconnected(ComponentName name) {
          Log.i(TAG, "MyRemoteService onServiceDisconnected");
      }
  };

  public void bindLocalService(View v){
      Intent intent = new Intent(this, MyRemoteService.class);
      bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);

  }

  @Override
  protected void onDestroy() {
      super.onDestroy();
      unbindService(myServiceConnection);
  }

}

最終造成的結構以下
![](https://user-images.githubusercontent.com/29369287/66197772-ac01e400-e6cd-11e9-84c5-815d5764feec.png)

- 在模塊清單文件 ``AndroidManifest.xml`` 中註冊``Activity`` 和服務,其所有代碼以下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.ccsoft.rsserver">

  <application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:supportsRtl="true"
      android:theme="@style/AppTheme" >

      <activity android:name=".MainActivity" android:label="MainActivity">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>

      <service android:name=".service.MyRemoteService" android:process=":remote">
          <intent-filter>
              <action android:name="com.jxx.server.service.bind" />
              <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
      </service>

  </application>

</manifest>

- 在服務端測試上面製做的服務,成功的話則完成了所有工做的70%,效果以下圖
![](https://user-images.githubusercontent.com/29369287/66197771-ab694d80-e6cd-11e9-98f6-e614ba7ec6b6.png)
- 建立客戶端模塊,而後將服務端模塊中的 ``AIDL`` 連帶其下的文件一塊兒拷貝到客戶端模塊中,以下圖
![](https://user-images.githubusercontent.com/29369287/66197770-ab694d80-e6cd-11e9-94a8-a1d001c2ff1c.png)
- 拷貝過去後切記要使用 ``ctrl + F9`` 構建下客戶端模塊,這樣纔會生成接口文件,以下圖
![](https://user-images.githubusercontent.com/29369287/66197768-ab694d80-e6cd-11e9-9880-fa361007d38e.png)

- 客戶端模塊中建立 ``activity`` 的佈局文件,其所有代碼以下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical" android:layout_width="match_parent"
  android:layout_height="match_parent">

  <TextView
      android:id="@+id/rsclient_textview"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="rsclient_textview" />

  <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="綁定遠程服務"
      android:onClick="bindRemoteService" />

</LinearLayout>

造成的結構以下
![](https://user-images.githubusercontent.com/29369287/66197766-aad0b700-e6cd-11e9-85aa-70bec1af872b.png)

- 建立 ``Activity``,所有代碼以下

package com.ccsoft.rsclient;

import android.app.Activity;
import android.content.ComponentName;
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.View;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.ccsoft.rsserver.IProcessInfo;

public class MainActivity extends Activity {

private static final String TAG = "chanchaw";

  TextView text = null;
  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      text = findViewById(R.id.rsclient_textview);
  }

  // 綁定遠程服務
  public void bindRemoteService(View v){
      Intent intent = new Intent();
      intent.setAction("com.jxx.server.service.bind");//Service的action
      intent.setPackage("com.ccsoft.rsserver");//App A的包名
      bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
  }


  ServiceConnection mServerServiceConnection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {

          Log.i(TAG, "MyRemoteService onServiceConnected");
          // 經過aidl取出數據
          IProcessInfo processInfo = IProcessInfo.Stub.asInterface(service);
          try {
              Log.i(TAG, "MyRemoteService process id = " + processInfo.getProcessId());
              text.setText("綁定成功!");
          } catch (RemoteException e) {
              e.printStackTrace();
          }
      }

      @Override
      public void onServiceDisconnected(ComponentName name) {
          Log.i(TAG, "MyRemoteService onServiceDisconnected");
      }
  };

}

最終造成的結構以下
![](https://user-images.githubusercontent.com/29369287/66197765-aad0b700-e6cd-11e9-938e-e22917399fab.png)

- 清單文件中註冊 ``Activity``,所有代碼以下

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

package="com.ccsoft.rsclient">

  <application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:supportsRtl="true"
      android:theme="@style/AppTheme" >

      <activity android:name=".MainActivity" android:label="MainActivity">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
  </application>

</manifest>

這裏不用註冊遠程的服務,由於是遠程調用的

- 在客戶端模塊中測試調用遠程服務
![](https://user-images.githubusercontent.com/29369287/66197763-aad0b700-e6cd-11e9-9880-4a4d1a5c2c61.png)

#### 結束語
相關文章
相關標籤/搜索