利用Java编写手机应用程序 Motorola iDEN篇

作者:王森

█前言

本文内容的所有操作皆在Windows 200操作系统上经过测试。如果您在不同的平台上验证本文内容,例如Windows 98或是Windows Me,则我们无法保证会有相同的结果。

█简介

不知从什幺时候开始,我们突然感觉到周围的人各各拥有一只手机。利用手机与别人沟通成了我们生活的一部份。北欧某些先进国家的人民可以利用手机购买自动售货机所出售的商品;他们也可以在从事行动商务(m-commence) 交易时利用手机做为一种认证工具。在日本,行动电话的许多附加功能更是年轻人文化的一部分。即将来临的3G,无线宽频的美梦带给了人们对无线通讯未来的期待。

然后,有些厂商开始行销它们自己生产的PDA,好象没有一台PDA在手,就不像现代人似的。各式各样的PDA充斥市面,畅销的与不畅销的,让人眼花撩乱。这时产生了一个大问题--要我们同时携带手机和PDA是一件很麻烦的事情-就算它们都还算轻薄短小。如果可以把手机和PDA合而为一岂不是更方便吗? 于是听到消费者心声的厂商,开始有推出手机和PDA合而为一的产品,从NOKIA 9000、Motorola A6188(太极),到最近要推出的Ericsson R380、NOKIA 9210,都是手机与PDA结合的例子,虽然因为售价的关系,但是这些产品仍然是许多人梦寐以求的高阶产品。

尽管有了PDA与手机结合的产品出现,我们仍然觉得有所不足,我想大家一定发现了,就是应用程序的不足,虽然手机和PDA 结合了,可是这些产品上的PDA功能看起来似乎是死的。我们可以任意在PalmOS、Windows CE、EPOC等PDA上使用C/C++或是Java编写这些平台上的应用程序,但是却从未有过机会将我们写好的程序下载到这些手机上执行(即使有些手机的PDA也是使用EPOC,如Ericsson R380)。

相信厂商也听到我们这些喜欢到处写程序的工程师的心声了,所以从2000年年底开始,即将陆陆续续有许多支持Java的手机即将推出,当然,这些厂商也提供了在手机上开发程序的SDK,我们终于可以在手机上面写些小程序自娱了!这是以前等了很久的美梦。

手机平台的多样性与复杂性,和PDA比较起来可以说是有过之而无不及,于是大家就可以知道 - 哈!又是Java派上用场的时候了。我们都知道在浏览器上执行的Java程序叫做Applet,在PalmOS上执行的Java程序叫做Spotlet,然而在手机上执行的应用程序我们称做MIDlet,相信对大家来说都是一个陌生的名词,这也就是本篇文章所要介绍的主角。

支持Java的手机大厂目前已知有NOKIA (预计在2001年年初推出一款支持Java的手机NOKIA 9210)、Motorola(会先在支持integrated Digital enhanced network(iDen)网络的手机上支持Java,然后再推出支持Java的GSM手机,该公司的手机大致会在2002年前全部支持Java)以及NTT DoCoMo(将在明年第二季度左右推出支持Java的手机)。

虽然这些大公司都准备支持Java,不过在决定编写本文时,只有Motorola将其SDK开放于网站上供程序开发者下载测试,所以本文内容都是根据Motorola J2ME SDK所编写。又由于这些手机市面上无法取得,所以我们的程序都是在SDK内附的模拟器中执行,相信最后这些手机上市时,我们所编写的MIDlet应该会很容易并成功地在Motorola的手机上执行才对。

█如何取得Motorola J2ME SDK

我们要做的第一件事情当然还是要先取得SDK罗!
请直接到https://commerce.motorola.com/idenonline/ideveloper/下载Motorola J2ME SDK Drop 7。如果您不是iDEN® Developer的会员,请先在网络上注册(免费)。注册成功之后,Motorola会将密码寄到您的email信箱之中,然后使用该密码登入网页上的My Development Center。进入之后选择网页上头的TOOLS & RESOURCES以进入可下载的开发工具的网页,请选择Motorola J2ME SDK Components Developer Edition, Drop 7.0下方的下载超连结以下载SDK,整个SDK的大小约为3 MB左右。

请注意下载前的Download Page for Motorola J2ME™ SDK安装说明,里头会告诉你安装密码,请先行记下。稍后当您在您的电脑上安装Motorola J2ME SDK的时候,会询问您安装密码,所以请务必记下该密码。

█Motorola J2ME SDK的安装

安装需求
根据官方需求,安装Motorola J2ME SDK的基本配备为:
Pentium 100 MHz微处理器
64 MB RAM
Windows NT WorkStation 4.0配合Service Pack 3 / Win98
(本文是在Windows 2000 Professional中文版上测试)
约6 MB的硬盘空间
Java 2 SDK 1.2.2
(本文使用Java 2 SDK 1.3.0_01作测试)

