Android調用相機和相冊獲取圖片並存入數據庫(反思)

最近在做項目的時候有一個需求,是要從相機中或相冊中獲取圖片,而且還要將其存入SQLite,最開始的時候我想的是直接將圖片存入數據庫,但是後來在Google上發現不行,sqlite不支持這種類型,但是我看到了它支持Blob這種類型,也就是二進制,這種類型可以儲存圖片和視頻,既然最基本的儲存解決了,那麼就開始動手寫代碼了。


直接用模板代碼調用相機和相冊

調用相機和相冊是有模板代碼的,可以考慮以後把它寫成一個自己的庫,方便以後的項目中調用。其大致思路就是:
1. 確定好圖片名稱,由於圖片名稱肯定不能重複,所以這裏直接用當前時間加上後綴來命名。
2. 獲取File路徑,創建File對象,也就是你要將圖片放在哪。
3. 將File對象轉換爲Uri對象。
4. 利用Intent放入要執行的動作,這裏從相冊選取和拍照選取圖片有所不同。
5. startActivityForResult直接啓動Intent,在onActivityResult 接受數據。
6. 通過requestCode判斷是拍照還是打開相冊。
7. 如果是拍照就準備Intent啓動裁剪工作,依然是使用startActivityForResult來啓動裁剪程序獲得圖片,從相冊選取得到照片也是一樣的。
8. 最後將圖片轉換爲Byte[]類型,存入數據庫。
9. 刷新UI ,展示獲得的圖片。

//此爲啓動相機
takePhotoButton.setOnClickListener(new View.OnClickListener()
                {
                    @Override
                    public void onClick(View v)
                    {
                        String photoName = bean.getDateInfo() + "_image.jpg";
                        Log.d("haha", "photoName is " + photoName);
                        outputImage = new File(Environment.getExternalStorageDirectory()+"/ASimpleCount/", photoName);
                        try
                        {
                            if(outputImage.exists())
                            {
                                outputImage.delete();
                            }
                            outputImage.createNewFile();
                            Log.d("haha", "創建圖片存儲目錄");
                        } catch (IOException e)
                        {
                            Log.d("haha", "創建目錄拋出異常");
                            e.printStackTrace();
                        }
                        imageUri = Uri.fromFile(outputImage);//將文件路徑轉化爲Uri對象
                        Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                        intent.putExtra("name", bean.getName());
                        startActivityForResult(intent,TAKE_PHOTO);
                        materialDialog.dismiss();
                    }
                });
//此爲啓動相冊
chooseGalleryButton.setOnClickListener(new View.OnClickListener()//選擇從相冊選擇相片
                {
                    @Override
                    public void onClick(View v)
                    {
                        String photoName = bean.getDateInfo() + "_image.jpg";
                        outputImage = new File(Environment.getExternalStorageDirectory()+ "/ASimpleCount/", photoName);
                        try
                        {
                            if(outputImage.exists())
                            {
                                outputImage.delete();
                            }
                            outputImage.createNewFile();
                        } catch (IOException e)
                        {
                            e.printStackTrace();
                        }
                        imageUri = Uri.fromFile(outputImage);//將文件路徑轉化爲Uri對象
                        Intent intent = new Intent(Intent.ACTION_PICK);
                        intent.setType("image/*");
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                        intent.putExtra("name", bean.getName());
                        startActivityForResult(intent, CHOOSE_PHOTO);
                        materialDialog.dismiss();
                    }
                });

            }
        });
 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case TAKE_PHOTO:
                    if(resultCode==RESULT_OK)
                    {
                        Intent intent = new Intent("com.android.camera.action.CROP");
                        intent.setDataAndType(imageUri, "image/*");
                        intent.putExtra("scale", true);
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                        startActivityForResult(intent,CROP_PHOTO);
                    }
                break;
            case CROP_PHOTO:
                    if (resultCode == RESULT_OK)
                    {
                            bitmap = Compressor.getDefault(this).compressToBitmap(outputImage);
                            bean.setPicInfo(BitmapHandler.convertBitmapToByte(bitmap));
                            /**
                             * 數據獲取完畢,增加卡片
                             */
                            addNewCard();
                    }
                break;
            case CHOOSE_PHOTO:
                ContentResolver resolver = getContentResolver();
                Uri imgUri = data.getData();
//                try
//                {
//                    bitmap = MediaStore.Images.Media.getBitmap(resolver,
//                            imgUri);
                    bitmap= tool.ImageUtil.getScaledBitmap(this, imgUri, 612.0f,  816.0f);
                    bean.setPicInfo(BitmapHandler.convertBitmapToByte(bitmap));
