2016년 8월 2일 화요일

Android Studio - Android IPC인 AIDL


RPC(Remote Procedure Call)
 : 서로다른 네트워크 또는 프로세스에 존재하는 Procedure(함수)를 호출하기 위한 기술

안드로이드에서는 AIDL(Android Interface Definition Language)로 제공 하고 있다.

IPC를 하는 Service와 Activity 간에 AIDL 이라는 인터페이스를 공유해서
Activity - Service 가 Bind Connect 된 이후에 Activity 에서 Service에서 구현 된 AIDL 인터페이스를 활용할 수 있도록 하는 기술이다.


1. AIDL 서비스 앱 생성
2. AIDL Client 앱 생성
 

[AIDL 서비스 앱]
1. res/raw 에 mp3 파일 준비
2. aidl 폴더, 패키지 및 aidl 파일 생성

IPlayService.aidl

// IPlayService.aidlpackage com.example.ch3_aidl;

// Declare any non-default types here with import statements
interface IPlayService {
    int currentPosition();
    int getMaxDuration();
    void start();
    void stop();
    int getMediaStatus();
}

3. Service 생성

PlayService.java

package com.example.ch3_aidl;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class PlayService extends Service {
    public PlayService() {
    }

    @Override    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.        throw new UnsupportedOperationException("Not yet implemented");
    }
}

4. manifest 수정

자동 생성된 service 태그 아래

<service    android:name=".PlayService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.multi.ACTION_PLAY"/>
    </intent-filter>
</service>

*5. 빌드 한번 하기

Build -> Make Module 'AIDL모듈명'
진행 해야 현 소스코드에서 해당 AIDL 인지 가능


6. 서비스 구현

package com.example.ch3_aidl;

public class CommonProperties {
   public static final int MEDIA_STATUS_STOP = 0;
   public static final int MEDIA_STATUS_RUNNING = 1;
   public static final int MEDIA_STATUS_COMPLETED = 2;
}


package com.example.ch3_aidl;

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

public class PlayService extends Service {

    MediaPlayer player;
    int status = 0;

    public PlayService() {
    }

    @Override    public void onCreate() {
        super.onCreate();
        player = new MediaPlayer();
    }

    @Override    public void onDestroy() {
        super.onDestroy();
        player.release();
    }

    @Override    public IBinder onBind(Intent intent) {
        // aidl 파일을 구현한 stub 객체(실 업무 객체가 넘어가는게 아니라)를 리턴        return new IPlayService.Stub(){// Make Module xxx 작업을 해서 에러가 발생하지 않음
            @Override            public int currentPosition() throws RemoteException {
                if(player.isPlaying()){
                    return player.getCurrentPosition();
                }else{
                    return 0;
                }
            }

            @Override            public int getMaxDuration() throws RemoteException {
                if(player.isPlaying()){
                    return player.getDuration();
                }else{
                    return 0;
                }
            }

            @Override            public void start() throws RemoteException {
                if(!player.isPlaying()){
                    player = MediaPlayer.create(PlayService.this, R.raw.music);
                    player.start();

                    player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                        @Override                        public void onCompletion(MediaPlayer mp) {
                            status = CommonProperties.MEDIA_STATUS_COMPLETED;
                        }
                    });
                }

                status = CommonProperties.MEDIA_STATUS_RUNNING;
            }

            @Override            public void stop() throws RemoteException {
                if(player.isPlaying())
                    player.stop();
                status = CommonProperties.MEDIA_STATUS_STOP;
            }

            @Override            public int getMediaStatus() throws RemoteException {
                return status;
            }
        };
    }
}




[AIDL Client 앱]

1. aidl 폴더, 패키지 생성 및 aidl 파일 복사

2. 빌드 한번 하기

Build -> Make Module 'AIDL모듈명'
진행 해야 현 소스코드에서 해당 AIDL 인지 가능

3. MainActivity

package com.example.ch3_aidl_client;

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.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ProgressBar;

