IT_Programming/Android_Java

[android] 다른 프로세스의 서비스를 제어하기

JJun ™ 2012. 3. 5. 04:04

----------------------------------------------------------------------------------------------

출처: http://pgm-progger.blogspot.com/2011/05/android_5959.html

----------------------------------------------------------------------------------------------

 

이 샘플소스는 안드로이드 설치폴더에 android-sdk-windowssamplesandroid-8ApiDemos 위치에

원본 소스 코드가 있습니다.

현재 프로세스와는 다른 프로세스에서 실행되는 서비스를 호출하기 위해서는
IPC(Inter Process Call)방식을 이용하여 다른 프로세스 상에서
실행되는 서비스의 메소드를 실행하고,
콜백 결과값을 받을 수 있습니다.


aidl 파일에 인터페이스를 정의하여, 다른 프로세스의 서비스 메소드와 콜백 메소드 등을 정의합니다.

이 인터페이스를 통하여 프로세스 간의 메소드 호출정보를 주고받아, 서로 메소드 호출과 콜백이
가능해 지게 됩니다.
메소드 호출은 원격 서비스를 호출하는 쪽에서 원격 서비스의 메소드를 호출하고,
콜백은 원격 서비스에서 자신을 호출한 쪽으로 값을 반환해 주는 것입니다.

그리고 서비스를 강제종료 하게되면 자동으로 시스템에서 다시 시작합니다.
참고로, UI갱신하는 코드는 반드시 메인쓰레드로 실행되는 상황에서만 수행할 수 있기 때문에,
서비스에서 UI를 갱신하는 등의 작업을 할 수 없고,
메인쓰레드의 메소드를 호출하는 식으로 처리를

하셔야 합니다.

먼저 두개의 액티비티와 서비스를 정의하고 있는 RemoteService.java 파일을 추가합니다.

RemoteService.Controller 액티비티는 단순히 서비스를 시작하고 종료하는 처리만 하고 있습니다.
RemoteService.Binding 액티비티에서는 원격 서비스를 호출하고, 콜백값을 받고, 원격 서비스를

강제 종료하는 등의 처리를 하고 있습니다.

 

 

 

/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package korsoft.net.Test011;


import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;


// Need the following import to get access to the app resources, since this class is

// in a sub-package.
import korsoft.net.Test011.R;


/**
* This is an example of implementing an application service that runs in a
* different process than the application. Because it can be in another
* process, we must use IPC to interact with it. The
* {@link Controller} and {@link Binding} classes
* show how to interact with the service.
*
* <p>Note that most applications <strong>do not</strong> need to deal with
* the complexity shown here. If your application simply has a service
* running in its own process, the {@link LocalService} sample shows a much
* simpler way to interact with it.
*/
public class RemoteService extends Service

{
     /**
       * This is a list of callbacks that have been registered with the
       * service. Note that this is package scoped (instead of private) so
       * that it can be accessed more efficiently from inner classes.
       *
       * 현재 서비스를 호출하는 다른 프로세스의 액티비티 등으로 반환값을
       * 전해주는 콜백리스트 변수입니다.
       */

       final RemoteCallbackList<IRemoteServiceCallback> mCallbacks
                                                      = new RemoteCallbackList<IRemoteServiceCallback>();

 

       int mValue = 0;
       NotificationManager mNM;

 

       @Override
       public void onCreate()

       {
             mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

 

             // Display a notification about us starting.
             showNotification();

 

             // While this service is running, it will continually increment a number.

             // Send the first message that is used to perform the increment.
             // 현재 서비스가 생성되었다는 초기값을 갖는 콜백메시지를 다른 프로세스의
             // 현재 서비스를 호출하는 액티비티 등으로 값을 전달합니다.
             mHandler.sendEmptyMessage(REPORT_MSG);
       }

 
       @Override
       public void onDestroy()

