AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似。 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。 在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。
反正我的理解就是用来跨进程访问的接口,AIDL文件创建后会生成.java的。
这次测试的是一个相当于玩游戏充值的模式,比如一个小游戏要充值,点击充值的时候会跳转到支付宝的支付界面,然后再输入密码支付,接着回到游戏中,学之前以为只是搞了个类似支付宝的界面,原来是需要和支付宝的程序进行连接,支付宝接收到充值信息然后打包SDK发送到这个游戏的服务器,服务器里的数据库的数据改变,然后更新到游戏中,就充值成功,但我们测试只是两个程序之间,充值的程序点击充值按钮跳转到模拟的支付宝程序的支付界面,然后充值后结束支付宝程序,回到充值的程序更新充值的内容。
首先开始写类似支付宝支付界面的程序,
刚开始写AIDL文件
创建的方式就是跟创建活动差不多,在活动上面
这里的两个AIDL文件的作用,两个程序交互时使用,比如第一个就是支付时的信息和金额数量(这只是模仿,一小部分而已参数),然后回调第二个AIDL文件,第二个文件里面的方法是判断支付成功与否。
再写支付的服务:
package com.example.zhifubao;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.Nullable;
import static android.content.ContentValues.TAG;
public class PayService extends Service {
private ThirdPartPayImpl thirdPartPay;
@Nullable
@Override
public IBinder onBind(Intent intent) {
String action = intent.getAction();
Log.d(TAG, "onBind --> action -->" + action);
if (action != null && "com.example.zhifubao.THIRD_PART_PAY".equals(action)) {
// 第三方软件要求支付宝进行支付
thirdPartPay = new ThirdPartPayImpl();
return thirdPartPay;
}
return new PlayAction();
}
public class PlayAction extends Binder{
public void Play(float payMoney){
Log.d(TAG,"Pay money is -->"+payMoney);
// 实际的支付还需要加密,比如向服务器发送请求,等待服务器的结果。
if (thirdPartPay!=null) {
thirdPartPay.paySuccess();
}
}
public void onUserCancel(){
if (thirdPartPay!=null) {
thirdPartPay.payFailed(1,"user cancel pay.");
}
}
}
private class ThirdPartPayImpl extends ThirdPartPay.Stub {
private ThirdPartPayResult mCallback;
@Override
public void requestPay(String orderInfo, float payMoney, ThirdPartPayResult callback) {
this.mCallback=callback;
// 第三方应用发起请求打开一个支付的界面
Intent intent =new Intent(PayService.this,PayActivity.class);
intent.putExtra(Const.KEY_BILL_INFO,orderInfo);
intent.putExtra(Const.KEY_PAY_MONEY,payMoney);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
public void paySuccess(){
if (mCallback!=null) {
try {
mCallback.onPaySuccess();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
public void payFailed(int errorCode,String errorMsg){
if (mCallback!=null) {
try {
mCallback.onPayFailed(errorCode, errorMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
在这个支付服务里面的Const是常量集,所以还得创建一个常量类Const
package com.example.zhifubao;
public class Const {
public static final String KEY_BILL_INFO="key_bill_info";
public static final String KEY_PAY_MONEY="key_pay_money";
}
接着给它在AndroidManifest.xml里注册服务并给他action-name,
<service
android:name=".PayService"
android:exported="true">
<intent-filter>
<action android:name="com.example.zhifubao.THIRD_PART_PAY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
然后我们就创建一个活动来显示支付界面并绑定服务
package com.example.zhifubao;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class PayActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG =PayActivity.class.getName();
private boolean isBind;
private TextView order_info_tv;
private TextView pay_money;
private EditText pay_password_input;
private Button pay_commit;
private PayService.PlayAction playAction;
private float floatExtra;
private String stringExtra;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pay);
// 因为Activity也要和服务进行通讯,告诉服务支付结果,所以要绑定服务
doService();
initView();
}
@Override
public void onBackPressed() {
super.onBackPressed();
if (playAction!=null) {
playAction.onUserCancel();
}
}
private void initView() {
Intent intent = getIntent();
stringExtra = intent.getStringExtra(Const.KEY_BILL_INFO);
floatExtra = intent.getFloatExtra(Const.KEY_PAY_MONEY, 0f);
order_info_tv = (TextView) findViewById(R.id.order_info_tv);
order_info_tv.setText("支付信息:"+ stringExtra);
order_info_tv.setOnClickListener(this);
pay_money = (TextView) findViewById(R.id.pay_money);
pay_money.setText("支付金额:"+ floatExtra);
pay_money.setOnClickListener(this);
pay_password_input = (EditText) findViewById(R.id.pay_password_input);
pay_password_input.setOnClickListener(this);
pay_commit = (Button) findViewById(R.id.pay_commit);
pay_commit.setOnClickListener(this);
}
private void doService() {
Intent intent = new Intent(this, PayService.class);
isBind = bindService(intent, MConnection, BIND_AUTO_CREATE);
}
private ServiceConnection MConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
playAction = (PayService.PlayAction) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if (isBind && MConnection != null) {
unbindService(MConnection);
MConnection = null;
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.pay_commit:
// 提交点击了
Toast.makeText(this, "点击提交", Toast.LENGTH_SHORT).show();
String trim = pay_password_input.getText().toString().trim();
if ("123456".equals(trim)&&playAction!=null) {
Log.d(TAG,"pay finished...");
Toast.makeText(this, "支付成功", Toast.LENGTH_SHORT).show();
playAction.Play(floatExtra);
finish();
}
else {
Toast.makeText(this, "密码错误", Toast.LENGTH_SHORT).show();
}
break;
}
}
private void submit() {
// validate
String input = pay_password_input.getText().toString().trim();
if (TextUtils.isEmpty(input)) {
Toast.makeText(this, "请输入支付密码", Toast.LENGTH_SHORT).show();
return;
}
// TODO validate success, do something
}
}
然后布局就比较简洁了
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="15dp"
tools:context=".PayActivity">
<TextView
android:id="@+id/order_info_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="账单:充值6480点券"
android:textSize="22sp" />
<TextView
android:id="@+id/pay_money"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="支付金额:648元"
android:textSize="20sp" />
<EditText
android:id="@+id/pay_password_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入支付密码"
android:inputType="numberPassword" />
<Button
android:id="@+id/pay_commit"
android:text="支付"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
然后调试下,没问题就可以了,
开始下一个程序
在下一个程序中,我们做一个充值648元的操作,但是比例是1:10,充值648获得6480点券
这里最关键的就是那个AIDL的转接,我用死方法的,首先AIDL要进行跨程序操作:主程序(游戏)需要调用支付宝(充值界面)充值,那么在支付宝(充值界面)写的AIDL就得原封不动(文件名,文件内部包名,都得是支付宝里的)
下面可以看到,这个程序是Pokemon,调用的AIDL文件夹里的文件夹名还是属于zhifubao里的,
可以看出,文件里的包名还是zhifubao里的,这个需要注意,
然后给他Make Project一下
这样就能让它产生.java的文件
然后在主活动写绑定服务,写布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:gravity="right"
android:layout_gravity="right"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_marginRight="20dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点劵: "
android:textSize="20dp"
android:textStyle="bold" />
<TextView
android:id="@+id/money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="120"
android:textSize="20dp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/sq1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/white"
android:text="充值6480点券"
android:textSize="30dp"
android:textStyle="bold" />
<Button
android:id="@+id/bt"
android:layout_width="205dp"
android:layout_height="184dp"
android:background="@drawable/sq6"
android:text="648元"
android:textColor="@android:color/white"
android:textSize="22dp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
一些图片之类的是我导入的
然后这个雷电狮这个是一个按钮,背景改成雷电狮了
点击它就进行充值交互
活动里的就是绑定服务,调用AIDL的接口和方法,
package com.example.pokemon;
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.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.zhifubao.ThirdPartPay;
import com.example.zhifubao.ThirdPartPayResult;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = MainActivity.class.getName();
private TextView tv;
private Button bt;
private TextView tv1;
private TextView money;
private TextView tv2;
private AliPayConnection aliPayConnection;
private boolean isbind;
private ThirdPartPay thirdPartPay;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 绑定 支付宝的服务,在现实开发中,绑定服务是由支付宝的SDK完成的
bindAliService();
//找到控件
initView();
}
private void bindAliService() {
Intent intent = new Intent();
intent.setAction("com.example.zhifubao.THIRD_PART_PAY");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setPackage("com.example.zhifubao");
aliPayConnection = new AliPayConnection();
isbind = bindService(intent, aliPayConnection, BIND_AUTO_CREATE);
}
private class AliPayConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "连接服务。。。");
thirdPartPay = ThirdPartPay.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "未连接服务。。。");
}
}
private void initView() {
bt = (Button) findViewById(R.id.bt);
bt.setOnClickListener(this);
tv1 = (TextView) findViewById(R.id.tv1);
tv1.setOnClickListener(this);
money = (TextView) findViewById(R.id.money);
money.setOnClickListener(this);
tv2 = (TextView) findViewById(R.id.tv2);
tv2.setOnClickListener(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (isbind && aliPayConnection != null) {
unbindService(aliPayConnection);
Log.d(TAG, "解绑服务。。");
aliPayConnection = null;
}
}
private class PayCallback extends ThirdPartPayResult.Stub {
@Override
public void onPaySuccess() {
//支付成功就修改布局的内容,不过真正的是修改服务器的数据,实际上支付宝是通过回调的URL地址通知服务器,
// 不会到客户端,不然被拦截就很容易开挂了
//the activity provides this method to be able to run code in the UI Thread
runOnUiThread(new Runnable() {
@Override
public void run() {
if (money.getText().toString() != null) {
int t = Integer.parseInt(money.getText().toString()) + 6480;
money.setText("" + t);
} else {
money.setText("0");
}
Toast.makeText(MainActivity.this, "充值成功!", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onPayFailed(int errorCode, String msg) {
Log.d(TAG, "失败 code-->" + errorCode + "信息" + msg);
Toast.makeText(MainActivity.this, "充值失败!", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt:
// 点击充值,进行跨进程操作
try {
if (thirdPartPay != null) {
thirdPartPay.requestPay("充值点券6480", 648.00f, new PayCallback());
}
} catch (RemoteException e) {
e.printStackTrace();
}
// TODO:一个标签,跟书签一样,下面的按钮中TODO就可以查看这些书签
break;
}
}
}
然后运行测试
初始的点券120
这里的支付密码当时是内定的123456
然后输入
支付完成,然后点券是6480+120=6600
再来一次
不过像我这种口袋空空的学生,怎么可能充648呢,只能做做软件过过瘾了。。。
加油,努力,勤能补拙。