应该是一个 Android 开发中的新手问题,请教一下 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
commoccoom
V2EX    Java

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

  commoccoom Feb 16, 2022 2555 views
This topic created in 1530 days ago, the information mentioned may be changed or developed.

前言

在之前的帖子中,我想实现在 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这个对象在哪里了,startButtononClick执行后,myApp被创建了,但是之后去了哪里?我又能在哪里再次操作它呢。。。

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

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

Supplement 1    Feb 24, 2022

又看了几天书,大致明白了接口的使用方法。用接口改写了。 创建一个飞行控制接口,定义起飞降落

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); 
13 replies    2022-02-24 16:27:01 +08:00
noahhhh
    1
noahhhh  
   Feb 16, 2022 via Android
关注的有在大疆 Android 开发的,你可以试试问他 http://weibo.com/u/1624312593
commoccoom
    2
commoccoom  
OP
   Feb 16, 2022 via iPad
@noahhhh 这,直接微博私信,是不是有点太突兀了。另外,我现在能用 app 起飞,降落了,只是不知道怎么把 post 发来的参数和起飞按钮对应起来。
r00tt
    3
r00tt  
   Feb 16, 2022
问:myApp 被创建了,但是之后去了哪里?我又能在哪里再次操作它呢?
答:在 MainActivity 中已经赋值给 myApp 了,可以通过`this.myApp`来进一步访问


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

---

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

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

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

再次感谢!两位的帮助 @r00tt @CharmingCheung
commoccoom
    9
commoccoom  
OP
   Feb 17, 2022
@CharmingCheung

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

这是报错了之后,我点了 iDEA 自动给我修复的,因为能够运行,所以我也没细究是什么意思
CharmingCheung
    10
CharmingCheung  
   Feb 17, 2022
@commoccoom 解决了就行

但还是提醒一下,如果求稳定的话,如#3 所说,还是要把 App 和 DJI SDK 的调用逻辑都挪到 Service 里运行,并且是 Foreground Service ,甚至是子进程的 Service ,通过 Activity 里的按钮触发逻辑,与 Service 通信来控制服务的开关。
除非你只是临时使用几分钟,否则 MainActivity 被销毁了,就接收不到 post 过来的指令了,控制无人机这种东西还是稳点好。
commoccoom
    11
commoccoom  
OP
   Feb 17, 2022
@CharmingCheung 好的,感谢建议。慢慢摸索了。
yawenimy122
    12
yawenimy122  
   Feb 17, 2022
啊这,
没有基础的,很难给你说明白,但是总体来说,逻辑不通, 代码运行起来报错了,只是 try catch 了, 所以没闪退, 这也是为什那么 myApp 是空的原因, App 的构建方法出问题了, 分清楚 View.OnClickedListener 和 Activity, this 的用法和代表的意思
commoccoom
    13
commoccoom  
OP
   Feb 24, 2022
@yawenimy122 又看了几天 java 的书,搞明白了接口怎么用了,把代码改成可以使用接口的了。感谢回复
About     Help     Advertise     Blog     API     FAQ     Solana     1069 Online   Highest 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 42ms UTC 23:27 PVG 07:27 LAX 16:27 JFK 19:27
Do have faith in what you're doing.
ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86