       {
            // Cancel the persistent notification.
            // 알림을 취소합니다.
            mNM.cancel(R.string.remote_service_started);

 

            // Tell the user we stopped.
            Toast.makeText(this, R.string.remote_service_stopped, Toast.LENGTH_SHORT).show();

 

            // Unregister all callbacks.
            // 콜백메소드들을 모두 등록해제합니다.
            mCallbacks.kill();

          

           // Remove the next pending message to increment the counter, stopping the increment loop.
            mHandler.removeMessages(REPORT_MSG);
       }

 

      @Override
      public IBinder onBind(Intent intent)

      {
            // Select the interface to return. If your service only implements a single interface,

            // you can just return it here without checking the Intent.
            // 다른 프로세스의 호출하는 쪽에서, 넘어온 인텐트의 액션값을 구분하여, 맞는 Stub 객체를

            // 반환합니다. Stub 객체는 서비스를 호출하는 쪽으로, 호출되는 서비스의 사용할 인터페이스
            // 정보입니다. 즉 서비스를 호출하는 쪽에서 현재 서비스의 메소드를 사용할 수 있게 인터페이스

            // 호출 정보를 넘겨주어, 호출하는 쪽에서 다른 프로세스의 서비스 메소드들을 호출할 수 있게

            // 됩니다.
            // 여기서는 두개의 스텁클래스를 선언하고 있습니다. 그 내용은 IRemoteService.aidl,

            // ISecondary.aidl 두 개 파일에 인터페이스를 선언하고 있습니다.
            // 여기서 호출하는 쪽에서 넘긴 인텐트의 액션값에 따라서 맞는 스텁객체를 반환해 주고

            // 있습니다.
            if (IRemoteService.class.getName().equals(intent.getAction()))

            {
                   return mBinder;
            }

 

            if (ISecondary.class.getName().equals(intent.getAction()))

            {
                  return mSecondaryBinder;
            }

 

            return null;
     }

     /**
        * The IRemoteInterface is defined through IDL
        * 다른 프로세스의 서비스의 메소드 호출정보를 넘겨주는 스텁객체를 반환하는 클래스를
        * 정의하고 있습니다. 서비스를 호출하는 쪽에 반환값을 넘겨주는 콜백메소드를 등록하고 해제하는

        * 메소드들을 정의하여, 그 처리를 하고 있습니다.
        * 이 스텁 클래스는 IRemoteService.aidl 파일에 정의되어있는 인터페이스를 구현하고 있습니다.
        */

      private final IRemoteService.Stub mBinder = new IRemoteService.Stub()

      {
             public void registerCallback(IRemoteServiceCallback cb)

             {
                    if (cb != null) mCallbacks.register(cb);
             }

 

             public void unregisterCallback(IRemoteServiceCallback cb)

             {
                   if (cb != null) mCallbacks.unregister(cb);
             }
      };

 

      /**
        * A secondary interface to the service.
        * 두번째 스텁클래스를 정의하여 객체변수를 생성하고 있습니다.
        * 이 스텁 클래스는 ISecondary.aidl 파일에 정의되어있는 인터페이스를 구현하고 있습니다.
        */

      private final ISecondary.Stub mSecondaryBinder = new ISecondary.Stub()

      {
            public int getPid()

            {
                  // 현재 서비스의 프로세스 아이디를 넘겨줍니다.
                  return Process.myPid();
            }

 

            public void basicTypes(int anInt, long aLong, boolean aBoolean,
                                                  float aFloat, double aDouble, String aString)

            {
            }
       };

 

       private static final int REPORT_MSG = 1;

      

       /**
         * Our Handler used to execute operations on the main thread.

         * This is used to schedule increments of our value.
         * 현재 서비스의 실제 실행 내용을 정이하고 있습니다. 정수 카운트를 증가하여,
         * 콜백으로 이 서비스를 호출하는 쪽으로 1초간격으로 이 정수 데이터를 계속해서 넘겨주고

         * 있습니다.
         */

       private final Handler mHandler = new Handler()

