游戏开发论坛

 找回密码
 立即注册
搜索
查看: 1534|回复: 0

多线程在JAVA ME应用程序中的使用1 wxh zt

[复制链接]

1367

主题

1993

帖子

2118

积分

金牌会员

Rank: 6Rank: 6

积分
2118
发表于 2006-7-31 17:53:00 | 显示全部楼层 |阅读模式
作者:magic003
    多线程技术是JAVA ME中的关键技术,应用十分频繁,尤其是在游戏中。但是对于新手来说,又容易忽略或错误的使用多线程,导致程序堵塞,而无法响应用户的输入请求。
    由于笔者对于游戏开发不是十分了解,所以本文将仅就多线程技术在JAVA ME应用程序中的使用展开讨论。本文主要包含如下部分:

         多线程与联网
         多线程与拍照
         Timer与TimerTask

多线程与联网

   手机中,所有的MIDlet程序都是由Application Manager Software(AMS)管理的。当MIDlet初始化后,AMS就会调用MIDlet的startApp()方法,此时MIDlet就进入了Acitive状态。在JAVA ME中有些操作可能会导致程序堵塞,比如连接网络等。如果这些操作与主程序在同一个主线程中完成,那么由于堵塞会造成程序长时间无法返回,也就无法响应用户的其他操作了。所以,如果我们在commandAction()中进行了联网的操作,则会造成如上所述的情况。
   下面,将通过一个例子来演示如上的情况,并使用多线程最终解决此问题。这是一个“Echo Message”实例,手机端向服务器端发送一条消息,服务器得到此消息后直接返回给手机端。
   首先,创建一个NetworkConnection类来封装联网相关的操作,这样,MIDlet中只需调用此类中的方法就可以完成联网的操作。代码如下:

以下是引用片段:
/*
* NetworkConnection.java
*
* Created on 2006年7月20日, 下午2:54
*
*/
package nju.hysteria.thread.connection;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
/**
*
* @author Magic
*/
public class NetworkConnection {
    private static final String URL = "http://localhost:8080/thread/";
    private HttpConnection httpConnection;
    private String message;
     
    public NetworkConnection(String message) {
        this.message = message;
        connect();
    }
     
    /**
     * Connect to web server.
     *
     */
    public void connect(){
        try {
            httpConnection = (HttpConnection) Connector.open(URL);
            httpConnection.setRequestMethod(HttpConnection.POST);
        } catch (IOException ex) {
            System.out.println("Can not open connection!");
            ex.printStackTrace();
        }
    }
     
     
    /**
     * Send message to server.
     * @throws java.io.IOException  
     */
    public void sendMessage() throws IOException{
        DataOutputStream out = httpConnection.openDataOutputStream();
        out.writeUTF(message);
        out.close();
    }
     
    /**
     * Receive message from server.
     * @throws java.io.IOException  
     * @return  
     */
    public String receiveMessage() throws IOException {
        DataInputStream in = httpConnection.openDataInputStream();
        String message = in.readUTF();
        in.close();
        return message;
    }
     
