转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-)
http://my.oschina.net/ryanhoo/blog/100589
今天我们来学习使用现有的服务构建应用的功能模块——利用有道的翻译API构建一个小的翻译程序。之前曾想用Google的翻译API来实现,后来发现这项服务是收费的,所以用有道的,免费才是王道。
先来看看效果
功能相当简单,主要提供英汉互译(其实参数无需传入语言,有道翻译机器人会自动识别)。
实现步骤如下:
- 申请有道翻译API的key并阅读文档
- 新建Android项目,编写远程调用服务AIDL
- 实现服务并供程序调用
- 实现UI
申请有道翻译API KEY
请在这个页面填写必要信息,并记录返回的Key和keyfrom,调用有道的翻译服务将需要使用它。
在申请到Key以后,请花几分钟时间简短的浏览下有道API调用的文档,就在申请页的下面。
请求接口非常简单:
http://fanyi.youdao.com/openapi.do?keyfrom=<keyfrom>&key=<key>&type=data&doctype=<doctype>&version=1.1&q=要翻译的文本
唯一在变动的参数是翻译文本,这让我们定义AIDL变得非常容易。
编写AIDL远程服务调用接口
新建ITranslateService.aidl,定义一个简单的接口,只需传入一个参数——待翻译文本。
package com.iedgeco.ryan.translate.service;
interface ITranslate{
String traslate(in String text);
}
实现服务
public class TranslateService extends Service {
public static final String TAG = "TranslateService";
//binder
private final ITranslate.Stub mBinder = new ITranslate.Stub() {
@Override
public String traslate(String text) throws RemoteException {
try {
return Translator.translate(text);
} catch (Exception e) {
Log.e(TAG, "Failed to translate", e);
return null;
}
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
在AIDL所在的包下实现一个Service类
TranslateService
,实现相当的简单。因为是远程调用服务而非本地服务,后期需要使用bindService,因此我们必须返回IBinder实例。这里的IBinder中具体的翻译操作,我交给一个工具类
Translator
的静态方法完成,只需传入待翻译文本即可。
public class Translator {
private static final String TAG = "Translator";
private static final String ENCODING = "UTF-8";
/**
* <pre>
* 请求标准: http://fanyi.youdao.com/openapi.do?
* keyfrom=<keyfrom>&
* key=<key>&
* type=data&
* doctype=<doctype>&
* version=1.1&
* q=要翻译的文本
* 版本:1.1,请求方式:get,编码方式:utf-8
* 主要功能:
* 中英互译,同时获得有道翻译结果和有道词典结果(可能没有)
* 参数说明:
* type - 返回结果的类型,固定为data
* doctype - 返回结果的数据格式,xml或json或jsonp
* version - 版本,当前最新版本为1.1
* q - 要翻译的文本,不能超过200个字符,需要使用utf-8编码
* errorCode:
* 0 - 正常
* 20 - 要翻译的文本过长
* 30 - 无法进行有效的翻译
* 40 - 不支持的语言类型
* 50 - 无效的key
* </pre>
* @throws IOException
* @throws ClientProtocolException
* */
public static String translate(String text) throws Exception {
String url = "http://fanyi.youdao.com/openapi.do?keyfrom=" + StaticDef.KEY_FROM
+ "&key=" + StaticDef.KEY_FOR_TRANSLATE
+ "&type=data"
+ "&doctype=json"
+ "&version=1.1"
+ "&q=" + text;
try{
HttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse response = client.execute(httpGet);
InputStream is = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, ENCODING));
StringBuffer result = new StringBuffer();
String string = null;
if(null != (string = reader.readLine()))
result.append(string).append('\n');
//TODO parse the response json string
String translation = (String) new JSONObject(result.toString())
.getJSONArray("translation").get(0);
Log.i(TAG, "result: " + result.toString());
return translation;
}finally{
//close stream here
}
}
}
实现UI并调用翻译服务
UI相对简单,具体的布局请见Github中的代码,后面我会给出地址。
MainActivity中的方法也比较简单,在onCreate中启用服务(doBindService),在onDestory中解除绑定,销毁服务(doUnBindService),可以使用doTranslate进行后台翻译(由于服务是直接运行在Main线程上的,我开启了一个异步线程来完成这个工作,并将结果反馈给Handler,由它来更新UI)。
doBindService:
private void doBindService(){
Intent service = new Intent(StaticDef.ACTION_TRANALATE);
bindService(service, serviceConn, Context.BIND_AUTO_CREATE);
}
doUnBindService:
private void doUnBindService(){
unbindService(serviceConn);
}
doTranslate:
//launch a thread for translation
private void doTranslate(){
new Thread(new Runnable() {
@Override
public void run() {
String result = null;
try {
String input = etInput.getText().toString();
//use the translate service
result = mTranslateService.traslate(input);
//send message callback
Message msg = new Message();
msg.obj = result;
if(result == null){
msg.what = TRANSLATE_ERROR;
throw new Exception("Failed to get a translation...");
}
msg.what = TRANSLATE_COMPLETED;
mHandler.sendMessage(msg);
} catch (Exception e) {
Log.e(TAG, "Error happened while translating...", e);
}
}
}).start();
}
Handler:
private static final int TRANSLATE_COMPLETED = 0;
private static final int TRANSLATE_ERROR = 1;
//controller
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TRANSLATE_COMPLETED:
String result = (String) msg.obj;
etTranslation.setText(result);
etInput.selectAll();
break;
case TRANSLATE_ERROR:
Log.e(TAG, "translation error");
break;
default:
break;
}
}
};
总结
代码不多,逻辑也相对简单,主要是学习如何使用AIDL调用远程服务。
提示:使用之前,请用申请到的KEY替换config包下的key以及keyfrom!
代码
详见我的Github:https://github.com/ryanhoo/TranslateService