       {
              @Override

              public void handleMessage(Message msg)

              {
                     switch (msg.what)

                     {

                            // It is time to bump the value! 
                            case REPORT_MSG: {
                                       // Up it goes.
                                       int value = ++mValue;

 

                                      // Broadcast to all clients the new value.
                                      final int N = mCallbacks.beginBroadcast();
                                      for (int i=0; i<N; i++)

                                      {
                                              try {
                                                       mCallbacks.getBroadcastItem(i).valueChanged(value);
                                              } catch (RemoteException e) {

                                                       // The RemoteCallbackList will take care of removing
                                                       // the dead object for us.
                                              }
                                       }

                                       mCallbacks.finishBroadcast();

 

                                       // Repeat every 1 second.
                                       // 1초씩 지연~~
                                       sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
                            }

                            break;

 

                            default:
                                       super.handleMessage(msg);
                    }
             }
      };


      /**
       * Show a notification while this service is running.
       * 알림을 발생시키고 있습니다.
       */
      private void showNotification()

      {
            // In this sample, we'll use the same text for the ticker and the expanded notification
            CharSequence text = getText(R.string.remote_service_started);

            // Set the icon, scrolling text and timestamp
            Notification notification = new Notification(R.drawable.stat_sample, text,
            System.currentTimeMillis());

            // The PendingIntent to launch our activity if the user selects this notification
            PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                                                                               new Intent(this, Controller.class), 0);

           // Set the info for the views that show in the notification panel.
           notification.setLatestEventInfo(this, getText(R.string.remote_service_label),
                                                              text, contentIntent);
           // Send the notification.
           // We use a string id because it is a unique number. We use it later to cancel.

