Android Studio Couldn't Find "libiconv.so"

Sage Hopkins picture Sage Hopkins · Jan 29, 2015 · Viewed 9k times · Source

I am having an issue with my barcode scanning application, the code runs perfectly until I test the Scan button, at this point it crashes, and I receive the following error:

01-28 23:15:51.314    2261-2261/com.example.sage.appp E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.sage.appp, PID: 2261
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.sage.appp-2/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]] couldn't find "libiconv.so"
        at java.lang.Runtime.loadLibrary(Runtime.java:366)
        at java.lang.System.loadLibrary(System.java:989)
        at com.example.sage.appp.ZBarScannerActivity.<clinit>(ZBarScannerActivity.java:28)
        at java.lang.reflect.Constructor.newInstance(Native Method)
        at java.lang.Class.newInstance(Class.java:1572)
        at android.app.Instrumentation.newActivity(Instrumentation.java:1065)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2199)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
        at android.app.ActivityThread.access$800(ActivityThread.java:144)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

These are the classes I am using:

MainActivity.java

package com.example.sage.appp;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;

import net.sourceforge.zbar.Symbol;


public class MainActivity extends Activity
{

private static final int ZBAR_SCANNER_REQUEST = 0;
private static final int ZBAR_QR_SCANNER_REQUEST = 1;

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
}

public void launchScanner(View v)
{
    if (isCameraAvailable())
    {
        Intent intent = new Intent(this, ZBarScannerActivity.class);
        startActivityForResult(intent, ZBAR_SCANNER_REQUEST);
    }
    else
    {
        Toast.makeText(this, "Rear Facing Camera Unavailable", Toast.LENGTH_SHORT).show();
    }
}

public void launchQRScanner(View v)
{
    if (isCameraAvailable())
    {
        Intent intent = new Intent(this, ZBarScannerActivity.class);
        intent.putExtra(ZBarConstants.SCAN_MODES, new int[]{Symbol.QRCODE});
        startActivityForResult(intent, ZBAR_SCANNER_REQUEST);
    }
    else
    {
        Toast.makeText(this, "Rear Facing Camera Unavailable", Toast.LENGTH_SHORT).show();
    }
}

public boolean isCameraAvailable()
{
    PackageManager pm = getPackageManager();
    return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    switch (requestCode)
    {
        case ZBAR_SCANNER_REQUEST:
        case ZBAR_QR_SCANNER_REQUEST:

            if (resultCode == RESULT_OK)
            {
                Toast.makeText(this, "Scan Result = " + data.getStringExtra(ZBarConstants.SCAN_RESULT), Toast.LENGTH_SHORT).show();
            }
            else if(resultCode == RESULT_CANCELED && data != null)
            {
                String error = data.getStringExtra(ZBarConstants.ERROR_INFO);
                if(!TextUtils.isEmpty(error))
                {
                    Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
                }
            }
            break;
    }
}

}

ZBarScannerActivity.java

package com.example.sage.appp;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.Window;
import android.view.WindowManager;
import net.sourceforge.zbar.Config;
import net.sourceforge.zbar.Image;
import net.sourceforge.zbar.ImageScanner;
import net.sourceforge.zbar.Symbol;
import net.sourceforge.zbar.SymbolSet;
public class ZBarScannerActivity extends Activity implements
Camera.PreviewCallback, ZBarConstants {
private static final String TAG = "ZBarScannerActivity";
private CameraPreview mPreview;
private Camera mCamera;
private ImageScanner mScanner;
private Handler mAutoFocusHandler;
private boolean mPreviewing = true;

static {
    System.loadLibrary("iconv");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if(!isCameraAvailable()) {
        // Cancel request if there is no rear-facing camera.
        cancelRequest();
        return;
    }

    // Hide the window title.
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

    mAutoFocusHandler = new Handler();

    // Create and configure the ImageScanner;
    setupScanner();

    // Create a RelativeLayout container that will hold a SurfaceView,
    // and set it as the content of our activity.
    mPreview = new CameraPreview(this, this, autoFocusCB);
    setContentView(mPreview);
}

public void setupScanner() {
    mScanner = new ImageScanner();
    mScanner.setConfig(0, Config.X_DENSITY, 3);
    mScanner.setConfig(0, Config.Y_DENSITY, 3);

    int[] symbols = getIntent().getIntArrayExtra(SCAN_MODES);
    if (symbols != null) {
        mScanner.setConfig(Symbol.NONE, Config.ENABLE, 0);
        for (int symbol : symbols) {
            mScanner.setConfig(symbol, Config.ENABLE, 1);
        }
    }
}

@Override
protected void onResume() {
    super.onResume();

    // Open the default i.e. the first rear facing camera.
    mCamera = Camera.open();
    if(mCamera == null) {
        // Cancel request if mCamera is null.
        cancelRequest();
        return;
    }

    mPreview.setCamera(mCamera);
    mPreview.showSurfaceView();

    mPreviewing = true;
}

@Override
protected void onPause() {
    super.onPause();

    // Because the Camera object is a shared resource, it's very
    // important to release it when the activity is paused.
    if (mCamera != null) {
        mPreview.setCamera(null);
        mCamera.cancelAutoFocus();
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mCamera.release();

        // According to Jason Kuang on http://stackoverflow.com/questions/6519120/how-to-recover-camera-preview-from-sleep,
        // there might be surface recreation problems when the device goes to sleep. So lets just hide it and
        // recreate on resume
        mPreview.hideSurfaceView();

        mPreviewing = false;
        mCamera = null;
    }
}

public boolean isCameraAvailable() {
    PackageManager pm = getPackageManager();
    return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
}

public void cancelRequest() {
    Intent dataIntent = new Intent();
    dataIntent.putExtra(ERROR_INFO, "Camera unavailable");
    setResult(Activity.RESULT_CANCELED, dataIntent);
    finish();
}

public void onPreviewFrame(byte[] data, Camera camera) {
    Camera.Parameters parameters = camera.getParameters();
    Camera.Size size = parameters.getPreviewSize();

    Image barcode = new Image(size.width, size.height, "Y800");
    barcode.setData(data);

    int result = mScanner.scanImage(barcode);

    if (result != 0) {
        mCamera.cancelAutoFocus();
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();
        mPreviewing = false;
        SymbolSet syms = mScanner.getResults();
        for (Symbol sym : syms) {
            String symData = sym.getData();
            if (!TextUtils.isEmpty(symData)) {
                Intent dataIntent = new Intent();
                dataIntent.putExtra(SCAN_RESULT, symData);
                dataIntent.putExtra(SCAN_RESULT_TYPE, sym.getType());
                setResult(Activity.RESULT_OK, dataIntent);
                finish();
                break;
            }
        }
    }
}
private Runnable doAutoFocus = new Runnable() {
    public void run() {
        if(mCamera != null && mPreviewing) {
            mCamera.autoFocus(autoFocusCB);
        }
    }
};

// Mimic continuous auto-focusing
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
    public void onAutoFocus(boolean success, Camera camera) {
        mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
    }
};

}

