
在之前的帖子中,我想实现在 PC 端(也就是网页上)控制大疆无人机 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这个对象在哪里了,startButton的onClick执行后,myApp被创建了,但是之后去了哪里?我又能在哪里再次操作它呢。。。
我怎么才能在获取到起飞的 Post 请求后,调用起飞按钮呢?
请教一下我应该去补充什么知识呢?感谢了!
又看了几天书,大致明白了接口的使用方法。用接口改写了。 创建一个飞行控制接口,定义起飞降落
interface FlyController { void takeOff(); void landing(); } MainActivity 实现接口 FlyController 重写两个方法
public class MainActivity extends AppCompatActivity implements FlyController { .... @Override public void takeOff() { Log.d("appinfo","Take Off"); } @Override public void landing() { Log.d("appinfo","Landing"); } } NanoHTTPD接收 FlyController 接口,后面调用接口的方法
class App extends NanoHTTPD { private FlyController f; public App(FlyController f) throws IOException { super(8080); this.f = f; start(NanoHTTPD.SOCKET_READ_TIMEOUT,false); } .... myApp实例化的时候传入 MainActivity 类
myApp = new App(MainActivity.this); 1 noahhhh Feb 16, 2022 via Android 关注的有在大疆 Android 开发的,你可以试试问他 http://weibo.com/u/1624312593 |
2 commoccoom OP @noahhhh 这,直接微博私信,是不是有点太突兀了。另外,我现在能用 app 起飞,降落了,只是不知道怎么把 post 发来的参数和起飞按钮对应起来。 |
3 r00tt Feb 16, 2022 问:myApp 被创建了,但是之后去了哪里?我又能在哪里再次操作它呢? 答:在 MainActivity 中已经赋值给 myApp 了,可以通过`this.myApp`来进一步访问 问:我怎么才能在获取到起飞的 Post 请求后,调用起飞按钮呢? 答:应该不需要调用起飞按钮,直接在收到起飞的 post 请求后,调用 DJI 的 sdk 起飞。 --- NanoHTTPD 实现的 App 可以直接放到一个 Service 里面,在后台运行,UI 与 Service 通过 AIDL 通讯 |
4 commoccoom OP @r00tt this.myApp 提示 on a null object reference |
5 CharmingCheung Feb 17, 2022 额,楼主是不是没有编程基础。。或者说没有 Java 编程基础 |
6 CharmingCheung Feb 17, 2022 App 类构造函数里传入 OnClickListener 是什么作用。。还有 staring 函数里的 this 就是 App 本身啊,怎么能强转成 OnClickListener 。。 |
7 CharmingCheung Feb 17, 2022 @commoccoom 根据 MainActivity 里的代码得出,myApp 的实例化是在 startButton 点击之后才发生的,所以你要不就把实例化挪到 onClickListener 外面,也就是 onCreate 层里,要不就是先点击了 startButton ,再去拿 this.myApp 。 另外,this.myApp 在这里的严格意义是 MainActivity.this.myApp ,你如果搞不懂 this 用法,最好先这样写 |
8 commoccoom OP @CharmingCheung 首先,感谢回复,是的,没什么 Java 基础,就是春节期间看了一下《 Java 核心技术》这本书,然后还有《第一行代码 Android 》。 另外,经过了一早上的搜索和思考,问题已经解决了。 把 Class App 定义为 MainActivity 的内部类,然后获取 Post 参数后,调用外部类 MainActivity.this.customizeFunction 的方法。 再次感谢!两位的帮助 @r00tt @CharmingCheung |
9 commoccoom OP @CharmingCheung 还有 staring 函数里的 this 就是 App 本身啊,怎么能强转成 OnClickListener 。。 这是报错了之后,我点了 iDEA 自动给我修复的,因为能够运行,所以我也没细究是什么意思 |
10 CharmingCheung Feb 17, 2022 @commoccoom 解决了就行 但还是提醒一下,如果求稳定的话,如#3 所说,还是要把 App 和 DJI SDK 的调用逻辑都挪到 Service 里运行,并且是 Foreground Service ,甚至是子进程的 Service ,通过 Activity 里的按钮触发逻辑,与 Service 通信来控制服务的开关。 除非你只是临时使用几分钟,否则 MainActivity 被销毁了,就接收不到 post 过来的指令了,控制无人机这种东西还是稳点好。 |
11 commoccoom OP @CharmingCheung 好的,感谢建议。慢慢摸索了。 |
12 yawenimy122 Feb 17, 2022 啊这, 没有基础的,很难给你说明白,但是总体来说,逻辑不通, 代码运行起来报错了,只是 try catch 了, 所以没闪退, 这也是为什那么 myApp 是空的原因, App 的构建方法出问题了, 分清楚 View.OnClickedListener 和 Activity, this 的用法和代表的意思 |
13 commoccoom OP @yawenimy122 又看了几天 java 的书,搞明白了接口怎么用了,把代码改成可以使用接口的了。感谢回复 |