在之前的帖子中,我想实现在 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
这个对象在哪里了,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 2022-02-16 21:56:40 +08:00 via Android
关注的有在大疆 Android 开发的,你可以试试问他 http://weibo.com/u/1624312593
|
2
commoccoom OP @noahhhh 这,直接微博私信,是不是有点太突兀了。另外,我现在能用 app 起飞,降落了,只是不知道怎么把 post 发来的参数和起飞按钮对应起来。
|
3
r00tt 2022-02-16 22:24:51 +08:00
问: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 2022-02-17 13:26:31 +08:00
额,楼主是不是没有编程基础。。或者说没有 Java 编程基础
|
6
CharmingCheung 2022-02-17 13:29:17 +08:00
App 类构造函数里传入 OnClickListener 是什么作用。。还有 staring 函数里的 this 就是 App 本身啊,怎么能强转成 OnClickListener 。。
|
7
CharmingCheung 2022-02-17 13:39:59 +08:00
@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 2022-02-17 14:47:28 +08:00
@commoccoom 解决了就行
但还是提醒一下,如果求稳定的话,如#3 所说,还是要把 App 和 DJI SDK 的调用逻辑都挪到 Service 里运行,并且是 Foreground Service ,甚至是子进程的 Service ,通过 Activity 里的按钮触发逻辑,与 Service 通信来控制服务的开关。 除非你只是临时使用几分钟,否则 MainActivity 被销毁了,就接收不到 post 过来的指令了,控制无人机这种东西还是稳点好。 |
11
commoccoom OP @CharmingCheung 好的,感谢建议。慢慢摸索了。
|
12
yawenimy122 2022-02-17 15:05:29 +08:00
啊这,
没有基础的,很难给你说明白欸,但是总体来说,逻辑不通, 代码运行起来报错了,只是 try catch 了, 所以没闪退, 这也是为什那么 myApp 是空的原因, App 的构建方法出问题了, 分清楚 View.OnClickedListener 和 Activity, this 的用法和代表的意思 |
13
commoccoom OP @yawenimy122 又看了几天 java 的书,搞明白了接口怎么用了,把代码改成可以使用接口的了。感谢回复
|