(新)HTTP通信をするS!アプリの作成

前々からこのタイトルで放置していたものがあったのですが、静止画で解説するには枚数が多くなると大変で限界なので、動画を取ってみました。

この時間からのブログ更新ということで、明日の仕事に一抹の不安を感じずにはいられないのですが、そこはYouTubeをはじめて使って感じた簡単さと、夜のテンションで乗り切ろうと思います。

※文章的に変なところは愛嬌ということにしといてください。

作るもの

NetBeansを使ってHTTP通信をするS!アプリを作成します。

今回はURL基づいたGETリクエストの後に、下記のレスポンスデータを表示するプログラムにします。

  • レスポンスコード
  • レスポンスメッセージ
  • レスポンスボディ

実際に動画で取ってみると10分強の作業になりました。

前提条件

NetBeansがインストールされ、なおかつS!アプリの開発環境をすでに整えられているものとします。

※詳細はNetBeansでS!アプリ:インストールからHelloWorldまで【その3】を参照してください。

プロジェクトの作成


ここではプロジェクト名に何を指定してもかまいません。また、動画中で「Hello MIDletを作成」チェックをはずしていますが、それもそのままでも問題ありません。

ただ、「エミュレータプラットフォーム」ではMEXAを指定しています。厳密には、今回のアプリを作成するだけならば「CLDC-1.1」「MIDP-2.0」を指定するだけで事足りるのですが、今後S!アプリを開発するに当たって必ず必要になる作業なので今回はMEXAを指定しました。

※MEXAが選択できない人はNetBeansでS!アプリ:インストールからHelloWorldまで【その3】を参照してください。

アプリケーション記述子の追加


属性ではSoftbank独自の拡張機能を使えるように、下記の設定を追加しています。

名前 配置
MIDxlet-API MEXA JAD

APIアクセス権ではHTTP通信ができるように「javax.microedition.io.Connector.http」を追加しています。

ビジュアルMIDletの作成


ここでは「ビジュアルMIDlet」というクラスを新規に作成しています。

表示可能項目の配置


ここでは画面遷移の元となるコンポーネントを配置しています。後にこのコンポーネント同士をコマンドによって結びつけることによって、画面遷移のフローを作成することになります。

配置したコンポーネントは以下の3つです。

  • フォーム(テキストフィールドやボタンを配置します)
  • 待機画面(裏でタスクと呼ばれる処理を担当するスレッドが走ります。これに通信のような機能を持たせます)
  • 警告(アラートメッセージを表示するためにのものです)

※待機画面と警告はNetBeans独自のコンポーネントです。

テキストフィールドやボタンの配置


フォームへ下記の順番でコンポーネントを配置しています。

  • テキストフィールド(URL入力用)
  • 文字列項目(GETボタン)
  • テキストフィールド(レスポンスコード表示用)
  • テキストフィールド(レスポンスメッセージ表示用)
  • テキストフィールド(レスポンスボディ表示用)

それぞれの設定項目は後述のとおりです。
そして最後に「項目コマンド」を文字列項目に割り当てています。

テキストフィールド(URL入力用)
項目
テキスト http://
ラベル URL
入力動作 URL
インスタンス url
文字列項目(GETボタン)
項目
テキスト GET
ラベル null(ユーザーコード)
レイアウト MIDP2:改行(後)
外観 BUTTON
インスタンス button

※ラベルにnullを指定することで、ラベルの位置に1行の空行ができないようにする。

テキストフィールド(レスポンスコード表示用)
項目
テキスト
ラベル ResponseCode
インスタンス responseCode
テキストフィールド(レスポンスメッセージ表示用)
項目
テキスト
ラベル ResponseMessage
インスタンス responseMessage
テキストフィールド(レスポンスボディ表示用)
項目
テキスト
ラベル ResponseBody
最大サイズ 1024
インスタンス responseBody

※最大サイズは適宜変更してください。

待機画面や警告の表示文字の変更

待機画面

ただ単純に「アクセス中」と入力。

警告

なぜアクセスに失敗したか分かる様に文字列に以下のユーザーコードを入力します。

"アクセス失敗:"+getTask().getFailureMessage()

フローの作成


ココまで設定した画面をつなぎ合わせる作業です。

タスクの設定


所定の場所に下記のコードを追加します。

HttpConnection http = (HttpConnection)Connector.open(getUrl().getString());
InputStream is = http.openInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int read;
while((read=is.read(buff))!=-1){
    baos.write(buff, 0, read);
}
is.close();
String body = new String(baos.toByteArray());
baos.close();
getResponseCode().setString(String.valueOf(http.getResponseCode()));
getResponseMessage().setString(http.getResponseMessage());
getResponseBody().setString((body.length()<getResponseBody().getMaxSize())?body:body.substring(0, getResponseBody().getMaxSize()-1));
http.close();

