java.lang에 정의되어 있는 RuntimeException의 서브 클래스 종류

 

복사 http://blog.naver.com/s9712094/140030018008

 

출처 : Beginning Java 2 SDK 1.4 Edition
(정보문화사)

* ArithmeticException

   - 정수를 0으로 나누려고 하는 등의 유효하지 않은 계산 조건을 사용하는 경우

* IndexOutOfBoundsException
   - 객체의 범위를 벗어난 인덱스를 사용하려고 하는 경우. 배열, String객체, 또는 Vector객체가 이에
      해당된다. Vector 클래스는 표준 패키지 java.util에 정의되어 있음.

* NegativeArraySizeException
   - 음의 크기를 갖는 배열을 정의하려 하는 경우

* NullPointerException
   - null을 포함하는 객체 변수를 사용하려는 경우. 정당한 작업을 위해서는 변수가 객체를 참조해야
      한다. 예를 들어, 메서드를 호출하거나 데이터 멤버에 접근하는 경우가 이에 속한다.

* ArrayStoreException
   - 배열 타입에 맞지 않는 객체를 배열에 저장하려는 경우

* ClassCaseException
   - 객체를 부적절한 타입으로 형변환하려는 경우. 즉, 객체가 지정한 클래스도 아니고, 지정한 클래스
     의 수퍼클래스나 서브클래스도 아닌 경우를 뜻한다.

* IllegalArgumentException
   - 메서드가 파라미터 타입과 일치하지 않는 인자를 전달하려는 경우

* SecurityException
   - 프로그램이 보안에 위반되는 부적절한 작업을 수행하려는 경우. 애플릿에서 로컬 컴퓨터에 있는
     파일을 읽으려 하는 경우가 이에 속한다.

* IllegalMonitorStateException
   - 스레드가 자기가 소유하지 않은 객체를 모니터링하려 할 때.

* IllegalStateException
   - 적절하지 않은 때에 메서드를 호출하는 경우

* UnsupportedOperationException
   - 객체가 지원하지 않는 작업을 수행하도록 요청하는 경우

 

MySQL에서 CUBRID의 변환을 생각하는 많은 개발자들이 궁금해 하는 MySQL의 BLOB타입 데이터를 CUBRID로 이전하는 방법에 대해 기술한다. 본FAQ는 http://blog.naver.com/windyhan/110052262344 블로그를 참조하여 기술하였음을 밝힌다.

이미 문서를 통하여 MySQL to CUBRID의 Data Type변경에 대해 소개하고 있다. 대부분의 경우 대응되는 Type으로의 스키마 변경으로 해결되나 Blob의 경우 약간의 작업이 필요하게 된다. 이 약간의 작업에 대해서 설명하도록 한다.

MySQL에서 사용하는 Blob타입 4종류와 각각의 허용 범위는 아래와 같다.


TinyBlob - maximum length of 255 characters.
Blob - maximum length of 65535 characters.
MediumBlob - maximum length of 16777215 characters.
LongBlob - maximum length of 4294967295 characters.

 


CUBRID에서 대응하는 Type인 Bit Varying의 범위는 아래와 같다.


Bit Varying(n) - n에 대한 값이 생략되면, 디폴트 길이 1,073,741,823이 가정된다. 이는 MySQL의 LongBlob을 제외한 Blob타입에 대응될 수 있다.
* MySQL의 LongBlob에 대해서는 CUBRID는 GLO라는 타입으로 대응이 가능하나 이는 다른 Tip으로 정리한다.
 
MySQL의 Blob을 CUBRID로 변환하는 과정을 아래의 예를 통하여 확인하고자 한다. 아래의 예시는 test_tbl이라는 테이블로 간단한 이미지를 저장한 저장해 놓은 테이블이다. 이미지의 사이즈는 30~60만 byte정도로 MySQL의 MediumBlob에 해당한다.


mysql> describe test_tbl;

+-----------+--------------+------+-----+---------+----------------+

| Field     | Type         | Null | Key | Default | Extra          |

+-----------+--------------+------+-----+---------+----------------+

| id        | int(11)      | NO   | PRI | NULL    | auto_increment |

| file_name | varchar(255) | YES  |     | NULL    |                |

| file_data | mediumblob   | YES  |     | NULL    |                |

+-----------+--------------+------+-----+---------+----------------+

3 rows in set (0.00 sec)

 

데이터는 아래와 같이 들어있다.

 


데이터를 추출하기 위해 JDBC를 이용한다. file_data칼럼에 있는 이미지를 추출하는 java코드는 아래와 같다.