安装Motorola J2ME SDK
要安装新版Motorola J2ME SDK之前,请先确认您已经将旧版的Motorola J2ME SDK完全删除。您可以由控制台里头的 「新增/删除程序」的功能删除旧版Motorola J2ME SDK。之后再将旧版Motorola J2ME SDK安装所在路径清除即可。
解开您从Motorola网站上下载的ZIP文件,您会看到一个名为Motorola_SDK.exe的文件,双击此文件即可开始安装动作。请注意,在安装时系统会要求您输入密码,请输入之前所纪录的安装密码即可。

█Motorola J2ME SDK目录结构

当您成功地安装Motorola J2ME SDK之后,其目录结构如下图:



█MIDlet程序结构

在手机上执行的程序我们统称MIDlet,其程序结构很简单,与大家熟悉的Applet结构有异曲同工之妙。
每一个MIDlet程序都必须继承自javax.microedition.midlet.MIDlet类别,并实做三个函式,它们分别是:

protected void startApp()
protected void pauseApp()
protected void destroyApp(Boolean unconditional)
而一个MIDlet程序的起始与结束之流程我们以下图来表示:

█编写并编译MIDlet

请先到您的Motorola J2ME SDK安装目录下的demo\midlets目录底下新建一个名为HelloMIDlet.java的文件,其内容为:
HelloMIDlet.java


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

public class HelloMIDlet extends MIDlet
{

HelloMIDlet()
{
}

protected void startApp() throws MIDletStateChangeException
{
}

protected void pauseApp()
{
}

protected void destroyApp(boolean unconditional)
throws MIDletStateChangeException
{
}
}


在demo\midlets目录之中您会看到一个名为compileAll.bat的批处理文件,前面有提到,这个批处理文件除了可以帮助您编译所有内附范例程序之外,还可以简化您自己所编写的MIDlet之编译流程。
请在命令列视窗输入:

compileAll_HelloMIDlet.java

如果成功编译,屏幕上输出的结果如下图所示:




从屏幕上的输出,您可以发现,要让MIDlet可以在手机上执行,大体上还是要经过两个动作(与Spotlet相似),分别是编译(compiling)以及预先审核(Preverifying)。
有关预先审核这个动作,笔者已经在RUN!PC 11月号的文章「利用Java 编写PalmOS应用程序基础篇」之中有向读者们提过,如果对这个方面有所疑问的朋友,请再将您的RUN!PC 11月号翻出来复习一下吧!

从这个简短的MIDlet制作流程里头,相信大家可以发现,比起纯粹用Sun的CLDC编写能在KVM上执行的Spotlet要简单上许多。原因是compileAll.bat这个批处理文件帮我们做掉大部分dirty work。
那幺,如果您写好的程序并非放在demo\midlets目录之中,是否就无法编译了? 从compileAll.bat里头,我们可以发现它帮我们完成上述两项工作的指令。如果我们写好的程序放在demo\midlets目录以外的地方,您可以执行下面的指令,您仍然可以成功地制作MIDlet:

(我们假设您的Motorola J2ME SDK安装在D\MotoSDK目录之中,并将您自行撰写的HelloMIDlet.java置于d\jdk1.3.0_01\my目录之中。同时我们假设您在d\jdk1.3.0_01\my目录底下执行下面指令。)

编译:

javac -O -bootclasspath d\MotoSDK\lib HelloMIDlet.java

注意:

1. -bootclasspath指向类别函式库的所在位置。


预先审核:

d\MotoSDK\bin\preverifier -classpath d\MotoSDK\lib;. -d . HelloMIDlet

注意:

1. -classpath指向类别函式库的所在位置,也要指向我们所撰写的MIDlet所在的位置。

2. -d指向您希望预先编译类别档产生之后所放置的路径,如果写"."表示本目录,会覆盖掉原先未经过预先编译的类别档。如果您没有指定,则默认值为".\output"目录。


当然,如果您嫌自己手动操作很麻烦,您可以将compileAll.bat复制到其它目录之中,并更改其编译指令与预先审核指令之中和类函数库有关的相关设定即可。
提到compileAll.bat,顺便向各位读者说明一下,在前面有提到,compileAll.bat可以帮助您编译所有位于demo\midlets目录下的范例程序,您只要在命列列下直接输入: compileAll即可。compileAll.bat会自动当您编译的package有以下几项:
com.mot.j2me.midlets.bounce
com.mot.j2me.midlets.imagetests
com.mot.j2me.midlets.paddleball
com.mot.j2me.midlets.scribble
com.mot.j2me.midlets.tests
com.mot.j2me.midlets.tutorials


如果您希望compileAll.bat自动帮您编译其它package底下的程序,请您开启compileAll.bat,修改其COMPILECLASS环境变数的设定即可。