           mNM.notify(R.string.remote_service_started, notification);
     }

 

     // ----------------------------------------------------------------------

     /**
       * <p>Example of explicitly starting and stopping the remove service.
       * This demonstrates the implementation of a service that runs in a different
       * process than the rest of the application, which is explicitly started and stopped
       * as desired.</p>
       *  
       * <p>Note that this is implemented as an inner class only keep the sample
       * all together; typically this code would appear in some separate class.
       *
       * 서비스 시작과 종료 버튼이 있는 액티비티 입니다.
       * 다른 프로세스상의 서비스를 시작하고 종료하고 있습니다.
       */
       public static class Controller extends Activity

       {
             @Override
              protected void onCreate(Bundle savedInstanceState)

              {
                    super.onCreate(savedInstanceState);
                    setContentView(R.layout.remote_service_controller);

                     // Watch for button clicks.
                     Button button = (Button)findViewById(R.id.start);
                     button.setOnClickListener(mStartListener);
                     button = (Button)findViewById(R.id.stop);
                     button.setOnClickListener(mStopListener);
               }


               private onClickListener mStartListener = new onClickListener()

               {
                      public void onClick(View v)

                      {
                            // Make sure the service is started. It will continue running
                            // until someone calls stopService().
                            // We use an action code here, instead of explictly supplying
                            // the component name, so that other packages can replace
                            // the service.
                            // 서비스 시작시 옵션으로 인텐트를 서비스 클래스의 경로를 넘기고 있습니다.
                            // 서비스는 한번 시작되면, 호출한 액티비티가 종료되어도 계속해서 실행이 됩니다.
                            // 밑에 stopService 로 명시적으로 서비스를 종료해야만 서비스가 종료됩니다.
                            // 그리고 시스템 등에서 서비스를 강제종료 시켜도, 나중에 다시 시스템 리소스가
                            // 확보되면, 다시 자동으로 시스템이 강제종료 되었던 서비스를 되살립니다.
                            startService(new Intent("korsoft.net.Test011.REMOTE_SERVICE"));
                       }
               };

               private onClickListener mStopListener = new onClickListener()

               {
                       public void onClick(View v)

                       {
                               // Cancel a previous call to startService(). Note that the
                               // service will not actually stop at this point if there are
                               // still bound clients.
                               // 인텐트에 넘긴 해당 경로의 서비스를 종료합니다.

                               stopService(new Intent("korsoft.net.Test011.REMOTE_SERVICE"));
                       }
               };
      }

 

 

      // ----------------------------------------------------------------------

      /**
        * Example of binding and unbinding to the remote service.
        * This demonstrates the implementation of a service which the client will
        * bind to, interacting with it through an aidl interface.</p>
        * 
        * <p>Note that this is implemented as an inner class only keep the sample
        * all together; typically this code would appear in some separate class.
        * 
        * 다른 프로세스에서 실행되고 있는 서비스를 호출하는 액티비티 입니다.
        */
       public static class Binding extends Activity

       {
                /** The primary interface we will be calling on the service.
                  * 다른 프로세스의 서비스의 메소드를 호출할 수 있는 인터페이스 변수입니다.
                  * 숫자를 증가시켜서 콜백으로 그 값을 반환해주는 메소드가 정의되어 있는

                  * 인터페이스 입니다.
                  **/
                IRemoteService mService = null;

                /** Another interface we use on the service. 
                  * 다른 프로세스의 서비스의 프로세스 아이디값을 받아오는 인터페이스 인스턴스 
                  * 변수입니다. 

                  * 서비스의 프로세스를 강제종료 시키기 위해서 그 서비스의 프로세스 아이디 값을 
                  * 받아옵니다.

                  * 그리고 여기서 서비스의 프로세스를 강제종료 시키면, 서비스의 특성상 조금 있다가
                  * 자동적으로 
다시 시작됩니다. 서비스의 안정적인 실행을 위하여 시스템이 명시적으로

                  * 종료되지 않고, 강제 종료 된 서비스들을 되 살리게 됩니다.
                 **/
                ISecondary mSecondaryService = null;

 

                Button mKillButton;
                TextView mCallbackText;

                private boolean mIsBound;
                /**
                  * Standard initialization of this activity. Set up the UI, then wait
                  * for the user to poke it before doing anything.
                  */
                @Override
                protected void onCreate(Bundle savedInstanceState)

                {
                       super.onCreate(savedInstanceState);
                       setContentView(R.layout.remote_service_binding);

                       // Watch for button clicks.
                       Button button = (Button)findViewById(R.id.bind);
                       button.setOnClickListener(mBindListener);
                       button = (Button)findViewById(R.id.unbind);
                       button.setOnClickListener(mUnbindListener);

                       mKillButton = (Button)findViewById(R.id.kill);
                       mKillButton.setOnClickListener(mKillListener);
                       mKillButton.setEnabled(false);

                       mCallbackText = (TextView)findViewById(R.id.callback);
                       mCallbackText.setText("Not attached.");
               }

              /**
                * Class for interacting with the main interface of the service.
                * 서비스에 접속할때와 접속이 끊길때 실행될 메소드 두개를 정의하고 있습니다.
                * IRemoteService.aidl 에 정의된 스텁 인터페이스 객체를 IBinder service 
                * 파라미터로 받아오게 됩니다.
                */

              private ServiceConnection mConnection = new ServiceConnection()

              {
                      public void onServiceConnected(ComponentName className, IBinder service)

                      {
                             // This is called when the connection with the service has been
                             // established, giving us the service object we can use to
                             // interact with the service. We are communicating with our
                             // service through an IDL interface, so get a client-side
                             // representation of that from the raw service object.
                             // 다른 프로세스의 서비스 객체를 mService 변수에 저장하고 있습니다.
                             // 이 객체 변수를 호출함으로서, 원격 서비스의 메소드들을 호출하게 됩니다.
                             mService = IRemoteService.Stub.asInterface(service);
                             mKillButton.setEnabled(true);
                             mCallbackText.setText("Attached.");

                             // We want to monitor the service for as long as we are connected to it.
                             try

                             {
                                      // 원격 서비스에서 실행하면서 콜백값을 전해줄때마다, 그 값을
                                      // 받을 콜백메소드를 등록하고 있습니다.
                                      // 원격 서비스에서 정수카운터 값이 1씩 증가할때마다 1초마다
                                      // 값을 전달해 주는것을 mCallback 메소드에서 받아서
                                      // 처리하게 됩니다.
                                      mService.registerCallback(mCallback);
                              } catch (RemoteException e) {
                                      // In this case the service has crashed before we could even
                                      // do anything with it; we can count on soon being
                                      // disconnected (and then reconnected if it can be restarted)
                                      // so there is no need to do anything here.
                              }

 

                              // As part of the sample, tell the user what happened.
                              Toast.makeText(Binding.this, R.string.remote_service_connected,
                                                              Toast.LENGTH_SHORT).show();
                   }


                   public void onServiceDisconnected(ComponentName className)

                   {
                              // This is called when the connection with the service has been
                              // unexpectedly disconnected -- that is, its process crashed.
                              mService = null;
                              mKillButton.setEnabled(false);
                              mCallbackText.setText("Disconnected.");

                              // As part of the sample, tell the user what happened.
                             Toast.makeText(Binding.this, R.string.remote_service_disconnected,
                             Toast.LENGTH_SHORT).show();
                   }
         };

 


        /**
          * Class for interacting with the secondary interface of the service.
          * 서비스에 접속할때와 접속이 끊길때 실행될 메소드 두개를 정의하고 있습니다.
          * ISecondary.aidl 에 정의된 스텁 인터페이스 객체를 IBinder service
          * 파라미터로 받아오게 됩니다.
          */
        private ServiceConnection mSecondaryConnection = new ServiceConnection()

        {
                  public void onServiceConnected(ComponentName className, IBinder service)

                  {
                          // Connecting to a secondary interface is the same as any other interface.
                          // 원격 서비스 객체를 mSecondaryService 인터페이스 변수에 저장하여,
                          // 이 객체의 메소드를 호출함으로서, 원격 서비스의 메소드를 호출하게 됩니다.
                          mSecondaryService = ISecondary.Stub.asInterface(service);
                          mKillButton.setEnabled(true);
                  }

                  public void onServiceDisconnected(ComponentName className)

                  {
                          mSecondaryService = null;
                          mKillButton.setEnabled(false);
                  }
        };

        private onClickListener mBindListener = new onClickListener()

        {
                  public void onClick(View v)

                  {
                          // Establish a couple connections with the service, binding by interface names.

                          // This allows other applications to be installed that replace the remote service

                          // by implementing the same interface.
                          // 바인드 버튼을 누르면, 실제로 원격 서비스를 호출하고 있습니다.
                          // IRemoteService.aidl 스텁객체와 ISecondary.aidl 스텁객체 두개의 인터페이스로

                          // 서비스 객체를 받아오기 위해서, 두개의 서비스를 연결하는 bindService 메소드를

                          // 호출하고 있습니다.
                          bindService(new Intent(IRemoteService.class.getName()),
                          mConnection, Context.BIND_AUTO_CREATE);
                          bindService(new Intent(ISecondary.class.getName()),
                          mSecondaryConnection, Context.BIND_AUTO_CREATE);
                          mIsBound = true;
                          mCallbackText.setText("Binding.");
                   }
         };


        /*
          * unbind 버튼을 눌렀을때, 원격 서비스에 접속했던것을 접속 해제하는 처리를 하고있습니다.
          */

        private onClickListener mUnbindListener = new onClickListener()

        {
                public void onClick(View v)

                {
                         if (mIsBound)

                         {
                                  // If we have received the service, and hence registered with
                                  // it, then now is the time to unregister.
                                  if (mService != null)

                                  {
                                           try {
                                                 // 숫자 증가값을 1초마다 받아오는 콜백 메소드의 등록을

                                                 // 해제합니다.
                                                 mService.unregisterCallback(mCallback);
                                           } catch (RemoteException e) {
                                                 // There is nothing special we need to do if the service
                                                 // has crashed.

                                           }
                                  }

 

                                  // Detach our existing connection.
                                  // 연결했던 두개의 서비스 연결을 해제합니다.

                                  unbindService(mConnection);
                                  unbindService(mSecondaryConnection);
                                  mKillButton.setEnabled(false);
                                  mIsBound = false;
                                  mCallbackText.setText("Unbinding.");
                         }
                 }
         };

         /*
           * 프로세스를 강제로 종료시키는 버튼을 클릭했을때 처리입니다.
           * 위의 mBindListener 클릭 리스너 메소드에서 가져온 원격 서비스의 스텁객체의
           * getPid() 메소드를 호출하여, 원격 서비스의 프로세스 아이디값을 가져와서,
           * 여기서 Process.killProcess 메소드로 종료시킵니다.
           * 이렇게 되면, 서비스가 강제종료되었으므로, 그 서비스가 잠시 있다가
           * 시스템이 자동적으로 재시작 합니다.
           * 서비스는 명시적으로 종료해 주어야 다시 시작을 안하고, 현재 액티비티가
           * 종료되도 서비스가 계속해서 실행이 되므로, 서비스를 종료하고 싶다면,
           * 액티비티 종료전에 서비스를 명시적으로 종료해야 합니다.
           */

          private onClickListener mKillListener = new onClickListener()

          {
                   public void onClick(View v)

                   {
                          // To kill the process hosting our service, we need to know its
                          // PID. Conveniently our service has a call that will return
                          // to us that information.
                          if (mSecondaryService != null)

                          {
                                 try {
                                         int pid = mSecondaryService.getPid();
                                        

                                         // Note that, though this API allows us to request to kill any process

                                         // based on its PID,

                                         // the kernel will still impose standard restrictions on which PIDs

                                         // you are actually able to kill.

                                         // Typically this means only the process running your application

                                         // and any additional processes created by that app as shown here;

                                         // packages sharing a common UID will also be able to kill each

                                         // other's processes.
                                         Process.killProcess(pid);
                                         mCallbackText.setText("Killed service process.");
                                 

                                  } catch (RemoteException ex) {
                                          // Recover gracefully from the process hosting the server dying.
                                          // Just for purposes of the sample, put up a notification.
                                          Toast.makeText(Binding.this, R.string.remote_call_failed,
                                                                          Toast.LENGTH_SHORT).show();
                                  }
                         }
                }
       };

 

 

       // ----------------------------------------------------------------------
       // Code showing how to deal with callbacks.
       // ----------------------------------------------------------------------

       /**
         * This implementation is used to receive callbacks from the remote service.
         * 원격서비스에서 정수값을 증가시킬때마다 콜백값을 현재 액티비티로 전달해줄 때,
         * 그 값을 받아오는 콜백메소드를 정의하고 있습니다.
         * 여기서 증가된 정수값을 1초마다 원격 서비스로부터 받아와서, 현재 액티비티의 텍스트 뷰에

         * 계속해서 갱신을 시키게 됩니다.
         */
        private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub()

        {
               /**
                 * This is called by the remote service regularly to tell us about
                 * new values. Note that IPC calls are dispatched through a thread
                 * pool running in each process, so the code executing here will
                 * NOT be running in our main thread like most other things -- so,
                 * to update the UI, we need to use a Handler to hop over there.
                 */
                 public void valueChanged(int value)

                 {
                          mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
                 }
         };

 

         private static final int BUMP_MSG = 1;

        

        /*
          * 실제 콜백 메소드에서 넘겨주는 값을 갱신처리하는 메소드 입니다.
          */
         private Handler mHandler = new Handler()

         {
                @Override

                public void handleMessage(Message msg)

                {
                         switch (msg.what)

                         {
                                 case BUMP_MSG:
                                         mCallbackText.setText("Received from service: " + msg.arg1);
                                         break;

                                 default:
                                         super.handleMessage(msg);
                          }
                 }

           };
     }
}

 

 

 