public void getBlobDataFromMySQL(){

          Connection conn=null;

          Statement stmt=null;

          ResultSet rs=null;

          String file_path = "pic";

 

          try{

               File curr = new File("./" + file_path);

               if( !curr.exists() ){

                  curr.mkdir();

               }

               Class.forName("com.mysql.jdbc.Driver");  //드라이버 로드

               conn = DriverManager.getConnection   // Connection객체 가져오기

                   ("jdbc:mysql://localhost/testdb?user=root&password=root");

 

               String sql = "select id, file_name, file_data from test_tbl";

               stmt = conn.createStatement();

               rs = stmt.executeQuery(sql);

               while(rs.next()){

                 int id = rs.getInt("id");

                 String file_name = rs.getString("file_name");

                 InputStream is = rs.getBinaryStream("file_data");

                 FileOutputStream fos = new FileOutputStream("./pic/" + file_name);

                 byte[] buff = new byte[2048000];

                 int len;

                 while ( (len = is.read(buff)) > 0 )

                      fos.write( buff, 0, len);

                 fos.close();

                 is.close();

               }

               rs.close();

               stmt.close();

               conn.close();

          }catch(Exception e){

              e.printStackTrace();

          }

    }

 

위의 코드를 간략히 설명하면 test_tbl 테이블의 이미지를 select 하여 pic이라는 디렉토리에 file_name 을 이름으로 하는 파일들을 생성한다. 이 파일들을 읽어서 CUBRID에 삽입할 것이다.

 

이제 추출한 데이터를 넣을 CUBRID테이블을 생성한다. CUBRID의 테이블 생성 예는 아래와 같다.


csql> create table test_tbl ( id integer auto_increment primary key not null, file_name varchar(255), file_data bit varying);

csql> ;x

 

Current transaction has been committed.

 

1 command(s) successfully processed.

csql> ;sc test_tbl

 

=== <Help: Schema of a Class> ===

 

 <Class Name>

 

     test_tbl

 

 <Attributes>

 

     id                   INTEGER AUTO_INCREMENT  NOT NULL

     file_name            CHARACTER VARYING(255)

     file_data            BIT VARYING(1073741823)

 

 <Constraints>

 

     PRIMARY KEY pk_test_tbl_id ON test_tbl (id)

 

Current transaction has been committed.

 


MySQL의 BLOB 타입에서 빼낸 이미지 파일들을 CUBRID의 test_tbl의 bit varying 타입인 file_data 컬럼에 넣는다.

public void setBlobDataToCUBRID_bit(){

           File curr = new File("./pic");                        

           String[] filelist = curr.list(new BlobFileNameFilter());              -------------------------  1

           cubrid.jdbc.driver.CUBRIDConnection conn=null;

           cubrid.jdbc.driver.CUBRIDPreparedStatement stmt=null;

 

           try{

               Class.forName("cubrid.jdbc.driver.CUBRIDDriver");

               conn = (cubrid.jdbc.driver.CUBRIDConnection)DriverManager.getConnection                   

("jdbc:cubrid:127.0.0.1:33004:testdb1:dba::");          ------------------------- 2

 

               for( int i = 0; i < filelist.length ; i++){

                  String filename  = filelist[i];

                  File file = new File("./pic/" + filename);

                  FileInputStream in = new FileInputStream(file);           ------------------------- 3

 

                  String sql = "insert into test_tbl(file_name, file_data) values ( ?,?)";

                  stmt=(cubrid.jdbc.driver.CUBRIDPreparedStatement)conn.prepareStatement(sql);

                  stmt.setString(1,filename);

                  stmt.setBinaryStream(2,in,(int)file.length());               ------------------------- 4

                  stmt.executeUpdate();                                 ------------------------- 5

                  stmt.close();       

                  in.close();

               }

               conn.commit();

               conn.close();

 

         }catch(Exception e){

            e.printStackTrace();

         }

}

 각각의 설명은 아래와 같다.
1. Bit varying 컬럼에 저장할 파일들의 리스트를 생성한다.

2. CUBRID DB의 Connection 을 가져온다.

3. 입력할 FILE의 InputStream 을 생성한다.

4. Bit Varying 타입인 file_data 컬럼에 setBinaryStream() 함수를 이용해 파라미터 바인딩을 한다.

5. insert 문을 실행한다.

 

위의 java파일을 컴파일 후 실행하면 test_tbl 테이블에 데이터가 삽입된다. 데이터가 제대로 들어갔는지 확인한다.


csql> select file_name, bit_length(file_data)/8 from test_tbl

csql> ;x

 

=== <Result of SELECT Command in Line 1> ===

 

  file_name              bit_length(file_data)/8

================================================

  'DSC_0193_007.JPG'                      329216

  'DSC_0199_010.JPG'                      266272

  'DSC_0202_011.JPG'                      329382

  'DSC_0190_004.JPG'                      419584

  'DSC_0206_013.JPG'                      319426

  'DSC_0187_002.JPG'                      321152

  'DSC_0197_008.JPG'                      273747

  'DSC_0191_005.JPG'                      391503

  'DSC_0192_006.JPG'                      291880

  'DSC_0198_009.JPG'                      169344

  'DSC_0183_001.JPG'                      216492

  'DSC_0208_014.JPG'                      345924

  'DSC_0203_012.JPG'                      333349

  'DSC_0189_003.JPG'                      262936

 

