下載 項(xiàng)目源代碼
在本系列的第一篇文章中,我們構(gòu)建了一個(gè)簡單的可在任何支持 TCP/ip 插槽的 MIDP 設(shè)備上運(yùn)行的終端模擬器。它包含一個(gè)實(shí)現(xiàn) Telnet 協(xié)議的 Connection
和一個(gè)經(jīng)過定制來顯示終端內(nèi)容的 Canvas
。在第二篇文章中,我假定您已經(jīng)閱讀了第一篇文章并熟悉了這些組件。
現(xiàn)在我們將進(jìn)一步加強(qiáng)這個(gè)應(yīng)用程序。首先,我們將通過添加一個(gè)更高級的終端類型來增加一些復(fù)雜性。然后我們將添加對用戶輸入的支持——注意大多數(shù)移動(dòng)設(shè)備的各種限制。完成后,我們就能夠使用這個(gè)應(yīng)用程序來通過 Telnet 連接到遠(yuǎn)程服務(wù)器并運(yùn)行許多種類的程序,而這一切都是從您的 MIDP 設(shè)備來進(jìn)行的。
在上一篇文章中我們實(shí)現(xiàn)了一個(gè)“啞巴”終端。它顯示的僅僅是一個(gè)字符流,當(dāng)進(jìn)入的字符到達(dá)屏幕的邊緣時(shí)就換行到下一行,在遇到一個(gè)換行符時(shí)就跳到下一行。雖然這種程度的交互性對于整代的命令行應(yīng)用程序是足夠的,但較復(fù)雜的軟件則將屏幕作為一個(gè)整體來處理,這就需要編寫和清除特定位置的字符以提供更好的用戶體驗(yàn)。
這里有一段有趣的歷史。在 20 世紀(jì) 70 年代,眾多生產(chǎn)視頻終端的廠商提供的屏幕操縱功能專有且各不相同,并以不兼容的方式來實(shí)現(xiàn)。要編寫可利用多種這樣的設(shè)備的面向屏幕的軟件就很困難。
American National Standards Institute (ANSI) 參與進(jìn)來。堅(jiān)持其通過可互操作性來支持商業(yè)的要求,ANSI 介入并發(fā)布了標(biāo)準(zhǔn) X3.64:Additional Controls for Use with American National Standard Code for Information Interchange。這個(gè)文件定義了現(xiàn)在所知的 ANSI 終端類型。它對將光標(biāo)移動(dòng)到屏幕上的特定位置、在光標(biāo)位置插入和刪除字符都規(guī)定了標(biāo)準(zhǔn)的命令序列。
最為重要的是命令序列定義自身,因?yàn)榧词故俏磳?shí)現(xiàn)所有命令的終端也至少能夠?qū)⒉恢С值拿钭R別為命令,并安全地忽視它們。這一進(jìn)步使得軟件開發(fā)商可以按照通用標(biāo)準(zhǔn)來編寫,并確信他們的應(yīng)用程序能夠至少占用較小的容量來在很多廠商的設(shè)備上運(yùn)行。ANSI 的故事是軟件行業(yè)一個(gè)重復(fù)出現(xiàn)的主題的一個(gè)很好的例子:采用一種標(biāo)準(zhǔn)會(huì)極大地?cái)U(kuò)展可互操作性。
ANSI 終端標(biāo)準(zhǔn)是一種很像 Telnet 協(xié)議的協(xié)議。它定義了多個(gè)特殊的字符序列,使一個(gè)應(yīng)用程序可區(qū)分要解釋的命令和要顯示到屏幕的數(shù)據(jù)。
ANSI 終端是我們將要模擬的終端類型。TelnetConnection
已經(jīng)在其到達(dá)屏幕之前過濾出 Telnet 握手和協(xié)商;現(xiàn)在我們將增加另一個(gè)過濾器來過濾出并解釋 ANSI 命令。因?yàn)檫@些命令是我們的終端的指令,實(shí)現(xiàn)該邏輯的最好的地方就是在 TelnetCanvas
類自身中。
當(dāng) Telnet 使用字節(jié)值 255 來以信號方式表示一個(gè)命令序列時(shí),ANSI 使用 ASCII 轉(zhuǎn)義序列,其值是 27。ANSI 將如下工作:
我們從輸入讀取一個(gè)字節(jié)。如果其值不是 27 (
ESC
),則它不是一個(gè)命令∶我們將它直接送到應(yīng)用程序并繼續(xù)讀取。我們讀取下一個(gè)字節(jié)。如果其值不是 133 (
[
),則它不是一個(gè)命令;我們將該字節(jié)后所跟的 27 直接送到應(yīng)用程序并繼續(xù)讀取。我們將繼續(xù)讀取,直到讀取到大于 63 的一個(gè)字節(jié)。這些字節(jié)形成一個(gè)字符串,其中含有命令的參數(shù)。最后一個(gè)字節(jié)(是 64 或更大)是命令代碼。我們處理(或忽略)該命令并繼續(xù)讀取。
許多命令從官方來講是標(biāo)準(zhǔn)的一部分。雖然完全模擬是一個(gè)值得的目標(biāo),我們?nèi)詫⒕Ψ旁讷@得足夠的功能來使眾多軟件可以接受地運(yùn)行。我們將實(shí)現(xiàn)以下命令:
Cursor Control SequencesErase SequencesA
Move cursor up n lines@
Insert n blank spacesB
Move cursor down n linesJ
Erase display: after cursor (n=0), before cursor (n=1), or entirely (n=2).C
Move cursor forward n spacesD
Move cursor backward n spacesK
Erase line: after cursor (n=0), before cursor (n=1), or entirely (n=2).G
Move cursor to column xH
Move cursor to column x, row yL
Insert n new blank linesd
Move cursor to row yM
Delete n lines from cursors
Save current cursor positionP
Delete n characters from cursoru
Return to saved cursor position
大多數(shù)這些命令期望參數(shù)采用分號分割字符串的形式。參數(shù)出現(xiàn)在序列 ESC [
后和命令字節(jié)前。例如,一條用于將光標(biāo)移動(dòng)到第 10 行和第 10 列的命令將如下所示:ESC [ 1 0 ; 1 0 H。
許多命令只帶一個(gè)參數(shù);例如,ESC [ 2 J
將清除屏幕顯示。漏掉的參數(shù)將默認(rèn)為 1,所以 ESC [ H
將把光標(biāo)返回到坐標(biāo) (1,1),而 ESC [ B
則將光標(biāo)移動(dòng)到下一行。
雖讓有很多的東西要處理,但協(xié)議卻相當(dāng)簡單。
我們需要在兩個(gè)領(lǐng)域中更新 TelnetCanvas。
它需要解釋從遠(yuǎn)程主機(jī)接收的 ANSI 命令并發(fā)送 ANSI 命令來響應(yīng)用戶輸入。對于輸入,我們將允許用戶使用設(shè)備上的鍵盤來移動(dòng)光標(biāo)。
雖然我們必須修改 TelnetCanvas
類的內(nèi)部,但卻沒有必要更改公共接口。所有數(shù)據(jù)將仍由 receive()
方法來接收。我們只更改其實(shí)現(xiàn)來監(jiān)視轉(zhuǎn)義序列:
/*** Appends the specified ascii byte to the output.*/public void receive( byte b ){ // ignore nulls if ( b == 0 ) return; if ( state == PARAMS_STATE ) { // if still receiving parameters if ( b < 64 ) { argbuf[0]++; // grow if needed if ( argbuf[0] == argbuf.length ) { char[] tmp = new char[ argbuf.length * 2 ]; System.arraycopy( argbuf, 0, tmp, 0, argbuf.length ); argbuf = tmp; } argbuf[ argbuf[0] ] = (char) b; } else // final byte: PRocess the command { processCommand( b ); // reset for next command argbuf[0] = 0; state = NORMAL_STATE; } } else if ( state == ESCAPE_STATE ) { // if a valid escape sequence if ( b == '[' ) { state = PARAMS_STATE; } else // not an escape sequence { // allow escape to pass through state = NORMAL_STATE; processData( (byte) 27 ); processData( b ); } } else // NORMAL_STATE { if ( b == 27 ) { state = ESCAPE_STATE; } else { processData( b ); } }}
該方法實(shí)現(xiàn)了一個(gè)具有三個(gè)狀態(tài)的簡單狀態(tài)機(jī)。NORMAL_STATE
監(jiān)視任何 ESC
字節(jié)并將其他任何東西發(fā)送到 processData()。
當(dāng)一個(gè) ESC
字節(jié)來到時(shí),ESCAPE_STATE
會(huì)接管并檢查下一個(gè)字節(jié)是否是 133 ([
)。如果是,我們就轉(zhuǎn)到 PARAMS_STATE
,同時(shí)累加參數(shù)字符串直到我們碰到此命令字符。我們在執(zhí)行該操作時(shí),調(diào)用 processCommand()
,然后轉(zhuǎn)回 NORMAL_STATE
。
讀取命令參數(shù)的代碼值得進(jìn)行檢查。為了避免創(chuàng)建和保存 StringBuffer
所帶來的系統(tǒng)開銷,我們使用一個(gè)稱為 argbuf
的字符數(shù)組。為了避免不斷的內(nèi)存重新分配,我們使其比所需的大一些并保持富余,同時(shí)根據(jù)需要擴(kuò)大它。最后,為了跟蹤下一個(gè)字符到達(dá)何處,我們借用 Pascal 的一個(gè)技巧,將參數(shù)字符串的長度存儲(chǔ)在數(shù)組的第一個(gè)元素中。getArgument()
和 getArgumentCount()
參數(shù)處理來自該數(shù)組的單個(gè)參數(shù)的分析和提取。
我們現(xiàn)在將曾經(jīng)位于 receive()
方法中的代碼轉(zhuǎn)到 processData()
方法。邏輯是一樣的,將進(jìn)入的字節(jié)放在當(dāng)前的光標(biāo)位置,除了接近方法主體末尾的這兩行外,代碼沒有變化:
/*** Appends the specified byte to the display buffer.*/protected void processData( byte b ){ ... // increment bound if necessary while ( cursor > bound ) bound += columns; ...}
采用較早版本的 MIDTerm 的簡單的面向流的方法時(shí),光標(biāo)不僅標(biāo)出了進(jìn)入數(shù)據(jù)的插入點(diǎn),同時(shí)標(biāo)出了應(yīng)該在屏幕上顯示的數(shù)據(jù)緩沖區(qū)的外界。由于光標(biāo)現(xiàn)在可以向上和向前移動(dòng)到數(shù)據(jù)緩沖區(qū),就需要一個(gè)額外的變量來跟蹤數(shù)據(jù)的外界,以便我們可以確定屏幕的底部在什么位置。這個(gè)變量稱為 bound
,必須跟蹤光標(biāo)并在光標(biāo)移動(dòng)時(shí)位于它前面。
雖然 processData()
處理了大部分字節(jié),但在接收到合法的終端命令時(shí)還是要調(diào)用 processCommand()
。這個(gè)方法是我們的 ANSI 實(shí)現(xiàn)的核心。
/*** Executes the specified ANSI command, oBTaining arguments* as needed from the getArgument() and getArgumentCount() * methods.*/protected void processCommand( byte command ){ try { switch ( command ) { ... // other commands go here case 'd': // cursor to row x if ( argbuf[0] > 0 ) { cursor = bound - ((rows-getArgument( 0 )+1)*columns) + ( cursor % columns ); } break; case 'G': // cursor to column x if ( argbuf[0] > 0 ) { cursor = cursor - ( cursor % columns ) + getArgument( 0 ); } break; ... // other commands go here default: System.err.println( "unsupported command: " + (char) command + " : " + new String( argbuf, 1, argbuf[0] ) ); } } catch ( Throwable t ) { // probably parse exception or wrong number of args System.err.println( "Error in processCommand: " ); t.printStackTrace(); }}
processCommand()
其實(shí)是一個(gè)大的 switch 語句,為所支持的每條命令都有一個(gè) case。不支持的命令進(jìn)入默認(rèn) case 并被忽略。這里所列出的兩個(gè) case 示范了處理命令的剩余部分的邏輯。
這里您可以了解操縱鼠標(biāo)所需的數(shù)組算法的種類了。記住,我們的二維屏幕是由一維字節(jié)數(shù)組來表示的,而且我們在內(nèi)存用盡之前將不會(huì)丟棄滾出屏幕頂端的數(shù)據(jù)。我們盡可能多地保留,以便用戶可以滾動(dòng)回來來查看他們可能漏看的任何內(nèi)容。由于這個(gè)原因,屏幕的原點(diǎn)必須相對于數(shù)組的末尾而不是開頭來計(jì)算,所以原點(diǎn)的位置在第 1 行、第 1 列。
如果我們不支持 scrollback 特性,要計(jì)算一對坐標(biāo)的數(shù)組下標(biāo)就很簡單:y * columns + x
。要支持這一特性,我們就需要多做一些工作,并必須相對于我們的顯示緩沖區(qū)的外界來計(jì)算坐標(biāo)。如果這會(huì)使我們的用戶滿意,做這些額外的工作是值得的。
為了使用戶更滿意而又不需要太多工作,可使他們能夠使用他們設(shè)備上的鍵盤來與遠(yuǎn)程主機(jī)交互。
我們較早的應(yīng)用程序與遠(yuǎn)程主機(jī)交互的方式,只能是通過執(zhí)行一組腳本形式的命令。用戶只能等待操作完成,然后使用箭頭鍵來滾動(dòng)數(shù)據(jù)緩沖區(qū)來查看返回的內(nèi)容。
在下一版本的 MIDTerm 中,我們將用戶的鍵擊信號直接發(fā)送到遠(yuǎn)程主機(jī)。要發(fā)送任何東西,我們都需要一個(gè)輸出流,所以 TelnetCanvas
現(xiàn)在有一個(gè) setOutputStream()
方法用于此目的。用戶輸入通過如下修改 keyPressed()
方法來處理:
...private byte[] move = new byte[] { 27, (byte) '[', 0 };...public void keyPressed( int keyCode ){ switch ( getGameAction( keyCode ) ) { case LEFT: // move cursor left one column move[2] = 'D'; send( move ); break; case RIGHT: // move cursor right one column move[2] = 'C'; send( move ); break; case DOWN: if ( isScrolling() ) { // scroll down one row scrollY++; if ( scrollY > calcLastVisibleScreen() ) { scrollY = calcLastVisibleScreen(); } repaint(); } else { // move cursor down one row move[2] = 'B'; send( move ); } break; case UP: if ( isScrolling() ) { // scroll up one row scrollY--; if ( scrollY < 0 ) scrollY = 0; repaint(); } else { // move cursor down one row move[2] = 'A'; send( move ); } break; case FIRE: // send a line feed: send( (byte) '/n' ); break; default: // send code directly send( (byte) keyCode ); }}
要注意的第一件事情是,在決定按哪個(gè)鍵之前我們要使用 getGameAction()
來將按鍵代碼轉(zhuǎn)換為游戲代碼。MIDP 設(shè)備的鍵盤布局不同:有些有箭頭按鍵和數(shù)字小鍵盤,有些只有數(shù)字小鍵盤可用作箭頭按鍵。getGameAction()
方法隱藏了這些復(fù)雜性。
如果所按的鍵是 UP、DOWN、LEFT 或
RIGHT,
我們生成相應(yīng)的 ANSI 命令并將其發(fā)送到遠(yuǎn)程主機(jī)。注意 UP
和 DOWN
有兩個(gè)模式:一個(gè)用于移動(dòng)光標(biāo),一個(gè)用于滾動(dòng)顯示內(nèi)容。當(dāng)前的滾動(dòng)模式由兩種新的方法 isScrolling()
和 setScrolling()
來發(fā)現(xiàn)和控制。如果打開滾動(dòng),UP
和 DOWN
將滾動(dòng)輸出而不是移動(dòng)光標(biāo)。注意,通過直接測試 scrolling
變量而不是調(diào)用 isScrolling()
來測試,我們可以節(jié)省一些系統(tǒng)開銷。
鍵發(fā)送一個(gè)換行符,它類似于一般鍵盤上的 Enter 或 Return 鍵。這種特性對于基于菜單的應(yīng)用程序很有用,由箭頭鍵突出顯示一個(gè)選項(xiàng)并用 Enter 鍵選擇突出顯示的選項(xiàng);Lynx web 瀏覽器就是一個(gè)很好的例子。這些應(yīng)用程序僅使用用戶手持設(shè)備上的鍵盤就可以完全發(fā)揮作用。FIRE
如果按鍵代碼沒有映射到任何游戲動(dòng)作,就會(huì)將它直接發(fā)送到遠(yuǎn)程主機(jī)。MIDP 規(guī)范中規(guī)定,具有比標(biāo)準(zhǔn)手機(jī)的按鍵多的設(shè)備應(yīng)發(fā)送等同的 ASCII 字符作為其按鍵代碼。直接發(fā)送這些鍵代碼使得應(yīng)用程序可以完全利用具有完整鍵盤的設(shè)備的優(yōu)勢。具有這些設(shè)備的用戶只需開始輸入即可;他們的鍵擊會(huì)通過要建立的連接而發(fā)送。
最后,因?yàn)?TelnetCanvas
組件隱藏了來自應(yīng)用程序的其他部分的所有終端模擬邏輯,我們需要新的方法 getRows()
、getColumns()
和 getTerminalType()
來宣布屏幕尺寸和實(shí)現(xiàn)所支持的終端模擬的種類。
與以前一樣,MIDlet 類自身 MIDTerm
將 TelnetConnection
和 TelnetCanvas
捆綁在一起。只需要首位改變一下連接的設(shè)置:
...connection = new TelnetConnection( (StreamConnection) Connector.open( connectString, Connector.READ_WRITE, true ), canvas.getColumns(), canvas.getRows(), canvas.getTerminalType() );input = connection.openInputStream();output = connection.openOutputStream();canvas.setOutputStream( output );...
但 MIDTerm 需要做更多。雖然 TelnetCanvas
處理基本用戶輸入,僅有數(shù)字鍵的鍵盤則需要特殊的方式來輸入文字,如重復(fù)按某個(gè)鍵來選擇某個(gè)字母,或預(yù)測式文字輸入法。利用這些內(nèi)在功能的唯一途徑就是使用 MIDP 的 TextField
和 TextBox
組件。因?yàn)檫@些組件不能與 Canvas
位于相同的屏幕,文字輸入需要另外一個(gè)屏幕。雖然我們在屏幕上,但應(yīng)用程序還應(yīng)提供另一個(gè)屏幕來建立連接,這要用到主機(jī)名稱和要在其上連接的端口的字段。MIDTerm 將用作中心代理來將這些屏幕捆綁在一起。
Form
類非常靈活,所以我們不需要每個(gè)類都有一個(gè)單獨(dú)的子類,而這一點(diǎn)很讓人高興。大多數(shù) MIDP 設(shè)備用于應(yīng)用程序存儲(chǔ)的空間都有限,并限制應(yīng)用程序的大小,通常是最大 32 KB 或 64 KB,所以大小問題很重要。應(yīng)用程序中的每個(gè)類都會(huì)至少將 JAR 文件的大小增加半個(gè)千字節(jié),即使在模糊處理后仍是如此,所以應(yīng)盡可能地避免創(chuàng)建子類。
輸入窗體會(huì)經(jīng)常用到,所以將它創(chuàng)建在 MIDTerm 的構(gòu)造函數(shù)中,并在應(yīng)用程序運(yùn)行期間保持待用狀態(tài)。這種方法消除了創(chuàng)建輸入窗體帶來的任何可感知的延遲,并避免為窗體進(jìn)行重復(fù)內(nèi)存分配和垃圾收集而引起的內(nèi)存擾動(dòng)(memory churn)。輸入窗體的設(shè)置非常簡單:
...inputForm = new Form( "Input" );inputField = new TextField( null, "", 255, TextField.ANY );inputForm.append( inputField );inputOptions = new ChoiceGroup( null, Choice.MULTIPLE );inputOptions.append( INPUT_ENTER, null );inputOptions.append( INPUT_CTRL, null );inputOptions.append( INPUT_ESC, null );inputOptions.setSelectedIndex( 0, true ); // default trueinputForm.append( inputOptions );scrollOptions = new ChoiceGroup( null, Choice.MULTIPLE );scrollOptions.append( INPUT_SCROLL, null );inputForm.append( scrollOptions ); inputForm.addCommand( okCommand );inputForm.addCommand( cancelCommand );inputForm.setCommandListener( this );...
TelnetCanvas
會(huì)在用戶希望發(fā)送一些文字時(shí)激活該窗體。第一個(gè)字段是 TextField
,而且在大部分平臺(tái)上它都應(yīng)獲得默認(rèn)焦點(diǎn)。稍微費(fèi)點(diǎn)功夫,用戶就可以快速調(diào)用該窗體,輸入一些文字,然后選擇 OK 命令來發(fā)送數(shù)據(jù)。
因?yàn)樵S多基于終端的應(yīng)用程序假定用戶應(yīng)采用終端類型的鍵盤,用戶就需要某種方法來發(fā)送特殊的鍵擊,如 Return、Escape 或 Control 加按鍵組合。我們?yōu)榇硕褂?ChoiceGroup
,將其設(shè)置為 MULTIPLE
模式,以便我們得到復(fù)選框而不是互斥的單選按鈕。根據(jù)兩種復(fù)選框設(shè)置,用戶的文本將在 Escape 字符后發(fā)送,或在換行字符后發(fā)送。如果選擇了 Send as CTRL 選項(xiàng),發(fā)送每個(gè)字符時(shí)就像是按下了 Control 鍵一樣(Control 加按鍵代碼的計(jì)算方法,是將一個(gè)字符轉(zhuǎn)換為其大寫形式,然后從其值減 64)。Append ENTER 選項(xiàng)默認(rèn)為 true
,因?yàn)榭捎眯詼y試表明大多數(shù)文字輸入后跟的是一個(gè)換行符。
最后,該窗體是允許客戶設(shè)置 TelnetCanvas
的滾動(dòng)模式的一個(gè)很好的地方。這一特性在功能上等同于終端鍵盤上的 Scroll Lock 鍵,而復(fù)選框也同樣地進(jìn)行標(biāo)記。為了進(jìn)行可視區(qū)分,這個(gè)選項(xiàng)加入一個(gè)單獨(dú)的 ChoiceGroup。
登錄窗體在一般的應(yīng)用程序生命周期內(nèi)很少會(huì)用到兩次以上,所以我們只根據(jù)需要來創(chuàng)建它,當(dāng)不用時(shí)就將其清除。其創(chuàng)建和布局在 onShowLogin()
中進(jìn)行:
public void onShowLogin(){ // create and populate login form loginForm = new Form( "Connect to" ); hostField = new TextField( "Host", host, 50, TextField.URL ); loginForm.append( hostField ); portField = new TextField( "Port", port, 6, TextField.NUMERIC ); loginForm.append( portField ); loginForm.addCommand( exitCommand ); loginForm.addCommand( openCommand ); loginForm.setCommandListener( this ); // show form display.setCurrent( loginForm );}
雖然這是一個(gè)簡單的窗體,可用性仍是我們最重要的考慮。一些實(shí)現(xiàn)可以利用一個(gè) TextField
(URL
或 NUMERIC
)上的“線索”并適當(dāng)?shù)囟ㄖ朴脩艚缑妗R驗(yàn)橛脩敉ǔ⒃?B> Host 中輸入服務(wù)器的域名,指出所期望的用戶輸入像一個(gè) URL 就很有意義。某些設(shè)備可能有特殊的屏幕布局,它們針對字母和符號而不是數(shù)字進(jìn)行優(yōu)化。同樣地,Port 字段應(yīng)僅限于數(shù)字輸入;一些設(shè)備可能允許用戶在它們的鍵盤上快速輸入數(shù)字,而繞過任何重復(fù)按鍵式文字輸入系統(tǒng)。
MIDlet 是登錄窗體、輸入窗以及 Telnet canvas 的命令監(jiān)聽器。可將其視作應(yīng)用程序工作流的向?qū)А?nbsp;
在啟動(dòng)時(shí),將出現(xiàn)登錄窗體,選項(xiàng)為 Open 或 Exit。 一旦打開,就會(huì)出現(xiàn) Telnet canvas,選項(xiàng)為 Close 或 Input。Input 選項(xiàng)顯示輸入窗體,其中有一個(gè) OK 選項(xiàng)用于隱藏窗體并發(fā)送文字,另有一個(gè) Cancel 選項(xiàng)僅僅隱藏窗體。Close 選項(xiàng)隱藏 Telnet canvas 并顯示一個(gè)登錄窗體。最后,Exit 選項(xiàng)調(diào)用 notifyDestroyed()
并退出應(yīng)用程序。所有這些命令由 MIDTerm
的 commandAction()
方法來處理。
現(xiàn)在所有組件已經(jīng)就緒,我們就可以使我們的終端模擬器工作起來了。終端應(yīng)用程序在企業(yè)和教育計(jì)算環(huán)境中得到廣泛應(yīng)用,用于企業(yè)應(yīng)用程序、軟件開發(fā)、系統(tǒng)管理甚至對策模擬。MIDTerm 使得所有這些種類的應(yīng)用程序和資源都可以從您的移動(dòng)設(shè)備來訪問。
更進(jìn)一步,任何這些應(yīng)用程序都可以被“刮擦”:您可以編寫一個(gè)移動(dòng)應(yīng)用程序來與遠(yuǎn)程服務(wù)器上的資源密集型程序進(jìn)行交互、提取輸出并將它呈現(xiàn)在一個(gè)用戶友好的圖形界面上。企業(yè)開發(fā)者通常使用這種技術(shù)來在遺留系統(tǒng)上構(gòu)造新式的用戶界面。
MIDP 平臺(tái)上的有效 Telnet 和 ANSI 終端實(shí)現(xiàn)清除了這些種類的軟件項(xiàng)目的主要障礙。
通過實(shí)現(xiàn)對 ANSI 的終端轉(zhuǎn)義序列的支持,我們已經(jīng)更新了第一篇文章的未完善的終端顯示。這個(gè)應(yīng)用程序還更好地利用了 MIDP 的用戶界面功能,并具有對用于用戶輸入的鍵盤和定制窗體的支持。您可以組合和匹配這些軟件組建來為新的種類的網(wǎng)絡(luò)意識(network-aware)移動(dòng)應(yīng)用程序提供基礎(chǔ)。
(出處:http://www.companysz.com)
新聞熱點(diǎn)
疑難解答
圖片精選