`
univasity
  • 浏览: 809946 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

[资料]线程在J2ME应用中的使用

    博客分类:
  • J2me
阅读更多
简要说明:
非常好的一篇文章,谈论到了线程各个方面的问题,包括创建,启动,终止和同步等,里面例举了大量例子作为说明。
Article

Using Threads in J2ME Applications

<!-- --><!-- -->
<!-- --><!-- --><!-- --><!-- --><!-- --> <!-- -->

 

One of the defining features of the Java environment is its built-in support for threads . Threads let an application perform multiple activities simultaneously. When used properly, threads let the application's user interface remain responsive while it performs lengthy operations like network communication or very complicated computations. While user interface responsiveness is important no matter what the platform, it's even more so on the handheld and consumer-oriented devices that the J2ME platform targets. A basic understanding of how to use threads is key to writing effective J2ME applications, whether you're using the more limited facilities of the Connected Limited Device Configuration (CLDC) or the fuller features of the Connected Device Configuration (CDC). This article explains the concepts behind threads and how you can use them in your own applications.

<!-- -->

 

What is a Thread?

The Java Tutorial defines a thread as "a single sequential flow of control within a program." Threads are the fundamental units of program execution. Every running application has at least one thread. An application consisting of two or more threads is known as a multithreaded application.

 

Each thread has a context associated with it. The context holds information about the thread, such as the address of the currently executing instruction and storage for local variables. The context is updated as the thread executes. The context also stores the thread's state. A thread can be in any of the following states:

  • A running thread is executing code.
  • A ready thread is ready to execute code.
  • A suspended thread is waiting on an external event. For example, it may be waiting for data to arrive from another device. After the event occurs, the thread transitions back to the ready state.
  • A terminated thread has finished executing code.

A thread that is running, ready, or suspended is a live thread. A terminated thread is also known as a dead thread.

 

Although an application may have many threads, the underlying device has a small, fixed number of processors (often just one or two) available for code execution. Threads share these processors by taking turns being in the running state. This arrangement is called thread scheduling .

 

Thread scheduling is a complicated affair over which the application has little control. The system (either the underlying operating system or the Java virtual machine, depending on how the threads are implemented) gives each ready thread a chance to run for a short time (the suspended threads are ignored), switching rapidly from one thread to another. This context switching can occur at any time. The only real influence the application has over thread scheduling is via the thread's priority level : higher-priority threads execute more often than those with lower priority.

<!-- -->

 

The Thread Object

Threads are dynamic in nature, existing only as part of a running application. The Java runtime associates each live thread with an instance of java.lang.Thread . This class is used to start new threads and to get and set thread priorities. Your code can obtain access to the current thread's instance of Thread at any time by invoking Thread.currentThread() .

 

J2ME provides two variants of the Thread class, both subsets of the J2SE version 1.3. CLDC's Thread class includes only these methods:

  • activeCount()
  • currentThread()
  • getPriority()
  • isAlive()
  • join()
  • run()
  • setPriority()
  • sleep()
  • start()
  • yield()

Notice the lack of methods to stop or interrupt a running thread - more on this later.

CDC's Thread class, on the other hand, supports the full J2SE functionality, including support for thread groups (groups of threads that can be manipulated as a set) and daemon threads (an application automatically terminates when all non-daemon threads have terminated, even if one or more daemon threads are still active). The CDC Thread class is not identical to the J2SE version, however, because it does not include deprecated methods, including those that stop or interrupt threads.

<!-- -->

 

Starting Threads

To start a new thread you need two things: an instance of java.lang.Thread and an object that implements the java.lang.Runnable interface.

The Runnable interface defines a run() method with no arguments and no return value. Here's a simple implementation:

<!-- -->
public class DoSomething implements Runnable {
    public void run(){
    	// here is where you do something
    }
}

<!-- -->

You create a Runnable object, use it to create a new Thread instance, then invoke the latter's start() method:

<!-- -->
...
DoSomething doIt = new DoSomething();
Thread myThread = new Thread( doIt );
myThread.start();
...

<!-- -->

The system starts a new thread to invoke the Runnable's run() method. When the thread returns from the run() method, the system terminates the thread.

 

As it happens, the Thread class itself implements the Runnable interface, so another way to accomplish the same thing is to create a subclass of Thread that overrides the run() method, as in this example:

<!-- -->
public class DoAnotherThing extends Thread {
    public void run(){
	// here is where you do something
    }
}

<!-- -->

You start the thread by creating an instance of the subclass and invoking its start() method:

<!-- -->
...
DoAnotherThing doIt = new DoAnotherThing();
doIt.start();
...

<!-- -->

Which way is preferable? Functionally, there is no real difference. If you can implement the Runnable interface on a class that also serves other purposes, however, you avoid the overhead of defining an entirely new class. That extra overhead is minimal, of course, but in very constrained environments (some mobile phones limit J2ME applications to 30K) every little bit helps.

 

Furthermore, inheriting from Thread is actually more limiting, because the start() method of any given Thread object can be invoked only once.

 

There are no restrictions on what a thread does in its run() method. A thread can perform a single action and immediately exit from run() . More commonly it repeats some action until some condition is satisfied:

<!-- -->
public void run(){
    boolean quit = false;

    while( !quit ){
	// repeat some action

	quit = .... // decide if it should quit
    }
}

<!-- -->

Note that a thread has access to all the methods and fields of the Runnable object used to create the thread. Because the run() method has no parameters, passing parameters to the start() method is the only way to initialize a thread. Here's a simple example:

<!-- -->
public class ThreadedDoIt implements Runnable {
    private SomeClass state;

    public ThreadedDoIt( SomeClass initState ){
	state = initState;
	new Thread( this ).start();
    }

    public void run(){
	// do something based on "state" field
    }
}

<!-- -->

If two threads simultaneously access the same data, thread synchronization (discussed later) becomes a concern.

<!-- -->

 

Stopping Threads

As I already mentioned, the stop() and interrupt() methods found in the J2SE Thread class are not available in either J2ME configuration. The stop() method has been deprecated because it is inherently unreliable and cannot be implemented on all platforms safely and consistently. The interrupt() method has been reintroduced in version 1.1 of the CLDC and will probably show up in the next revision of the CDC.

 

Once started, then, a J2ME thread lives until it intentionally or unintentionally (as a result of an uncaught exception) exits the run() method it invoked on startup. (The system may also terminate running threads when the application stops running, of course, but that is really another case of unintentional exit.) There is no way for one thread to force another thread to stop: You can stop a live thread only by convincing it to terminate itself.

 

It's important to provide a way to stop an application's threads in short order. The simplest way is to have each thread periodically check a boolean variable:

<!-- -->
public class MyThread implements Runnable {
    private boolean quit = false;

    public void run(){
	while( !quit ){
	    // do something
	}
    }

    public void quit(){
	quit = true;
    }
}

<!-- -->

This works well when only one thread at a time executes a given run() method, but it fails when multiple threads share the same run() method (in other words, share the same Runnable object) and you want only some of the threads to stop. For example, it's common to want all but the most recent thread to stop executing. Instead of using a boolean variable, store a reference to the most recently created thread, and in the run() method check to see whether it matches the current thread instance:

<!-- -->
public class AnotherThread implements Runnable {
    private Thread mostRecent = null;

    public Thread restart(){
	mostRecent = new Thread( this );
	mostRecent.start();
    }

    public void run(){
	Thread thisThread = Thread.currentThread();
	while( mostRecent == thisThread ){
	    // do something
	}
    }

    public void stopAll(){
	mostRecent = null;
    }
}

<!-- -->

It often makes sense to perform the check several times within the loop, before and after lengthy operations, and to break out of the loop immediately:

<!-- -->
...
while( mostRecent == thisThread ){
    doLengthyOperation1();
    if( mostRecent != thisThread ) break;
    doLengthyOperation2();
    if( mostRecent != thisThread ) break;
    doLengthyOperation3();
}
...

<!-- -->

Normally, you want a thread to exit as quickly as possible, delaying only enough to clean up after itself.

 

If a thread is using any kind of system resource - such as a network connection or a record store - that it created or opened itself, remember to use a finally block to release those resources properly no matter how the thread is terminated. For example:

<!-- -->
public void run(){
    RecordStore rs = null;

    try {
	rs = RecordStore.openRecordStore("myrs", true );
	// do something with the record store...
    }
    finally {
	if( rs != null ){
	    try {
	    	rs.close();
	    }
	    catch( RecordStoreException e ){}
	}
    }
}

<!-- -->

Otherwise, the first thread that exits when an exception isn't caught may prevent other threads from accessing the resource. Using a finally block also avoids memory leaks that can cause the application to fail when run over long periods. Releasing resources and avoiding memory leaks is especially important in J2ME applications. Some J2ME-enabled phones, for example, limit applications to one or two active network connections at any given time.

 

Once one thread has "asked" another thread to stop, the first thread can see whether the second is still alive by calling its isAlive() method. If the first thread needs to wait for the second thread to terminate before proceeding, it can call the thread's join() method:

<!-- -->
...
MyThread other = .... // some other active thread
other.quit();  // tell it to quit
other.join();  // wait 'til it does
...

<!-- -->

Be careful, though: If you invoke join() on a thread that isn't stopping, you'll just end up blocking the calling thread indefinitely.

<!-- -->

 

Thread Synchronization

Starting and stopping threads is easy. The hard part is getting threads to interact safely. Because a context switch can occur at any time, the newly running thread can find itself reading data that a previously running thread was not done writing. Interacting threads must use thread synchronization to control who can read or write shared data at any given time.

 

Thread synchronization depends on a construct called a monitor . A monitor acts as a gatekeeper, ensuring that only one thread at a time has access to data. Whenever a thread needs to access protected data, it asks the monitor to lock the data for its own use. Other threads interested in the same data are forced to wait until the data is unlocked, at which point the monitor will lock the data for one of the waiting threads and allow it to proceed with its processing.

 

Any Java object can act as a monitor to control access to data using the synchronized keyword. For example:

<!-- -->
public void appendTime( StringBuffer buf ){
    synchronized( buf ){
	buf.append( "Current time is: " );
	buf.append( System.currentTimeMillis() );
    }
}

<!-- -->

If two threads call this method simultaneously, one is forced to wait until the other has left the synchronized block.

 

Because any Java object can act as a monitor, the this reference can be used for synchronization within a non-static method:

<!-- -->
public class Counter {
    private int counter;

    public int increment(){
	synchronized( this ){
	    return ++counter;
	}
    }

    public int decrement(){
	synchronized( this ){
	    if( --counter < 0 ){
		counter = 0;
	    }

	    return counter;
	}
    }
}

<!-- -->

Synchronizing with this is a simple way for a class to control access to its internal state. The Java language even provides a shortcut: Instead of enclosing the contents of a method in a synchronized block, you can add the synchronized keyword to the method declaration. For example, the Counter class can be written more simply:

<!-- -->
public class Counter {
    private int counter;

    public synchronized int increment(){
	return ++counter;
    }

    public synchronized int decrement(){
	if( --counter < 0 ){
	    counter = 0;
	}

	return counter;
    }
}

<!-- -->

Static methods can be declared this way as well, even though there is no this object to use as a monitor:

<!-- -->
public class SomeClass {
    public static synchronized void someMethod(){
	// do something
    }
}

<!-- -->

Instead, static methods use the class object of the class in which they are declared. The example above is equivalent to:

<!-- -->
public class SomeClass {
    public static void someMethod(){
	synchronized( SomeClass.getClass() ){
	    // do something
	}
    }
}

<!-- -->

Many of the core Java classes use synchronized methods. The java.lang.StringBuffer class is extensively synchronized, for example. The synchronization carries over to the J2ME versions of those classes. Remember, however, that method synchronization does not guarantee the order in which different threads invoke the object's methods, only that only one thread will be executing any of the synchronized methods at a time. Recall the appendTime() example:

<!-- -->
public void appendTime( StringBuffer buf ){
    synchronized( buf ){
	buf.append( "Current time is: " );
	buf.append( System.currentTimeMillis() );
    }
}

<!-- -->

Although StringBuffer.append() is synchronized, guaranteeing that each value passed to it is appended to the string buffer as a single unit, appendTime() needs the synchronized block to ensure that the sequence of calls is also made as a single unit.

 

Thread synchronization works only when threads cooperate. To protect mutable data (data that can be changed) properly, all threads accessing the data must access it through synchronized code blocks. If the data is encapsulated in a class, this is easily done by making all (or most) of the methods synchronized - but make sure you understand which monitor is being used to do the synchronization. For example, notice that a significant change occurs when you remove the synchronized block from appendTime() and declare the method itself to be synchronized :

<!-- -->
public synchronized void appendTime(
    StringBuffer buf ){
    buf.append( "Current time is " );
    buf.append( System.currentTimeMillis() );
}

<!-- -->

This version of appendTime() and the preceding one are not equivalent, because they are synchronizing using different monitors. The new version is actually equivalent to:

<!-- -->
public void appendTime( StringBuffer buf ){
    synchronized( this ){
	buf.append( "Current time is " );
     	buf.append( System.currentTimeMillis() );
    }
}

<!-- -->

The first version precluded any other changes to the string buffer while a thread was executing appendTime() , but the second version only ensures that different threads invoking appendTime() on the same class instance will not interfere with each other. Another thread with access to the same string buffer could still interfere with the thread actually executing the appendTime method.

 

Of course, thread synchronization comes at a cost. Locking and unlocking data takes time, even on

the fastest of processors. If your classes are immutable - their external state does not change after initialization - then you can often dispense with synchronization. The java.lang.String class, for example, does not define any synchronized methods. Data that is not accessed by more than one thread does not need to be synchronized, either.

 

Thread synchronization does entail one danger: Two threads can deadlock if each attempts to lock data already locked by the other. Suppose thread A locks object X and thread B locks object Y, and then A attempts to lock Y at the same time B tries to lock X. Both threads block, and there's no way to unblock them. Deadlock avoidance is an important consideration when designing your application. A simple avoidance technique is to lock objects in the same order every time - always lock X before Y, for example. For more information about deadlock and deadlock avoidance, see the Resources section.

<!-- -->

 

Waiting and Notifications

Synchronization controls access to shared data, but often you want a thread to wait for an event to occur before accessing the data. A common solution is to have a thread read and process commands from a queue, commands placed there by other threads. If you use a java.util.Vector as the queue, a simple approach is to use an endless loop:

<!-- -->
import java.util.Vector;

public class Worker implements Runnable {
    private boolean quit = false;
    private Vector queue = new Vector();
    private Thread thread;

    public Worker(){
	new Thread( this ).start();
    }

    public void run(){
	Object o;

	while( !quit ){ // busy wait!
	    o = null;

	    synchronized( queue ){
		if( queue.size() > 0 ){
		    o = queue.elementAt( 0 );
		    queue.removeElementAt( 0 );
	    	}
	    }

	    if( o != null ){
	    	// do something
	    }
    	}
    }

    public boolean addToQueue( Object o ){
    	if( !quit ){
	    queue.addElement( o );
	    return true;
    	}

    	return false;
    }

    public void quit(){
	quit = true;
    }
}

<!-- -->

This kind of loop is known as a busy wait , because the thread is always busy executing code. You want to avoid busy waits because they waste valuable processor cycles that other threads could be putting to good use. What you want is a suspended wait , where the thread is suspended until the desired event occurs. Suspended threads do not affect the scheduling of other threads.

 

As it happens, the monitors used for thread synchronization can also be used for thread suspension. If you think about it, thread synchronization is really just a special case of thread suspension, because each thread entering a synchronized block waits its turn for access to the data. The monitor maintains a queue of waiting threads, allowing only one thread at a time to enter the block.

 

Because every Java object can act as a monitor, the java.lang.Object class defines three methods that expose this basic functionality: wait() , notify() , and notifyAll() . Any thread can suspend itself by calling an object's wait() method:

<!-- -->
...
Object obj = .... // some object to use as a lock

synchronized( obj ){
    // here is where you'd check obj's state

    try {
	obj.wait();
    }
    catch( InterruptedException e ){
    }
}
...

<!-- -->

The thread must lock the object before invoking its wait() method. It must also catch the java.lang.InterruptedException and deal appropriately with thread interruptions. The thread implicitly releases its lock on the object after it suspends itself.

 

The wait() method is overloaded: The thread can specify an optional timeout in milliseconds if it doesn't want to wait indefinitely.

 

Once a thread suspends itself, another thread releases it by invoking the same object's notify() or notifyAll() method:

<!-- -->
...
Object obj = .... // same object used as lock!

synchronized( obj ){
    obj.notify();  // or notifyAll
}
...

<!-- -->

Again, the second thread must lock the object before calling notify() or notifyAll() . These two methods behave the same except that one wakes one waiting thread while the other wakes all waiting threads. The order in which threads are woken is not specified. Each newly woken thread must re-obtain its lock on the object before it can actually proceed, because its lock on the object was implicitly released when it suspended itself.

 

Armed with this knowledge, you can rework the Worker class to avoid busy waiting:

<!-- -->
import java.util.Vector;

public class Worker implements Runnable {
    private boolean quit = false;
    private Vector queue = new Vector();

    public Worker(){
	new Thread( this ).start();
    }

    public void run(){
	Object o;

	while( !quit ){
	    o = null;

	    synchronized( queue ){
		if( queue.size() > 0 ){
		    o = queue.elementAt( 0 );
		    queue.removeElementAt( 0 );
		} else {
		    try {
		    	queue.wait();
		    }
		    catch( InterruptedException e ){
		    }
		}
	    }

	    if( o != null ){
		// do something
	    }
	}
    }

    public boolean addToQueue( Object o ){
	synchronized( queue ){
	    if( !quit ){
	 	queue.addElement( o );
		queue.notify();
		return true;
	    }

	    return false;
	}
    }

    public void quit(){
	synchronized( queue ){
	quit = true;
	queue.notify();
	}
    }
}

<!-- -->

This is a much better behaved version, because the worker thread executes only when there are items in the queue.

 

As a rule, you should avoid suspending threads that your application did not create. System-defined threads - including those that deliver user-interface events and other notifications - are often shared among multiple applications. Suspending a system thread can affect the user interface, making the application appear to lock up. It may also prevent critical notifications from being delivered to other applications. Read the article Networking, User Experience, and Threads for more details.

<!-- -->

 

A Real-Life Example

I'll conclude this look at threads with a real-world example involving the Wireless Messaging API (WMA), a J2ME optional package that enables applications to send and receive Short Message Service (SMS) messages.

 

An application using the WMA can register to be notified whenever a message arrives for it. The WMA defines a MessageListener interface:

<!-- -->
package javax.wireless.messaging;

public interface MessageListener {
    void notifyIncomingMessage(
	MessageConnection conn );
}

<!-- -->

The application creates an object that implements this interface and registers it with the WMA subsystem. When a message arrives for the application, the WMA subsystem invokes the notifyIncomingMessage() method on a thread of its own choosing. The WMA specification does not allow the application to receive or process the message on its thread - the system is merely notifying the application that a message has arrived. The application must use a separate thread to handle the message. Here's one way to do this processing:

<!-- -->
import java.io.IOException;
import java.util.Vector;
import javax.wireless.messaging.*;

// A message receiver waits for messages to arrive
// on a message connection and then uses a separate
// thread to receive the actual message.

public class MessageQueuer
    implements Runnable, MessageListener {

    // A data structure representing either a
    // connection-message pair (if a message was read)
    // or a connection-exception pair (if an error
    // occurred during the reading).

    public static class Entry {
        MessageConnection connection;
        Message           message;
        IOException       exception;
    }

    private Vector incoming = new Vector();
    private Vector outgoing;
    private boolean stop;

    // Pass the outgoing vector into the constructor.

    public MessageQueuer( Vector outgoing ){
        this.outgoing = outgoing;
        new Thread( this ).start();
    }

    // Called whenever a message arrives for the 
    // given connection. Queues the connection object 
    // and wakes up the thread to actually receive 
    // the message.

    public void notifyIncomingMessage( 
        MessageConnection conn ){
        if( !stop ){
            synchronized( incoming ){
                incoming.addElement( conn );
                incoming.notify();
            }
        }
    }

    // Waits for incoming connection objects to be 
    // queued. When one arrives, pulls it off the queue  
    // and receives the messags. Queues the resulting
    // connection-message pair on the outgoing
    // queue and wakes anyone listening to the queue.

    public void run(){
        while( !stop || !incoming.isEmpty() ){
            MessageConnection conn = null;

            synchronized( incoming ){
                while( !stop && incoming.isEmpty() ){
                    try {
                        incoming.wait();
                    }
                    catch( InterruptedException e ){
                    }
                }

                if( !incoming.isEmpty() ){
                    conn = (MessageConnection) 
                          incoming.elementAt( 0 );
                    incoming.removeElementAt( 0 );
                }
            }

            if( conn == null ) continue;

            Entry entry = new Entry();
            entry.connection = conn;

            try {
                entry.message = conn.receive();
            }
            catch( IOException e ){
                entry.exception = e;
            }

            synchronized( outgoing ){
                outgoing.addElement( entry );
                outgoing.notify();
            }
        }
    }

    // Stops the processing of messages.

    public void stop(){
        synchronized( incoming ){
            stop = true;
            incoming.notify();
        }
    }
}

<!-- -->

The MessageQueuer class looks complicated, but it's not - it follows the same basic outline as the Worker class we just created. The MessageQueuer 's constructor takes a Vector to serve as the outgoing queue, where received messages are placed. Typical setup code:

<!-- -->
...
Vector receiver = new Vector();
MessageQueuer queuer = new MessageQueuer( receivef );
MessageConnection conn1 = ... // a server connection
MessageConnection conn2 = ... // another connection
conn1.setMessageListener( queuer );
conn2.setMessageListener( queuer );
...

<!-- -->

Notice how the MessageQueuer can listen to more than one connection. When a message arrives at a connection, the queuer receives it and places an instance of MessageQueuer.Entry on the outgoing queue. Another thread can wait on the outgoing queue:

<!-- -->
...
Vector receiver = .... // the receiving queue
boolean quit = false;
     
while( !quit ){
    MessageQueuer.Entry entry;

    synchronized( receiver ){
        while( receiver.isEmpty() ){
            try {
                receiver.wait();
            }
            catch( InterruptedException e ){
            }
        }

        entry = (MessageQueuer.Entry)
            receiver.elementAt( 0 );
        receiver.removeElementAt( 0 );
    }

    if( entry.message != null ){
        // handle entry.message here
    } else {
        // handle entry.exception here
    }

    entry = null;
}
...

<!-- -->

Although this code fragment doesn't show it, the real advantage to the MessageQueuer class is that it lets an application scan through the list of received messages before deciding which one to process. The application could give priority to messages arriving on a certain connection, for example.

 

An alternative implementation is to write a message receiver that creates a new thread to read and process each message as it arrives. Note that threads may be slow to create and individual devices may limit the number of threads that are active at any given time, so this technique may not be practical if many messages are to be received in a short timespan.

<!-- -->

 

Resources

The Threads and Multithreading section of Sun's Java Developer web site is also a good resource for more information, as are the following books:

  • Concurrent Programming in Java , by Doug Lea
  • Java Thread Programming , by Paul Hyde
  • Taming Java Threads , by Allen Holub
<!-- --><!-- -->

<!-- --><!-- -->

 

原文:http://developers.sun.com/mobility/midp/articles/threading2/

分享到:
评论

相关推荐

    基于Eclipse环境的J2ME应用程序开发实例源码

    7. **线程管理**:J2ME应用中的多任务处理通常需要自定义线程,以避免阻塞UI。源码会展示如何创建和管理`Thread`对象。 8. **错误处理和调试**:学习如何在Eclipse中设置断点、查看日志和调试J2ME应用,这对于问题...

    J2ME的多线程教程和测试

    不过,不同设备对优先级的支持可能不同,因此在实际应用中效果可能不一致。 四、线程池 虽然J2ME的标准库没有提供线程池功能,但开发者可以自行实现简单的线程池管理,通过复用已创建的线程来减少资源消耗。 五、...

    J2ME里面的线程和网络

    在Java Micro Edition (J2ME)中,线程和网络编程是两个至关重要的概念,尤其对于开发移动设备上的应用程序来说。J2ME为资源有限的设备提供了轻量级的Java平台,因此理解和掌握这两个主题是编写高效、响应式以及能够...

    j2me应用小实例j2me_pro

    在开发J2ME应用时,我们需要编写配置文件如`midlet.jad`和`midlet.jar`,它们分别用于描述应用程序的元数据和包含编译后的代码。 2. **开发环境与工具** 常用的J2ME开发工具有NetBeans、Eclipse和JCreator等,它们...

    J2ME手机应用项目开发实践 源代码

    1. **KVM(Java Virtual Machine for Embedded Devices)**:J2ME应用程序运行在KVM上,这是一个专为小型设备优化的虚拟机,它不支持完整的Java Class库,而是采用CLDC(Connected Limited Device Configuration)和...

    J2ME API 2.0 J2ME使用手册 J2ME帮助文档

    6. **J2ME应用发布** - **JAR (Java Archive)**:包含应用的类文件和资源文件。 - **jad (Java Application Descriptor)**:描述应用的元数据,如MIDlet(应用程序的主体)信息、版本、权限等。 7. **安全与限制*...

    线程池技术在J2ME网络通信中的应用研究

    本文聚焦于线程池技术在J2ME网络通信中的应用,旨在通过优化多线程管理,提高网络通信效率,同时减少资源消耗。 #### J2ME网络通信中的多线程挑战 在J2ME环境下,网络通信通常需要在独立的线程中执行,以确保主线...

    j2me 开发工具的使用

    8. **Profiler和Performance Analysis**: 为了优化J2ME应用的性能,开发者可以使用像VisualVM这样的Java性能分析工具。它们提供内存使用、CPU消耗和线程状态的详细视图,帮助定位并修复性能瓶颈。 9. **文档和学习...

    如何使用Netbeans调试J2ME应用程序.rar

    在NetBeans中,调试J2ME应用程序主要有两种方式:模拟器调试和实际设备调试。 1. **模拟器调试**:NetBeans集成了多个J2ME模拟器,如Sun Wireless Toolkit (SWT) 和Nokia Java SDK。在项目属性中配置所需的模拟器,...

    J2ME应用实例源代码

    本篇将围绕“J2ME应用实例源代码”这一主题,深入探讨J2ME的游戏开发实践,特别是通过“坦克大战”等经典游戏的源码解析,来阐述J2ME开发中的关键技术和设计思路。 1. **J2ME架构与环境搭建** J2ME由配置...

    j2me中精灵知识的应用

    理解如何在J2ME中使用精灵对于创建动态、交互式的用户体验至关重要。 1. **精灵的概念**: - 精灵是一种二维图形对象,可以在游戏场景中独立移动和动画化。它们通常由多个帧组成,通过快速切换帧来创建连续的动作...

    线程池技术在J2ME网络通信中的应用研究.

    ### 线程池技术在J2ME网络通信中的应用研究 #### 一、引言 随着嵌入式设备的迅速发展,特别是智能手机和平板电脑等手持移动设备的普及,...通过合理的实现和优化,线程池技术将成为J2ME应用开发中不可或缺的一部分。

    j2Me 实例 笔记源码

    7. **资源管理**:在“rec”文件夹中存放的图片资源是J2ME应用中常见的图形元素,它们可能被用来装饰用户界面或者在游戏中作为角色或背景。 8. **编译与打包**:J2ME应用的开发需要使用专用的J2ME集成开发环境(IDE...

    J2ME中文教程,J2ME手机程序开发

    完成开发后,J2ME应用通常通过OTA(Over-The-Air)方式分发,用户可以通过手机浏览器下载安装。此外,应用也可以预装在设备中或通过应用商店提供。 ### 总结 J2ME作为历史悠久且广泛应用于移动开发的平台,虽然...

    j2me无线应用学习源码.rar

    这部分源码可能会展示如何在J2ME应用中实现数据持久化,包括查询、插入、更新和删除操作。 总的来说,这个压缩包提供了全面的J2ME无线应用开发实践案例,涵盖了从基础到高级的各种主题。对于想要深入学习J2ME的...

    基于J2ME平台的手机应用程序研究与开发

    在J2ME中,数据存储通常使用Record Management System (RMS),它提供了一种存储和检索小数据集的方法。RMS中的RecordStore类可以看作是小型数据库,适合保存应用程序的状态和用户数据。 **应用发布与安装** MIDlet...

    Socket编程在J2ME中的应用

    Socket编程在J2ME中的应用主要涉及到网络通信的基础知识,特别是Java Micro Edition (J2ME) 平台上的网络编程。J2ME是Java平台的一个子集,主要用于嵌入式设备和移动设备,如手机、PDA等。在这个场景下,Socket编程...

    J2ME 中文教程 MIDP2.0

    开发J2ME应用通常需要使用Java IDE,如Eclipse或NetBeans,它们都提供了J2ME项目模板和调试工具。模拟器是测试应用的重要工具,帮助开发者在没有实际设备的情况下预览和测试应用。 **4. GUI编程** J2ME使用MIDP的...

    J2ME 小游戏(多线程操作示例)

    在J2ME中,多线程的使用可以实现游戏的流畅运行,例如,一个线程负责游戏的图形渲染,另一个线程则处理用户的输入事件,这样即使在复杂的交互中,游戏画面也不会出现卡顿。 对于新手开发者来说,了解并实践多线程是...

    j2me中文教程

    - Wireless Toolkit(WTK):Sun Microsystems提供的官方开发工具,包含模拟器,用于测试和调试J2ME应用程序。 - NetBeans或Eclipse:现代IDE,支持J2ME开发,提供了更友好的界面和集成的开发环境。 3. MIDP...

Global site tag (gtag.js) - Google Analytics