Android DJI Mobile-SDK 开发

一身转战三千里,一剑曾当百万师。这篇文章主要讲述Android DJI Mobile-SDK 开发相关的知识,希望能为你提供帮助。

由于大疆的文档是英文的,看着不太舒服,虽然靠翻译插件进行翻译可也能看,但是来回的切换看比较麻烦,就搞一个中文的教程,基于SDK Version: 3.5.1(书到用时方恨少,后悔当年没好好学习英语)

开发准备
  • 先去开发者中心注册账号
  • 登录个人中心
Android DJI Mobile-SDK 开发

文章图片

  • 创建应用
Android DJI Mobile-SDK 开发

文章图片


开发
可以参考大疆的android文档 —— [ DJI ]
  • 下载Demo,将Demo 中的lib项目导入自己的项目工程中
  • 配置AndroidManifest.xml
添加SDK所需要的权限
< !-- SDK 需要的权限 --> < uses-permission android:name=" android.permission.BLUETOOTH" /> < uses-permission android:name=" android.permission.BLUETOOTH_ADMIN" /> < uses-permission android:name=" android.permission.VIBRATE" /> < uses-permission android:name=" android.permission.INTERNET" /> < uses-permission android:name=" android.permission.ACCESS_WIFI_STATE" /> < uses-permission android:name=" android.permission.WAKE_LOCK" /> < uses-permission android:name=" android.permission.ACCESS_COARSE_LOCATION" /> < uses-permission android:name=" android.permission.ACCESS_NETWORK_STATE" /> < uses-permission android:name=" android.permission.ACCESS_FINE_LOCATION" /> < uses-permission android:name=" android.permission.CHANGE_WIFI_STATE" /> < uses-permission android:name=" android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> < uses-permission android:name=" android.permission.WRITE_EXTERNAL_STORAGE" /> < uses-permission android:name=" android.permission.READ_EXTERNAL_STORAGE" /> < uses-permission android:name=" android.permission.SYSTEM_ALERT_WINDOW" /> < uses-permission android:name=" android.permission.READ_PHONE_STATE" /> < uses-feature android:name=" android.hardware.camera" /> < uses-feature android:name=" android.hardware.camera.autofocus" /> < uses-feature android:name=" android.hardware.usb.host" android:required=" false" /> < uses-feature android:name=" android.hardware.usb.accessory" android:required=" true" />

【Android DJI Mobile-SDK 开发】配置key以及服务
< application android:name=" .MyApplication" android:allowBackup=" true" android:icon=" @mipmap/ic_launcher" android:label=" @string/app_name" android:supportsRtl=" true" android:theme=" @style/AppTheme" > < meta-data android:name=" com.dji.sdk.API_KEY" android:value=https://www.songbingjia.com/android/" 自己应用申请的key" /> < !-- 以下是官方dmeo默认的配置 --> < service android:name=" dji.sdk.sdkmanager.DJIGlobalService" /> < service android:name=" dji.internal.geofeature.flyforbid.FlyforbidUpdateService" /> < !-- Required for receiving GEO system pushing. --> < service android:name=" dji.sdk.sdkmanager.GeoSyncFileService" /> < activity android:name=" dji.sdk.sdkmanager.DJIAoaControllerActivity" android:theme=" @android:style/Theme.Translucent" > < intent-filter> < action android:name=" android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> < /intent-filter> < meta-data android:name=" android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource=" @xml/accessory_filter" /> < /activity> < !-- 以上是官方dmeo默认的配置 --> < activity android:name=" .MainActivity" android:screenOrientation=" portrait" > < intent-filter> < action android:name=" android.intent.action.MAIN" /> < category android:name=" android.intent.category.LAUNCHER" /> < /intent-filter> < /activity> < /application>


