반응형

이번 포스팅은 View 구성 시 터치 영역을 넓히는 방법에 대하여 알아보도록 하겠습니다.


Layout 구조는 아래와 같습니다.

전체 위젯을 감싸고 있는 test_parent layout 안에 test_view가 들어있는 상황입니다.


위젯의 터치영역이 너무 작아 Layout의 크기만큼 늘리고 싶을 때 아래와같이 touchDelegate를 셋팅해주면 됩니다.

View mTestLayout = rootView.findViewById(R.id.test_parent_layout); ViewGroup.LayoutParams params = mTestLayout.getLayoutParams(); mTestLayout.setTouchDelegate(

new TouchDelegate(

new Rect(0, 0, params.width, params.height),

rootView.findViewById(R.id.test_view)

)

);

위와 같이 구현을 하면 test_parent_layout 영역을 눌러도 test_view의 클릭 리스너가 호출이 됩니다.


View가 MATCH_PARENT이거나 WRAP_CONTENT일 경우, Size가 정확하게 선언되어 있지 않으므로

아래와 같이 OnGlobalLayoutListener에서 처리하는 것도 하나의 방법입니다.

rootView.findViewById(R.id.test_parent_layout).getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() {     @Override     public void onGlobalLayout() {     View mTestLayout = rootView.findViewById(R.id.test_parent_layout);     mTestLayout .setTouchDelegate(

new TouchDelegate(

                    new Rect(0, 0, mTestLayout .getWidth(), mTestLayout .getHeight()),

                    rootView.findViewById(R.id.test_view)

)

          );     } });



반응형

'Development > Android' 카테고리의 다른 글

[Android] GridLayout  (0) 2016.04.18
[Android] dp를 px값으로 변환  (0) 2016.04.14
[Android] AsyncTask 중지하기  (0) 2016.03.23
[Android] Intent로 객체 전달하기  (0) 2016.02.12
[Android] ListView 계층 구조  (0) 2015.08.11
반응형

이번 포스팅은 AsyncTask를 중간에 중지하는 방법에 대하여 알아보도록 하겠습니다.


  Asynctask변수.cancel(true);


 - 만약 HttpClient를 사용중이라면

       httpClient.getConnectionManager().shutdown();


- 만약 dialog를 사용중이라면 

  dialog의 OnCancelListener 에서 위의 명령어 사용


- onPreExecute() 함수는 Thread로 동작하지 않고, Main Thread에서 동작하는 부분이라서 본문에 넣어 주신 코드 같은 경우는 AsyncTask execute 하는 경우 정상적으로 처리 되었다는 onPostExecure() 콜백 함수가 호출이 됩니다.

- AsyncTask 의 경우 doInBackground() 콜백 함수가 Thread에서 동작하는 부분이며, doInBackground() 가 동작 중에 AsyncTask cancel을 하는 경우  doInBackground() 함수 로직이 완료 된 뒤에 onCancelled() 콜백이 호출이 되도록 되어 있습니다. 


- AsyncTask cancel 을 호출 하지 않은 경우에는 doInBackground() 함수 로직 완료 후 onPostExecure() 콜백이 호출이 되구요. 

@Override
protected Void doInBackground(Void... params) {
    int count = 0;
    while( !isCancelled() && count < 10 ) {
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
              ++count; 
      }
        return null;
}


- AsyncTask에서 위와 같이 doInBackground 를 오버라이드를 하시면 캔슬 하였을 때 onCancelled 콜백이 호출 될꺼에요.


저 AsyncTask의 주요한 세가지 동작에 대해서 알아야 합니다.



1.onPreExecute(): Background 작업 시작전에 UI 작업을 진행을 합니다. 

2.doInBackground(): Background 작업을 진행을 합니다. 

3.onPostExecute(): Background 작업이 끝난 후 UI 작업을 진행합니다. 


- AsyncTask의 숨겨진 문제점 


안드로이드에서 AsyncTask 를 소개할 당시에 ‘문제점 없는 Threading’ 이란 제목이 붙어졌고. 이것은 성공적 인듯 보였지만 완벽 하지 않았습니다. AsyncTask는 UI Thread 와 쉽게 상호 작용 하는 목적을 가지고 있습니다. 하지만 몇몇 경우에서는 AsyncTask는 묘책이 아니에요. 정확하게 알지 못하고, 맹목적으로 AsyncTask를 사용하게 된다면 그 것을 제대로 구현할 수 없습니다. 