実行


ここまで済めば動画のような実行結果が得られるはずです。

実際にコードを書いたのは16行です。これぞIDEという感じがします。

最後にスクラッチでくみ上げたときの大変さを感じるために生成されたソースをすべて記載します。

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import org.netbeans.microedition.lcdui.WaitScreen;
import org.netbeans.microedition.util.SimpleCancellableTask;

/**
 * @author sakurai
 */
public class HelloHttp extends MIDlet implements CommandListener, ItemCommandListener {

    private boolean midletPaused = false;

    //<editor-fold defaultstate="collapsed" desc=" Generated Fields ">
    private Form form;
    private TextField url;
    private TextField responseCode;
    private StringItem button;
    private TextField responseBody;
    private TextField responseMessage;
    private WaitScreen waitScreen;
    private Alert alert;
    private Command itemCommand;
    private SimpleCancellableTask task;
    //</editor-fold>

    /**
     * The HelloHttp constructor.
     */
    public HelloHttp() {
        
    }

    //<editor-fold defaultstate="collapsed" desc=" Generated Methods ">
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: initialize ">
    /**
     * Initilizes the application.
     * It is called only once when the MIDlet is started. The method is called before the <code>startMIDlet</code> method.
     */
    private void initialize() {
        // write pre-initialize user code here
 
        // write post-initialize user code here
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: startMIDlet ">
    /**
     * Performs an action assigned to the Mobile Device - MIDlet Started point.
     */
    public void startMIDlet() {
        // write pre-action user code here
        switchDisplayable(null, getForm());
        // write post-action user code here
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: resumeMIDlet ">
    /**
     * Performs an action assigned to the Mobile Device - MIDlet Resumed point.
     */
    public void resumeMIDlet() {
        // write pre-action user code here
 
        // write post-action user code here
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: switchDisplayable ">
    /**
     * Switches a current displayable in a display. The <code>display</code> instance is taken from <code>getDisplay</code> method. This method is used by all actions in the design for switching displayable.
     * @param alert the Alert which is temporarily set to the display; if <code>null</code>, then <code>nextDisplayable</code> is set immediately
     * @param nextDisplayable the Displayable to be set
     */
    public void switchDisplayable(Alert alert, Displayable nextDisplayable) {
        // write pre-switch user code here
        Display display = getDisplay();
        if (alert == null) {
            display.setCurrent(nextDisplayable);
        } else {
            display.setCurrent(alert, nextDisplayable);
        }
        // write post-switch user code here
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: commandAction for Displayables ">
    /**
     * Called by a system to indicated that a command has been invoked on a particular displayable.
     * @param command the Command that was invoked
     * @param displayable the Displayable where the command was invoked
     */
    public void commandAction(Command command, Displayable displayable) {
        // write pre-action user code here
        if (displayable == waitScreen) {
            if (command == WaitScreen.FAILURE_COMMAND) {
                // write pre-action user code here
                switchDisplayable(getAlert(), getForm());
                // write post-action user code here
            } else if (command == WaitScreen.SUCCESS_COMMAND) {
                // write pre-action user code here
                switchDisplayable(null, getForm());
                // write post-action user code here
            }
        }
        // write post-action user code here
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: form ">
    /**
     * Returns an initiliazed instance of form component.
     * @return the initialized component instance
     */
    public Form getForm() {
        if (form == null) {
            // write pre-init user code here
            form = new Form("form", new Item[] { getUrl(), getButton(), getResponseCode(), getResponseMessage(), getResponseBody() });
            // write post-init user code here
        }
        return form;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: waitScreen ">
    /**
     * Returns an initiliazed instance of waitScreen component.
     * @return the initialized component instance
     */
    public WaitScreen getWaitScreen() {
        if (waitScreen == null) {
            // write pre-init user code here
            waitScreen = new WaitScreen(getDisplay());
            waitScreen.setTitle("waitScreen");
            waitScreen.setCommandListener(this);
            waitScreen.setText("\u30A2\u30AF\u30BB\u30B9\u4E2D");
            waitScreen.setTask(getTask());
            // write post-init user code here
        }
        return waitScreen;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: alert ">
    /**
     * Returns an initiliazed instance of alert component.
     * @return the initialized component instance
     */
    public Alert getAlert() {
        if (alert == null) {
            // write pre-init user code here
            alert = new Alert("alert", , null, null);
            alert.setTimeout(Alert.FOREVER);
            // write post-init user code here
        }
        return alert;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: task ">
    /**
     * Returns an initiliazed instance of task component.
     * @return the initialized component instance
     */
    public SimpleCancellableTask getTask() {
        if (task == null) {
            // write pre-init user code here
            task = new SimpleCancellableTask();
            task.setExecutable(new org.netbeans.microedition.util.Executable() {
                public void execute() throws Exception {
                    // write task-execution user code here
                    HttpConnection http = (HttpConnection)Connector.open(getUrl().getString());
                    InputStream is = http.openInputStream();
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    byte[] buff = new byte[1024];
                    int read;
                    while((read=is.read(buff))!=-1){
                        baos.write(buff, 0, read);
                    }
                    is.close();
                    String body = new String(baos.toByteArray());
                    baos.close();
                    getResponseCode().setString(String.valueOf(http.getResponseCode()));
                    getResponseMessage().setString(http.getResponseMessage());
                    getResponseBody().setString((body.length()<getResponseBody().getMaxSize())?body:body.substring(0, getResponseBody().getMaxSize()-1));
                    http.close();
                }
            });
            // write post-init user code here
        }
        return task;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: url ">
    /**
     * Returns an initiliazed instance of url component.
     * @return the initialized component instance
     */
    public TextField getUrl() {
        if (url == null) {
            // write pre-init user code here
            url = new TextField("URL", "http://", 32, TextField.URL);
            // write post-init user code here
        }
        return url;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: button ">
    /**
     * Returns an initiliazed instance of button component.
     * @return the initialized component instance
     */
    public StringItem getButton() {
        if (button == null) {
            // write pre-init user code here
            button = new StringItem(null, "GET", Item.BUTTON);
            button.addCommand(getItemCommand());
            button.setItemCommandListener(this);
            // write post-init user code here
        }
        return button;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: responseCode ">
    /**
     * Returns an initiliazed instance of responseCode component.
     * @return the initialized component instance
     */
    public TextField getResponseCode() {
        if (responseCode == null) {
            // write pre-init user code here
            responseCode = new TextField("RessponseCode", "", 32, TextField.ANY);
            // write post-init user code here
        }
        return responseCode;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: responseMessage ">
    /**
     * Returns an initiliazed instance of responseMessage component.
     * @return the initialized component instance
     */
    public TextField getResponseMessage() {
        if (responseMessage == null) {
            // write pre-init user code here
            responseMessage = new TextField("ResponseMessage", "", 32, TextField.ANY);
            // write post-init user code here
        }
        return responseMessage;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: responseBody ">
    /**
     * Returns an initiliazed instance of responseBody component.
     * @return the initialized component instance
     */
    public TextField getResponseBody() {
        if (responseBody == null) {
            // write pre-init user code here
            responseBody = new TextField("ResponseBody", "", 1024, TextField.ANY);
            // write post-init user code here
        }
        return responseBody;
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: commandAction for Items ">
    /**
     * Called by a system to indicated that a command has been invoked on a particular item.
     * @param command the Command that was invoked
     * @param displayable the Item where the command was invoked
     */
    public void commandAction(Command command, Item item) {
        // write pre-action user code here
        if (item == button) {
            if (command == itemCommand) {
                // write pre-action user code here
                switchDisplayable(null, getWaitScreen());
                // write post-action user code here
            }
        }
        // write post-action user code here
    }
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: itemCommand ">
    /**
     * Returns an initiliazed instance of itemCommand component.
     * @return the initialized component instance
     */
    public Command getItemCommand() {
        if (itemCommand == null) {
            // write pre-init user code here
            itemCommand = new Command("\u9805\u76EE", Command.ITEM, 0);
            // write post-init user code here
        }
        return itemCommand;
    }
    //</editor-fold>

    /**
     * Returns a display instance.
     * @return the display instance.
     */
    public Display getDisplay () {
        return Display.getDisplay(this);
    }

    /**
     * Exits MIDlet.
     */
    public void exitMIDlet() {
        switchDisplayable (null, null);
        destroyApp(true);
        notifyDestroyed();
    }

    /**
     * Called when MIDlet is started.
     * Checks whether the MIDlet have been already started and initialize/starts or resumes the MIDlet.
     */
    public void startApp() {
        if (midletPaused) {
            resumeMIDlet ();
        } else {
            initialize ();
            startMIDlet ();
        }
        midletPaused = false;
    }

    /**
     * Called when MIDlet is paused.
     */
    public void pauseApp() {
        midletPaused = true;
    }

    /**
     * Called to signal the MIDlet to terminate.
     * @param unconditional if true, then the MIDlet has to be unconditionally terminated and all resources has to be released.
     */
    public void destroyApp(boolean unconditional) {
    }

}