원하시는 액티비티 클래스 파일을 원하시는 패키지 경로에 추가해 줍니다.

여기서는 Test011.java 로 추가했습니다. 위의 두개의 액티비티를 선택할 수 있도록 버튼 두개를 두어서,

각 버튼 클릭시 해당 액티비티를 실행하고 있습니다.

 

 

 

package korsoft.net.Test011;

 

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import korsoft.net.Test011.R;

 

public class Test011 extends Activity

{

       /** Called when the activity is first created. */
       @Override
       public void onCreate(Bundle savedInstanceState)

       {
               super.onCreate(savedInstanceState);
               setContentView(R.layout.main);

 

               Button btn1 = (Button)findViewById(R.id.btnRemoteServiceStart);
               Button btn2 = (Button)findViewById(R.id.btnRemoteServiceBind);

 

               btn1.setOnClickListener(new onClickListener()

               {
                       public void onClick(View v)

                       {
                               startActivity(new Intent(Test011.this,RemoteService.Controller.class));
                       }
               });

 

               btn2.setOnClickListener(new onClickListener()

               {
                       public void onClick(View v)

                       {
                               startActivity(new Intent(Test011.this,RemoteService.Binding.class));
                       }
               });

        }
}

 

 


위의 세개의 액티비티 레이아웃 파일을 추가해 줍니다.

