存档

文章标签 ‘Java’

使用Merapi来在AIR和Java中通信

2010年2月2日 ColorHook 没有评论

Merapi是一个用于Java和AIR之间通信的桥接库。通过它可以极大的扩展AIR在本地的执行能力,当然前提是客户端不仅装有AIR运行时虚拟机,还需要安装有Java虚拟机。Merapi通过Socket来通信,使用Adobe的AMF作为数据传输格式。下面看看如何在Flash Builder中创建一个基于Adobe AIR的桌面软件。

在Java和ActionScript两端都存在一名为Bridge的类,它是一个Singleton,用来注册消息类型和发送消息。在这个例子中,AIR向Java端发送一个名为”flashEvent”的消息,消息内容是一个Dictionary实例;并且监听名为”javaEvent”的消息。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
	layout="absolute" creationComplete="initApp()">
	<mx:Script>
	<![CDATA[
	import demo.JavaMessageHandler;
	import demo.SerializeMessageHandler;
	import merapi.Bridge;
	import merapi.messages.Message;
	import merapi.error.MerapiErrorMessage;
 
	private function initApp():void{
		//register a error handler
		new SerializeMessageHandler();
		//send a message
		var message:Message=new Message();
		message.type="flashEvent";
		var data:Dictionary=new Dictionary();
		data["name"]="colorhook";
		message.data=data;
		message.send();
		//register a message handler
		Bridge.getInstance().registerMessageHandler("javaEvent",
					new JavaMessageHandler());
	}
	]]>
	</mx:Script>
</mx:WindowedApplication>

CoreErrorHandler继承自EventDispatcher,实现了IMessageHandler接口,它在构造函数中就集成了错误处理。所以不需要显示的在Bridge中注册,而是构造一个实例就行了。SerializeMessageHandler继承自CoreErrorHandler,用来处理全局错误。

package demo{
 
	import merapi.handlers.mxml.CoreErrorHandler;	
	import mx.rpc.events.ResultEvent;
 
	public class SerializeMessageHandler extends CoreErrorHandler{
 
		public function SerializeMessageHandler(type:String=null){
			super(type);
			this.addEventListener(ResultEvent.RESULT,onMessageResult);
		}
 
		private function onMessageResult(event:ResultEvent):void{
			trace("SerializeMessageHandler:",event);
		}
	}
}

JavaMessageHandler实现了接口IMessageHandler,并在handleMessage方法中输出从Java中返回的数据。

package demo{
 
	import merapi.handlers.IMessageHandler;
	import merapi.messages.IMessage;
 
	public class JavaMessageHandler implements IMessageHandler{
 
		public function handleMessage(message:IMessage):void{
			trace(message.type);
			trace(message.data);
		}
	}
}

编译运行会发现控制台输出SerializeMessageHandler中的trace信息,因为目前还没有开启Java端的ServerSocket。所以下面来启动一个Java程序。首先调用Bridge.open()来启动ServerSocket。然后监听名为”flashEvent”的消息。

package demo;
 
import org.apache.log4j.Logger;
import merapi.Bridge;
 
public class Application {
 
	protected Logger logger=Logger.getLogger(Application.class);
 
	public Application(){
		logger.info("Application start");
		//open Bridge
		Bridge.open();
		//register a message handler
		Bridge.getInstance().registerMessageHandler("flashEvent",
			new AIRMessageHandler());
	}
 
	public static void main(String[] args){
		new Application();
	}
}

在消息处理器AIRMessageHandler中,输出来自Flash的数据,AMF将ActionScript中的Dictionary映射为Java中的Map。接受到数据之后,我们再从Java中发出一个Message,该Message携带了一个List类型的数据,在Flash中,它会被映射成Array类型。

package demo;
 
import java.util.*;
import org.apache.log4j.Logger;
import merapi.handlers.IMessageHandler;
import merapi.messages.IMessage;
import merapi.messages.Message;
 
public class AIRMessageHandler implements IMessageHandler {
 
	protected Logger logger=Logger.getLogger(AIRMessageHandler.class);
 
	@Override
	public void handleMessage(IMessage message) {
		//handle message
		logger.info(message.getType());
		Map<String,String> map=(Map<String, String>)message.getData();
		logger.info(map.get("name"));
		//send a new message
		Message newMessage=new Message();
		newMessage.setType("javaEvent");
		List<String> list=new ArrayList<String>();
		list.add("Flash Player");
		list.add("AIR");
		newMessage.setData(list);
		newMessage.send();
	}
}

Merapi在Java端用到了Spring, log4j和Adobe AMF相关包, 所以运行时要保证项目包含了这些jar包。可以从MerapiGoogle Code中得到这些包。