14 rows selected.

데이터가 잘 들어가 있는지 확인해 보았다. 질의에서 Bit_length(file_data)/8 을 한 이유는 저장된 데이터의 byte 수를 알기 위해 한 것이다. 특별히 다른 점은 없고, JDBC 표준 인터페이스를 사용하여 저장을 한 것이다.

 

CUBRID에 저장된 데이터를 추출하는 방법은 위의 MySQL에서 추출한 방법과 거의 같다 아래의 두가지 부분만 변경해 주면 된다.

 1. 함수 이름을 변경한다.

 2. MySQL Connection 연결하는 방법을 CUBRID용으로 변경한다.
[출처] MySQL의 Blob타입을 CUBRID로 변환하기 (큐브리드 공부하기) |작성자 Neo

'PHP > Mysql_이미지저장및 뷰' 카테고리의 다른 글

MySQL 쿼리에 이미지 저장  (0) 2013.07.04

이 소스는 요기에서 퍼왔습니다.

책 - 프로 안드로이드 미디어 그래픽, 오디오, 비디오를 비록한 리치 미디어 앱 개발하기

지은이 - Shawn Van Every

옮긴이 - 배장열

출판사 - 제이펍(Jpub)]

참고할 라이브러리 소스 ==>

ca.zip

Activity


import android.app.Activity;

import android.graphics.Bitmap;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.media.AudioFormat;

import android.media.AudioRecord;

import android.media.MediaRecorder;

import android.os.AsyncTask;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.ImageView;

import ca.uol.aig.fftpack.RealDoubleFFT;


// FFT(Fast Fourier Transform) DFT 알고리즘 : 데이터를 시간 기준(time base)에서 주파수 기준(frequency base)으로 바꾸는데 사용.

public class AudioProcessing extends Activity implements OnClickListener{

    // AudioRecord 객체에서 주파수는 8kHz, 오디오 채널은 하나, 샘플은 16비트를 사용

int frequency = 8000;

    int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;

    int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;

    // 우리의 FFT 객체는 transformer고, 이 FFT 객체를 통해 AudioRecord 객체에서 한 번에 256가지 샘플을 다룬다. 사용하는 샘플의 수는 FFT 객체를 통해

    // 샘플들을 실행하고 가져올 주파수의 수와 일치한다. 다른 크기를 마음대로 지정해도 되지만, 메모리와 성능 측면을 반드시 고려해야 한다.

    // 적용될 수학적 계산이 프로세서의 성능과 밀접한 관계를 보이기 때문이다.

    private RealDoubleFFT transformer;

    int blockSize = 256;

    Button startStopButton;

    boolean started = false;

    

    // RecordAudio는 여기에서 정의되는 내부 클래스로서 AsyncTask를 확장한다.

    RecordAudio recordTask;

    

    // Bitmap 이미지를 표시하기 위해 ImageView를 사용한다. 이 이미지는 현재 오디오 스트림에서 주파수들의 레벨을 나타낸다.

    // 이 레벨들을 그리려면 Bitmap에서 구성한 Canvas 객체와 Paint객체가 필요하다.

    ImageView imageView;

    Bitmap bitmap;

    Canvas canvas;

    Paint paint;

    

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        

        startStopButton = (Button)findViewById(R.id.StartStopButton);

        startStopButton.setOnClickListener(this);

        

        

        // RealDoubleFFT 클래스 컨스트럭터는 한번에 처리할 샘플들의 수를 받는다. 그리고 출력될 주파수 범위들의 수를 나타낸다.

        transformer = new RealDoubleFFT(blockSize);

        

        // ImageView 및 관련 객체 설정 부분

        imageView = (ImageView)findViewById(R.id.ImageView01);

        bitmap = Bitmap.createBitmap((int)256, (int)100, Bitmap.Config.ARGB_8888);

        canvas = new Canvas(bitmap);

        paint = new Paint();

        paint.setColor(Color.GREEN);

