应该是一个 Android 开发中的新手问题,请教一下

2022-02-16 21:30:37 +08:00
 commoccoom

前言

在之前的帖子中,我想实现在 PC 端(也就是网页上)控制大疆无人机 https://www.v2ex.com/t/823520

在和大疆的客服沟通后,发现只能通过手机 APP 接遥控器的方式控制无人机

然后在网上找到了一个 NanoHTTPD 的库,心想我只要在网页上实现无人机的起飞降落和获取图传就可以了,那么是不是在 APP 里实现一个小型的 Web Server ,然后发送 Post 请求到手机 APP 是不是就可以了呢?

然后开始自学 Java 和 Android ,买了一台最便宜的大疆无人机 Mini SE

终于参照大疆的 Mobile SDK ,做了一个可以起飞降落和获取图传的 APP 。

心想接下去就是把 NanoHTTPD 库跟 APP 结合就可以了。

难题

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(this); 
                    Log.e("onClick", "WebServer started");

                }catch (IOException e){
                    e.printStackTrace();
                    Log.e("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.e("onClick", "Web server close");
                }
            }
        });
    }
}

class App extends NanoHTTPD{

    private String commandName;
    private String operator;

    public String getCommandName()
    {
        return commandName;
    }

    public String getOperator()
    {
        return operator;
    }

    public App(View.OnClickListener onClickListener) throws IOException {
        super(8080);
        start(NanoHTTPD.SOCKET_READ_TIMEOUT,false);
    }

    public void starting() throws IOException {
        new App((View.OnClickListener) this);
    }

    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);
                commandName = map.get("commandName");
                operator = map.get("operator");
                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");
    }
}

我在 APP 类里实现了获取 Post 发送过来的是起飞还是降落commandName,但是我在MainActivity根本不知道myApp这个对象在哪里了,startButtononClick执行后,myApp被创建了,但是之后去了哪里?我又能在哪里再次操作它呢。。。

我怎么才能在获取到起飞的 Post 请求后,调用起飞按钮呢?

请教一下我应该去补充什么知识呢?感谢了!

1846 次点击
所在节点    Java
13 条回复
noahhhh
2022-02-16 21:56:40 +08:00
关注的有在大疆 Android 开发的,你可以试试问他 http://weibo.com/u/1624312593
commoccoom
2022-02-16 22:08:00 +08:00
@noahhhh 这,直接微博私信,是不是有点太突兀了。另外,我现在能用 app 起飞,降落了,只是不知道怎么把 post 发来的参数和起飞按钮对应起来。
r00tt
2022-02-16 22:24:51 +08:00
问:myApp 被创建了,但是之后去了哪里?我又能在哪里再次操作它呢?
答:在 MainActivity 中已经赋值给 myApp 了,可以通过`this.myApp`来进一步访问


问:我怎么才能在获取到起飞的 Post 请求后,调用起飞按钮呢?
答:应该不需要调用起飞按钮,直接在收到起飞的 post 请求后,调用 DJI 的 sdk 起飞。

---

NanoHTTPD 实现的 App 可以直接放到一个 Service 里面,在后台运行,UI 与 Service 通过 AIDL 通讯
commoccoom
2022-02-17 10:12:38 +08:00
@r00tt this.myApp 提示 on a null object reference
CharmingCheung
2022-02-17 13:26:31 +08:00
额,楼主是不是没有编程基础。。或者说没有 Java 编程基础
CharmingCheung
2022-02-17 13:29:17 +08:00
App 类构造函数里传入 OnClickListener 是什么作用。。还有 staring 函数里的 this 就是 App 本身啊,怎么能强转成 OnClickListener 。。
CharmingCheung
2022-02-17 13:39:59 +08:00
@commoccoom 根据 MainActivity 里的代码得出,myApp 的实例化是在 startButton 点击之后才发生的,所以你要不就把实例化挪到 onClickListener 外面,也就是 onCreate 层里,要不就是先点击了 startButton ,再去拿 this.myApp 。
另外,this.myApp 在这里的严格意义是 MainActivity.this.myApp ,你如果搞不懂 this 用法,最好先这样写
commoccoom
2022-02-17 14:32:06 +08:00
@CharmingCheung 首先,感谢回复,是的,没什么 Java 基础,就是春节期间看了一下《 Java 核心技术》这本书,然后还有《第一行代码 Android 》。

另外,经过了一早上的搜索和思考,问题已经解决了。

把 Class App 定义为 MainActivity 的内部类,然后获取 Post 参数后,调用外部类 MainActivity.this.customizeFunction 的方法。

再次感谢!两位的帮助 @r00tt @CharmingCheung
commoccoom
2022-02-17 14:33:53 +08:00
@CharmingCheung

还有 staring 函数里的 this 就是 App 本身啊,怎么能强转成 OnClickListener 。。

这是报错了之后,我点了 iDEA 自动给我修复的,因为能够运行,所以我也没细究是什么意思😂
CharmingCheung
2022-02-17 14:47:28 +08:00
@commoccoom 解决了就行

但还是提醒一下,如果求稳定的话,如#3 所说,还是要把 App 和 DJI SDK 的调用逻辑都挪到 Service 里运行,并且是 Foreground Service ,甚至是子进程的 Service ,通过 Activity 里的按钮触发逻辑,与 Service 通信来控制服务的开关。
除非你只是临时使用几分钟,否则 MainActivity 被销毁了,就接收不到 post 过来的指令了,控制无人机这种东西还是稳点好。
commoccoom
2022-02-17 14:50:39 +08:00
@CharmingCheung 好的,感谢建议。慢慢摸索了。
yawenimy122
2022-02-17 15:05:29 +08:00
啊这,
没有基础的,很难给你说明白欸,但是总体来说,逻辑不通, 代码运行起来报错了,只是 try catch 了, 所以没闪退, 这也是为什那么 myApp 是空的原因, App 的构建方法出问题了, 分清楚 View.OnClickedListener 和 Activity, this 的用法和代表的意思
commoccoom
2022-02-24 16:27:01 +08:00
@yawenimy122 又看了几天 java 的书,搞明白了接口怎么用了,把代码改成可以使用接口的了。感谢回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/834355

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX