QQ登录

只需一步,快速开始

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 1002|回复: 2

J2ME Wireless Toolkit 常见问题

[复制链接]
发表于 2004-5-10 11:36:35 | 显示全部楼层 |阅读模式
J2ME Wireless Toolkit 常见问题

转自Move2008

问:Windows  上支持 J2ME Wireless Toolkit 吗?

答:不支持。

问:可以在 Solaris 或 Linux 操作系统上使用 J2ME Wireless Toolkit 吗?

答:可以。

问:在 Linux 上运行一个应用程序时,仿真器窗口不显示整个设备图像。

A. 由于在 inux 中 KDE 2.0 和更高版本中存在 JDK 错误 4392053,当运行默认仿真器时可能会遇到问题。我们还没有找到避免此问题的解决办法。使用 GNOME 或 KDE 1.0 或更高版本,这可能是一种解决方案。

Q. 我无法从 Sun ONE Studio 4 移动版运行 J2ME Wireless Toolkit。

A. 有关从 Sun ONE Studio 4 移动版运行 Wireless Toolkit 的帮助信息,请参见 Sun ONE Studio 4 移动版的文档。

问:我可以从哪些地方获得有关 MIDP 和 CLDC 的更多信息?

答:访问 http://java.sun.com/products/midp/ 和 http://java.sun.com/products/cldc/。

问:我可以从哪些地方获得有关 Sun ONE Studio 4 移动版 IDE 的更多信息?

答:访问 http://www.sun.com/forte/ffj/ 和 http://www.netbeans.org/。

问:我可以将此工具箱与其他 Java 实现一起使用吗?

答:不可以。您应当安装 Java 2 Platform,Standard Edition 1.3 或更高版本,正如系统要求一节中说明的那样。

问:当我尝试编译一个 MIDlet 时,有消息提示我需要使用完全的 J2SE SDK。

答:若要用 J2ME Wireless Toolkit 开发应用程序,需要使用 J2SE SDK,版本 1.3 或更高,如系统需求 一节所述。如果您只是要运行应用程序,而不是进行开发,请使用 Java 2 Standard Edition Runtime Environment。

若要解决此问题,请遵循以下步骤:

卸载 J2ME Wireless Toolkit。
安装 Java 2 Standard Edition SDK。可以从 http://java.sun.com/j2se/1.4/download.html 下载您需要的版本。
重新安装 J2ME Wireless Toolkit。

问:我要使用一个外部库,但当我试图生成一个使用此库的 MIDlet 套件时,出现“预先验证类...时出错”消息。

答:发生此错误可能是因为您正在使用一个为 Java 2 Platform,Standard Edition 所写的类库。J2ME Wireless Toolkit 支持 MIDP/CLDC 应用程序的开发,与使用 J2SE 开发的对等应用程序相比,它们的功能则大为受限。

问:运行预先验证程序之前,出现“加载类...时出错”消息。

答:首先,检查 Java 源和库是否可用于 CLDC 和 MIDP API。

然后,确保在一个其路径不包含任何空格的目录下安装了 J2ME Wireless Toolkit。例如,此工具箱不处理路径 C:\Program Files\wtk104,因为 C:\Program 和 Files\wtk104 之间存在空格。

如果您在一个其路径包含空格的目录下安装此工具箱,您必须卸载此工具箱并将其重新安装在一个其路径不包含任何空格的目录下。

问:我可以在 J2ME 应用程序中使用 HTTP 和 HTTPS 之外的网络协议吗?

答:MIDP 规范仅要求具备通过 HTTP 通信的能力。例如,此工具箱的“Palm OS 设备”仿真仅支持 HTTP。然而,在提供的其他仿真设备中,此工具箱支持 HTTPS 连接。

此工具箱还执行 TCP/IP 连通性(借助 socket:和 serversocket:URL)、UDP 数据报(借助 diagram:URL)和串行连通性(借助 comm:URL),但是仅出于实验目的包括这些执行程序。它们不像 HTTP 和 HTTPS 执行程序那样完全经过测试,因而不受支持。