        imageView.setImageBitmap(bitmap);

    }

    

    // 이 액티비티의 작업들은 대부분 RecordAudio라는 클래스에서 진행된다. 이 클래스는 AsyncTask를 확장한다.

    // AsyncTask를 사용하면 사용자 인터페이스를 멍하니 있게 하는 메소드들을 별도의 스레드로 실행한다.

    // doInBackground 메소드에 둘 수 있는 것이면 뭐든지 이런 식으로 실행할 수 있다.

    private class RecordAudio extends AsyncTask<Void, double[], Void>{

    @Override

    protected Void doInBackground(Void... params) {

    try{

    // AudioRecord를 설정하고 사용한다.

    int bufferSize = AudioRecord.getMinBufferSize(frequency, channelConfiguration, audioEncoding);

   

    AudioRecord audioRecord = new AudioRecord(

    MediaRecorder.AudioSource.MIC, frequency, channelConfiguration, audioEncoding, bufferSize);

   

    // short로 이뤄진 배열인 buffer는 원시 PCM 샘플을 AudioRecord 객체에서 받는다.

    // double로 이뤄진 배열인 toTransform은 같은 데이터를 담지만 double 타입인데, FFT 클래스에서는 double타입이 필요해서이다.

    short[] buffer = new short[blockSize];

    double[] toTransform = new double[blockSize];

   

    audioRecord.startRecording();

   

    while(started){

    int bufferReadResult = audioRecord.read(buffer, 0, blockSize);

   

    // AudioRecord 객체에서 데이터를 읽은 다음에는 short 타입의 변수들을 double 타입으로 바꾸는 루프를 처리한다. 

    // 직접 타입 변환(casting)으로 이 작업을 처리할 수 없다. 값들이 전체 범위가 아니라 -1.0에서 1.0 사이라서 그렇다

    // short를 32,768.0(Short.MAX_VALUE) 으로 나누면 double로 타입이 바뀌는데, 이 값이 short의 최대값이기 때문이다. 

    for(int i = 0; i < blockSize && i < bufferReadResult; i++){

    toTransform[i] = (double)buffer[i] / Short.MAX_VALUE; // 부호 있는 16비트

    }

   

    // 이제 double값들의 배열을 FFT 객체로 넘겨준다. FFT 객체는 이 배열을 재사용하여 출력 값을 담는다. 포함된 데이터는 시간 도메인이 아니라

    // 주파수 도메인에 존재한다. 이 말은 배열의 첫 번째 요소가 시간상으로 첫 번째 샘플이 아니라는 얘기다. 배열의 첫 번째 요소는 첫 번째 주파수 집합의 레벨을 나타낸다.

   

    // 256가지 값(범위)을 사용하고 있고 샘플 비율이 8,000 이므로 배열의 각 요소가 대략 15.625Hz를 담당하게 된다. 15.625라는 숫자는 샘플 비율을 반으로 나누고(캡쳐할 수 있는

    // 최대 주파수는 샘플 비율의 반이다. <- 누가 그랬는데...), 다시 256으로 나누어 나온 것이다. 따라서 배열의 첫 번째 요소로 나타난 데이터는 영(0)과 15.625Hz 사이에

    // 해당하는 오디오 레벨을 의미한다.

    transformer.ft(toTransform);

    // publishProgress를 호출하면 onProgressUpdate가 호출된다.

    publishProgress(toTransform);

    }

   

    audioRecord.stop();

    }catch(Throwable t){

    Log.e("AudioRecord", "Recording Failed");

    }

   

    return null;

    }

   

    // onProgressUpdate는 우리 엑티비티의 메인 스레드로 실행된다. 따라서 아무런 문제를 일으키지 않고 사용자 인터페이스와 상호작용할 수 있다.

    // 이번 구현에서는 onProgressUpdate가 FFT 객체를 통해 실행된 다음 데이터를 넘겨준다. 이 메소드는 최대 100픽셀의 높이로 일련의 세로선으로

    // 화면에 데이터를 그린다. 각 세로선은 배열의 요소 하나씩을 나타내므로 범위는 15.625Hz다. 첫 번째 행은 범위가 0에서 15.625Hz인 주파수를 나타내고,

    // 마지막 행은 3,984.375에서 4,000Hz인 주파수를 나타낸다.

    @Override

    protected void onProgressUpdate(double[]... toTransform) {

    canvas.drawColor(Color.BLACK);

   

    for(int i = 0; i < toTransform[0].length; i++){

    int x = i;

    int downy = (int) (100 - (toTransform[0][i] * 10));

    int upy = 100;

   

    canvas.drawLine(x, downy, x, upy, paint);

    }

    imageView.invalidate();

    }

    }

    

    @Override

    public void onClick(View arg0) {

    if(started){

    started = false;

    startStopButton.setText("Start");

    recordTask.cancel(true);

    }else{

    started = true;

    startStopButton.setText("Stop");

    recordTask = new RecordAudio();

    recordTask.execute();

    }

    }

}



main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    >

<TextView  

    android:layout_width="fill_parent" 

    android:layout_height="wrap_content" 

    android:text="@string/hello"

    />

<ImageView

android:id="@+id/ImageView01"

android:layout_width="wrap_content"

android:layout_height="wrap_content" />

<Button

android:text="Start"

android:id="@+id/StartStopButton"

android:layout_width="wrap_content"