CameraPreview.java

package com.example.sage.appp;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import java.io.IOException;
import java.util.List;
public class CameraPreview extends ViewGroup implements SurfaceHolder.Callback
{
 private final String TAG = "CameraPreview";

SurfaceView mSurfaceView;
SurfaceHolder mHolder;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
Camera mCamera;
PreviewCallback mPreviewCallback;
AutoFocusCallback mAutoFocusCallback;

CameraPreview(Context context, PreviewCallback previewCallback, AutoFocusCallback autoFocusCb) 
{
    super(context);

    mPreviewCallback = previewCallback;
    mAutoFocusCallback = autoFocusCb;
    mSurfaceView = new SurfaceView(context);
    addView(mSurfaceView);

    // Install a SurfaceHolder.Callback so we get notified when the
    // underlying surface is created and destroyed.
    mHolder = mSurfaceView.getHolder();
    mHolder.addCallback(this);
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

public void setCamera(Camera camera) 
{
    mCamera = camera;
    if (mCamera != null)
    {
        mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
        requestLayout();
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // We purposely disregard child measurements because act as a
    // wrapper to a SurfaceView that centers the camera preview instead
    // of stretching it.
    final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
    setMeasuredDimension(width, height);

    if (mSupportedPreviewSizes != null) {
        mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
    }
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (changed && getChildCount() > 0) {
        final View child = getChildAt(0);

        final int width = r - l;
        final int height = b - t;

        int previewWidth = width;
        int previewHeight = height;
        if (mPreviewSize != null) {
            previewWidth = mPreviewSize.width;
            previewHeight = mPreviewSize.height;
        }

        // Center the child SurfaceView within the parent.
        if (width * previewHeight > height * previewWidth) {
            final int scaledChildWidth = previewWidth * height / previewHeight;
            child.layout((width - scaledChildWidth) / 2, 0,
                    (width + scaledChildWidth) / 2, height);
        } else {
            final int scaledChildHeight = previewHeight * width / previewWidth;
            child.layout(0, (height - scaledChildHeight) / 2,
                    width, (height + scaledChildHeight) / 2);
        }
    }
}

public void hideSurfaceView() {
    mSurfaceView.setVisibility(View.INVISIBLE);
}

public void showSurfaceView() {
    mSurfaceView.setVisibility(View.VISIBLE);
}

public void surfaceCreated(SurfaceHolder holder) {
    // The Surface has been created, acquire the camera and tell it where
    // to draw.
    try {
        if (mCamera != null) {
            mCamera.setPreviewDisplay(holder);
        }
    } catch (IOException exception) {
        Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
    }
}

public void surfaceDestroyed(SurfaceHolder holder) {
    // Surface will be destroyed when we return, so stop the preview.
    if (mCamera != null) {
        mCamera.cancelAutoFocus();
        mCamera.stopPreview();
    }
}


private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio = (double) w / h;
    if (sizes == null) return null;

    Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    int targetHeight = h;

    // Try to find an size match aspect ratio and size
    for (Size size : sizes) {
        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
        if (Math.abs(size.height - targetHeight) < minDiff) {
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        }
    }

    // Cannot find the one match the aspect ratio, ignore the requirement
    if (optimalSize == null) {
        minDiff = Double.MAX_VALUE;
        for (Size size : sizes) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }
    return optimalSize;
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    if (holder.getSurface() == null){
      // preview surface does not exist
      return;
    }

    if (mCamera != null) {
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
        requestLayout();

        mCamera.setParameters(parameters);
        mCamera.setPreviewCallback(mPreviewCallback);
        mCamera.startPreview();
        mCamera.autoFocus(mAutoFocusCallback);
    }
}

}

File Structure : http://imgur.com/hG3skNl

Answer

Andrea Motto picture Andrea Motto · Mar 18, 2015

With Gradle 0.7.2+ you can put your native libraries directly into src/main/jniLibs and it will work automatically. https://stackoverflow.com/a/22488155/1636584 https://stackoverflow.com/a/16993006/1636584