/layout/main.xml , remote_service_binding.xml, remote_service_controller.xml 세 개 추가해줍니다.

 

/layout/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"
/>

<Button android:id="@+id/btnRemoteServiceStart"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/bind_service">
<requestFocus />
</Button>

<Button android:id="@+id/btnRemoteServiceBind"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/bind_service">
<requestFocus />
</Button>

</LinearLayout>

 

 

 

 

/layout/remote_service_binding.xml

 

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Demonstrates starting and stopping a local service.
See corresponding Java code com.android.sdk.app.LocalSerice.java. -->


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">


<TextView
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip"
android:text="@string/remote_service_binding"/>


<Button android:id="@+id/bind"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/bind_service">
<requestFocus />
</Button>


<Button android:id="@+id/unbind"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/unbind_service">
</Button>


<Button android:id="@+id/kill"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/kill_process">
</Button>


<TextView android:id="@+id/callback"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:gravity="center_horizontal" android:paddingTop="4dip"/>


</LinearLayout>

 

 

 

/layout/remote_service_controller.xml

 

 

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Demonstrates starting and stopping a local service.
See corresponding Java code com.android.sdk.app.LocalSerice.java. -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent" android:layout_height="match_parent">


<TextView
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip"
android:text="@string/remote_service_controller"/>


