SL4A 之实现原理解析

标签: sl4a 原理 解析 | 发表时间:2011-09-12 20:11 | 作者:(author unknown) vento
出处:http://simple-is-better.com/

关于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 原理 解析] 推荐:

SL4A 之实现原理解析

- vento - python.cn(jobs, news)
关于SL4A的简介和在Android系统的安装及使用,请参考我的上一篇博文《Android 脚本设计之 SL4A》,本篇来分析其内部的实现机制. SL4A架构实现了本地脚本和原生态Android程序的内部消息通信,所以任何本地脚本语言,只要实现了这套兼容的JSON RPC通信接口,就可以呼叫SL4A的RPC Server端程序.

Dubbo原理解析-监控

- - zzm
1.  监控中心启动,我们先看下dubbo的属性文件. 相比于provider, consumer的启动注册中心多了registry, jetty容器启动. 它们都是基于dubbo的spi扩展机制的. SpringContainer容器启动就是加载classpath*:META-INF/spring/ *.xml spring的配置文件.

Quartz集群实战及原理解析

- - CSDN博客推荐文章
  选Quartz的团队基本上是冲着Quartz本身实现的集群去的, 不然JDK自带Timer就可以实现相同的功能, 而Timer存在的单点故障是生产环境上所不能容忍的. 在自己造个有负载均衡和支持集群(高可用、伸缩性)的调度框架又影响项目的进度, 所以大多数团队都直接使用了Quartz来作为调度框架.

DNS原理及其解析过程

- - 操作系统 - ITeye博客
 网络通讯大部分是基于TCP/IP的,而TCP/IP是基于IP地址的,所以计算机在网络上进行通讯时只能识别如“202.96.134.133”之类的IP地址,而不能认识域名. 我们无法记住10个以上IP地址的网站,所以我们访问网站时,更多的是在浏览器地址栏中输入域名,就能看到所需要的页面,这是因为有一个叫“DNS服务器”的计算机自动把我们的域名“翻译”成了相应的IP地址,然后调出IP地址所对应的网页.

记一次MongoDB性能问题,附原理解析

- zffl - NoSQLFan
下面文章转载自火丁笔记,原作者描述了一次MongoDB数据迁移过程中遇到的性能问题及其解决方案,中间追查问题的方法和工具值得我们学习. 另外NoSQLFan还对作者略讲的问题产生原理进行了分析,希望对您有用. 最近忙着把一个项目从MySQL迁移到MongoDB,在导入旧数据的过程中,遇到了些许波折,犯了不少错误,但同时也学到了不少知识,遂记录下来.

高性能JavaScript模板引擎原理解析

- - 腾讯CDC
  随着 web 发展,前端应用变得越来越复杂,基于后端的 javascript(Node.js) 也开始崭露头角,此时 javascript 被寄予了更大的期望,与此同时 javascript MVC 思想也开始流行起来. javascript 模板引擎作为数据与界面分离工作中最重要一环,越来越受开发者关注,近一年来在开源社区中更是百花齐放,在 Twitter、淘宝网、新浪浪微博、腾讯QQ空间、腾讯微博等大型网站中均能看到它们的身影.

数据库水平切分的实现原理解析

- - 数据库 - ITeye博客
本文系转载,原文地址: http://lishuaibt.iteye.com/blog/409294. 随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题. 对于一个大型的 互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载. 对于系统的稳定性和扩展性造成了极大的问题.

Galera:多主同步MySQL集群原理解析

- - 进击的程序猿
Galera Cluster是基于MySQL/innodb二次开发而成的一个支持“多主同步”的数据库主从集群. 强调主从集群意味着Galera Cluster的每个节点充当一个数据冗余,而没有在节点间做分库分表的水平扩展. Galaer官网中为Galera Cluster洋洋洒洒罗列了10大优势,其实总结下来无非上文用引号注明的两点:.

Android SQLite数据库版本升级原理解析

- - CSDN博客推荐文章
Android使用SQLite数据库保存数据,那数据库版本升级是怎么回事呢,这里说一下. 安装v1.0,假设v1.0版本只有一个account表,这时走继承SQLiteOpenHelper的onCreate,不走onUpgrade. 1、v1.0(直接安装v1.0). 1、v1.0   -->  v2.0              不走onCreate,走onUpgrade.

Java线程池架构原理和源码解析(ThreadPoolExecutor)

- - ImportNew
在前面介绍JUC的文章中,提到了关于线程池 Execotors的创建介绍,在文章:《 java之JUC系列-外部Tools》中第一部分有详细的说明,请参阅;. 使用Executors最常用的莫过于是使用:Executors.newFixedThreadPool(int)这个方法,因为它既可以限制数量,而且线程用完后不会一直被cache住;那么就通过它来看看源码,回过头来再看其他构造方法的区别:.