因为公司项目的原因,不得不开始学习Java和Android开发,好在使用Java就能开发Android。

大致的需求是通过Web网页的方式来控制大疆无人机。由于大疆无人机的SDK只有Android和iOS版本,所以就必须使用以下的架构:

Web ----> Android ----> 遥控器 ----> 无人机

这就要求在Android上实现一个Web Server来接收我的Post请求了。通过搜索找到了NanoHttpd这个库,一个实现一个简单的Web Server

用iDEA新建一个Android项目,选择Empty Activity,语言选择Java。

修改app目录下的build.gradle,添加nanohttpd依赖:

dependencies {
    ...    
    implementation 'org.nanohttpd:nanohttpd:2.3.1'
    ...
}

activity_main.xml中添加按钮:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="wrap_content">
    <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/take_off"
            android:text="Take off" />

    <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/landing"
            android:text="Landing" />

    <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/start_httpd"
            android:text="Start HTTPD" />

    <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/stop_httpd"
            android:text="Stop HTTDP" />

</LinearLayout>

每一个layout目录下的xxx.xml可以理解为Android App里面的布局,类似HTML文件,用来显示组件。而MainActivity.java是活动,可以把它当作MVC模型中的控制器,里面包含的是逻辑活动。
MainActivity

package com.example.myapplication;

// NanoHTTPD require library
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.regex.*;

import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import fi.iki.elonen.NanoHTTPD;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    private App myApp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startButton = (Button) findViewById(R.id.start_httpd);
        Button stopButton = (Button) findViewById(R.id.stop_httpd);
        Button take_off = (Button) findViewById(R.id.take_off);
        Button landing = (Button) findViewById(R.id.landing);

        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try{
                    myApp = new App(MainActivity.this);
                    Log.d("onClick", "WebServer started");

                }catch (IOException e){
                    e.printStackTrace();
                    Log.d("onClick", "WebServer start failed" + e.getMessage());
                }
            }
        });

        stopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(myApp != null)
                {
                    myApp.closeAllConnections();
                    myApp = null;
                    Log.d("onClick", "Web server close");
                }
            }
        });
    }

    protected void test()
    {
        Log.d("OOP","Take off sucessed!");
    }
}

class App extends NanoHTTPD{

    private MainActivity obj;

    public App(MainActivity obj) throws IOException {
        super(8080);
        this.obj = obj;
        start(NanoHTTPD.SOCKET_READ_TIMEOUT,false);
    }

    private void requestBodyProcess(Map<String, String> map,String requestBody)
    {
        String requestBodyRegex = "\\w+=\\w+&\\w+=\\w+";

        boolean isRequestBodyMatch = Pattern.matches(requestBodyRegex, requestBody);
        if(isRequestBodyMatch)
        {
            Pattern command = Pattern.compile("(?<=\\bcommandName=)\\w+");
            Matcher command_matcher = command.matcher(requestBody);
            if(command_matcher.find())
            {
                map.put("commandName", command_matcher.group(0));
            }

            command = Pattern.compile("(?<=\\boperator=)\\w+");
            command_matcher = command.matcher(requestBody);
            if(command_matcher.find())
            {
                map.put("operator", command_matcher.group(0));
            }
        }
        else
        {
            map = null;
        }
    }

    @Override
    public Response serve(IHTTPSession session) {
        Map<String, String> files = new HashMap<String, String>();
        Map<String, String> map = new HashMap<String, String>();
        // HTTP GET
        if (session.getMethod() == Method.GET) {
            String itemIdRequestParameter = session.getParameters().get("itemId").get(0);
            return newFixedLengthResponse("Requested itemId = " + itemIdRequestParameter);
        }
        // HTTP POST
        if (session.getMethod() == Method.POST) {
            try {
                session.parseBody(files);
                String requestBody = session.getQueryParameterString();
                requestBodyProcess(map,requestBody);
                obj.test();
                return newFixedLengthResponse("Command Name = " + map.get("commandName") + "\n" + "Operator = "
                        + map.get("operator") );

            } catch (IOException | ResponseException e) {
                // handle
            }
        }
        return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT,
                "The requested resource does not exist");
    }
}

由于刚开始对Java不是很熟悉,这个页面我写了很久,还在V2EX上提问过https://www.v2ex.com/t/834355,后来我把App类当作MainActivity的内部类来实现调用MainActivity类中的方法。

今天回过头来思考了以下,如果App类是一个复杂的类,那么当作内部类来实现,那么MainActivity就非常的大了。所以,还是想改成独立的类,然后通过传参的方式,来建立彼此之间的联系。

通过myApp = new App(MainActivity.this);在实例化App的时候将MainActivity传入其中,再根据不同的Post请求来调用MainActivity中实现的方法。

最后在AndroidManifest.xml中注册应用程序所需要的权限

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

然后调试应用程序,向手机所在的IP地址:8080发送Get/Post请求即可。

标签: none

评论已关闭