    /**
     * Close connection.
     */
    public void close(){
        if(httpConnection!=null){
            try {
                httpConnection.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
     
}
    构造函数的参数是将要被发送的消息。服务器端的代码在此不再列出,详细请见本文的源代码。
    接着,我们写一个MIDlet调用类中的方法。MalConnectionMidlet在commandAction()方法中直接调用NetworkConnection中的方法,而没有重新创建一个线程。代码如下:

以下是引用片段:
/*
* MalConnectionMidlet.java
*
* Created on 2006年7月20日, 下午2:53
*/
package nju.hysteria.thread.connection;
import java.io.IOException;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
*
* @author  Magic
* @version
*/
public class MalConnectionMidlet extends MIDlet implements CommandListener {
    private Display display;
    private TextBox text;
    private Command showCommand;
     
    public MalConnectionMidlet(){
        display = Display.getDisplay(this);
        text = new TextBox("Message","请使用‘问候’命令发送消息",100,TextField.ANY);
        showCommand = new Command("问候",Command.SCREEN,1);
        text.addCommand(showCommand);
        text.setCommandListener(this);
    }
     
    public void startApp() {
        display.setCurrent(text);
    }
     
    public void pauseApp() {
    }
     
    public void destroyApp(boolean unconditional) {
    }
    public void commandAction(Command command, Displayable displayable) {
        if(command==showCommand){
            /**
             *  在当前的线程中直接进行联网操作,造成程序堵塞。
       */
            String message = null;
            
            NetworkConnection connection = new NetworkConnection("Hello World");
            try {
                connection.sendMessage();
                message = connection.receiveMessage();
                connection.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            text.setString(message);
        }
    }
}
    当用户按下“问候”命令时,就会向服务器发送“Hello World”的消息,然后再得到服务器返回的消息,并显示在TextBox中。
    运行程序,如图1所示。当用户按下“问候”命令后,程序就僵死了,并在Console窗口中得到如下警告:



    图1

以下是引用片段:
Warning: To avoid potential deadlock, operations that may block, such as  
networking, should be performed in a different thread than the  
commandAction() handler.


    这就是因为没有使用多线程造成的。下面,就来看看如何使用多线程来解决此问题。
    新建类NetworkThread,它继承在Thread,并将原先commandAction()中发送,接受消息的操作移到此类中完成。代码如下:

以下是引用片段:
/*
* NetworkThread.java
*
* Created on 2006年7月20日, 下午4:16
*
*/
package nju.hysteria.thread.connection;
import java.io.IOException;
import javax.microedition.lcdui.TextBox;
/**
*
* @author Magic
*/
public class NetworkThread extends Thread {
    private NetworkConnection connection;
    private TextBox text;
     
    public NetworkThread(TextBox text) {
        super();
        this.text = text;
    }
    public void run() {
        String message = null;
            
        connection = new NetworkConnection("Hello World");
        try {
            connection.sendMessage();
            message = connection.receiveMessage();
            connection.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        text.setString(message);
    }
     
}
    同时,修改原先的MIDlet,得到新的MIDlet:

以下是引用片段:
/*
* ConnectionMidlet.java
*
* Created on 2006年7月20日, 下午2:53
*/
package nju.hysteria.thread.connection;
import java.io.IOException;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
*
* @author  Magic
* @version
*/
public class ConnectionMidlet extends MIDlet implements CommandListener {
    private Display display;
    private TextBox text;
    private Command showCommand;
     
    public ConnectionMidlet(){
        display = Display.getDisplay(this);
        text = new TextBox("Message","请使用‘问候’命令发送消息",100,TextField.ANY);
        showCommand = new Command("问候",Command.SCREEN,1);
        text.addCommand(showCommand);
        text.setCommandListener(this);
    }
     
    public void startApp() {
        display.setCurrent(text);
    }
     
    public void pauseApp() {
    }
     
    public void destroyApp(boolean unconditional) {
    }
    public void commandAction(Command command, Displayable displayable) {
        if(command==showCommand){
            /**
             * 创建新的线程完成联网操作
             */
            (new NetworkThread(text)).start();
        }
    }
}
    此时,在commandAction()中,我们创建了新的线程来完成消息的发送和接受。运行程序,可以成功接受到返回的消息,如图2所示。



  图2

多线程与拍照

    同样,在移动多媒体API(JSR 135)的使用过程中也需要应用到多线程。利用手机摄像头拍照的操作也会引起的堵塞,因此当在commandAction()中调用拍照操作时,若未开辟新线程来处理,同样也会造成程序没有响应。让我们通过一个例子来观察一下吧。
    这是一个很简单的摄像头程序,首先程序将会调用摄像头取景,然后当用户按下“拍照”命令后就捕捉当前的画面并显示给用户。图3是程序的UML类图。



  图3

    MalCameraMidlet和CameraMidlet分别是错误和正确的MIDlet,它们都创建一个CameraView(用于显示摄像头画面)的对象,唯一的不同在于前者创建MalCamera的对象,后者创建Camera的对象(MalCamera和Camera都是CameraView的子类)。SnapShot则是将摄像头捕捉到的图像显示给用户的类。
    首先,我们还是来看一下未使用多线程,而造成程序没有响应的情况。如下是MalCameraMidlet类的代码,其中保存着一个指向CameraView的引用,并在startApp()中创建了MalCamera对象。

以下是引用片段:
/**
* MalCameraMidlet.java
*  
*/
package nju.hysteria.thread.camera;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
/**
* This MIDlet create the mal camera view, so the program
* will block after calling Capture command.
* @author Magic
*
*/
public class MalCameraMidlet extends MIDlet {
protected Display display;
private CameraView camera;
  
public MalCameraMidlet() {
  super();
  display = Display.getDisplay(this);
}
protected void startApp() {
  camera = new MalCamera(this);
  display.setCurrent(camera);
}
  
       /**
        * Show current camera.
        */
public void showCamera(){
  display.setCurrent(camera);
}
  
protected void pauseApp() {
}
protected void destroyApp(boolean arg0) {
}
}
    CameraView是一个抽象类,是两个显示Camera的类的父类。它负责显示Camera,并将commandAction()方法留给子类实现,代码如下:

以下是引用片段:
/**
* CameraView.java
*/
package nju.hysteria.thread.camera;
import java.io.IOException;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Item;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.media.control.VideoControl;
import javax.microedition.midlet.MIDlet;
/**
* This is an abstract class, which display camera to user.
* @author Magic
*
*/
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-1-25 01:23

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表