출처 : http://sje0114121.blog.me/150089240741

background에서 작업하기

<Service >

     예) 미디어 플레이어 : 꼭 Service로 만들어야 하는건 아니다. Activity로도 만들수있음.

          장시간으로 하면서 user Interface가 거의 없는 것을 Service로 만드는 게 좋다.

 

* intent filter 존재 : intent로 시작됨.

Intent i = new Intent(명시적 or 암시적)

startService(i);  // 한 life cycle 돌게됨.

  - 객체는 singleton이라서 다른곳에서 startService()를 다시 호출해도 onCreate()는 되지않고 onStart()만 다시불린다.

 

stopService(i);

 

bindService(i);  // 내 액티비티에서 저 서비스 연결해서 뭔가 뽑아내고자 할때 

unbindService(i);

 

* Life Cycle

          onCreate()

      |                       |

onStart()               onBind()

                 ...

             thread

      (장시간 이용할시)

                  ...

 onStop()               unBind()

      |                       |

          onDestroy()

 

 

onCreate() -> onStart() -> onStop()  -> onDestroy();

Binding할때 : onCreate() ->onBind() -> unBind() -> onDestroy();

 

onBind()

{

return new My();

}

 

class My extends Binder {

service getService();

return 0;

}

 

ServiceConnection타입의 객체 (interface) 를 inner class로 만듬.

 -  onServiceConnected(Binder b);, onServiceDisConnected() 두개 overriding해야됨.

    Connection완료된 다음에 Binding정보 넘겨주는 게 맞음.

    b.getService() 이런식으로..

 

BindService할때 매개변수로 ServiceConnection을 매개변수로 넘겨줌.

 

쓰레드를 얼마나 깔끔하게 짜느냐도 관건..

 

* Background Thread  (<-> Main Thread(UI Thread))

오래걸리는 작업이 있을 시 Background thread로 돌린다.

왜냐하면 5초이상 반응해주지 못하면 에러이기때문.

Binder-post()

 

[실습]

프로젝트 새로 생성 후,

res폴더밑에 폴더 raw하나 만들어서 kara.mp3, start.png, stop.png파일을 넣는다.

 

[AndroidManifest.xml]

Service 하나 추가한다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="chap8.Service"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".Main"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    <service android:name="PlayService"></service>
</application>
    <uses-sdk android:minSdkVersion="7" />

</manifest>

 

