TB-LAB BLOG

坪川研究室ブログです!適当に更新します!

Android上でNanoHTTPDを動かす

Android端末上にNanoHTTPDを組込んでWebサーバとして動かします。

下記のサイトを参考にさせてもらった komamitsu.log

[android] Android上でWeb serverを動かしてみた

http://d.hatena.ne.jp/komamitsu/20120223/1330013934

だが、NanoHTTPDのバージョンの関係(?)で、動いてない。

いろいろ見て回って

NanoHTTPD ver 1.*

public Response serve( String uri, String method, Properties header, Properties parms, Properties files ){

}

NanoHTTPD ver 2.*

public abstract Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms,Map<String, String> files);

APIのところが変わってたから、自分なりに調べて書き換えた。

準備

  1. Eclipseで適当なAndroidアプリケーションプロジェクトを作る(プロジェクト名、パッケージ名は適当で)

  2. GitHubでNanoHTTPDをダウンロード NanoHttpd/nanohttpd · GitHub

  3. ダウンロードしたZIPを解凍して / core / src / main / java / fi / iki / elonen / の中にあるNanoHTTPD.javaをsrcフォルダのパッケージの中に入れる

f:id:tb-lab:20140109011701p:plain

  1. 端末をPCと接続して、デヴァイスとして認識されたら、/の直下にwwwフォルダを作る

  2. wwwフォルダに簡単なindex.htmlを書いて保存

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
    <head>
       <title>サンプル</title>
   </head>
    <body>
        <p>Hello, World!</p>
    </body>
</html>

実装

AndroidManifest.xmlに以下の二行を設定

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>

MainActivity.javaを作る。端末のIPを表示するためTextViewを置いてる

package com.example.myserver;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Map;

import org.apache.http.conn.util.InetAddressUtils;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends Activity {
    private static final String TAG = "MYSERVER";
    private WebServer server;
    // サーバのポートを8080に設定
    private static final int PORT = 8080;
    String ipAddress;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // TextViewの作成
        TextView text = (TextView) findViewById(R.id.textView1);
        ipAddress = getLocalIpAddress();
        if (ipAddress != null) {
            text.setText("Please Access:" + "http://" + ipAddress + ":" + PORT);
        } else
            text.setText("Wi-Fi Network Not Available");
        server = new WebServer();
        try {
            // サーバをスタート
            server.start();
        } catch (IOException ioe) {
            Log.w(TAG, "The server could not start.");
        }
        Log.w(TAG, "Web server initialized.");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (server != null)
            /*
            * 注意 アクティビティを破棄する時は サーバをストップする
            */
            server.stop();
    }

    /* 端末のIPを取得 */
    public String getLocalIpAddress() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface
                    .getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf
                        .getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()
                            && InetAddressUtils.isIPv4Address(inetAddress
                                    .getHostAddress())) {

                        String ipAddr = inetAddress.getHostAddress();
                        return ipAddr;
                    }
                }
            }
        } catch (SocketException ex) {
            Log.d(TAG, ex.toString());
        }
        return null;
    }

    /*
    * NanoHTTPDを継承したクラスを作る
    */

    private class WebServer extends NanoHTTPD {
        public WebServer() {
            super(PORT);
        }

        @Override
        public Response serve(String uri, Method method,
                Map<String, String> header, Map<String, String> parameters,
                Map<String, String> files) {
            String answer = "";
            try {
                // 端末のSDカードからファイルを開く
                File root = Environment.getExternalStorageDirectory();
                FileReader index = new FileReader(root.getAbsolutePath()
                        + "/www/index.html");
                BufferedReader reader = new BufferedReader(index);
                String line = "";
                while ((line = reader.readLine()) != null) {
                    answer += line;
                }

            } catch (IOException ioe) {
                Log.w("NanoHTTPD", ioe.toString());
            }

            return new NanoHTTPD.Response(answer);
        }
    }

}

activity_main.xmlはこんな感じ

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:text="" />

</RelativeLayout>

実行

MainActivityを実行して、Please Access http://<<IPアドレス>>:8080が表示されれば、とりあえずNanoHTTPDは問題なく動いてるはず。現環境はAVDだけだから、イメージはこんな感じ。近々実機でのキャプチャをアップする。

実行結果

f:id:tb-lab:20140110230421p:plain

ブラウザで表示されてるIPアドレスにアクセスすると

f:id:tb-lab:20140110230419p:plain

みたいな感じになれば成功です。

考察

なんと言ってもJAVA一枚だけで構成されているから、軽量!! ドキュメントがない分、GitHubのサンプルを真似て、実装は 継承したクラスを作って、Mainでstartぐらいだから、かなり簡単に出来た。 今回はApacheなどWebサーバを意識して、wwwフォルダを作ってその中にindex.htmlを入れたが、もっと簡単にやるとしたらResponse serveの中に直接ブラウザに返す内容が書ける。こんな感じかな

@Override
        public Response serve(String uri, Method method,
                Map<String, String> header, Map<String, String> parameters,
                Map<String, String> files) {
            String answer = "<html><head><title>サンプル</title></head><body><p>Hello, World!</p></body></html>";
            return new NanoHTTPD.Response(answer);
        }

これでも結果は同じなはず。

独自の機能も発想次第でいろいろ開発できそうなのは、グッと来た。

ぜひ役に立ってください。