import com.example.ch3_aidl.IPlayService;


public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    IPlayService pService;
    ImageButton start;
    ImageButton stop;
    ProgressBar mProgress;
    boolean isRunning = true;
    ProgressThread pt;
    Handler handler;
    Intent intent;


    //--add 1---------------    // bindService callback함수를 가지는 interface구현 클래스    ServiceConnection connection = new ServiceConnection() {
        // bindService에 의해 bind객체가 넘어온 순간 호출        // 두번째 객체가 bind 객체 stub 객체가 넘어 옴        @Override        public void onServiceConnected(ComponentName name, IBinder service) {
            pService = IPlayService.Stub.asInterface(service);
            start.setEnabled(true);
            checkService();
        }

        @Override        public void onServiceDisconnected(ComponentName name) {
            pService = null;
        }
    };
   //------------------------
    @Override    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        start = (ImageButton) findViewById(R.id.start);
        stop = (ImageButton) findViewById(R.id.stop);
        mProgress = (ProgressBar) findViewById(R.id.pb);
        mProgress.setProgress(0);

        start.setOnClickListener(this);
        stop.setOnClickListener(this);
        start.setEnabled(false);
        stop.setEnabled(false);

        handler = new Handler() {
            // Activity ANR - Thread에서 sendMessage하는 순간 UI Thread에 의해 자동 호출            @Override            public void handleMessage(Message msg) {
                switch(msg.what) {
                    case 1:    // stopped media                        start.setEnabled(true);
                        stop.setEnabled(false);
                        mProgress.setProgress(0);
                        break;
                }
                super.handleMessage(msg);
            }
        };

      //add2-------------------------------------        // service 구동을 위한 intent 준비        intent = new Intent("com.multi.ACTION_PLAY");
        // 이전버전에서는 그냥 intent를 실행. lollipop부터는 bindService의 경우 package명을 명시        intent.setPackage("com.example.ch3_aidl");
      //--------------------------------------    }

    private void checkService() {
        if(pService != null) {
            try {
                if(pService.getMediaStatus() == CommonProperties.MEDIA_STATUS_STOP) {
                    Log.d("kkang", "MEDIA_STATUS_STOP");
                    stop.setEnabled(false);
                } else if(pService.getMediaStatus() == CommonProperties.MEDIA_STATUS_RUNNING) {
                    Log.d("kkang", "MEDIA_STATUS_RUNNING");
                    start.setEnabled(false);
                    isRunning = true;
                    pt = new ProgressThread();
                    pt.start();
                }
            } catch (RemoteException e) {

            }
        }

    }

    //add3-------------------------------
    @Override    protected void onResume() {
        super.onResume();
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
        checkService();
    }

    @Override    protected void onPause() {
        super.onPause();
        unbindService(connection);
        isRunning = false;
    }
    //---------------------------------
    @Override    public void onClick(View v) {
        if(v == start) {
            try {
                pService.start();
                mProgress.setMax(pService.getMaxDuration());
                isRunning = true;
                pt = new ProgressThread();
                pt.start();

                start.setEnabled(false);
                stop.setEnabled(true);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block                e.printStackTrace();
            }
        } else if(v == stop) {
            try {
                pService.stop();
                isRunning = false;
                start.setEnabled(true);
                stop.setEnabled(false);
            } catch (RemoteException e) {
                // TODO Auto-generated catch block                e.printStackTrace();
            }
        }

    }


    class ProgressThread extends Thread {
        @Override        public void run() {
            while(isRunning) {
                try {

                    if(pService.getMediaStatus() == CommonProperties.MEDIA_STATUS_COMPLETED) {
                        handler.sendEmptyMessage(1);
                        break;
                    } else {
                        mProgress.setProgress(pService.currentPosition());
                        SystemClock.sleep(1000);
                    }
                } catch (RemoteException e) {
                    // TODO Auto-generated catch block                    e.printStackTrace();
                }
            }
        }
    }
}

댓글 없음:

댓글 쓰기