dji-sdk / Mobile-SDK-Android

DJI Mobile SDK for Android: http://developer.dji.com/mobile-sdk/
Other
998 stars 580 forks source link

UnsatisfiedLinkError when registering DJI SDK in application #211

Closed kwong93 closed 6 years ago

kwong93 commented 6 years ago

Is this issue about bugs or crash issues of DJI Android SDK and Sample Code?

Yep, then please delete this template and provide the following infos for us to help investigate the issue:

Michael-DJI commented 6 years ago

@kwong93 could u pls help to check if the issue is platform associated (try with other phones or tablets)?

kwong93 commented 6 years ago

@Michael-DJI I tried it on a Galaxy S4 5.0.1 as well, same error when DJISDKManager.getInstance().registerApp(applicationContext, mDJISDKManagerCallback); is called

kwong93 commented 6 years ago

I also tried on a Verizon Ellipsis 10 (Android 5.1) and also receiving the error

I am currently on Windows, but I have AS installed in a Mac environment, I will try to run the app on Mac to see if its not a problem with my build environment

Michael-DJI commented 6 years ago

@kwong93 thanks for the feedback, we will try to fix it in next version.

Michael-DJI commented 6 years ago

Please make sure you don't miss the following steps:

In your project's build.gradle file, please add the packagingOptions to avoid unexpected crashes, and also add the compile and provided dependencies to to import the DJI Android SDK maven dependency as shown in this Integrate SDK into Application tutorial.

Create a new MApplication class and invoke the install() method of Helper class to load the SDK classes before using any SDK functionality. Failing to do so will result in unexpected crashes. Also, you should initialize SDK class objects (Like BaseProduct.BaseProductListener, BaseComponent.ComponentListener and DJISDKManager.SDKManagerCallback) inside the onCreate() method. For more details, please check the Creating a Camera Application tutorial.

https://developer.dji.com/mobile-sdk/documentation/faq/index.html#after-upgrading-to-dji-android-sdk-4-4-1-my-android-project-crashes-with-java-lang-noclassdeffounderror-error-how-to-fix-it

Michael-DJI commented 6 years ago

pls refer to this link: https://github.com/dji-sdk/Mobile-SDK-Android/issues/220

kwong93 commented 6 years ago

@Michael-DJI I got it to build the app successfully from my mac, but on Windows I will need to try your fix

kwong93 commented 6 years ago

@Michael-DJI I didn't add the do not strips in the gradle, I was following only the camera application tutorial and it didn't mention that

https://developer.dji.com/mobile-sdk/documentation/android-tutorials/index.html

but here did

https://developer.dji.com/mobile-sdk/documentation/application-development-workflow/workflow-integrate.html

maybe add a reference to the above in the camera tutorial?

    packagingOptions {
        exclude 'META-INF/rxjava.properties'
        doNotStrip "*/*/libdjivideo.so"
        doNotStrip "*/*/libSDKRelativeJNI.so"
        doNotStrip "*/*/libFlyForbid.so"
        doNotStrip "*/*/libduml_vision_bokeh.so"
        doNotStrip "*/*/libyuv2.so"
        doNotStrip "*/*/libGroudStation.so"
        doNotStrip "*/*/libFRCorkscrew.so"
        doNotStrip "*/*/libUpgradeVerify.so"
        doNotStrip "*/*/libFR.so"
    }

In gradle dependencies, if you are including dji UI library, only include one provide

    compile 'com.dji:dji-sdk:4.4.1'
    compile 'com.dji:dji-uilibrary:4.4.1'
    provided 'com.dji:dji-sdk-provided:4.4.1'

I restructured the FPVDemoApplication.java from the tutorial, I don't think we need to actually override application, just a basic class was enough. Also I didn't register immediately as I had a use case for registering on demand

public class DJIHelper {
    private DJISDKManager.SDKManagerCallback mDJISDKManagerCallback;
    private BaseProduct.BaseProductListener mDJIBaseProductListener;
    private BaseComponent.ComponentListener mDJIComponentListener;

    private static BaseProduct mProduct;
    public Handler mHandler;

    public Context getApplicationContext() {
// return an instance of your application, I had a method in the application class to return the context
        return YourApplicationClass.getInstance().getContext();
    }