问:我的一个使用 HTTP 的应用程序可以在发行版 1.0.1 中运行,但在当前发行版中却无法运行。

答:有关使用 HTTP 1.0 和 HTTP 1.1 的信息,请参见 Wireless Toolkit 用户指南 中第 3 章“仿真器”中的“选择 HTTP 版本”

问:如何模拟在提供的示例设备之外的设备上运行 MIDlet?

答:请参见 Wireless Toolkit 基本定制指南 中第 3 章“设备属性文件”。

问:仿真器不能正确显示英语以外的其他语言版本的文本。如何改变字体?

答:更新您正在使用的设备的主设备属性文件。有关详细信息,请参见 Wireless Toolkit 基本定制指南 中第 3 章“设备属性文件”中的“字体”。

问:仿真器显示的设备图像太小。如何放大该图形?

答:更新您正在使用的设备的主设备属性文件。有关详细信息,请参见 Wireless Toolkit 基本定制指南 中第 3 章“设备属性文件”中的“缩放”。

问:在仿真器中如何启用触摸屏事件?

答:更新您正在使用的设备的主设备属性文件。有关详细信息,请参见 Wireless Toolkit 基本定制指南 中第 3 章“设备属性文件”中的“触摸屏”。

问:如何改变一台设备的字符编码转换器设置?

答:有关更多信息,请参见 Wireless Toolkit 基本定制指南 中第 3 章“设备属性文件”中的“字符编码”。

问:基本定制指南没有提供有关我需要进行的定制的信息。

答:发送邮件至 [email protected],询问是否能对 J2ME Wireless Toolkit 进行更进一步的定制。

问:如何将一个 MIDlet 套件传动到真实设备?

答:完全取决于设备。支持 MIDP 的每个设备制造商都必须提供将 MIDlet 传送到其设备上的机制。

问:如何将 MIDlet 套件部署到一个 Palm OS 设备上?

答:有关更多信息,请参见 http://java.sun.com/products/midp/palmOS.html 上提供的 Palm OS 产品的 MIDP。

问:我有另外的问题,在这找不到答案。

答:将有关 J2ME Wireless Toolkit 的问题发送至 [email protected]
 楼主| 发表于 2004-5-10 11:50:04 | 显示全部楼层
深入应用MIDP函数类型
作者:王森    本文选自:开放系统世界—赛迪网  2003年04月10日        

不管多么花俏的平台,都是用几个最简单的功能慢慢架构出来的。因此要全盘了解任何一个平台的程序设计技巧,首先一定要了解最底层的函数库。本文将深入探讨MIDP所提供的函数类型,这些函数也是Java手机程序设计的基础。



系统参数的撷取



MIDlet在运作时,必须通过java.lang.System. getProperty()函数来取得系统属性。这些系统属性可以让MIDlet了解它们深处的环境相关信息。它们分别是:

1. microedition.profiles:取得系统所支持的所有Profile信息;

2. microedition.configuration:取得系统所支持的Configuration信息;

3. microedition.locale:取得系统目前所使用的地区信息;

4. microedition.plarform:MIDlet所在平台(或机器)的名称或型号;

5. microedition.encoding:取得系统默认使用的语言编码信息。



字符串与基本类型的转换



MIDlet在运作时,可以透过MIDlet.getAppProperty()函数来取得清单文件或描述文件之中属性的属性值,取得之后都视为字符串。如果需要做一些数学运算,就必须先把字符串转换成数值才行。

要把字符串转换成数值,必须依靠定义在java.lang之中的Byte、Short、Integer、Long四个类型之中的parseXXX()方法。这四个类型都是整型,只有范围上的不同。如果我们截取的字符串,转换成数值之后超过该类型所能负担的范围,就会产生异常情况。

由于CLDC 1.0并不支持浮点数,所以并没有对应浮点数的类型,只有整数类型。但是从CLDC 1.1之后开始支持浮点数,所以CLDC 1.1之后新增Float和Double两个类型。

我们还可以利用java.lang.Character来判断某个字符是否为数字、英文字母大小写,也可以利用此类别转换英文字母的大小写。



