'E/Volley: [904] NetworkUtility.shouldRetryException: Unexpected response code 400 for url

I am trying to upload image from android studio to my flask server running on local host. On POSTMAN the response is ok, image is uploaded and returns response in json format. But here I am getting below error as shown in logcat:

2022-03-07 20:11:27.008 13444-13484/com.example.finalflask E/Volley: [904] NetworkUtility.shouldRetryException: Unexpected response code 400 for http://192.168.2.81:5000/predict 
2022-03-07 20:11:27.010 13444-13444/com.example.finalflask D/AndroidRuntime: Shutting down VM 2022-03-07 20:11:27.013 13444-13444/com.example.finalflask E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.finalflask, PID: 13444
        java.lang.AssertionError
            at com.example.finalflask.MainActivity$3.lambda$onClick$1$com-example-finalflask-MainActivity$3(MainActivity.java:104)
            at com.example.finalflask.MainActivity$3$$ExternalSyntheticLambda0.onErrorResponse(Unknown Source:2)
            at com.android.volley.Request.deliverError(Request.java:652)
            at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:104)
            at android.os.Handler.handleCallback(Handler.java:938)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:223)
            at android.app.ActivityThread.main(ActivityThread.java:7656)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Here is the link of image from POSTMAN POSTMAN Screenshot

Also, when i click on "upload" button to send image to flask, android studio is sending request on flask but somehow may be it is not being received in the required format i guess.

Here is the screenshot from flask terminal. Screenshot from flask terminal

Flask Code

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = upload


@app.route('/')

def index():
    return 'Hello World'

@app.route('/predict', methods=['POST','GET'])
def predict():
    if request.method == 'POST':
        file = request.files['image']
        filename = secure_filename(file.filename)
        path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
        image = cv2.imread(path)
        .....
        .....
        .....

if __name__ == '__main__':
    app.run(host='0.0.0.0')

Above code is working fine on POSTMAN

MainActivity.java

public class MainActivity extends AppCompatActivity {
private static final int requestcamera_code = 12;
ImageView image;
Button choose, upload, capture;
int PICK_IMAGE_REQUEST = 111;

String URL ="http://192.168.2.81:5000/predict";
String url = URL.replaceAll(" ", "%20");
Bitmap bmp;
Bitmap bitmap;
ProgressDialog progressDialog;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    capture = (Button) findViewById(R.id.capture);
    image = (ImageView) findViewById(R.id.image);
    choose = (Button) findViewById(R.id.choose);
    upload = (Button) findViewById(R.id.upload);

    capture.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            startActivityForResult(camera, requestcamera_code);
        }
    });


    //opening image chooser option
    choose.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent();
            intent.setType("image/*");
            intent.setAction(Intent.ACTION_PICK);
            startActivityForResult(Intent.createChooser(intent, "Select Image"), PICK_IMAGE_REQUEST);
        }
    });

    upload.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            progressDialog = new ProgressDialog(MainActivity.this);
            progressDialog.setMessage("Uploading, please wait...");
            progressDialog.show();

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap = BitmapFactory.decodeResource(getResources(), R.id.image);
            bitmap = ((BitmapDrawable) image.getDrawable()).getBitmap();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            byte[] imageBytes = baos.toByteArray();
            String imageString = Base64.encodeToString(imageBytes, Base64.DEFAULT);

            StringRequest request = new StringRequest(Request.Method.POST, url, s -> {
                progressDialog.dismiss();
                if(s.equals("true")){
                    Toast.makeText(MainActivity.this, "Uploaded Successful", Toast.LENGTH_LONG).show();
                }
                else{
                    Toast.makeText(MainActivity.this, "Some error occurred!", Toast.LENGTH_LONG).show();
                }
            }, volleyError -> {
                VolleyError error = null;
                assert false;
                NetworkResponse response = error.networkResponse;

                if (error instanceof ServerError && response != null) {
                    try {
                        String res = new String(response.data,
                                HttpHeaderParser.parseCharset(response.headers, "utf-8"));
                        // Now you can use any deserializer to make sense of data
                        JSONObject obj = new JSONObject(res);
                    } catch (UnsupportedEncodingException | JSONException e1) {
                        // Couldn't properly decode data to string
                        e1.printStackTrace();
                    } // returned data is not JSONObject?


                }
                Toast.makeText(MainActivity.this, "Some error occurred -> "+volleyError, Toast.LENGTH_LONG).show();;
            }) {
                @Override
                public Map<String, String> getHeaders() throws AuthFailureError {
                    HashMap<String, String> headers = new HashMap<String, String>();
                    headers.put("Content-Type", "application/json");
                    return headers;
                }
                @Override
                protected Map<String, String> getParams() {
                    Map<String, String> parameters = new HashMap<String, String>();
                    parameters.put("image", imageString);
                    Log.d("tag", parameters.toString());
                    
                    return parameters;
                }
            };

            request.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 3, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

            RequestQueue rQueue = Volley.newRequestQueue(MainActivity.this);
            rQueue.add(request);
        }
    });
}


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == PICK_IMAGE_REQUEST) {
        Uri filePath = data.getData();

        try {
            //getting image from gallery
            bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), filePath);

            //Setting image to ImageView
            image.setImageBitmap(bitmap);

        } catch (Exception e) {
            e.printStackTrace();
        }
    } else if (requestCode == requestcamera_code) {
        Bitmap imgbitmap = (Bitmap) data.getExtras().get("data");
        image.setImageBitmap(imgbitmap);
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:paddingBottom="15dp"
    android:paddingLeft="15dp"
    android:paddingRight="15dp"
    android:paddingTop="15dp"
    tools:context=".MainActivity"
    android:orientation="vertical">
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="268dp" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Capture"
        android:id="@+id/capture"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Choose"
        android:id="@+id/choose"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Upload"
        android:id="@+id/upload"/></LinearLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.finalflask">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:usesCleartextTraffic="true"
        android:theme="@style/Theme.AppCompat"
        android:networkSecurityConfig="@xml/network_security_config"
        tools:ignore="MissingClass">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

build.gradle

plugins {
    id 'com.android.application'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.example.finalflask"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    implementation 'com.android.volley:volley:1.2.1'

}

I have read similar issues where mostly it was suggested to check url and make sure the value is in string. I have checked both of them, POSTMAN is working fine and parameter sent is specified as string.

Any kind of help and suggestion is appreciated. Thank you very much



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source