//                } catch (IOException e)
//                {
//                    e.printStackTrace();
//                }
                addNewCard();
                break;
        }
    }
 private void addNewCard()
    {
        saveBeanToDataBase();
        Log.d("haha", "在addcard方法中"+bean.toString());
        showRecyclerView();
    }

問題的發生及解決

當然不會這麼一帆風順,在調試app時發現從相冊取出圖片可以正常工作,但是調用相機拍照時,裁剪完畢點擊確定時,app進入黑屏狀態,停留10多秒後直接閃退,待我馬上再次點開app時系統報錯。

01-29 13:41:56.520: W/CursorWindow(4121): Window is full: requested allocation 5140987 bytes, free space 2096617 bytes, window size 2097152 bytes
01-29 13:41:56.520: E/CursorWindow(4121): Failed to read row 0, column 0 from a CursorWindow which has 0 rows, 9 columns.

01-29 13:43:30.932: W/System.err(4121): java.lang.IllegalStateException: Couldn't read row 0, col 0 from CursorWindow.  Make sure the Cursor is initialized correctly before accessing data from it. 
01-29 13:43:30.932: W/System.err(4121):     at android.database.CursorWindow.nativeGetLong(Native Method) 

這是什麼?從來沒有見過這種報錯,不過CursorWindow倒是給我提醒可能是數據庫的問題,我不信邪,就卸了app重新運行,這次發現從相冊選取照片也是有事行有時不行,我還沒有遇到過這麼怪的事,於是我就把上段報錯信息google了一下,馬上就在StackoverFlow上找到了答案,裏面有一個和我一模一樣的問題,下面已有人解決了:

http://stackoverflow.com/questions/21432556/android-java-lang-illegalstateexception-couldnt-read-row-0-col-0-from-cursorw

這裏寫圖片描述

原來是數據庫的讀取中出現了問題,Cursor對象只能夠存儲1MB的數據(網上有人說是1MB,我驗證了一下,好像確實是1MB),多了會發生這樣的報錯。爲了驗證這個原因,我想之前從相冊選取照片有時成功有時失敗的問題,我猜想可能是選取了大於1MB的圖片和小於1MB的圖片的問題,爲了驗證這個猜想我準備了兩張不同大小的照片,果然在選取小的那張時就成功了,大的系統就崩潰了。
現在問題已經找出來了,那麼在我面前有兩條路。
1. 重新設計數據庫結構,改直接儲存圖片爲間接儲存圖片的Uri地址。
2. 不用改變數據庫結構,將圖片壓縮至1MB內,存入數據庫。
兩種方法各有好處,第一種方法的好處是數據庫本就不適合儲存數據大的文件,間接儲存可以提高數據庫讀寫效率。但是壞處是數據的整體性不強,如果用戶在手機裏把圖片刪了,那麼從數據庫裏讀取就會報錯,而且對於我現在來說代碼改動太大了,不利於維護;第二種方法的好處是整體性強,直接存入數據庫,就算用戶在手機裏刪除了圖片,也不怕,數據庫裏還有。但是相應的效率就會下降。
我當即決定採用第二種方法,我實在是不想動代碼太多了,但是我又不懂圖片壓縮算法,不可能去現學吧。於是我馬上想到了Github這個牛人云集的地方,我在github搜索“Android Compress”於是馬上就有寫好的庫給我調用了。在這裏再次感謝

https://github.com/zetbaitsu/Compressor
提供的圖片壓縮算法。
好了僅僅只需要一行代碼就可以壓縮圖片了bitmap= tool.ImageUtil.getScaledBitmap(this, imgUri, 612.0f, 816.0f); 但是最開始這個庫並沒有給我提供Uri轉Bitmap的方法,而是隻有File轉Bitmap,我不信這個邪,於是我點進源代碼看。

 public Bitmap compressToBitmap(File file) {
        return ImageUtil.getScaledBitmap(context, Uri.fromFile(file), maxWidth, maxHeight);
    }

原來是調用了ImageUtil.getScaledBitmap方法,而這個方法是調用的uri,之不過多了一個將File轉化爲Uri的過程,但是我現在不需要這個過程怎麼辦呢?於是我機智的修改了下源代碼爲我所用,那麼一切都OK了。
最後運行程序,不管是相冊還是相機都運行得很好了。


反思總結

今天的這個找bug我收益良多,總結起來就是,注意報錯信息,利用好Google,Stackoverflow,並注意多調試驗證從網上看到的答案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章