android:layout_height="wrap_content" />    

</LinearLayout>


AndroidManifest.xml

<uses-permission android:name="android.permission.RECORD_AUDIO">

'실내 측위 시스템 > 참고자료' 카테고리의 다른 글

주파수(음파) 측정 관련  (0) 2013.07.03
 

웹 애플리케이션 서버에 관해 알아보자.

1. 웹 서버와 웹 애플리케이션 서버(WAS)

웹 서버는 Web Client(웹 브라우저)에게 제공하는 컨텐츠를 제공하는 서버이다.

주로 정적인 컨텐츠를 제공할 때 사용하는 서버이다.

  • 정적인 컨텐츠 : HTML, Jpeg 혹은 gif같은 이미지를 HTTP 프로토콜을 통해 웹 브라우저로 제공
  • Ex) apahce, IIS, Nginx

웹 애플리케이션 서버는 서버 단에서 애플리케이션을 동작할 수 있도록 지원. 일반적으로 컨테이너 라는 용어로도 사용한다.

주로 동적인 컨텐츠를 제공할 때 사용하는 서버이다.

  • 동적인 컨텐츠 : 초창기 CGI, 현재는 Servlet, JSP, ASP, PHP 등의 프로그램으로 사용
  • Ex) Tomcat, JBoss, Weblogic

 

2. WAS만 쓰면 되는 것 아닌가? 라고 질문할 수 있다.

웹 서버와 WAS를 둘 다 사용하는 이유?

  • 최적화 측면 : WAS는 동적인 처리에 최적화 되어 있다.
  • WAS가 정적 데이터를 처리하는 동안 정적 데이터의 입출력 처리로 인해 동적 데이터의 처리에 지연이 생긴다. 즉 웹 애플리케이션의 수행을 방해함.
  • 웹 애플리케이션의 수행으로 인해 정적 데이터에 영향을 줄 수도 있다.

정적인 데이터는 주로 앞의 웹 서버에서 처리를 하고, 동적인 데이터는 뒤의 WAS에서 처리한다.

 

3. 웹 서버와 웹 애플리케이션서버(WAS)의 관계 그림

 

 

'ICT 이야기' 카테고리의 다른 글

어노니머스  (0) 2013.04.04

[Intro]

Android에서 사용하는 이미지는 Bitmap이라는 클래스에서 다~ 알아서 해줍니다.
그리고 이런 Bitmap Object를 쉽게 만들 수 있도록 도와주는
BitmapFactory 클래스 라는 것도 있습니다.

BitmapFactory는 여러가지 소스로 부터 Bitmap Object를 만들어 주는 일을 하는데,
전부 static이며 decodeXXX 라는 이름을 가진 메소드들로 이루어져 있습니다.

XXX에는 어떤 것으로 부터 decode를 하여
Bitmap Object를 만들어 낼지에 대한 말들이 들어 가겠죠.

 


[Decoding Methods]

BitmapFactory.decodeByteArray() 메소드는 Camera.PictureCallback 으로 부터 받은
Jpeg 사진 데이터를 가지고 Bitmap으로 만들어 줄 때 많이 사용 합니다.
Camera.PictureCallback에서 들어오는 데이터가 byte[] 형식이기 때문에
저 메소드를 사용 해야 하는 것이죠.

 

BitmapFactory.decodeFile() 메소드는 파일을 그대로 읽어 옵니다.
내부적으로는 파일 경로를 가지고 FileInputStream을 만들어서 decodeStream을 합니다.
그냥 파일 경로만 쓰면 다 해주는게 편리 한 것이죠.

 

BitmapFactory.decodeResource() 메소드는 Resource로 부터 Bitmap을 만들어 내며
BitmapFactory.decodeStream() 메소드는 InputStream으로 부터 Bitmap을 만들어 냅니다.
뭐 그냥 이름만 봐도 알 수 있는 것들이지요.

 


[OutOfMemoryError??]

 

보통 이미지 파일을 읽어서 Resizing을 해야 할 때가 있는데,
그럴때는 BitmapFactory로 읽어서 Bitmap.createScaledBitmap() 메소드를 사용하여 줄이면

간단하게 처리 할 수 있습니다.

 

그런데 BitmapFactory를 사용할 때 주의해야 할 점이 있습니다.
아래의 예를 한번 보시죠.

Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg");
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);

 

이미지 파일로부터 Bitmap을 만든 다음에

다시 dstWidth, dstHeight 만큼 줄여서 resized 라는 Bitmap을 만들어 냈습니다.
보통이라면 저렇게 하는게 맞습니다.읽어서, 줄인다.

그런데 만약 이미지 파일의 크기가 아주 크다면 어떻게 될까요?
지금 Dev Phone에서 카메라로 촬영하면
기본적으로 2048 x 1536 크기의 Jpeg 이미지가 촬영된 데이터로 넘어옵니다.
이것을 decode 하려면 3MB 정도의 메모리가 필요 할 텐데,