AIR2的发布,让我们在AIR和Java通信方面有了新的选择: flerry
flerry使用了AIR2的新特性NativeProcess,它直接创建了一个进程了执行Java程序。从其作者的blog上可以了解更多信息: http://www.riaspace.net/2010/01/flerry-flex-java-bridge-for-adobe-air-2-0/

基于Android平台的视频播放实践

2009年10月26日 ColorHook 4 条评论

Android的android.widget包中有个名为VideoView的View类,见其名,就知道它是一个视频组件。下面就用它来实现Android手机系统下视频的播放。

添加一个3pg视频文件

3pg是手机上非常流行的视频文件,本次以一个3pg格式的视频文件作为best practice,将其命名为sample.3gp并保存在res/raw文件夹下。

添加一个VideoView到试图布局文件

声明它的名称为videoView,并填充父容器。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <VideoView 
    	android:id="@+id/videoView"
    	android:layout_width="fill_parent"
    	android:layout_height="fill_parent"/>
</LinearLayout>
设定VideoView的视频路径

在Activity中,通过Uri来获得视频路径,然后由VideoView来播放它。

package demo.media;
 
import android.app.Activity;
import android.net.Uri;
import android.os.Bundle;
import android.widget.MediaController;
import android.widget.VideoView;
 
public class MediaDemo2 extends Activity {
 
   protected VideoView videoView;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        videoView=(VideoView)findViewById(R.id.videoView);
        initialize();
    }
 
    private void initialize(){
    	Uri uri=Uri.parse("android.resource://demo.media/"+R.raw.sample);
    	videoView.setVideoURI(uri);
    	videoView.setMediaController(new MediaController(this));
    	videoView.requestFocus();
    }
}
分类: Develop & Design 标签: ,

基于Android平台的声音播放实践

2009年10月23日 ColorHook 1 条评论

Android API中有一个类MediaPlayer来控制媒体的音频播放,使用它有两种方式来达到声音播放的效果:

  1. MediaPlayer实例.setDataSource(Path)
  2. MediaPlayer类.create(Context, uri)

这里使用第二种方式来播放声音,首先需要在res/raw文件夹下放一个名为sound_test.mp3格式的音乐文件。界面上只用一个按钮用于控制停止和播放,在这里,按钮没有声明在XML布局文件中,而是用代码生成的。
贴上代码:

package demo.media;
 
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import android.view.ViewGroup.LayoutParams;
import android.view.View;
import android.view.View.OnClickListener;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
 
public class MediaTest01 extends Activity{
	protected MediaPlayer mediaPlayer;
	protected Button controlButton;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initializeMediaPlayer();
        initializeView();
    }
    //初始化声音
    private void initializeMediaPlayer(){
    	mediaPlayer=MediaPlayer.create(this, R.raw.sound_test);
    	mediaPlayer.setOnCompletionListener(new OnCompletionListener(){
			public void onCompletion(MediaPlayer player) {
				controlButton.setText("Play");
                                releaseMediaPlayer();
			}
		});
    }
    //初始化视图界面
    private void initializeView(){
    	controlButton=new Button(this);
    	LayoutParams params=new LayoutParams(LayoutParams.WRAP_CONTENT,
                          LayoutParams.WRAP_CONTENT);
    	setContentView(R.layout.main);
    	addContentView(controlButton, params);
    	controlButton.setOnClickListener(new OnClickListener(){
    		public void onClick(View v){
    			MediaTest01.this.handleControlButtonClick();
    		}
    	});
    	if(mediaPlayer.isPlaying()){
    		controlButton.setText("Stop");
    	}else{
    		controlButton.setText("Play");
    	}
    }
    //处理按钮动作
    public void handleControlButtonClick(){
    	if(!mediaPlayer.isPlaying()){
			this.playSound();
		}else{
			this.stopSound();
		}
	}
    }
    //播放声音
    private void playSound(){
    	Toast.makeText(this,"Play",Toast.LENGTH_SHORT).show();
    	releaseMediaPlayer();
    	initializeMediaPlayer();
	mediaPlayer.start();
	controlButton.setText("Stop");
    }
    //停止声音
    private void stopSound(){
        Toast.makeText(this,"Stop",Toast.LENGTH_SHORT).show();
    	mediaPlayer.stop();
    	controlButton.setText("Play");
    }
    //释放播放器资源
    private void releaseMediaPlayer(){
    	if(mediaPlayer!=null){
    		mediaPlayer.release();
    		mediaPlayer=null;
    	}
    }
    @Override
    protected void onDestroy(){
    	super.onDestroy();
    	releaseMediaPlayer();
    }  
}
分类: Develop & Design 标签: ,

创建一个基于Android的Google Map应用

2009年10月13日 ColorHook 1 条评论

在Web中使用Google Map,需要根据domain来申请一个apiKey,用来标识客户端。Android手机中的Google Map也需要一个apiKey来标识客户端。

