1
votes

i'm getting this error which i can't understand why. I'm fairly new to java. What i'm trying to do is start a new thread which will handle all the save image processes.

Error:

 java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
                at android.os.Handler.<init>(Handler.java:200)
                at android.os.Handler.<init>(Handler.java:114)
                at android.app.Dialog.<init>(Dialog.java:108)
                at android.app.AlertDialog.<init>(AlertDialog.java:125)
                at android.app.AlertDialog$Builder.create(AlertDialog.java:967)
                at android.app.AlertDialog$Builder.show(AlertDialog.java:986)
                at com.example.matts.myapplication.MainActivity$1.handleMessage(MainActivity.java:37)
                at com.example.matts.myapplication.Saving.run(Saving.java:54)
                at java.lang.Thread.run(Thread.java:818)

Here's my code.

Main:

package com.example..myapplication;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.text.SimpleDateFormat;


public class MainActivity extends Activity {

    static final int REQUEST_IMAGE_CAPTURE = 1;
    ImageView imageView;
    private static final String TAG = "MyActivity";
    Bitmap image;

    Handler goodHandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
            builder.setMessage("File Saved").show();

        }

    };

    Handler badHandler = new Handler() {
        @Override
        public void handleMessage(Message message) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
            builder.setMessage("File Save failed").show();

        }

    };



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        openCamera();
        setContentView(R.layout.activity_main);
        findViewById(R.id.captureImage).bringToFront();
        findViewById(R.id.saveImage).bringToFront();
        imageView = (ImageView) findViewById(R.id.imageView2);

    }

    protected void onSaveInstanceState(Bundle outState){
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + "TMP.jpg");
        file.delete();
    }

    public void openCamera() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + "TMP.jpg");
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    }


    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        //Check that request code matches ours:
        if (requestCode == REQUEST_IMAGE_CAPTURE) {
            //Get our saved file into a bitmap object:
            File file = new File(Environment.getExternalStorageDirectory() + File.separator + "TMP.jpg");
            Bitmap image = decodeSampledBitmapFromFile(file.getAbsolutePath(), 1000, 700);
            imageView.setImageBitmap(image);
        }
    }

// Reduce the amount of dynamic heap used by expanding the JPEG into a memory array that's already scaled to match the size of the destination view
    public static Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) { // BEST QUALITY MATCH
        //First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        // Calculate inSampleSize, Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        int inSampleSize = 1;

        if (height > reqHeight) {
            inSampleSize = Math.round((float) height / (float) reqHeight);
        }
        int expectedWidth = width / inSampleSize;

        if (expectedWidth > reqWidth) {
            //if(Math.round((float)width / (float)reqWidth) > inSampleSize) // If bigger SampSize..
            inSampleSize = Math.round((float) width / (float) reqWidth);
        }

        options.inSampleSize = inSampleSize;

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;

        return BitmapFactory.decodeFile(path, options);
    }

    public void capture_btn(View v) {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File file = new File(Environment.getExternalStorageDirectory() + File.separator + "TMP.jpg");
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
        startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
    }



    public void storeImage(Bitmap image) {
        imageView.buildDrawingCache();
        Bitmap bm_img = imageView.getDrawingCache();
        Saving saving = new Saving(bm_img, goodHandler, badHandler);
        Thread thread = new Thread(saving);
        thread.start();
    }

    public void save_btn(View v) {
        DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                switch (which){
                    case DialogInterface.BUTTON_POSITIVE:
                        storeImage(image);


                        break;

                    case DialogInterface.BUTTON_NEGATIVE:

                        break;
                }
            }
        };

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("Are you sure?").setPositiveButton("Yes", dialogClickListener)
                .setNegativeButton("No", dialogClickListener).show();

    } //notification messages
}

Saving:

package com.example..myapplication;

import android.graphics.Bitmap;
import android.os.Environment;
import android.os.Message;
import android.util.Log;
import android.os.Handler;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;


public class Saving implements Runnable {
    private Bitmap image;
    private android.os.Handler goodHandler;
    private android.os.Handler badHandler;
    private static final String TAG = "Saving";

    public Saving(Bitmap in, Handler hinGood,Handler hinBad) {
        image = in;
        goodHandler = hinGood;
        badHandler = hinBad;


    }

    @Override
    public void run() {
        File file = getOutputMediaFile();
        if (file == null) {
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(file);
            image.compress(Bitmap.CompressFormat.PNG, 90, fos);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.d(TAG, "File not found: " + e.getMessage());
            badHandler.handleMessage(new Message());
            return;
        } catch (IOException e) {
            Log.d(TAG, "Error accessing file: " + e.getMessage());
            badHandler.handleMessage(new Message());
            return;
        }
        goodHandler.handleMessage(new Message());


    }

    public File getOutputMediaFile(){
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this.
        File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
                + "/Pictures/Wiki_camera"
                + "/Photos");

        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                return null;
            }
        }
        // Create a media file name
        String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date());
        File mediaFile;
        String mImageName="camera_wiki"+ timeStamp +".jpg";
        mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName);
        return mediaFile;
    }

}

I've added whole of the main class and the saving class.

2
Post the storeImage() code please.pathfinderelite
Done, added to question.smither123
Where are goodHandler and badHandler defined?pathfinderelite
At the top of public class saving "private android.os.Handler goodHandler; private android.os.Handler badHandler;"smither123
That's where they are declared, not defined. Where are they assigned a value? Seeing that the error is Can't create handler, it is critical to know how they are being created.pathfinderelite

2 Answers

1
votes

The issue is that you are calling handleMessage() instead of sendMessage(), here

try {
    FileOutputStream fos = new FileOutputStream(file);
    image.compress(Bitmap.CompressFormat.PNG, 90, fos);
    fos.close();
} catch (FileNotFoundException e) {
    Log.d(TAG, "File not found: " + e.getMessage());
    badHandler.handleMessage(new Message());
    return;
} catch (IOException e) {
    Log.d(TAG, "Error accessing file: " + e.getMessage());
    badHandler.handleMessage(new Message());
    return;
}
goodHandler.handleMessage(new Message());

As a result, the below is not called on the UI thread

AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
builder.setMessage("File Saved").show();

and thus an exception is thrown.

1
votes

Try

runOnUiThread (new Thread(new Runnable() { 
         public void run() {
              storeImage(image);
         }
     }));