과연 어떤 모바일 디바이스에서 얼마나 처리 할 수 있을까요?

 

실제로 촬영된 Jpeg 이미지를 여러번 decoding 하다보면

아래와 같은 황당한 메세지를 발견 할 수 있습니다.

java.lang.OutOfMemoryError: bitmap size exceeds VM budget

네... OutOfMemory 입니다.
더 이상 무슨 말이 필요 하겠습니까...
메모리가 딸려서 처리를 제대로 못합니다.

이것이 실제로 decoding 후 메모리 해제가 제대로 되지 않아서 그런 것인지,
하더라도 어디서 Leak이 발생 하는지에 대한 정확한 원인은 알 수 없습니다.
이것은 엔지니어들이 해결해야 할 문제 겠죠...

하지만 메모리 에러를 피할 수 있는 방법이 있습니다.

 

[BitmapFactory.Options.inSampleSize]

 

BitmapFactory.decodeXXX 시리즈는 똑같은 메소드가 두 개씩 오버로딩 되어 있습니다.
같은 이름이지만 Signature가 다른 메소드의 차이점은
BitmapFactory.Options를 파라메터로 받느냐 안받느냐의 차이죠.

BitmapFactory.Options를 사용하게 되면 decode 할 때 여러가지 옵션을 줄 수 있습니다.


여러가지 많지만 저희가 지금 사용할 것은 inSampleSize 옵션 입니다.

 

inSampleSize 옵션은,
애초에 decode를 할 때 얼마만큼 줄여서 decoding을 할 지 정하는 옵션 입니다.

 

inSampleSize 옵션은 1보다 작은 값일때는 무조건 1로 세팅이 되며,
1보다 큰 값, N일때는 1/N 만큼 이미지를 줄여서 decoding 하게 됩니다.
즉 inSampleSize가 4라면 1/4 만큼 이미지를 줄여서 decoding 해서 Bitmap으로 만들게 되는 것이죠.

 

2의 지수만큼 비례할 때 가장 빠르다고 합니다.
2, 4, 8, 16... 정도 되겠죠?

 

그래서 만약 내가 줄이고자 하는 이미지가 1/4보다는 작고 1/8보다는 클 때,
inSampleSize 옵션에 4를 주어서 decoding 한 다음에,

Bitmap.createScaledBitmap() 메소드를 사용하여 한번 더 줄이면 됩니다.

 

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap src = BitmapFactory.decodeFile("/sdcard/image.jpg", options);
Bitmap resized = Bitmap.createScaledBitmap(src, dstWidth, dstHeight, true);

 

당연한 이야기 이겠지만,
내가 원하고자 하는 사이즈가 딱 1/4 크기라면

Bitmap.createScaledBitmap() 메소드를 쓸 필요가 없지요.

inSampleSize 옵션을 잘 활용하면 메모리 부족 현상을 대략적으로 해소 할 수 있습니다.
참고로 제가 저 옵션을 사용한 뒤로는 메모리 에러를 본적이 한~번도 없답니다.


[Appendix]

inSampleSize 옵션을 사용하면

SkScaledBitmapSampler Object (Library Level) 를 생성 하게 되는데,
Object를 만들때 정해진 SampleSize 만큼 축소하여 width와 height를 정한 뒤에 만들게 됩니다.
그러니까 애초에 축소된 사이즈로 이미지를 decoding 하는 것이죠.

원본 사이트 : http://webdev.berber.co.il/articles/MySqlandImages.php3
이글은 위의 페이지에 있는 글을 번역해 본 것으로 약간의 오역이 있을수도 있습니다.
또한 의미를 파악하는데는 문제가 없겠지만, 원본을 참조해 보시기를 권해드리고 싶습니다.

때때로 이미지를 데이터베이스에 저장하는 것이 파일로 저장하는 것보다 더 편리할때가 있습니다.
MySQL과 PHP는 이런 일을 매우 쉽게 만들어줍니다.
이 글에서 저는 MySQL 데이터베이스에 이미지를 저장하는 방법과 나중에 그것을 드러내 보이는 방법을 설명하고자 합니다.

데이터베이스 설정하기.
~~~~~~~~~~~~~~~~~~~~~~
어떤 보통의 텍스트나 정수영역과 이미지가 저장되어지기 위해 필요한 영역 사이의 차이점은 그 영역속에 지니게 되는데 필요한 데이터의 양입니다.
MySQL은 커다란 양의 데이터를 지니기위해 특별한 영역을 사용합니다.
이런 영역들은 BLOBs(BLOB)라고 알려져 있습니다.

MySQL 사이트에서는 BLOB를 아래와 같이 정의하고 있습니다.