执行时间的测量



当MIDlet运作时,随时都可以利用System. currentTimeMillis()取得目前时间与1970年1月1日零时 UTC时间(协调世界时)的差距,传回值为Long类型,而且以毫秒来表示。程序任何两个地方调用System. currentTimeMillis()之后,将所得的结果相减,就是所经过的毫秒数。范例TimeMeasureTest.java如下:



import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class TimeMeasureTest extends MIDlet
{
public TimeMeasureTest()
{
}
public void startApp()
{
  long start = System.currentTimeMillis() ;
  //随便做一些事情
  for(int i=0 ; i <1000000 ; i++) ;
  long end = System.currentTimeMillis() ;
  System.out.println("Pass " + (end-start) + " Milliseconds") ;
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}

执行结果:
Pass 234 Milliseconds




日期处理



在java.util中,Calendar、Date、TimeZone是三个用来处理与日期相关功能的类型。下面将介绍它们的用法。

TimeZone类型所代表的是时区,全球的统一时间称作UTC。也就是说,在全世界任何时间点上,时间的表示都一样。根据每个地区所在的位置不同,又制定了时区的概念。这些时区如表1所示,详细的时区表请参见http://linux.ccidnet.com期刊浏览2003年第4期。

表1 时区表



时区与 UTC 的偏移量 (+1:00代表往描述
后一个小时,-1:00代表往前一
个小时,与一般标准表示法相反)
NZDT+13:00            新西兰白昼时间(夏时制)
IDLE+12:00            国际日期变更线,东边
NZST+12:00            新西兰标准时间
NZT+12:00            新西兰时间
AESST+11:00             澳大利亚东部标准夏时制
ACSST+10:30             中澳大利亚标准夏时制
NT-11:00            州时间(Nome Time)
IDLW-12:00            国际日期变更线,西边
............            ......


格林威治时间(GMT)与UTC是没有时间差的。其它时区都会和GMT或UTC有个时间差。所以如果以时区来分的话,每个时区相同的时间有可能是不同的UTC。

TimeZone无法用new来产生,只能通过TimeZone.getDefault()取得系统默认使用的时区,或使用TimeZone.getTimeZone(id)指定要使用的时区来产生。产生之后,就可以利用某个TimeZone类型的实体调用getID()方法来取得所使用的时区。useDaylightTime()可以得知是否使用日光节约时间,getRawOffset()可以得知此TimeZone使用的时区和GMT相差多少。

TimeZone有个静态方法叫做getAvailableIDs(),可以取得系统所有支持的时区。除非特殊目的,否则一般请用TimeZone.getDefault()取得系统默认使用的时区。

Date类别负责记录与1970年1月1日零时UTC时间的差距,一般可以使用“Date date = new Date(time) ;”来产生,time就是与1970年1月1日零时UTC时间的差距,以毫秒来计。如果使用“Date date = new Date();”来产生Date类别,就如同调用了“Date date = new Date(System.currentTimeMillis());”,也就是Date会封装new时的时间。

Date类型本身除了以毫秒存放与1970年1月1日零时 UTC时间的差距外,本身没有其它功能了。要从Date类型解译出正确的年、月、日、星期几、几点、几分、几秒的信息,就必须依靠Calendar类别才行。Calendar无法用new产生,只能通过Calendar.getInstance()取得使用系统默认时区与地区所建立的Calendar,或使用Calendar. getInstance(TimeZone tz)指定要使用的时区来产生Calendar。所以调用Calendar.getInstance(),实际上等同于调用Calendar.getInstance(TimeZone.getDefault())。

有了Calendar的实体之后,就可以利用其setTime()生成一个Date对象的实体,或是setTimeMillis()生成一个long类型的数值来设定Calendar的内容。范例CalendarTest.java如下:



import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
public class CalendarTest extends MIDlet
{
public CalendarTest()
{
}
public void startApp()
{
  Calendar cal = Calendar.getInstance() ;
  Date now = new Date() ;
  cal.setTime(now) ;
  System.out.println(cal) ;
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}


执行结果:



Sun, 10 Nov 2002 10:04:19 UTC


Calendar、Date与TimeZone类型将来都可以和LCDUI函数库之中的DateField类型紧密配合。除了直接将Calendar对象的实体打印出来之外,还可以利用set与get方法来设定或取得部分信息。



Timer与TimerTask的使用

java.util套件之中有两个类型与工作排程非常有关系,它们分别是Timer与TimerTask。其中,Timer是一个定时器,可以设定成特定时间或特定时间周期产生信号。不过,只有Timer是没有用的,必须配合TimerTask才起作用。Timer一旦与某个TimerTask产生关联,就会在产生信号的同时连带执行TimerTask所定义的工作。

TimerTask的制作非常容易,任何一个类型只要继承TimerTask类型,并调用其run()方法即可。run()方法就是自行定义的工作。一旦Timer在特定时间或特定时间周期产生信号,run()方法就会被执行。我们会透过Timer的schedule()方法来设定特定时间或特定时间周期,并将它与某个TimerTask联系(进行TimerTask的排程)。最后,可以使用Timer的cancel()方法来停止Timer。调用cancel()之后,Timer就会和TimerTask脱离关系。TimerTask本身也有cancel()方法。



在特定时间产生信号

假设希望在3秒之后执行某些工作,范例程序MyTask1.java如下:



import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class MyTask1 extends TimerTask
{
public void run()
{
  //打印出run()被调用的时间
  System.out.println("Task1 Fire Time:") ;
  System.out.println(System.currentTimeMillis());
}
}

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
public class TimerTest1 extends MIDlet
{
public TimerTest1()
{
}
public void startApp()
{
  Timer timer = new Timer() ;
  //规定3000毫秒之后(3秒)激活MyTask1
  timer.schedule(new MyTask1(),3000);
  //显示出排程后的时间
  System.out.println("Task Schedule Time:") ;
  System.out.println(System.currentTimeMillis());
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}

执行结果如下:
Task Schedule Time:
1036932682671
Task1 Fire Time:
1036932685671


执行时,屏幕上会先显示出:



Task Schedule Time:
1036932682671


大约3秒钟之后才会显示出:



Task1 Fire Time:
1036932685671


我们可以发现两者的时间差距为3000毫秒。

Timer的schedule()方法除了可以给定一个Long类型外,也可以给定一个Date类型的实体。如果希望从现在起5秒之后执行某些工作,范例程序MyTask2.java如下:



import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class MyTask2 extends TimerTask
{
public void run()
{
  System.out.println("Task2 Fire Time:") ;
  System.out.println(System.currentTimeMillis());
}
}

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
public class TimerTest2 extends MIDlet
{
public TimerTest2()
{
}
public void startApp()
{
  Timer timer = new Timer() ;
  Date after5sec = new Date(System.currentTimeMillis()+5000) ;
  timer.schedule(new MyTask2(),after5sec);
  System.out.println("Task Schedule Time:") ;
  System.out.println(System.currentTimeMillis());
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}


执行结果如下:



Task Schedule Time:
1036933242234
Task2 Fire Time:
1036933247250


执行时,屏幕上会先显示出:



Task Schedule Time:
1036933242234


大约5秒钟之后才会显示出:



Task2 Fire Time:
1036933247250


我们可以发现两者的时间差距大约为5000毫秒。之所以无法确定差距是5000毫秒,是因为在排程(呼叫timer.schedule(new MyTask2(),after5sec))之前,系统必须先取得目前时间,再产生Date对象,这些动作都会造成些许的时间差。

在特定时间周期产生信号

上述两个范例都是设定在某个特定时间产生信号,接下来将示范如何在某个特定的时间周期产生信号。假设希望在5秒钟之后,每隔4秒中产生信号,范例程序MyTask3.java如下:



import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class MyTask3 extends TimerTask
{
long lasttime ;
public void run()
{
  long now = System.currentTimeMillis();
  System.out.print("Task3 Fire Time: ") ;
  System.out.print(now);
  System.out.print(" ; Intervel :") ;
  //
  System.out.println(now-lasttime);
  //
  lasttime = now ;
}
}

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
public class TimerTest3 extends MIDlet
{
Timer timer ;
public TimerTest3()
{
}
public void startApp()
{
  Form f = new Form("Test") ;
  Display.getDisplay(this).setCurrent(f) ;
  timer = new Timer() ;
  //Date after5sec = new Date(System.currentTimeMillis()+5000) ;
  MyTask3 mt = new MyTask3() ;
  mt.lasttime = System.currentTimeMillis() ;
  //timer.schedule(mt,after5sec,4000);
  timer.schedule(mt,5000,4000);
  System.out.println("Task Schedule Time:") ;
  System.out.println(System.currentTimeMillis());
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
  timer.cancel() ;
}
}


执行结果如下:



Task Schedule Time:
1036934383453
Task3 Fire Time: 1036934388453 ; Intervel :5000
Task3 Fire Time: 1036934392453 ; Intervel :4000
Task3 Fire Time: 1036934396453 ; Intervel :4000
Task3 Fire Time: 1036934400453 ; Intervel :4000
Task3 Fire Time: 1036934404453 ; Intervel :4000
....


从范例MyTask3.java中可以看出,第一个信号出现在5秒之后,以后的信号都是每隔4秒产生一次。周期性产生信号所使用的sechdule()方法,其第二个参数可以是Long型或Date型。

在MyTask3.java之中,我们使用“long now = System.currentTimeMillis();”来抓取run()一开始执行时的时间。如果任何时候想知道这一次run()的激活时间,可以随时调用TimerTask的scheduledExecutionTime()方法。所以上述取得开始时间的方式可以用“long now = scheduledExecutionTime();”来取代,而MyTask3.java可以改成下面方式:



import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class MyTask3 extends TimerTask
{
long lasttime ;
public void run()
{
  System.out.print("Task3 Fire Time: ") ;
  long now = scheduledExecutionTime() ;
  System.out.print(now);
  System.out.print(" ; Intervel :") ;
  System.out.println(now-lasttime);
  lasttime = now ;
}
}




由于scheduledExecutionTime()方法只传回这次run()的起始时间,所以在任何地方呼叫都不会影响到时间精确度。更重要的一点,如果scheduledExecutionTime()和System.currentTimeMillis()都可以达到目的时,使用scheduledExecutionTime()不会浪费系统资源。

另外要注意的是,当不希望Timer再产生信号时,可以使用Timer的cancel()方法来停止Timer。调用cancel()之后,Timer就会和TimerTask脱离关系,而且不再产生任何信号,此时TimerTask才能重新被排程。Timer属于系统资源,每次MIDlet离开之前,都应该使用Timer的cancel()方法来停止Timer。TimerTask本身也有cancel()方法,如果TimerTask被安排在特定的时间周期内执行,那么调用TimerTask的cancel()方法之后,也可以保证以后其工作都不会再被执行。



信号精确度

最后,我们要讨论的是信号精确度的问题。前面的范例中,TimerTask里只做简单的计算。由于简单的计算花的时间不多,所以都可以在下次重新产生信号前完成。可是,如果run()里的工作无法在下次产生信号前完成呢?举例来说,我们希望每4秒钟产生一次信号,但是run()方法有时却需要5.5秒完成工作,有时2.2秒就完成了。有关范例MyTask4.java如下:



import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;

public class MyTask4 extends TimerTask
{
long lasttime ;
public void run()
{
  long now = System.currentTimeMillis();
  System.out.print("Task4 Fire Time: ") ;
  System.out.print(now);
  System.out.print(" ; Intervel :") ;
  System.out.println(now-lasttime);
  lasttime = now ;
  doSomething() ;
}
public void doSomething()
{
  //用随机数来仿真工作所需时间,有时超过4000毫秒,有时少于4000毫秒
  try
  {
   Random rdm = new Random() ;
   int i = rdm.nextInt()%2 ;
   if(i==0)
   {
    Thread.sleep(2500) ;
    System.out.println("doSomething : 2500 millisecond");
   }else
   {
    Thread.sleep(5500) ;
    System.out.println("doSomething : 5500 millisecond");
   }
  }catch(Exception e){}
}
}
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import java.util.*;
public class TimerTest4 extends MIDlet
{Timer timer ;
public TimerTest4()
{}
public void startApp()
{
  Form f = new Form("Test") ;
  Display.getDisplay(this).setCurrent(f) ;
  timer = new Timer() ;
  //Date after5sec = new Date(System.currentTimeMillis()+5000) ;
  MyTask4 mt = new MyTask4() ;
  mt.lasttime = System.currentTimeMillis() ;
  //timer.schedule(mt,after5sec,4000);
  timer.schedule(mt,5000,4000);
  System.out.println("Task Schedule Time:") ;
  System.out.println(System.currentTimeMillis());
}
public void pauseApp()
{}
public void destroyApp(boolean unconditional)
{
  timer.cancel() ;
}
}


执行结果如下:



Task Schedule Time:
1036936893265
Task4 Fire Time: 1036936898265 ; Intervel :5000
doSomething : 5500 millisecond
Task4 Fire Time: 1036936903765 ; Intervel :5500
doSomething : 2500 millisecond
Task4 Fire Time: 1036936907765 ; Intervel :4000
doSomething : 2500 millisecond
Task4 Fire Time: 1036936911765 ; Intervel :4000
doSomething : 5500 millisecond
Task4 Fire Time: 1036936917265 ; Intervel :5500
doSomething : 5500 millisecond
Task4 Fire Time: 1036936922765 ; Intervel :5500
doSomething : 2500 millisecond
Task4 Fire Time: 1036936926765 ; Intervel :4000
doSomething : 5500 millisecond


从这个范例可以看出,schedule()方法的运行属于一种称为fixed-delay execution的模式。也就是说,如果run()在时间周期之内完成,那么下一次的信号就会在下一次预定的时间产生。但是,如果run()无法在时间周期之内完成,那么下一次的信号就延迟,而且上一个run执行之后就会立刻产生信号。所以,会有上述的结果。

另外一个可以在固定周期产生信号的方法为scheduleAtFixedRate(),但是其特性和schedule()不太相同。有关范例TimerTest5.java请参见http://linux.ccidnet.com期刊浏览2003年第4期(TimerTask的部分仍采用MyTask4)。

从范例TimerTest5.java中可以看出,scheduleAtFixedRate()方法的运行属于一种称为fixed-rate execution的模式。也就是说,如果run()在时间周期之内完成,那么下一次的信号就会在下一次预定的时间产生。但是,如果run()无法在时间周期之内完成,那么等到run完成之后,下一次的信号产生就尽量赶上原本应该产生信号的时间。以上面的例子来说,第二次的讯号应该在第一次产生信号4000毫秒之后产生,可是却因为上一次的执行使得必需延迟1500毫秒。同样,第三次信号应该在第一次产生信号8000毫秒之后产生,可是由于第二次的run()延迟1500毫秒才完成,所以总计就比预计时间延迟了3000毫秒。而第三次所需工作时间只要2500毫秒。完成第三次run()之后,系统必须要尽快赶上原本预计的时间。所以立刻产生信号,使得第四次与第三次之间的时间差只有2500毫秒,此时就赶上了1500毫秒。第四次所需工作时间只要2500毫秒,因此完成第四次run()之后,系统必须要尽快赶上原本预计的时间,所以立刻产生信号,使得第五次与第四次之间的时间差只有2500毫秒,此时又赶上了1500毫秒。到目前为止,已经把落后的时间都追回来了,这就是尽量赶上的意思。即使第五次的工作只花了2500毫秒就完成,但是由于没有落后的进度需要追赶,所以和第六次的时间相差就是预计的4000毫秒。
回复

使用道具 举报

 楼主| 发表于 2004-5-10 11:58:30 | 显示全部楼层
http://www-900.ibm.com/developerWorks/cn/linux/wireless/wi-pvcapps/index.shtml
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

GMT+8, 2024-11-20 04:48 , Processed in 0.038712 second(s), 16 queries .

© 2021 Powered by Discuz! X3.5.

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