MyApplication
用来初始化DJI SDK的一个类,写好后,别忘了配置到AndroidManifest.xml中
import android.app.Application; import android.content.Intent; import android.os.Handler; import android.os.Looper; import android.support.multidex.MultiDex; import android.util.Log; import android.widget.Toast; import dji.common.error.DJIError; import dji.common.error.DJISDKError; import dji.sdk.base.DJIBaseComponent; import dji.sdk.base.DJIBaseProduct; import dji.sdk.products.DJIAircraft; import dji.sdk.sdkmanager.DJISDKManager; /** * Created by kitty * Application类用来初始化SDK */public class MyApplication extends Application { private static DJIBaseProduct mProduct; private Handler mHandler; public static final String FLAG_CONNECTION_CHANGE =" com_example_dji_sdkdemo3_connection_change" ; @Override public void onCreate() { super.onCreate(); //初始化SDK DJISDKManager.getInstance().initSDKManager(this, mDJISDKManagerCallback); }/** * 注册监听 */ private DJISDKManager.DJISDKManagerCallback mDJISDKManagerCallback = new DJISDKManager.DJISDKManagerCallback() { //判断是否注册成功回调 @Override public void onGetRegisteredResult(DJIError error) {if (error == DJISDKError.REGISTRATION_SUCCESS) {Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), " 注册成功" , Toast.LENGTH_LONG).show(); } }); DJISDKManager.getInstance().startConnectionToProduct(); } else {Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), " 注册失败,或者网络问题" , Toast.LENGTH_LONG).show(); } }); } Log.e(" TAG" , error.toString()); }//监听状态 @Override public void onProductChanged(DJIBaseProduct oldProduct, DJIBaseProduct newProduct) { mProduct = newProduct; if (mProduct != null) { mProduct.setDJIBaseProductListener(mDJIBaseProductListener); } notifyStatusChange(); } }; private DJIBaseProduct.DJIBaseProductListener mDJIBaseProductListener = new DJIBaseProduct.DJIBaseProductListener() { @Override public void onComponentChange(DJIBaseProduct.DJIComponentKey key, DJIBaseComponent oldComponent, DJIBaseComponent newComponent) { if (newComponent != null) { newComponent.setDJIComponentListener(mDJIComponentListener); } notifyStatusChange(); }@Override public void onProductConnectivityChanged(boolean isConnected) { notifyStatusChange(); } }; private DJIBaseComponent.DJIComponentListener mDJIComponentListener = new DJIBaseComponent.DJIComponentListener() {@Override public void onComponentConnectivityChanged(boolean isConnected) { Log.d(" Alex" , " onComponentConnectivityChanged: " + isConnected); notifyStatusChange(); }}; private void notifyStatusChange() { mHandler.removeCallbacks(updateRunnable); mHandler.postDelayed(updateRunnable, 500); }private Runnable updateRunnable = new Runnable() {@Override public void run() { Intent intent = new Intent(FLAG_CONNECTION_CHANGE); sendBroadcast(intent); } }; /** * 获得指定的实例 */ public static synchronized DJIBaseProduct getProductInstance() { if (null == mProduct) { mProduct = DJISDKManager.getInstance().getDJIProduct(); } return mProduct; } }


MainActivity
  • 界面
< ?xml version=" 1.0" encoding=" utf-8" ?> < RelativeLayout xmlns:android=" http://schemas.android.com/apk/res/android" xmlns:tools=" http://schemas.android.com/tools" android:layout_width=" match_parent" android:layout_height=" match_parent" android:orientation=" vertical" > < TextView android:id=" @+id/text_connection_status" android:layout_width=" wrap_content" android:layout_height=" wrap_content" android:gravity=" center" android:text=" Status: No Product Connected" android:textColor=" @android:color/black" android:textSize=" 20dp" android:textStyle=" bold" android:layout_alignBottom=" @+id/text_product_info" android:layout_centerHorizontal=" true" android:layout_marginBottom=" 89dp" /> < TextView android:id=" @+id/text_product_info" android:layout_width=" wrap_content" android:layout_height=" wrap_content" android:layout_centerHorizontal=" true" android:layout_marginTop=" 150dp" android:text=" product_information" android:textColor=" @android:color/black" android:textSize=" 20dp" android:gravity=" center" android:textStyle=" bold" /> < Button android:id=" @+id/btn_open" android:layout_width=" 150dp" android:layout_height=" 55dp" android:layout_centerHorizontal=" true" android:layout_marginTop=" 250dp" android:background=" @drawable/round_btn" android:text=" Open" android:textColor=" @android:color/white" android:textSize=" 20dp" /> < TextView android:layout_width=" wrap_content" android:layout_height=" wrap_content" android:textAppearance=" ?android:attr/textAppearanceSmall" android:text=" Demo" android:id=" @+id/textView" android:layout_marginTop=" 30dp" android:textStyle=" bold" android:textSize=" 20dp" android:textColor=" @color/colorAccent" android:layout_alignParentTop=" true" android:layout_centerHorizontal=" true" /> < /RelativeLayout>

  • Code
import android.Manifest; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import dji.sdk.base.DJIBaseProduct; import dji.sdk.products.DJIAircraft; /** * Created by kitty on 2017/3/1. * 连接页面 */public class MainActivityextends Activity implements View.OnClickListener, DJIBaseProduct.DJIVersionCallback {private static final String TAG = MainActivity.class.getName(); private TextView mTextConnectionStatus; private TextView mTextProduct; private Button mBtnOpen; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //判断版本,进行动态获取权限 if (Build.VERSION.SDK_INT > = Build.VERSION_CODES.M) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.VIBRATE, Manifest.permission.INTERNET, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.WAKE_LOCK, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CHANGE_WIFI_STATE, Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.SYSTEM_ALERT_WINDOW, Manifest.permission.READ_PHONE_STATE, } , 1); } setContentView(R.layout.activity_main); initUI(); //注册广播,连接成功 更新UI用 IntentFilter filter = new IntentFilter(); filter.addAction(MainActivity.FLAG_CONNECTION_CHANGE); registerReceiver(mReceiver, filter); }private void initUI() { mTextConnectionStatus = (TextView) findViewById(R.id.text_connection_status); mTextProduct = (TextView) findViewById(R.id.text_product_info); //open按钮 mBtnOpen = (Button) findViewById(R.id.btn_open); mBtnOpen.setOnClickListener(this); mBtnOpen.setEnabled(false); } //连接成功广播更新UI protected BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { refreshSDKRelativeUI(); } }; //更新UI private void refreshSDKRelativeUI() { DJIBaseProduct mProduct = MainActivity.getProductInstance(); if (null != mProduct & & mProduct.isConnected()) { mBtnOpen.setEnabled(true); String str = mProduct instanceof DJIAircraft ? " DJIAircraft" : " DJIHandHeld" ; mTextConnectionStatus.setText(" Status: " + str + " connected" ); mProduct.setDJIVersionCallback(this); if (null != mProduct.getModel()) { mTextProduct.setText(" " + mProduct.getModel().getDisplayName()); } else { mTextProduct.setText(R.string.product_information); } } else { mBtnOpen.setEnabled(false); mTextProduct.setText(R.string.product_information); mTextConnectionStatus.setText(R.string.connection_loose); } }@Override public void onClick(View v) { switch (v.getId()) {case R.id.btn_open: Intent intent = new Intent(this, OpenActivity.class); startActivity(intent); break; default: break; } }@Override public void onProductVersionChange(String s, String s1) { Log.e(" ceshi" , " ConnectionActivity.java--> onProductVersionChange()" ); }@Override public void onResume() { Log.e(TAG, " onResume" ); super.onResume(); }@Override public void onPause() { Log.e(TAG, " onPause" ); super.onPause(); }@Override public void onStop() { Log.e(TAG, " onStop" ); super.onStop(); }public void onReturn(View view) { Log.e(TAG, " onReturn" ); this.finish(); }@Override protected void onDestroy() { Log.e(TAG, " onDestroy" ); unregisterReceiver(mReceiver); super.onDestroy(); } }


OpenActivity
  • 界面
< RelativeLayout xmlns:android=" http://schemas.android.com/apk/res/android" xmlns:tools=" http://schemas.android.com/tools" android:layout_width=" match_parent" android:layout_height=" match_parent" android:orientation=" vertical" > < TextureView android:id=" @+id/video_previewer_surface" android:layout_width=" match_parent" android:layout_height=" match_parent" android:layout_gravity=" center" android:layout_centerHorizontal=" true" /> < /RelativeLayout>

  • Code
/** * 连接成功显示页面 */ public class OpenActivity extends AppCompatActivity implementsTextureView.SurfaceTextureListener {protected TextureView mVideoSurface = null; private static final String TAG = OpenActivity.class.getName(); protected DJICamera.CameraReceivedVideoDataCallback mReceivedVideoDataCallBack = null; DJICodecManager mCodecManager; private DJICamera camera; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout. open); //初始化界面 initUI(); //显示回调 mReceivedVideoDataCallBack = new DJICamera.CameraReceivedVideoDataCallback() { @Override public void onResult(byte[] videoBuffer, int size) {if (mCodecManager != null) { // Send the raw H264 video data to codec manager for decoding mCodecManager.sendDataToDecoder(videoBuffer, size); } else { Log.e(TAG, " mCodecManager is null" ); } } }; }protected void onProductChange() { initPreviewer(); }@Override public void onResume() { super.onResume(); initPreviewer(); onProductChange(); if (mVideoSurface == null) { Log.e(" ceshi" , " mVideoSurface 显示界面为空" ); }}private void initPreviewer() { DJIBaseProduct product = MyApplication.getProductInstance(); if (product == null || !product.isConnected()) { //showToast(getString(R.string.disconnected)); Toast.makeText(getApplicationContext(), " 无连接" , Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), " 以连接" , Toast.LENGTH_LONG).show(); if (null != mVideoSurface) { mVideoSurface.setSurfaceTextureListener(this); } if (!product.getModel().equals(product.getModel().UnknownAircraft)) { DJICamera camera = product.getCamera(); if (camera != null) { // Set the callback camera.setDJICameraReceivedVideoDataCallback(mReceivedVideoDataCallBack); } } } }private void uninitPreviewer() { DJICamera camera = MyApplication.getProductInstance().getCamera(); if (camera != null) { // Reset the callback MyApplication.getProductInstance().getCamera().setDJICameraReceivedVideoDataCallback(null); } }@Override public void onPause() { super.onPause(); }@Override public void onStop() { uninitPreviewer(); super.onStop(); }@Override protected void onDestroy() { super.onDestroy(); uninitPreviewer(); }@Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { Log.e(TAG, " onSurfaceTextureAvailable" ); if (mCodecManager == null) { mCodecManager = new DJICodecManager(this, surface, width, height); } }@Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { Log.e(TAG, " onSurfaceTextureSizeChanged" ); }@Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { Log.e(TAG, " onSurfaceTextureDestroyed" ); if (mCodecManager != null) { mCodecManager.cleanSurface(); mCodecManager = null; } return false; }@Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { }private void initUI() { // 准备界面 mVideoSurface = (TextureView) findViewById(R.id.video_previewer_surface); if (null != mVideoSurface) { mVideoSurface.setSurfaceTextureListener(this); }} }


测试步骤
  • 手机连接遥控器
  • 选择dmeo打开
  • 连接飞机成功,点击open按钮,成功的话会显示画面

    推荐阅读