項目介紹
- 創建時間:2019年10月4日16:30:09
- 實現功能:
Android
遠程服務的製作與測試運行,AIDL服務。 - 開發環境介紹:
Android API = 29
即Android 10
,開發IDE是Android Studio
吐槽
網上搜了N多文章,要麼年代久遠,要麼開發IDE不同操作不懂(小白搞不懂。。),本文以最詳細的步驟實現最簡的 AIDL 遠程服務的製作和調用。
實現步驟
-
概述
在
Android Studio
中創建了空的工程(其實後來沒用到,不過爲了配合源碼還是要說下),創建模塊rsserver
- 由於Android Studio
和IDEA
一個德行的,這裏只能叫它模塊了 - 英文名是module
嗎。本模塊用來製作一個服務可以在本模塊的activity
中調用,可以提供給其他app
或者模塊使用,再創建一個客戶端模塊rsclient
- 這裏的rs
表示remote service
。那麼最終形成的結構如下圖:
後面的步驟爲先製作服務端模塊中的服務並且測試通過後再製作客戶端模塊,讓我們開始吧。
-
製作
AIDL
接口文件,在服務端模塊的根節點上通過右鍵菜單創建一個AIDL
文件
將其命名爲IProcessInfo
,其中只要定義一個方法不用實現,全部代碼如下(創建完畢後會有一個默認的方法,將其刪除掉,然後追加一個我們自己的方法)// IProcessInfo.aidl package com.ccsoft.rsserver; // Declare any non-default types here with import statements interface IProcessInfo { int getProcessId(); }
保存後
AS
會自動創建同名的接口文件,之後創建服務的時候要用到
-
實現
AIDL
文件中定義的方法
在項目包com.ccsoft.rsserver
下創建包service
再在其下創建服務IProcessInfoImpl
繼承自IProcessInfo.Stub
,其全部代碼如下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; } }
最終實現的結構如下
-
接下來爲服務端創建一個
Activity
用來測試服務是否可用,先創建佈局文件activity_main
,其全部代碼如下:<?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 * @param service */ @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); } }
最終形成的結構如下
-
在模塊清單文件
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%,效果如下圖
-
創建客戶端模塊,然後將服務端模塊中的
AIDL
連帶其下的文件一起拷貝到客戶端模塊中,如下圖
-
拷貝過去後切記要使用
ctrl + F9
構建下客戶端模塊,這樣纔會生成接口文件,如下圖
-
客戶端模塊中創建
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>
形成的結構如下
-
創建
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"); } }; }
最終形成的結構如下
-
清單文件中註冊
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://gitee.com/chanchaw/rsdemo