• Android服务的AIDL跨进程(程序)操作


    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呢,只能做做软件过过瘾了。。。

    加油,努力,勤能补拙。




  • 相关阅读:
    经典回溯问题--八皇后dfs递归回溯求解【DFS】
    CSP认证考试(第九次)第一题
    C++字符串和数字格式转化(使用sprintf()和sscanf()函数)
    2016蓝桥杯C++A组第六题 寒假作业【暴力搜索】
    先序非递归建立二叉树
    sqlsrv数据库复杂语句1
    tp5域名配置
    JavaScript使用 value 属性
    数据库随机查询6条数据
    文件目录问题
  • 原文地址:https://www.cnblogs.com/aolong/p/13472941.html
Copyright © 2020-2023  润新知