在eclipse开发环境中,可以使用emulator来测试一个Android应用,在emulator中运行的Google Map需要有一个与emulator对应的apiKey,获取apiKey的步骤如下:

1. 获得emulator的MD5认证码。

在Android SDK的bin目录下,用如下指令得到MD5认证码,-keystore后跟的是debug.keystore的地址,可以在eclipse首选项Android配置中找到。

keytool -list -alias androiddebugkey -keystore "C:\Documents and Settings
\MS\.android\debug.keystore" -storepass android -keypass android
根据MD5码生成API Key。

http://code.google.com/android/maps-api-signup.html下可以生成一个API Key。

得到API Key之后就可以试试创建一个Google Map应用了。

首先,要保证能够使用Google Map,这个应用必须要能够访问Internet,所以AndroidManifest.xml中需要加入如下的权限声明:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />

其次,该应用用到了Google Map的Java库,所以也需要在AndroidManifest.xml中声明。

<uses-library android:name="com.google.android.maps" />

然后,Activity必须继承MapActivity,而不是默认的Activity。

package com.colorhook.android;
import android.os.Bundle;
import com.google.android.maps.*;
 
public class GoogleMapApp extends MapActivity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
 
    @Override
    protected boolean isRouteDisplayed() {
	// TODO Auto-generated method stub
	return false;
    }
}

最后在res/layout/main.xml文件夹中声明一个MapView。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main"
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent">
    <com.google.android.maps.MapView
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"
        android:enabled="true"
        android:clickable="true"
        android:apiKey="apisamples"
        />
</LinearLayout>
分类: Develop & Design 标签: , ,

使用Spring来配置RemoteObject

2009年4月9日 ColorHook 没有评论

在Flex中使用RemoteObject可以很容易地对Java对象进行RPC调用,但是其配置较为复杂。使用AMFPHP对PHP进行RPC调用只需要指定RemoteObject的destination和endpoint属性即可,而在BlazeDS环境下,需要创建ChannelSet和Channel对象。

通常的做法是使用一个xml文件来配置一个Java destination,Flex在编译的时候会根据配置信息来创建一个完整的RemoteObject对象。但是如果需要更改该RemoteObject对象的destination或者ChannelSet等属性,则需要重新编译,这样的话很是麻烦。

Spring的IOC容器可以很好解决这个问题,它把对象的创建方式声明在外部,然后由容器来动态生成。

使用我的Spring框架来实现这个想法。

1.创建远程的Java对象,编译成class文件放在文件夹WEB-INF\classes\demo下面。

package demo;
public class HelloBlazeDS{
 
	public HelloBlazeDS(){
	}
 
	public String sayHello(String info){
		return "[BlazeDS return] : "+info;
	}
}

2.配置BalzeDS。
打开文件WEB-INF\flex\remoting-config.xml,向其中添加一个destination定义。

<destination id="HelloBlazeDS">
	<properties>
		<source>demo.HelloBlazeDS</source>
	</properties>
</destination>

3.创建Spring的配置文件spring_config.xml。

<?xml version="1.0" encoding="utf-8"?>
<spring-config>
	<beans>
		<bean id="remoteBean" class="mx.rpc.remoting.mxml.RemoteObject">
			<property name="destination" value="HelloBlazeDS"/>
			<property name="channelSet" ref="channelSet"/>
		</bean>
 
		<bean id="channelSet" class="mx.messaging.ChannelSet">
			<method name="addChannel">
				<method-arg ref="channel"/>
			</method>
		</bean>
 
		<bean id="channel" class="mx.messaging.channels.AMFChannel">
			<property name="id" value="my-amf"/>
			<property name="uri" value="http://localhost:8080/blazeds/messagebroker/amf"/>
		</bean>
	</beans>
</spring-config>

该配置文件声明了一个Bean名字叫remoteBean,它是一个RemoteObject对象,这个Bean还引用了channelSet和channel这两个Bean。

4.在Flex中使用Spring,下面是代码片段。

//导入加载配置文件的类ContextLoader
import com.colorhook.spring.context.ContextLoader;
 
private var contextLoader:ContextLoader;
private var remoteService:RemoteObject;
 
//初始化时加载配置文件
private function init():void{
	contextLoader=new ContextLoader();
	contextLoader.addEventListener("complete",onContextLoaderComplete);
}
 
//加载完成之后创建RemoteObject,并调用远程方法sayHello
private function onContextLoaderComplete(event:Event):void{
	contextLoader.removeEventListener("complete",onContextLoaderComplete);
	remoteService=contextLoader.contextInfo.getBean("remoteBean");
	remoteService.sayHello.addEventListener(ResultEvent.RESULT,onSayHelloResult);
	remoteService.sayHello("Spring and BlazeDS");
}
//输出返回结果
private function onSayHelloResult(event:ResultEvent):void{
	Alert.show(String(event.result));
}
分类: Develop & Design 标签: , , , ,