    public void init() {
        mHandler = new Handler(Looper.getMainLooper());

        mDJIComponentListener = new BaseComponent.ComponentListener() {
            @Override
            public void onConnectivityChange(boolean isConnected) {
                notifyStatusChange();
            }
        };

        mDJIBaseProductListener = new BaseProduct.BaseProductListener() {
            @Override
            public void onComponentChange(BaseProduct.ComponentKey key, BaseComponent oldComponent, BaseComponent newComponent) {
                if (newComponent != null) {
                    newComponent.setComponentListener(mDJIComponentListener);
                }
                notifyStatusChange();
            }

            @Override
            public void onConnectivityChange(boolean isConnected) {
                notifyStatusChange();
            }
        };

        mDJISDKManagerCallback = new DJISDKManager.SDKManagerCallback() {
            @Override
            public void onRegister(DJIError error) {
                if (error == DJISDKError.REGISTRATION_SUCCESS) {
                    Handler handler = new Handler(Looper.getMainLooper());
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), "DJI SDK registered successfully", 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(), "Failed to register DJI SDK, check if network is available", Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }

            @Override
            public void onProductChange(BaseProduct oldProduct, BaseProduct newProduct) {
                mProduct = newProduct;
                if (mProduct != null) {
                    mProduct.setBaseProductListener(mDJIBaseProductListener);
                }

                notifyStatusChange();
            }
        };
    }

    public void registerDJI() {
        Toast.makeText(getApplicationContext(), "Registering DJI SDK", Toast.LENGTH_LONG).show();

        DJISDKManager.getInstance().registerApp(getApplicationContext(), mDJISDKManagerCallback);
    }

    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);
            LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
        }
    };

    public static final int PHOTO_MODE_DELAY = 2000;
    public static final int DOWNLOAD_DELAY = 2000;

    public static final String[] SDK_PERMISSIONS = new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.VIBRATE,
            android.Manifest.permission.INTERNET, android.Manifest.permission.ACCESS_WIFI_STATE,
            android.Manifest.permission.WAKE_LOCK, android.Manifest.permission.ACCESS_COARSE_LOCATION,
            android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION,
            android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS,
            android.Manifest.permission.READ_EXTERNAL_STORAGE, android.Manifest.permission.SYSTEM_ALERT_WINDOW,
            android.Manifest.permission.READ_PHONE_STATE};

    public static final String FLAG_CONNECTION_CHANGE = "DJI_CONNECTION_CHANGE";

    public static synchronized Camera getCameraInstance() {
        BaseProduct baseProduct = getProductInstance();

        if (baseProduct != null) {
            Camera camera = null;

            if (baseProduct instanceof Aircraft) {
                camera = baseProduct.getCamera();
            } else if (baseProduct instanceof HandHeld) {
                camera = baseProduct.getCamera();
            }

            return camera;
        }

        return null;
    }

    public static synchronized Aircraft getAircraftInstance() {
        BaseProduct baseProduct = getProductInstance();

        if (baseProduct != null) {
            if (baseProduct instanceof Aircraft) {
                return (Aircraft) baseProduct;
            }
        }

        return null;
    }

    public static synchronized FlightAssistant getFlightAssistant() {
        Aircraft aircraft = getAircraftInstance();

        if (aircraft != null) {
            FlightController flightController = aircraft.getFlightController();

            if (flightController != null) {
                return flightController.getFlightAssistant();
            }
        }

        return null;
    }

    public static synchronized FlightController getFlightController() {
        Aircraft aircraft = getAircraftInstance();

        if (aircraft != null) {
            return aircraft.getFlightController();
        }

        return null;
    }

    public static synchronized BaseProduct getProductInstance() {
        return DJISDKManager.getInstance().getProduct();
    }

    public static boolean hasSdkPermissions(Context context) {
        for (String permission : SDK_PERMISSIONS) {
            if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }

        return true;
    }
}

and this is how I initialized in Application class

    public static YourApplicationClass getInstance() {
        return ourInstance;
    }

    public Context getContext() {
        return this;
    }

    private DJIHelper djiHelper;

    public DJIHelper getDjiHelper() {
        return djiHelper;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        ourInstance = this;
        djiHelper.init();
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        Helper.install(this);
        if (djiHelper == null) {
            djiHelper = new DJIHelper();
        }
    }
williamjiangweiming commented 6 years ago

I have the same problem, Is the problem has solved? Process: com.huawei.droneplan.droneplan, PID: 23105 java.lang.VerifyError: Verifier rejected class dji.sdk.sdkmanager.DJISDKManager: void dji.sdk.sdkmanager.DJISDKManager.() failed to verify: void dji.sdk.sdkmanager.DJISDKManager.(): [0x0] Constructor returning without calling superclass constructor (declaration of 'dji.sdk.sdkmanager.DJISDKManager' appears in /data/app/com.huawei.droneplan.droneplan-2/split_lib_dependencies_apk.apk) at dji.sdk.sdkmanager.DJISDKManager.getInstance(Unknown Source) at com.huawei.droneplan.droneplan.DJIApplication.onCreate(DJIApplication.java:142) at com.huawei.droneplan.droneplan.DronePlanApplication.onCreate(DronePlanApplication.java:92) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1028) at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5658) at android.app.ActivityThread.-wrap2(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1637) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:156) at android.app.ActivityThread.main(ActivityThread.java:6523) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)