<Button android:id="@+id/start"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/start_service">
<requestFocus />
</Button>

<Button android:id="@+id/stop"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/stop_service">
</Button>

</LinearLayout>

 

 


이제 프로세스간에 메소드 호출 정보를 정의하는 aidl 파일을 세개 추가해 줍니다.

원하시는 경로에 추가해 주시면 됩니다. 그리고 코드와 설정파일에서 해당 패키지 경로만

맞춰주시면 됩니다.


IRemoteService.aidl

원격 서비스에서 정수를 증가시켜서 반환해주는 메소드를 정의하는 인터페이스 입니다.

 

 

/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package korsoft.net.Test011;

import korsoft.net.Test011.IRemoteServiceCallback;

/**
* Example of defining an interface for calling on to a remote service
* (running in another process).
* 다른 프로세스의 서비스의 특정 메소드들을 호출하기 위하여 프로세스간에
* 호출정보를 받아오기 위한 인터페이스를 정의하고 있습니다.
*/

interface IRemoteService {
/**
* Often you want to allow a service to call back to its clients.
* This shows how to do so, by registering a callback interface with
* the service.
*/

void registerCallback(IRemoteServiceCallback cb);

/**
* Remove a previously registered callback interface.
*/

void unregisterCallback(IRemoteServiceCallback cb);
}

 

 

IRemoteServiceCallback.aidl

위에서 원격 서비스에서 증가한 정수값을 액티비티로 받아오는 콜백 메소드를 정의하는 인터페이스 입니다.

 