또한 문제가 발생될 수 있다. 


문제: 

AyncTask의 목표는 백그라운드에서 UI Thread와의 상호 작용을 쉽게 하는 것이다.

 

그러므로 onPostExecute가 완료 될때까지 많은 UI thread간의 부분적인 업데이트가 실시간으로 이루어집니다. 

화면전환이 일어날 때 이러한 동작은 매우 큰 작업입니다.

 

App 전환이 일어날때 전체의 Activity는 파괴되고, 재생성이 됩니다.

Activity가 재시작 할 때 실행했던 AsyncTask의 reference의 Acitvity는 invalid 하며,  onPostExecute는 새로운 Activity에 아무런 영향을 미치지 못합니다.

 

이것은 AsyncTask의 Activity에 의해 현재 Activity의 잠재적인 referencing에 문제가 발생했다는 것입니다. 



해결: 

보통의 해결책은 속성 변경이나 target Activity의 재시작으로 인한 업데이트를 하는 AsyncTask의 reference 꽉 잡고 있는 것이다.

 

 또다른 다양한 해결 방법은 global holder(전역변수??)를 사용하거나 Activity.onRetainNonConfigurationInstance() 를 사용하는 것입니다. Fragment 에 기초된 시스템을 위해 남은 Fragment들을 AsyncTask에 저장할 수 있습니다. 




- AsyncTask 와 Lifecycle: 

AsyncTask의 dead가 Activity에 원래 이러한 영향을 미치는 것은 아닙니다. AsyncTask는 장시간 사용하면서 문제가 발생합니다.

 

 AyncTask를 예정보다 빨리 종료시키는 방법은 AsyncTask.cancel() 사용하는 방법 뿐입니다.

 

- App의 불필요한 백그라운드 tasking 이나 메모리 누수를 막기 위해서는 cancel을 잘 사용 해야 합니다. 


Cancelling AsyncTasks: 

검색 쿼리를 AsyncTask에서 실행해야 한다고 가정합니다. 유저는 검색 파라미터를 AsyncTask가 동작하는 동안 변경하기를 원하고, 그래서 AsyncTask.cancel()을 호출하고 새로운 AsyncTask를 호출했습니다. AsyncTask 완료되었는지 고려하지 않고 cancel을 하거나 그렇지 않을 것입니다. 이것은 mayInterruptIfRunning(실행중인 작업을 취소) 를 하게 될 것입니다. 

무슨 일이 일어나고 있는 것일까요? 



문제는 AsyncTask.cancel() 에 관한 잘못된 개념에 있습니다. 

AsyncTask.cancel() 은 결과를 고려하지 않고 thread를 죽이지 않습니다. 모든 동작은 “취소된” 상태입니다. 

당신이 동작을 중단하기 위해 Asynctask를 취소된 상태로 만들지 만들지는 당신이 알아서 확인 해야 합니다. 




그러므로 두 가지 간단한 솔루션이 있습니다. 

AsyncTask.isCancelled() 을 오랫동안 동작 중일때 확인하거나 thread를 중단을 계속해서 시키는 것입니다. 둘 중 어느 쪽이든 AsyncTask.cancel()을 호출하는 것은 필요한 시간보다 더 많은 시간의 동작을 멈추게 된다. 



앞서 말햇던 것들이 항상은 아니지만 오랜 시간 동안 메소드를 호출한다면 중단 할 수 없습니다. (BitmapFactory.decodeStream()) 이런 상황은 exception이 발생합니다. 

cancel()이 문제 해결책의 전부는 아니다. 




AsynTask의 동시성 한계 

저는 사람들에게 백개의 백그라운드 thread를 돌리도록 권유하지 않습니다. 시작 할 수 있는 AsyncTasks의 숫자를 제한하는 것은 가치 없는 일입니다. 현재 AsyncTask 는 128개의 동시성 task로 제한 되어 있습니다. 큐는 10개의 task로 제한 되어 있습니다. 138개의 task보다 많은 일을 이것들이 완료되기 전에 한다면 당신의 Application은 망가질 것입니다. 저는 Bitmap을 net으로부터 받아오는 과정에서 이런 일이 발생하는 것은 본적 있습니다.