█修改并执行MIDlet

写好程序之后,大家最希望的事情当然就是让它在手机上执行,不过由于目前大家无法取得Motorola这些支持Java的手机,所以我们只能在Motorola J2ME SDK内附的模拟器上执行我们写好的MIDlet。相信手机正式推出时,应该可以顺利地在手机上执行。底下笔者将告诉您如何使用Motorola J2ME SDK内附的模拟器来测试您所编写的MIDlet。
在这之前,由于之前我们所编写的范例程序只是简单的MIDlet空壳,我们必须让它能够在模拟器上显示出一些信息才可以,因此请修改上一个程序范例,使它的内容如下:
HelloMIDlet.java

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

public class HelloMIDlet extends MIDlet
{

private Display firstDisplay ;
private Form firstForm ;
HelloMIDlet()
{
firstDisplay = Display.getDisplay(this) ;
firstForm = new Form("Hello MIDlet") ;
StringItem firstStrItem = new StringItem("Hello","MIDlet") ;
firstForm.append(firstStrItem) ;
}

protected void startApp() throws MIDletStateChangeException
{
firstDisplay.setCurrent(firstForm) ;
}

protected void pauseApp()
{
}

protected void destroyApp(boolean unconditional)
throws MIDletStateChangeException
{
}
}

请将此文件放置在\demo\midlets目录下。编译完成之后,请将目录切换到scripts子目录之中,您会在此目录里发现一些写好的批处理文件。请在该目录下执行这些批处理文件指令以启动模拟器。执行的指令与执行结果如下所示:

指令: runEmul HelloMIDlet
输出结果:


指令: runMotoi1000 HelloMIDlet
输出结果:


指令: runMotoiDEN HelloMIDlet
输出结果 :


指令: runStarTac HelloMIDlet
输出结果:


指令: runMyDevice HelloMIDlet

输出结果

Error loading property file C/properties/mydevice.props (系统找不到指定的路径。)

会出现错误讯息是因为您没有指定属于使用者自订的手机外观的缘故,后面将会教您如何设定使用者自订的手机外观。





如果我们将写好的程序放在demo\midlets目录以外的地方,您可以执行下面的指令,仍然可以成功地启动模拟器并执行MIDlet:
(我们假设您的Motorola J2ME SDK安装在D:\MotoSDK目录之中,并将您自行编写的HelloMIDlet.java置于d:\jdk1.3.0_01\my目录之中。同时我们假设您在d:\jdk1.3.0_01\my目录底下执行下面指令。)

执行一般仿真器

java -Djava.library.path=d\MotoSDK\lib
-classpath d
\MotoSDK\bin\Emulator.jar;d\MotoSDK\bin\ConfigTool.jar com.mot.tools.j2me.emulator.Emulator
-classpath.;d
\MotoSDK\lib javax.microedition.midlet.AppManager HelloMIDlet-JSA 1 1

请注意:
第一个-classpath设定,-classpath 与路径名称之间有空格。
第二个-classpath设定,-classpath 与路径名称之间没有空格。

执行仿真器并套用i1000手机外观

java -Djava.library.path=d\MotoSDK\lib
-classpath d
\MotoSDK\bin\Emulator.jar;d\MotoSDK\bin\ConfigTool.jar
com.mot.tools.j2me.emulator.Emulator
-classpath.;d
\MotoSDK\lib
-deviceFile resources\Motorolai1000.props
javax.microedition.midlet.AppManager
HelloMIDlet
-JSA 1 1

请注意:
第一个-classpath设定,-classpath与路径名称之间有空格。
第二个-classpath设定,-classpath与路径名称之间没有空格。

执行仿真器并套用iDEN手机外观

java -Djava.library.path=d\MotoSDK\lib
-classpath d
\MotoSDK\bin\Emulator.jar;d\MotoSDK\bin\ConfigTool.jar
com.mot.tools.j2me.emulator.Emulator
-classpath.;d
\MotoSDK\lib

-deviceFile resources\MotorolaiDENPlatform.props

javax.microedition.midlet.AppManager
HelloMIDlet
-JSA 1 1

请注意:
第一个-classpath设定,-classpath与路径名称之间有空格。
第二个-classpath设定,-classpath与路径名称之间没有空格。

执行仿真器并套用StarTac手机外观

java -Djava.library.path=d\MotoSDK\lib
-classpath d
\MotoSDK\bin\Emulator.jar;d\MotoSDK\bin\ConfigTool.jar
com.mot.tools.j2me.emulator.Emulator
-classpath.;d
\MotoSDK\lib

-deviceFile resources\StarTac.props

javax.microedition.midlet.AppManager
HelloMIDlet
-JSA 1 1

请注意:
第一个-classpath设定,-classpath与路径名称之间有空格。
第二个-classpath设定,-classpath与路径名称之间没有空格。