/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package korsoft.net.Test011;
/**
  * Example of a callback interface used by IRemoteService to send
  * synchronous notifications back to its clients. Note that this is a
  * one-way interface so the server does not block waiting for the client.
  * 원격 서비스에서 호출하는 쪽으로 값을 반환해줄때 사용할 메소드를 정의하는 인터페이스 입니다.
  */
oneway interface IRemoteServiceCallback {
/**
  * Called when the service has a new value for you.
  */

void valueChanged(int value);


}

 

 

 

ISecondary.aidl

액티비티에서 원격 서비스를 강제 종료시키기 위해서,

원격 서비스의 프로세스 아이다값을 받아오기 위한 메소드를 정의하는 인터페이스 입니다.

 

/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package korsoft.net.Test011;

/**
* Example of a secondary interface associated with a service. (Note that
* the interface itself doesn't impact, it is just a matter of how you
* retrieve it from the service.)
*/
interface ISecondary

{
/**
* Request the PID of this service, to do evil things with it.
* 원격 서비스의 프로세스 아이디값을 반환해주기 위한 메소드를 정의하고 있습니다.
*/

int getPid();

/**
* This demonstrates the basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);


}

 

 

 

/res/drawable-hdpi/stat-sample.png 파일을 안드로이드 sdk 폴더에서 현재 프로젝트로 복사해 옵니다.

문자열 리소스를 /res/values/strings.xml 파일에 설정해 줍니다.

 

 

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


<resources>


<string name="hello">Hello World, Test011!</string>
<string name="app_name">Test011</string>

<string name="remote_service_started">Remote service has started</string>
<string name="remote_service_stopped">Remote service has stopped</string>
<string name="remote_service_label">Sample Remote Service</string>

<string name="activity_remote_service_controller">App/Service/Remote Service Controller</string>
<string name="remote_service_controller">This demonstrates how you can implement persistent services
running in a separate process that may be started and stopped as desired.</string>
<string name="activity_remote_service_binding">App/Service/Remote Service Binding</string>
<string name="remote_service_binding">This demonstrates how you can connect with a persistent
service running in another process. Use the kill button to see what happens when
the process crashes.</string>
<string name="kill_process">Kill Process</string>
<string name="remote_service_connected">Connected to remote service</string>
<string name="remote_service_disconnected">Disconnected from remote service</string>
<string name="remote_call_failed">Failure calling remote service</string>

<string name="bind_service">Bind Service</string>
<string name="unbind_service">Unbind Service</string>

<string name="start_service">Start Service</string>
<string name="stop_service">Stop Service</string>

<string name="btnRemoteServiceStart">remote service start</string>
<string name="btnRemoteServiceBind">remote service bind</string>

 

</resources>

 

 

 

마지막으로 AndroidManifest.xml 파일을 다음과 같이 설정해 줍니다.

 

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="korsoft.net.Test011"
android:versionCode="1"
android:versionName="1.0">


<uses-sdk android:minSdkVersion="8" />

<application android:icon="@drawable/icon" android:label="@string/app_name">

<activity android:name=".Test011"
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 태그의 process 속성값을 프로세스이름을 줍니다.
그리고 그 안에 액션태그를 등록하여, 프로그램에서 인텐트 액션값을
비교하는 등 사용할 수 있게 등록해줍니다.
-->

<service android:name=".RemoteService" android:process=":remote">
<intent-filter>
<!-- These are the interfaces supported by the service, which you can bind to. -->
<action android:name="korsoft.net.Test011.IRemoteService" />
<action android:name="korsoft.net.Test011.ISecondary" />


<!-- This is an action code you can use to select the service without explicitly supplying the implementation class. -->
<action android:name="korsoft.net.Test011.REMOTE_SERVICE" />
</intent-filter>
</service>

<activity android:name=".RemoteService$Controller"
android:label="@string/activity_remote_service_controller"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>

<activity android:name=".RemoteService$Binding"
android:label="@string/activity_remote_service_binding">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>

</application>

</manifest>