一个AIDL的安卓demo,建立一个服务端和一个客户端的通信

it2022-05-05  105

报告文档

任务描述

了解android service服务,使用AIDL进行交互;写一个简单的类似于sayhello的demo,做一个客户端和一个服务器端,两个apk,建立起通信,搞清楚通信流程;使用bundle作为数据传输,从activity传输信息到达service端;使用markdown写报告文档,并画出时序图,为期两周。

关于android service

什么是service

经过网上资料,以及书本资料的查阅,service与activity较为类似,就好比java中jsp与servlet的关系,作为android四大组件之一的后台服务,我对它的主要理解是:适合是去执行那些不需要和用户交互而且还要长期运行的任务,即使程序被切换到后台,或者用户打开了另一个应用程序,服务仍然能够保持独立运行。类似与activity,定义好的service必须在AndroidManifest.xml中进行注册。

service生命周期

service的主要生命周期方法有:onCreate(),onBind(),onStartCommand(),onDestroy()。

service的启动方式

service的启动方式有两种,一种是在没有绑定状态下直接使用startService(),另一种是通过调用 bindService() 与服务绑定,需要重写onBind()方法,并返回一个IBinder的实现类。

关于AIDL

AIDL的概述

AIDL 即 Android Interface Definition Language,就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,在android通信过程中真正起作用的并不是写好的AIDL文件,而是调用据此生成的接口实例模板。目的就是实现进程之间的通信,不同进程之间需要进行数据的传输,从而调用暴露出的方法来获取数据,满足了进程之间通信的需求。 在本次写的demo中,暴露方法的应用为服务端,客户端通过调用服务端暴露出来的方法来获取数据,交互的方式为绑定服务端service。

注意事项

AIDL支持八种基本数据类型,实现了Parcelable接口的数据类型,并且在使用AIDL时,需要明确导包。

服务端服务编码

传输的文本数据用自定义的MyString类来封装,方便后续使用Bundle包装以及AIDL文件调用,实现了Serializable与Parcelable接口,在MyString类中只有一个成员变量str,用来存入字符串数据。类具体定义如下:

public class MyString implements Parcelable, Serializable { private String str; public MyString(String str){ this.str=str; } public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeString(this.str); } public void readFromParcel(Parcel dest) { str = dest.readString(); } protected MyString(Parcel in) { this.str = in.readString(); } public static final Creator<MyString> CREATOR = new Creator<MyString>() { @Override public MyString createFromParcel(Parcel source) { return new MyString(source); } @Override public MyString[] newArray(int size) { return new MyString[size]; } }; }

具体的AIDL文件定义如下: IMyAidlInterface.aidl

package com.example.sayhello; import com.example.sayhello.MyString; parcelable MyString; // Declare any non-default types here with import statements interface IMyAidlInterface { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ String getString(); void setString(inout MyString mystring); }

定义了两个方法,其中getString()用来获取传输的数据,setString()是客户端用作写入数据,反馈服务端的方法。 定义的服务器MyService.class内容如下

public class MyService extends Service { private static final String TAG = "MyService"; private MyString myStrings=new MyString(""); public MyService(){ } @Override public IBinder onBind(Intent intent) { if(intent.getExtras()!=null){ Bundle bundle=intent.getExtras(); MyString myString= (MyString) bundle.getSerializable("MyBundle"); Log.e(TAG,"收到了Bundle传值:"+myString.getStr()); } return stub; } @Override public void onCreate() { super.onCreate(); Log.e(TAG,"服务启动"); myStrings.setStr("Hello!Client"); } @Override public void onDestroy() { super.onDestroy(); Log.e(TAG,"服务停止"); } private final IMyAidlInterface.Stub stub=new IMyAidlInterface.Stub() { @Override public String getString() throws RemoteException { Log.e(TAG,"发送了"+myStrings.getStr()); return myStrings.getStr(); } @Override public void setString(MyString mystring) throws RemoteException { myStrings.setStr(mystring.getStr()); Log.e(TAG,"接受了"+myStrings.getStr()); } }; }

其中, onBind 方法返回的就是 IMyAidlInterface.Stub 对象,实现当中定义的两个方法。为了可以让客户端进行绑定服务,通过先指定包名,之后再配置Action值或者直接指定Service类名的方式来绑定Service。 Service的声明改为如下所示:

<service android:name=".MyService" android:exported="true" android:enabled="true"> <intent-filter> <action android:name="com.example.sayhello.action" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>

客户端编码

把服务端的AIDL文件以及MyString类复制过来,将 aidl 文件夹整个复制到和Java文件夹同个层级下,不需要改动任何代码,创建和服务端MyString类所在的相同包名来存放 MyString类。 布局包含两个按钮和一个文本框,一个按钮用来接受服务器消息,一个按钮用来反馈服务器消息。 其中,客户端Client1.Class如下:

public class Client1 extends AppCompatActivity { private static final String TAG = "Client1"; private IMyAidlInterface iMyAidlInterface; private boolean connected; private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { iMyAidlInterface=IMyAidlInterface.Stub.asInterface(iBinder); connected = true; } @Override public void onServiceDisconnected(ComponentName componentName) { connected = false; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_client1); findViewById(R.id.str2).setOnClickListener(clickListener); findViewById(R.id.str3).setOnClickListener(clickListener); } private void bindService() { Intent intent = new Intent(); intent.setPackage("com.example.sayhello"); intent.setAction("com.example.sayhello.action"); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } private View.OnClickListener clickListener=new View.OnClickListener() { @Override public void onClick(View view) { if(view.getId()==R.id.str2){ bindService(); if(connected){ try { EditText editText = findViewById(R.id.editText); editText.setText(iMyAidlInterface.getString()); Log.e(TAG,"向服务器获取到数据:"+iMyAidlInterface.getString()); } catch (RemoteException e) { e.printStackTrace(); } } else { Toast.makeText(Client1.this,"服务未启动",Toast.LENGTH_LONG).show(); } } else if(view.getId()==R.id.str3){ try { Intent intent = new Intent(); EditText editText = findViewById(R.id.editText); MyString myString=new MyString(editText.getText().toString()); Bundle bundle=new Bundle(); bundle.putSerializable("MyBundle",myString); intent.putExtras(bundle); intent.setPackage("com.example.sayhello"); intent.setAction("com.example.sayhello.action"); iMyAidlInterface.setString(myString); unbindService(serviceConnection); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); Log.e(TAG,"向服务器发送数据:"+myString.getStr()); } catch (RemoteException e) { e.printStackTrace(); } } } }; }

获取按钮可以启动服务器,并且获取到服务器发送来的字符信息,并且显示在文本框,然后编辑文本框内容,选择发送按钮,将信息反馈到服务器中,这一步的字符串信息封装成MyString类型之后,选择用Bundle类通过Intent传输,在服务器中取出传递的Bundle的内容。 操作流程:打开服务端与客户端,点击客户端的接收消息按钮,再编辑文本框,点击发送消息按钮,服务器接受反馈,日志记录如下:

E/Client1: 向服务器获取到数据:Hello!Client E/Client1: 向服务器发送数据:Hello!Server E/MyService: 服务启动 E/MyService: 发送了Hello!Client E/MyService: 服务停止 E/MyService: 服务启动 E/MyService: 接受了Hello!Server E/MyService: 收到了Bundle传值:Hello!Server

最新回复(0)