BLOB는 데이터의 가변적인 양을 지니고 있을 수 있는 거대한 바이너리 객체이다.

TINYBLOB, BLOB, MEDIUMBLOB와 LONGBLOB의 네가지 BLOB의 형태는 단지 그들이 지니고 있을 수 있는 값의 최대 길이에서만 다를뿐입니다.

MySQL BLOB에 대한 더 많은 정보는 아래에서 확인해 보십시오.

http://www.mysql.com/Manual_chapter/manual_Reference.html#BLOB

이미지를 지니게 될 기본적인 테이블을 생성하기 위해 다음의 구문을 사용하십시오.

CREATE TABLE Images( PicNum int NOT NULL AUTO_INCREMENT PRIMARY KEY, Image BLOB );

업로드 스크립트 설정하기.
~~~~~~~~~~~~~~~~~~~~~~~~~
파일을 업로드하기 위한 예는 Berber(29/06/99)의 File Uploading(http://webdev.berber.co.il/articles/FileUpload.php3)에서 볼 수 있을 것입니다.

지금 우리에게 필요한 것은 MySQL에 파일을 넣고 그것을 얻는 PHP 스크립트입니다.

다음의 스크립트는 단지 그것 뿐입니다. 스크립트에서 저는 파일필드의 이름을 Picture인 것으로 가정하고 있습니다.

If($Picture != “none”)
{
$PSize = filesize($Picture);
$mysqlPicture = addslashes(fread(fopen($Picture, “r”), $PSize));
mysql_connect($host, $username, $password) or die(“SQL 서버에 접속할 수 없습니다.”);
@mysql_select_db($db) or die(“데이터베이스를 선택할 수 없습니다.”)
mysql_query(“INSERT INTO Image (Image) VALUES ‘($mysqlPicture)’”) or die(“쿼리를 수행할 수 없습니다.”);
}
else
{
echo “어떤 그림도 업로드하지 않으셨습니다.”;
}

이것이 데이터베이스에 이미지를 넣기 위해 필요한 모든 것입니다.
어떤경우에는 여러분이 MySQL에 이미지를 넣으려고 할 때 에러가 발생할 지도모르는데, 그런 경우에는 여러분의 MySQL 버전이 허락하는 최대 패킷의 크기를 확인해 보는 것이 좋습니다.
그런 경우는 매우 적을 테지만, 여러분은 MySQL의 에러 로그내에서 이 에러에 대한 것을 볼 수 있습니다.

위의 파일에서 우리가 한 것은,
1. 만약, If($Picture != “none”)으로 파일이 업로드되었는지 확인하고,
2. MySQL내에서 에러들을 피하기 위해 그림의 스트림에 addslashes()를 주고,
3. MySQL에 접속하여 데이터베이스를 선택하고 이미지를 넣도록 한 것입니다.

이미지 나타내기.
~~~~~~~~~~~~~~~~
이제 우리는 이미지를 데이터베이스에 넣는 법을 알았습니다. 우리는 그것들을 얻고 보이는 방법을 만들어 낼 필요가 있습니다. 이것은 이미지를 넣는 것보다 더 복잡하지만, 만약 여러분이 이 단계를 따른다면 이것을 만들어내고 실행하는데 시간이 걸리지 않을 것입니다.

그림을 보이는 데에는 보내어질 헤더가 필요하기 때문에 우리는 단지 하나의 그림외에는 더 볼 수밖에 없는 그런 상태에 봉착하게 될 수도 있습니다.

한번 헤더를 보내게 되면 더 이상의 헤더를 보낼 수 없게 됩니다.

이것이 트릭이 필요한 부분입니다.
시스템을 약간 속이기 위해 우리는 두개의 파일을 사용합니다.
첫번째 파일은  우리가 보이고자 하는 그림이 어디에 있는지를 알고 있는 HTML 템플릿 파일입니다.
이것은 우리가 보이고자 하는 하는 < IMG > 태그를 포함하고 있는 HTML 파일로서, 일반적인 PHP 파일입니다.

두번째 파일은 < IMG > 태그의 SRC 속성에 직접 데이터베이스로 부터 실제 파일스트림을 제공하기 위해 불리워지는 파일입니다.

첫번째 형태는 보이는 것처럼 매우 간단한 스크립트입니다.

mysql_connect($host, $username, $password) or die(“SQL 서버에 접속할 수 없습니다.”);
@mysql_select_db($db) or die(“데이터베이스를 선택할 수 없습니다.”);
$result=mysql_query(“select * from Images”) or die(“쿼리를 실행할 수 없습니다.”);
while($row=mysql_fetch_object($result))
{
echo ” SRC=\”SecondType.php3?PicNum=$row->PicNum\”>”;
}

그 HTML이 보여지는 동안, 두번째의 SecondType.php3 파일은 우리가 보이고자 하는 각 이미지를 위해 불리워집니다. 이 스크립트는 우리가 이미지를 가져오고 보이는 것을 가능케 하는 Picture ID(PicNum)을 가지고서 불리워 집니다.

SecondType.php3 파일은 이와 같습니다.

$result=mysql_query(“select * from Images where PicNum=$PicNum”) or die(“쿼리를 실행할 수 없습니다.”);
$row=mysql_fetch_object($result);
Header(“Content-type:image/gif”);
echo $row->Image;

이것이 이미지와 MySQL 뒤의 모든 이론입니다. 이 예의 스크립트들은 기본적인 것입니다. 이제 여러분은 썸네일(손톱조각그림)을 포함시키고, 다양한 위치에 이미지를 설정할 수 있게 향상시킬 수 있습니다. ALT 영역을 지닐 수 있도록 데이터베이스를 향상시킬 수도 있습니다. 이미지를 데이터베이스에 넣기 전에 그것의 폭과 높이를 확인하고, 그것의 테이블 등등의 것 역시 갖고 계십시요.

'PHP > Mysql_이미지저장및 뷰' 카테고리의 다른 글

MySQL에서 CUBRID의 변환  (0) 2013.07.15

http://stackoverflow.com/questions/6277584/android-honeycomb-networkonmainthreadexception-even-when-using-asynctask-and-no

android의 보안 모드 상태일 경우 그를 해지시켜주도록 해야 하는데 기본적으로 AndroidManifest.xml에 

<uses-permission android:name="android.permission.INTERNET"/>

를 기입해주면 네트워크 허용상태가 되나 (이유는 아직 모르겠으나)추가 적으로 StrictMode라는 경고창과 함꼐 보안에 걸리는데 이떄는 setContentView() 하단에 

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); 
StrictMode.setThreadPolicy(policy);
와 같이 기입해주면 된다. 이때, 두줄 주제에 많은 빨간줄이 그어지는데.. StrictMode의 경우는 Class를 Import시켜주면 되고 나머지는 경우에따라 특정 OS만 허용하게 class상단에 @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)와 같이 걸어주거나 전체 적용할 경우는 빨간 줄 중 하나를 클릭한 후 F2(MAC의 경우)를 누르고 Disable Check를 눌러주면 된다.

