项目介绍
- 创建时间: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