执行仿真器并套用使用者自订手机外观

java -Djava.library.path=d\MotoSDK\lib
-classpath d
\MotoSDK\bin\Emulator.jar;d\MotoSDK\bin\ConfigTool.jar
com.mot.tools.j2me.emulator.Emulator
-classpath.;d
\MotoSDK\lib

-deviceFile < 您的props文件所在的绝对路径 >

javax.microedition.midlet.AppManager
HelloMIDlet
-JSA 1 1

请注意:
第一个-classpath设定,-classpath与路径名称之间有空格。
第二个-classpath设定,-classpath与路径名称之间没有空格。
如果您将您的props檔放在d\MotoSDK\binresources目录之下,则上述指令只要改成:

-deviceFile resources\< 您的props檔名 >

注意,当您您直接使用上述指令激活仿真器,如果出现底下错误讯息:

Error loading property file resources/defaultdevice.props (系统找不到指定的路径 )

这是因为您没有将d\MotoSDK\bin目录下的resources子目录复制到d\JDK1.3.0_01\my目录之下的缘故。

MIDlet进行除错

撰写PalmOS上的Spotlet时,我们可以利用System.out.println()函式帮我们印出一些讯息以帮助除错,那么在手机上的MIDlet? 原则上,我们还是可以利用System.out.println()函式做一些输出。当仿真器执行时,就会在命令列上输出一些讯息。

另外,在PalmOS上,有KVMutil.prc可以帮助我们纪录程序所输出的讯息。那手机上呢? 因为没有实际的机器可以测试,因此这个问题到现在还不得而知,相信Motorola到时候会有完善的解决方案吧!

Motorola J2ME SDK内附的说明文件之中,概略地提到了除错的问题,里头提到,往后如果我们要进行上机除错(on-device debugging)的话,必须要满足几个条件:

1. 机器本身要具备除错相关功能,并与KDWP(Kvm Debug Wire Protocol)兼容。因为除错时,除错工具需要利用KDWP和机器上交谈以取得除错信息。

2. 制造厂商本身要提供下载MIDlet到手机上以进行除错的方法。

3. 提供对MIDlet除错的工具,必须支持手机在利用KDWP除错时所此用的传输媒介(例如序列阜或UDP)

嗯,看起来除错方式有解,MIDlet程序的撰写应该是很方便的事情了。

Motorola J2ME SDK对中文的支持

相信看过RUN!PC 11月号的文章「利用Java 撰写PalmOS应用程序基础篇」的读者,在撰写PalmOS上的Spotlet时一定会遇到中文无法正常显示的问题。中文的问题分成两个部分,一个是在使用者接口上的中文问题,一个是在命令列输出(利用System.out.println()函式所做的输出)上的中文问题,请大家做个小实验,将前面我们所撰写的程序改如下:

HelloMIDlet.java

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

public class HelloMIDlet extends MIDlet
{
<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>private Display firstDisplay ;
<shape id="_x0000_i1026" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>private Form firstForm ;
<shape id="_x0000_i1027" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>HelloMIDlet()
<shape id="_x0000_i1028" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>{
<shape id="_x0000_i1029" style="WIDTH: 30pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>firstDisplay = Display.getDisplay(this) ;
<shape id="_x0000_i1030" style="WIDTH: 30pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>firstForm = new Form("
哈啰!MIDlet") ;
<shape id="_x0000_i1031" style="WIDTH: 30pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>StringItem firstStrItem = new StringItem("
哈啰","米德列特") ;
<shape id="_x0000_i1032" style="WIDTH: 30pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>firstForm.append(firstStrItem) ;
<shape id="_x0000_i1033" style="WIDTH: 30pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>System.out.println("MIDlet
激活") ;
<shape id="_x0000_i1034" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>}

<shape id="_x0000_i1035" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>protected void startApp() throws MIDletStateChangeException
<shape id="_x0000_i1036" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>{
<shape id="_x0000_i1037" style="WIDTH: 30pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>firstDisplay.setCurrent(firstForm) ;
<shape id="_x0000_i1038" style="WIDTH: 7.5pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>}

<shape id="_x0000_i1039" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>protected void pauseApp()
<shape id="_x0000_i1040" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>{
<shape id="_x0000_i1041" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>}

<shape id="_x0000_i1042" style="WIDTH: 15pt; HEIGHT: 7.5pt" alt="" type="#_x0000_t75"><imagedata o:href="http://edu.uuu.com.tw/article/images/0.gif" src="file:///C:/DOCUME~1/Owner/LOCALS~1/Temp/msoclip1/01/clip_image001.gif"></imagedata></shape>protected void destroyApp(boolean unconditional)
<shape id="_x0000_i1043" style="WIDTH: 30pt; HEIGHT: 7.5pt" alt="" type="#_x0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值