`

SL4A 之实现原理解析【转】

 
阅读更多

关于SL4A的简介和在Android系统的安装及使用,请参考我的上一篇博文《Android 脚本设计之 SL4A》,本篇来分析其内部的实现机制。

深入理解SL4A

SL4A架构实现了本地脚本和原生态Android程序的内部消息通信,所以任何本地脚本语言,只要实现了这套兼容的JSON RPC通信接口,就可以呼叫SL4A的RPC Server端程序。至于为什么要选用JSON,及这种格式的优点和特征,此处就不详细叙述了,大家可以查看JSON官网。

“JavaScript Object Notation (JSON) is a lightweight, text-based,
language-independent data interchange format. It was derived from the
ECMAScript Programming Language Standard. JSON defines a small
set of formatting rules for the portable representation of structured data.
JSON can represent four primitive types (strings, numbers, booleans,
and null) and two structured types (objects and arrays).”

SL4A总体架构

从上图可以看出,SL4A总体包括Client和Server两部分来实现通信和整体架构的独立性,Client端负责解析本地脚本,这样只要本地脚本实现了兼容的接口,就可以方便实现脚本语言的扩展,而Server端则封装了Android原生态程序的设计,即使在android底层API发生API变化的时候,Client端也基本不会受到影响,Client把脚本中解析出来的函数调用通过RPC通信,远程呼叫Server端代理接口,然后由Server调用原生态的Android API(Android Facade架构)来完成具体的功能,在调用结束后,Server端将执行结果反馈给Client端。

整个执行过程如下:

 -­‐-­‐ Script Interpreter
-­‐-­‐-­‐-­‐ Client/Caller/Consumer Script
-­‐-­‐-­‐-­‐-­‐-­‐ "Android" Script Object (locally wraps RPC calls) -­‐ Local Proxy
-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ Remote Procedure Calls (RPC) – Exchanges contain a JSON payload
-­‐-­‐-­‐-­‐-­‐-­‐ Android API Java Facade -­‐ Remote Proxy
-­‐-­‐-­‐-­‐ API Server/Provider -­‐ Android Java application
-­‐-­‐ The Android Platform itself

Local Proxy 的实现机制

其实每个本地模块都封装实现了一个android实体类,然后开放其调用接口,内部通过RPC与SL4A Server端通信。

Python 模块(android.py)之实现

 

# Copyright (C) 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
 
__author__ = 'Damon Kohler <damonkohler@gmail.com>'
 
import collections
import json
import os
import socket
import sys
 
PORT = os.environ.get('AP_PORT')
HOST = os.environ.get('AP_HOST')
HANDSHAKE = os.environ.get('AP_HANDSHAKE')
Result = collections.namedtuple('Result', 'id,result,error')
 
class Android(object):
 
  def __init__(self, addr=None):
    if addr is None:
      addr = HOST, PORT
    self.conn = socket.create_connection(addr)
    self.client = self.conn.makefile()
    self.id = 0
    if HANDSHAKE is not None:
      self._authenticate(HANDSHAKE)
 
  def _rpc(self, method, *args):
    data = {'id': self.id,
            'method': method,
            'params': args}
    request = json.dumps(data)
    self.client.write(request+'\n')
    self.client.flush()
    response = self.client.readline()
    self.id += 1
    result = json.loads(response)
    if result['error'] is not None:
      print result['error']
    # namedtuple doesn't work with unicode keys.
    return Result(id=result['id'], result=result['result'],
                  error=result['error'], )
 
  def __getattr__(self, name):
    def rpc_call(*args):
      return self._rpc(name, *args)
    return rpc_call
 

BeanShell 模块(android.bsh)之实现

 

// Copyright (C) 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
 
import org.json.*;
 
Android() {
  String AP_PORT = System.getenv().get("AP_PORT");
  String AP_HOST = System.getenv().get("AP_HOST");
  String AP_HANDSHAKE = System.getenv().get("AP_HANDSHAKE");
  Socket conn = new Socket(AP_HOST, Integer.decode(AP_PORT));
  BufferedReader in = new BufferedReader(
      new InputStreamReader(conn.getInputStream(), "8859_1"), 1 << 13);
  OutputStream out_stream = new BufferedOutputStream(
      conn.getOutputStream(), 1 << 13);
  PrintWriter out = new PrintWriter(
      new OutputStreamWriter(out_stream, "8859_1"), true);
  int id = 0;
 
  call(String method, JSONArray params) {
    JSONObject request = new JSONObject();
    request.put("id", id);
    request.put("method", method);
    request.put("params", params);
    out.write(request.toString() + "\n");
    out.flush();
    String data = in.readLine();
    if (data == null) {
      return null;
    }
    return new JSONObject(data);
  }
 
  call(String method) {
    JSONArray args = new JSONArray();
    call(method, args);
  }
 
  call(String method, Object arg1) {
    JSONArray args = new JSONArray();
    args.put(arg1);
    call(method, args);
  }
 
  call(String method, Object arg1, Object arg2) {
    JSONArray args = new JSONArray();
    args.put(arg1);
    args.put(arg2);
    call(method, args);
  }
 
  call(String method, Object arg1, Object arg2, Object arg3) {
    JSONArray args = new JSONArray();
    args.put(arg1);
    args.put(arg2);
    args.put(arg3);
    call(method, args);
  }
 
  call(String method, Object arg1, Object arg2, Object arg3, Object arg4) {
    JSONArray args = new JSONArray();
    args.put(arg1);
    args.put(arg2);
    args.put(arg3);
    args.put(arg4);
    call(method, args);
  }
 
  JSONArray handshake = new JSONArray();
  handshake.put(AP_HANDSHAKE);
  call("_authenticate", handshake);
 
  return this;
}
 

JavaScript 模块(android.js)之实现

 

/* * Copyright 2009 Brice Lambson * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */
 
var AP_PORT = java.lang.System.getenv("AP_PORT");
var AP_HOST = java.lang.System.getenv("AP_HOST");
var AP_HANDSHAKE = java.lang.System.getenv("AP_HANDSHAKE");
 
load('/sdcard/com.googlecode.rhinoforandroid/extras/rhino/json2.js');
 
function Android() {
 
  this.connection = new java.net.Socket(String(AP_HOST), AP_PORT),
  this.input = new java.io.BufferedReader(
      new java.io.InputStreamReader(this.connection.getInputStream(), "8859_1"),
                                    1 << 13),
  this.output = new java.io.PrintWriter(new java.io.OutputStreamWriter(
      new java.io.BufferedOutputStream(this.connection.getOutputStream(),
                                       1 << 13),
      "8859_1"), true),
  this.id = 0,
 
  this.rpc = function(method, args) {
    this.id += 1;
    var request = JSON.stringify({'id': this.id, 'method': method,
                                  'params': args});
    this.output.write(request + '\n');
    this.output.flush();
    var response = this.input.readLine();
    return eval("(" + response + ")");
  },
 
  this.__noSuchMethod__ = function(id, args) {
    var response = this.rpc(id, args);
    if (response.error != null) {
      throw response.error;
    }
    return response.result;
  }
 
  this._authenticate(String(AP_HANDSHAKE));
}
 

android-cruft(ndk-to-sl4a.c)之C语言实现

 

// Released into the public domain, 15 August 2010
 
// This program demonstrates how a C application can access some of the Android
// API via the SL4A (Scripting Languages for Android, formerly "ASE", or Android
// Scripting Environment) RPC mechanism. It works either from a host computer
// or as a native binary compiled with the NDK (rooted phone required, I think)
 
// SL4A is a neat Android app that provides support for many popular scripting
// languages like Python, Perl, Ruby and TCL. SL4A exposes a useful subset of
// the Android API in a clever way: by setting up a JSON RPC server. That way,
// each language only needs to implement a thin RPC client layer to access the
// whole SL4A API.
 
// The Android NDK is a C compiler only intended for writing optimized
// subroutines of "normal" Android apps written in Java. So it doesn't come
// with any way to access the Android API.
 
// This program uses the excellent "Jansson" JSON library to talk to SL4A's
// RPC server, effectively adding native C programs to the list of languages
// supported by SL4A.
 
// To try it, first install SL4A: http://code.google.com/p/android-scripting/
//
// Start a private server with View->Interpreters->Start Server
//
// Note the port number the server is running on by pulling down the status
// bar and tapping "SL4A service". 
 
// This program works just fine as either a native Android binary or from a
// host machine.
 
// ------------
 
// To compile on an ordinary linux machine, first install libjansson. Then:
 
// $ gcc -ljansson ndk-to-sl4a.c -o ndk-to-sl4a
 
// To access SL4A on the phone use "adb forward tcp:XXXXX tcp:XXXXX" to port
// forward the SL4A server port from your host to the phone. See this
// page for more details:
// http://code.google.com/p/android-scripting/wiki/RemoteControl
 
// ------------
 
// To compile using the NDK:
// 1. Make sure you can compile "Hello, world" using the NDK. See:
// http://credentiality2.blogspot.com/2010/08/native-android-c-program-using-ndk.html
//
// 2. If you followed the above instructions, you have a copy of the agcc.pl
// wrapper that calls the NDK's gcc compiler with the right options for
// standalone apps.
//
// 3. Unpack a fresh copy of the jansson sources. Tell configure to build for
// Android:
//
// $ CC=agcc.pl ./configure --host=arm
// $ make
//
// 4. Cross your fingers and go! (I'm quite certain there's a more elegant
// way to do this)
//
// $ agcc.pl -I/path/to/jansson-1.3/src -o ndk-to-sl4a-arm ndk-to-sl4a.c /path/to/jansson-1.3/src/*.o
//
// 5. Copy to the phone and run it with the port of the SL4A server!
 
#include <stdio.h>
#include <jansson.h>
#include <unistd.h>
#include <string.h>
 
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
 
// This mimics SL4A's android.py, constructing a JSON RPC object and
// sending it to the SL4A server.
int sl4a_rpc(int socket_fd, char *method, json_t *params) {
  static int request_id = 0; // monotonically increasing counter
 
  json_t *root = json_object();
 
  json_object_set(root, "id", json_integer(request_id));
  request_id++;
 
  json_object_set(root, "method", json_string(method));
 
  if (params == NULL) {
    params = json_array();
    json_array_append(params, json_null());
  }
 
  json_object_set(root, "params", params);
 
  char *command = json_dumps(root, JSON_PRESERVE_ORDER | JSON_ENSURE_ASCII);
  printf("command string:'%s'\n", command);
 
  write(socket_fd, command, strlen(command));
  write(socket_fd, "\n", strlen("\n"));
 
  // At this point we just print the response, but really we should buffer it
  // up into a single string, then pass it to json_loads() for decoding.
  printf("Got back:\n");
  while (1) {
    char c;
    read(socket_fd, &c, 1);
    printf("%c", c);
    if (c == '\n') {
      break;
    }
  }
  fflush(stdout);
  return 0;
}
 
// This function is just boilerplate TCP socket setup code
int init_socket(char *hostname, int port) {
  int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (socket_fd == -1) {
    perror("Error creating socket");
    return 0;
  }
 
  struct hostent *host = gethostbyname(hostname);
  if (host == NULL) {
    perror("No such host");
    return -1;
  }
 
  struct sockaddr_in socket_address;
 
  int i;
  for (i=0; i < sizeof(socket_address); i++) {
    ((char *) &socket_address)[i] = 0;
  }
 
  socket_address.sin_family = AF_INET;
 
  for (i=0; i < host->h_length; i++) {
    ((char *) &socket_address.sin_addr.s_addr)[i] = ((char *) host->h_addr)[i];
  }
 
  socket_address.sin_port = htons(port);
 
  if (connect(socket_fd, (struct sockaddr *) &socket_address, sizeof(socket_address)) < 0) {
    perror("connect() failed");
    return -1;
  }
 
  return socket_fd;
}
 
main(int argc, char **argv) {
  int port = 0;
  if (argc != 2) {
    printf("Usage: %s port\n", argv[0]);
    return 1;
  }
  port = atoi(argv[1]);
 
  int socket_fd = init_socket("localhost", port);
  if (socket_fd < 0) return 2;
 
  json_t *params = json_array();
  json_array_append(params, json_string("w00t!"));
  sl4a_rpc(socket_fd, "makeToast", params);
}
 

Hello Android之多语言实现

在文章的最后,给大家一个经典的Hello Android多语言实现,O(∩_∩)O哈哈~

BeanShell:
source("/sdcard/com.googlecode.bshforandroid/extras/bsh/android.bsh");
droid = Android();
droid.call("makeToast", "Hello Android!");

JavaScript:
load("/sdcard/com.googlecode.rhinoforandroid/extras/rhino/android.js");
var droid = new Android();
droid.makeToast("Hello Android!");

Perl:
use Android;
my $a = Android-->new();
$a-->makeToast("Hello Android!");

Python:
import android
andy = android.Android()
andy.makeToast("Hello Android!")

Ruby:
droid = Android.new
droid.makeToast "Hello Android!"

TCL:
package require android
set android [android new]
$android makeToast "Hello Android!"

分享到:
评论

相关推荐

    SL4A之Python_API_中英文参考

    SL4A,全称为Scripting Layer for Android,是一款强大的Android平台上的自动化工具,它允许用户通过编程语言(如Python)来控制和与Android设备交互。这个压缩包包含了SL4A的Python API的中英文参考文档,是开发者...

    SL4A-of-Python_API_Chinese.rar_SL4A之Python_API_python android_py

    标题中的"SL4A-of-Python_API_Chinese.rar"表明这是一个关于SL4A的Python API的中文资源包,而"SL4A之Python_API_python android_py"进一步确认了这个资源是关于在Android上使用Python编程的API文档。 SL4A的主要...

    ApiReference - sl4a-chinese - 中文版api - SL4A之Python资源中文化项目 - Google Project Hosting

    ApiReference - sl4a-chinese - 中文版api - SL4A之Python资源中文化项目 - Google Project Hosting ApiReference - sl4a-chinese - 中文版api - SL4A之Python资源中文化项目 - Google Project Hosting

    sl4a_r4.apk

    SL4A提供了丰富的API,允许编写简单的脚本来实现对Android设备的各种操作,如访问硬件、控制相机、发送短信、网络通信等,极大地降低了Android应用开发的门槛。 SL4A_R4是SL4A的一个版本,它包含了该框架在第四次...

    sl4a-r6.1.1-arm-debug

    SL4A的强大之处在于它的灵活性和开放性。它允许开发者轻松地创建新的脚本或修改已有的脚本来适应不同的需求。此外,SL4A还有一个活跃的社区,用户可以在这里分享自己的脚本和经验,共同推动项目的发展。 总的来说,...

    SL4A 的一本教程

    在SL4A的帮助下,开发者可以利用脚本语言快速实现一些简单应用的开发。例如,利用Python语言,开发者可以很快地搭建出原型,测试和验证他们的想法。SL4A也支持开发者为已有的Java应用嵌入脚本,从而快速实现功能的...

    sl4a-r6x05-armv7-debug

    通过SL4A,开发者可以轻松地编写脚本来实现如控制硬件传感器、处理多媒体文件、网络通信、读取和写入系统设置等功能。 在描述中提到的"sl4a-r6x05-armv7 sl4a sl4a-r6 sl4a-r6x05-arm",这些标签表明这个版本是SL4A...

    Android脚本语言环境SL4A.zip

    Google官方博客介绍了Android Scripting Environment(ASE、SL4A),将脚本语言带入Android,允许用户编辑和执行脚本,直接在Android设备上运行交互式解释器。脚本将能大幅度简化任务界面,用户能在交互式终端中使用...

    SL4A之Python_API_中文参考

    SL4A,全称为Scripting Layer for Android,是Android平台上的一种技术,它使得开发者和用户能够使用各种脚本语言在Android设备上编写和运行应用程序。SL4A的主要目的是提高开发效率,通过提供对Android系统API的...

    SL4A开发工具包

    SL4A开发APK文档集合 pro-android-python-with-sl4a.pdf PythonForAndroid_r4.apk sl4a_r6.apk [android.开发书籍].Practical.Android.Projects.pdf

    sl4a-r6x04-armv7

    SL4A开发APK文档集合之 sl4a-r6 sl4a-r6x04-armv7

    Pro.Android.Python.with.SL4A(第1版)

    通过本书,读者不仅可以学习到如何使用Python在Android上构建应用程序,还能掌握如何有效地利用SL4A这一强大的脚本层,实现设备的自动化管理。无论是初学者还是有经验的开发者,都能从中找到有价值的信息,拓展自己...

    PythonForAndroid-r7b1+sl4a.zip

    PythonForAndroid-r7b1+sl4a.zip这个压缩包文件是关于在Android平台上运行Python环境的工具集合,其中包含了PythonForAndroid和SL4A(Scripting Layer for Android)两个重要组件。这两个工具使得开发者可以在...

    sl4a_r6.apk

    sl4a_r6 供自己学习,也分享出来给大家使用,需要的话可以下载,有什么问题可以留言

    安卓SL4A_R6.APK文件

    android脚本环境的APK应用,目前已支持Python,Lua, BeanShell, Perl等语言。

    sl4a_r6.zip

    SL4A_R6.apk

    sl4a-r3.rar

    5. **扩展性**:SL4A允许开发自定义的脚本模块,以实现特定的功能。 SL4A对于初学者和经验丰富的开发者都极具价值。对于初学者,它提供了一个学习Android编程的低门槛入口;对于专业开发者,它则提供了一种快速原型...

    Apress.Pro.Android.Python.with.SL4A.Jul.2011.rar

    SL4A提供了一个API,开发者可以利用这些API实现丰富的功能。 3. **Python在移动开发中的优势**:Python在编写快速原型、数据处理和科学计算等方面具有优势,SL4A使得这些优势能够在移动设备上得以发挥,降低了...

    android sl4a

    sl4a for android script 开发

    Android代码-sl4a

    Scripting Layer for Android (SL4A) brings scripting languages to Android by allowing you to edit and execute scripts and interactive interpreters directly on the Android device. These scripts have ...

Global site tag (gtag.js) - Google Analytics