AWTK 在 Android 平台实现跨进程服务调用

原创
2020/11/21 18:14
阅读数 306

AWTK 在 Android 平台实现跨进程服务调用

前几天,有朋友想在 AWTK 的应用程序中,调用打印机提供的服务,去打印采集到的数据。打印机本身有一个 APP,它提供了一个对外的服务,并附带一个 AIDL 文件。

要说这个东西本身也不难,但是要在 AWTK 应用程序中,通过 C 语言函数去调用 java 的函数,再通过 java 的函数去调用另外一个进程的服务,对于只会 C 语言的嵌入式程序员来说,不懂 java 也不懂 android,还绕了几个大弯,那确实太难了。

这里用一个 FooBar 的服务,演示一下整个开发过程,供有需要的童鞋参考。

1. 开发提供服务的 APP

先用 Android Studio 新建一个普通的 Android Java 项目 FooBarServer。

1.1 新建一个 aidl 文件。

文件位置:app/src/main/aidl/org/zlgopen/foobarserver/IFooBarService.aidl

// IFooBarService.aidl
package org.zlgopen.foobarserver;

// Declare any non-default types here with import statements

interface IFooBarService {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    int add(int a, int b);
}

为了方便说明,这里只是提供了一个 add 函数,输入 a 和 b 两个参数,返回两者之和。

1.2 新建一个服务。

文件位置:app/src/main/java/org/zlgopen/foobarserver/FooBarService.java

package org.zlgopen.foobarserver;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class FooBarService extends Service {
    public FooBarService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return binder;
    }

    private final IFooBarService.Stub binder = new IFooBarService.Stub() {
        public int add(int a, int b) {
            return a + b;
        }
    };
}

1.3 修改 AndroidManifest.xml 对外声明提供的服务。

        <service
            android:name=".FooBarService"
            android:enabled="true"
            android:process=":remote">
            <intent-filter>
                <action android:name="org.zlgopen.foobarserver.IFooBarService"/>
            </intent-filter>
        </service>

要跨进程调用,这行代码很关键:android:process=":remote"

2. 开发使用服务的 APP

为了验证服务端的正确性,我们再用 Android Studio 创建一个普通的 Android Java 应用程序 FooBarClient。

2.1 拷贝 IFooBarService.aidl

把 app/src/main/aidl/org/zlgopen/foobarserver/IFooBarService.aidl 从 FooBarService 拷贝到 FooBarClient 下,并保持目录结构不变。

2.2 修改 MainActivity.java

建立与服务的连接,并调用相应的函数。


    public static IFooBarService mFooBarService;
    private ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("client", "onServiceDisconnected");
            mFooBarService = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("client", "onServiceConnected");
            mFooBarService = IFooBarService.Stub.asInterface(service);
            try {
                int result = mFooBarService.add(100, 200);
                Log.e("client", Integer.toString(result));
            }catch (RemoteException e) {
                Log.e("client", e.toString());
            }
        }
    };

    public void bindService() {
        Intent intent = new Intent("org.zlgopen.foobarserver.IFooBarService");
        intent.setPackage("org.zlgopen.foobarserver");
        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
    }

    public void unbindService() {
        unbindService(mServiceConn);
    }

编译并安装 FooBarServer 和 FooBarClient。

3. 编写 AWTK 插件

3.1 C 语言 API 接口

AWTK 的 UI 线程和 Android 的 Activity 线程并不是同一个,所以 AWTK 的插件全部是异步接口,调用完成后通过回调函数返回执行结果。

参数和返回值都使用 JSON 格式的字符串。

/**
 * @method foobar_add
 * 调用 add 函数。
 *
 * > 回调函数是在 AWTK UI 线程中执行的,可以在回调函数中直接操作 AWTK 控件。
 * 
 * @annotation ["static"]
 * @param {int} a a。
 * @param {int} b b。
 * @param {platform_request_on_result_t} on_result 完成时的回调函数。
 * @param {void*} on_result_ctx 完成时的回调函数的上下文。
 *
 * @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。
 */
ret_t foobar_add(int a, int b, platform_request_on_result_t on_result, void* on_result_ctx);

实现很简单:把参数打包成 JSON 格式的字符串,再用 platform_request_send 发送给插件管理器,由插件管理器负责把请求转发给插件。

ret_t foobar_add(int a, int b, platform_request_on_result_t on_result, void* on_result_ctx) {
  ret_t ret = 0;
  char args[64];
  return_value_if_fail(on_result, RET_BAD_PARAMS);
  tk_snprintf(args, sizeof(args)-1, "{\"a\":%d, \"b\":%d}", a, b);
  ret = platform_request_send("foobar", "add", args, on_result, on_result_ctx);

  return ret;
}

3.2 Java 语言实现插件

文件位置:src/foobar/android/java/org/zlgopen/plugins/foobar/FooBarPlugin.java

插件负责建立与服务之间的连接,并将 AWTK C API 发送过来的请求,转发给 FooBarServer。

建立连接的过程与 FooBarClient 类似,只是由于 Plugin 不是 Activity,如果要调用 Activity 的函数,需要通过 this.activity 调用。

	public static IFooBarService mFooBarService;
    private ServiceConnection mServiceConn = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("client", "onServiceDisconnected");
            mFooBarService = null;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e("client", "onServiceConnected");
            mFooBarService = IFooBarService.Stub.asInterface(service);
        }
    };

    public void bindService() {
        Intent intent = new Intent("org.zlgopen.foobarserver.IFooBarService");
        intent.setPackage("org.zlgopen.foobarserver");

        this.activity.bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
    }

    public void unbindService() {
        this.activity.unbindService(mServiceConn);
    }

run 函数负责处理来自 AWTK C API 的请求,并转发给 FooBarServer:

  public boolean run(String action, String callerInfo, String args) {
    try {
      this.callerInfo = callerInfo;
      JSONObject json = new JSONObject(args);

      if (action.equals("add")) {
        int a = json.getInt("a");
        int b = json.getInt("b");
        this.Add(a, b);
      } else {
        Log.v("AWTK", "not supported action");
      }
    } catch (JSONException e) {
      Log.v("AWTK", e.toString());
      PluginManager.writeResult(this.callerInfo, "fail");
    }

    return true;
  }

  void Add(int a, int b) {
		int sum = 0;

		try {
				sum = mFooBarService.add(a, b);
				Log.w("client", Integer.toString(sum));
		}catch (RemoteException e) {
				Log.e("client", e.toString());
		}

    String result = "{\"action\":\"add\"" + ",\"result\":" + Integer.toString(sum) + "}";
    PluginManager.writeResult(this.callerInfo, result);

    return;
  }

3.3 AWTK C 语言调用示例

  • 包含头文件
#include "foobar/foobar.h
  • 调用函数并显示结果
static ret_t foobar_on_result(void* ctx, const char* data){
  widget_t* result_label = WIDGET(ctx);
  conf_doc_t* doc = conf_doc_load_json(data, strlen(data));
  int result = conf_doc_get_int(doc, "result", 0);
  log_debug("foobar:%s\n result:%d\n", data, result);
  conf_doc_destroy(doc);

  widget_set_text_utf8(result_label, data);
  widget_invalidate_force(result_label, NULL);

  return RET_OK;
}

static ret_t on_click(void* ctx, event_t* e) {
  foobar_add(100, 200, foobar_on_result, ctx);
  return RET_OK;
}

完整代码请参考 awtk-mobile-plugins

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部