[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">
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal" android:layout_width="fill_parent"
  android:layout_height="wrap_content">
  <ImageButton android:id="@+id/ImageButton01"
   android:layout_width="wrap_content" android:layout_height="wrap_content"
   android:src="@raw/start"></ImageButton>
  <ImageButton android:id="@+id/ImageButton02"
   android:layout_width="wrap_content" android:layout_height="wrap_content"
   android:src="@raw/stop"></ImageButton>
 </LinearLayout>

 <ProgressBar
  android:id="@+id/ProgressBar01"
  android:layout_width="fill_parent" android:layout_height="wrap_content"
  style="?android:attr/progressBarStyleHorizontal"></ProgressBar>
</LinearLayout>

 

 

[PlayService.java]

package chap8.Service;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;

public class PlayService extends Service implements Runnable {

 MediaPlayer player;
 
 boolean flag = true;
 private int duration;
 private int currentPosition;
 
 // getter, setter함수

 public boolean isFlag() {
  return flag;
 }

 public void setFlag(boolean flag) {
  this.flag = flag;
 }

 public int getDuration() {
  return duration;
 }

 public void setDuration(int duration) {
  this.duration = duration;
 }

 public int getCurrentPosition() {
  return currentPosition;
 }

 public void setCurrentPosition(int currentPosition) {
  this.currentPosition = currentPosition;
 }
 
 // thread 에 의해 실행되는 코드...
 // Bind되는 순간부터 run이 실행되면서 매 초마다 현재 Position를 설정한다.
 public void run() {
  // run을 빠져나가면 thread는 끝난다.
  // 장시간 해야되는건 주로 loop를 만들어 놓는다.
  while(flag) // 음원이 끝나면 나가라.
  {
   setCurrentPosition(player.getCurrentPosition());
   
   try {
    // sleep은 항상 try~cath와 같이 쓰여야 한다.
    Thread.sleep(1000); // milisecond unit
   } catch (Exception e) {
    // TODO: handle exception
   }   
  }  
 }
 
 @Override
 public IBinder onBind(Intent arg) {
  // TODO Auto-generated method stub
  player = MediaPlayer.create(this, R.raw.kara);
  setDuration(player.getDuration());
  player.start();
  
  // MediaPlayer의 음원 플레이 시간을 얻어내서 service의 데이터를
  // 업데이트 시키는 작업을 하는 thread
  //(null: 특별히 group값이 필요없음.
  // this: thread 돌릴 source, player는 이름
  Thread t = new Thread(null, this, "player");
  t.start(); // bind되는 순간 PlayService의 run()이 시작됨.
  
  return new MyBinder(); // bind될 객체 리턴해야됨.
 }

 @Override
 public void onDestroy() {
  // TODO Auto-generated method stub
  super.onDestroy();
  player.stop(); // service 멈추기
  flag = false; // thread가 멈출 수 있게 flag값 설정
 }

 @Override
 public void onStart(Intent intent, int startId) {
  // TODO Auto-generated method stub
  super.onStart(intent, startId);
  
//  player = MediaPlayer.create(this, R.raw.kara);
//  player.start();
  // SD카드의 여러 음악프로그램중 어떤걸 play할 것인지?
  // 음악이 끝났다는 신호를 받을 수 있다. 그러면 다음곡 실행시키기.
 }
 
 // Activity bind시 Activity에 전달시킬 객체
 public class MyBinder extends Binder {
  PlayService getService() {
   return PlayService.this;
  }
 }

}

 

[Main.java]

package chap8.Service;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ProgressBar;

public class Main extends Activity implements OnClickListener, Runnable{
    /** Called when the activity is first created. */
 private ImageButton bt1;
 private ImageButton bt2;
 private ProgressBar bar;
 
 // service랑 bind해서 service 객체를 얻어 service 의 데이터에 접근하기 위해
 private PlayService pService;
 
 // UI Thread에 UI 변경작업을 지시하기 위해 선언
 private Handler handler = new Handler();
 
 // user event와 상관없이 장시간 동안 service에 접근해서 데이터 획득하는 역할
 private Thread background;
 
    @Override
    // 1. Main이 실행되면서 onCreate()함수가 가장 먼저 실행된다.
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        bt1 = (ImageButton)findViewById(R.id.ImageButton01);
        bt2 = (ImageButton)findViewById(R.id.ImageButton02);
       
        bt1.setOnClickListener(this);
        bt2.setOnClickListener(this);
       
        bar = (ProgressBar)findViewById(R.id.ProgressBar01);
        //bar.setOnClickListener(this);
    }

   
 public void onClick(View v) {
  // TODO Auto-generated method stub
  if(v == bt1)
  {
   Intent intent = new Intent(this, PlayService.class); // 명시적
   //startService(intent);
   // 2. 재생버튼을 누르면 OnClick이 실행되고,
   // 여기서 service를 bind시킨다.
   // Service에서는 onCreate(), onBind()까지 실행되게된다.
   bindService(intent, connection, Context.BIND_AUTO_CREATE);
   background = new Thread(null, this, "test");
   
  } else if(v == bt2)  {
   Intent intent = new Intent(this, PlayService.class);
   //stopService(intent);
   // stop버튼을 누르면 binding이 끊긴다.
   unbindService(connection);   
  } else if(v == bar) {
   //handler.post(updateBar);
   // UserEvent받아들일 수 있는 거는 SeekBar로만 할 수 있다.
   // ProgressBar는 시스템에 의해서만 동작된다.
  }
   
  
 }
 
 // bind 상태에 따라 자동 호출되는 메서드를 가지는 객체 선언
 private ServiceConnection connection =
  new ServiceConnection() {
   
   public void onServiceDisconnected(ComponentName name) {
    // TODO Auto-generated method stub
    pService = null;
   }
   
   public void onServiceConnected(ComponentName name, IBinder service) {
    // TODO Auto-generated method stub
    // IBinder타입을 PlayService.MyBinder타입으로 type-casting하고
    // getService로 서비스 가져온다.
    //3. Binding이 완료되면 Service 정보를 가져오고
    pService = ((PlayService.MyBinder)service).getService();
    //4. background로 thread를 실행시킨다.
    background.start(); // 이때부터 run()이 실행된다.
   }
 };
  
 // UI Thread 에게 넘겨줄 작업
 // Inner class - Runnable이 inteface이므로..
 private Runnable updateBar = new Runnable() {
  
  public void run() {
   // UI thread는 간단하게 짠다. loop문같은거 넣지않는다.
   // 6. bar가 새롭게 그려진다.
   bar.setProgress(pService.getCurrentPosition());
  }
 };
 
 // background thread가 작업할 내용을 가지는 메서드
 // 5. background thread는 돌면서 1초마다
 // UI updateBar thread한테 progress를 표시하라고 post한다.
 public void run() {
  // 전체 음원의 시간값으로 bar의 Max를 설정한다.
  bar.setMax(pService.getDuration());
  while(pService.isFlag())
  {
   try {
    Thread.sleep(1000);
    // 오늘의 key point!!
    // 1초마다 main thread한테 update progress bar하라고 날린다.

    handler.post(updateBar);
   } catch (Exception e) {
    // TODO: handle exception
   }
  }
  
 }
}


+ Recent posts