http://blog.naver.com/PostView.nhn?blogId=dimigozzang&logNo=110140580636
이 와중에 그와 관계없이 다양한 Error가 떠주시는데 특히 "FATAL EXCEPTION: main" Error의 경우는 바로 아래 라인에 Error가 발생한 object의 정보가 기입되어 있으므로 그것을 보고 수정해주면 된다.
또한, 이 Error는 주로 
1. 엑티비티 이름을 메니페스트xml 파일에 등록하지 않았거나 오타가 있다 - 엑티비티를 여러개 작성하는 경우가 아니라면 발생하는 경우는 적습니다. 그렇지만 확인해 보아야 하는 것입니다. 
2. 콤퍼넌트가 실질적으로 xml화면에 등록은 시켰지만, R.java 파일에 등록되지 않았거나, 등록되었어도 컴파일되지 못했다
이와 같은 경우에 주로 뜨므로 참고해 둘 것.

url을 통해 image를 load할 경우는 URL class를 이용한다.
URL의 openconnection() method로 여결 시 data값을 return값으로 나오므로 이 값을 getInputStream() method로 받아서 
Data값을 bitmap type의 Image로 변환시키는 BitmapFactory.decodeStream()의 parameter로 넣고 결과 값을 Bitmap Class 변수로 받아 사용하면 된다.
위 내용을 source code로 변환하면 아래와 같다.
Ex)
URL url = new URL("http://image10.bizrate-images.com/resize?sq=60&uid=2216744464");
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
imageView.setImageBitmap(bmp);

'PHP > php_mysql연동' 카테고리의 다른 글

PHP 메모리 부족 134217728  (2) 2013.07.03
PHP로 Mysql 연동(1)  (0) 2013.07.03
mysql_fetch_assoc 와 mysql_fetch_array의 차이점  (0) 2013.07.02

에뮬레이터 돌리다가 혹은 실기기를 연결해 돌리다가도 가끔 로그캣이 안 뜨는 현상
혹은 adb 에러 나면서 콘솔에 빨간글자 나올때

그럴때는 cmd창을 여시고 아래와 같이 적어주세요.
참고로 맨 첫 줄은 adb 파일이 있는 경로를 입력합니다.
platform-tools 혹은 tools 둘 중에 하나입니다.

cd \Program Files\Android\android-sdk-windows\platform-tools
adb kill-server
adb start-server

'Android > 이클립스' 카테고리의 다른 글

이클립스 주석 한글깨짐 해결방법  (0) 2013.06.19

+ Recent posts