모든 것들이 한번에 실행되지 않도록 백그라운드의 Task들의 갯수를 잘 조절하고. 디자인하며 pool 사이즈를 code스스로 적용되도록 해야 할 것입니다. 




출처 : http://logc.at/2011/11/08/the-hidden-pitfalls-of-asynctask/ 


반응형
반응형

이번 포스팅에서는 Android에서 사운드 재생할 수 있는 객체 MediaPlayer에 알아보도록 하겠습니다. MediaPlayer는 비디오 뿐만 아니라 오디오를 재생할 수 있는 Anroid 내장 객체입니다.


Android에서 사운드를 재생하는 두 가지 경로가 있으며 아래 그림을 통해서 좀 더 자세히 알아보도록 하겠습니다.




- 생성자는 디폴트 생성자만 제공하며 인수는 받아들이지 않고 객체만 생성합니다.


public MediaPlayer()


- 객체만 생성된 상태에서는 재생할 대상이 없으므로 아무것도 할 수 없으며 재생할 미디어를 전달해야 합니다. 두 가지 방법이 있는데 첫번째는 아래 메소드를 호출하는 것입니다. 스트림의 종류에 따라 여러 버전으로 오버로딩되어 있습니다.

void setDataSource (String path) 
void setDataSource (Context context, Uri uri)
void setDataSource (FileDescriptor fd, [long offset, long length])

로컬 파일이나 Uri 객체로부터 원격지의 미디를 엽니다. 리턴값은 없으며 에러 발생 시 예외가 리턴되는데 이 예외는 반드시 처리해야 합니다. 대용량 스트림인 경우 상당한 시간이 걸릴 수 있으므로 오른 직후 자동으로 준비 상태가 되지 않으며 다음 메소드를 호출해야 합니다.


void prepare()

void prepareAsync()


- prepare 메소드는 동기적으로 준비를 하며 준비가 끝나면 리턴합니다. 그에 반해 prepareAsync 메소드는 비동기적으로 준비를 하며 준비 완료를 통보 받는다. 만약 데이터 로딩 시간이 오래 걸린다면 비동기적으로 데이터 로딩하는 prepareAsync 메소드를 사용해야 합니다.


- 사운드를 재생하는 두번째 방법은 아래 메소드를 호출하는 겁니다.


static MediaPlayer create (Context context, int resid)

static MediaPlayer create (Context context, Uri uri, [SurfaceHolder holder])


- create 메소드는 리소스로부터 스트림을 열 수 있지만 파일을 열지는 못합니다. 리소스의 미디어는 보통 크기가 작고 에러 가능성이 낮으므로 오픈 직후 자동으로 준비 상태가 되며 바로 재생이 가능합니다. 에러 발생시는 예외를 던지는 대신 null을 리턴합니다. 대용량 미디어에는 효율이 좋지 않으므로 아주 짧은 간단한 효과음에 사용하는 것이 적합합니다.


void start ()

void stop ()

void pause ()


- start 메소드는 비동기 방식으로 동작하며 재생을 시작한 후 즉시 리턴하므로 사운드 재생 중에도 다른 작업을 할 수 있습니다. 재생이 시작되면 스트림의 끝까지 재생한 후 자동으로 멈춥니다. 만약 반복적으로 재생하려면 setLooping 메소드를 사용하면 반복 재생 기능을 사용할 수 있습니다. 특히 한 스트림을 계속 반복하므로 배경음악으로 사용하기 적합합니다.


void release ()

void reset ()


- release는 객체를 완전히 파괴하여 더 이상 사용할 수 없는 상태로 만듭니다. 음악을 재생하는 중에도 release는 언제든지 호출 가능하며 이때 음악은 즉시 멈춥니다. 그에 반해 reset은 초기화 되지 않은 처음 상태로 객체를 되돌리며 이후 재초기화하여 다시 사용할 수 있다는 점에서 release와 다릅니다.


- 이번 포스팅에서는 사운드를 재생하는 간단한 구조와 그 메소드를 알아보았습니다.

  다음 포스팅에서는 좀 더 자세히 알아보는 시간을 갖도록 하겠습니다.


참고서적 : 안드로이드 프로그래밍 정복(김상형 저)





반응형

+ Recent posts