DJI Developer
详细内容请参见DJI提供的开发者文档,这里讨论在执行过程中的一些细节。
APP注册与认证在上篇随笔中已经谈到最终选择的技术路线,也就是基于DJI MobileSDK进行开发,随后就可以立即开工,希望在开发者文档中解决过程中遇到的问题。虽然如此,安卓开发对笔者来说仍然是全新的领域,在配置环境,调整API版本和大量的依赖中周旋了很久。实际上,开发者文档可能是在持续更新的,这使得一些内容并不统一,甚至同一个标程拆出来的示例程序的细节都有差异,在尝试直接套用之后总会报出各种错误。
例如,在根据开发文档开始新建项目,并进行配置之后,运行时出现了闪退问题,无论是在模拟器还是在手机上都无法正常运行。然而在前面给的示例又能够正常运行。查看示例,发现已经迭代过许多版本,怀疑是示例中修改了代码而文档内并没有体现。最终导致闪退的原因被锁定在http请求上,似乎在安卓9之后直接进行http请求不再被允许,使得按照之前的文档进行的gradle配置失效。根据其它教程,添加部分插件后,又出现了无法编译的问题。
经过长时间的调整和对java sdk版本的回调,终于得到了能够基本运行的程序。此时模拟器又成了问题,然而笔者已经无暇再匹配模拟器的版本,尝试之后发现手机竟然可以直接运行,就硬着头皮继续往下做,用手机来调试了。
可是对于该项目来说,手机必须要连接RemoteController才可由app控制无人机,而显然手机只有一个type-c插口,一旦连接遥控器就无法再连接电脑进行动态调试,只能在电脑上毫无运行地写好代码,装到手机上,祈祷它能够正常运行。这对于调试来说是灾难性的。
首先是App的注册问题,根据大疆的要求,所有app都需要拥有一个开发者账号,在其官网上申请到一个app key,使用这个key来验证app后才可以运行。好在目前官网对开发者已经开放免费申请,只要验证身份基本就可以获得key,要注意申请key时填写的package name同样是作为验证的一部分的(怀疑被hash进去了),因此应用的package name必须和申请key时填写的完全相同才可通过认证。此外,key一旦注册其package name不可再修改,只能删掉重新来,这里值得注意。
注册部分代码相对固定,根据教程进行即可:
private void startSDKRegistration() {
if (isRegistrationInProgress.compareAndSet(false, true)) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
showToast( "registering, pls wait...");
DJISDKManager.getInstance().registerApp(getApplicationContext(), new DJISDKManager.SDKManagerCallback() {
@Override
public void onRegister(DJIError djiError) {
if (djiError == DJISDKError.REGISTRATION_SUCCESS) {
DJILog.e("App registration", DJISDKError.REGISTRATION_SUCCESS.getDescription());
DJISDKManager.getInstance().startConnectionToProduct();
showToast("Register Success");
} else {
showToast( "Register sdk fails, check network is available");
}
Log.v(TAG, djiError.getDescription());
}
}
注册之后通过回调检查结果,使用前需要把官方提供的key填写在AndroidManifast.xml的相关位置上供调用。在使用任何功能之前,也就是app刚开始运行的时候,都需要运行这个Registeration函数,返回Success的时候才可以继续运行。
此时App已经可以运行了,屏幕上也能够显示简单的文字,但是为了实现无人机控制这还远远不够,首先要解除禁飞区限制——需要用户登录,这个和app的认证不同,app认证只是表明开发者的身份得到确认,但是登录是要保证控制无人机的人得到了身份认证。如果不登陆的话,飞行器会被限制在目测范围内(其实只有几十米),以及视频流会被禁用。
对于安卓应用来说,另一个需要在运行前准备完毕的是获取用户的权限,任何app获取额外的权限都要征求用户的许可,这部分知识对于笔者来说也是新的。我们需要根据样例的要求生成一个需求权限列表,并检查该列表内的权限是否得到满足。如果有未得到满足的权限,则先加入一个临时列表MissingPermissions,等到找到了所有待获取权限,再一次性向用户申请。
private void checkAndRequestPermissions() {
// Check for permissions
for (String eachPermission : REQUIRED_PERMISSION_LIST) {
if (ContextCompat.checkSelfPermission(this, eachPermission) != PackageManager.PERMISSION_GRANTED) {
missingPermission.add(eachPermission);
}
}
// Request for missing permissions
if (!missingPermission.isEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ActivityCompat.requestPermissions(this,
missingPermission.toArray(new String[missingPermission.size()]),
REQUEST_PERMISSION_CODE);
}
}
由于用户可能在给予权限后手动取消,每次运行app之前都要询问一下权限,结合个人体验,这个几乎是所有安卓app的惯例了。每当获取到一个权限,都会调用
onRequestPermissionsResult
函数,在这个函数中我们需要从missing列表里移除已经获取的权限。如果missing仍然不为空,继续索要权限(直到用户无奈退出)
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// Check for granted permission and remove from missing list
if (requestCode == REQUEST_PERMISSION_CODE) {
for (int i = grantResults.length - 1; i >= 0; i--) {
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
missingPermission.remove(permissions[i]);
}
}
}
// If there is enough permission, we will start the registration
if (missingPermission.isEmpty()) {
startSDKRegistration();
} else {
showToast("Missing permissions!!!");
}
}
注意所有的Permission都是常量,需要查表。权限未能完全获得,则终止运行。
接下来要做一些简单的界面设计,以及与遥控器和无人机建立连接,尽管绝大多数函数在API文档里面已经给与,想要搞清楚需要做什么还是不简单的事情。



