프로그래밍 정리/안드로이드

로컬서비스 시작시에 인텐트를 통해서 임의의 데이터를 넘기기

주누다 2012. 6. 11. 16:10
반응형

주소 : http://pgm-progger.blogspot.kr/2011/05/android_26.html



안녕하세요? 
프쟁이 입니다 ^^

이 샘플소스는 안드로이드 설치폴더에 android-sdk-windowssamplesandroid-8ApiDemos 위치에 원본소스가 있습니다.

로컬서비스를 호출할 때, 단순히 서비스를 시작하는 것이 아니고, 서비스 시작시에 넘기는 인텐트에

임의의 값을 넘겨서, 서비스 클래스의 onStartCommand 메소드에서 넘어온 값에 따라서 임의의 처리를

해주는 식으로 처리가 가능합니다. onStartCommand 메소드는 액티비티에서 startService 메소드를

호출하여 서비스를 명시적으로 시작시키면, 호출되는 메소드 입니다.

먼저 ServiceStartArguments.java 서비스와 액티비티가 정의되어 있는 파일을 임의의 패키지 경로에 추가합니다.


/*
 * 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.Test012;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import korsoft.net.Test012.R;
/**
 * This is an example of implementing an application service that runs locally
 * in the same process as the application.  The {@link Controller}
 * class shows how to interact with the service. 
 *
 * <p>Notice the use of the {@link NotificationManager} when interesting things
 * happen in the service.  This is generally how background services should
 * interact with the user, rather than doing something more disruptive such as
 * calling startActivity().
 * 
 * <p>For applications targeting Android 1.5 or beyond, you may want consider
 * using the {@link android.app.IntentService} class, which takes care of all the
 * work of creating the extra thread and dispatching commands to it.
 * 
 * 호출하는 액티비티와 동일한 프로세스안에 로컬 서비스로 실행이 됩니다.
 * 액티비티에서 자신의 프로세스를 KillProcess 메소드로 강제종료하게 되면,
 * 동일한 프로세스 안에 있는 이 서비스도 같이 강제 종료되게 됩니다.
 * 그리고 onStartCommand 메소드에서 START_STICKY / START_NOT_STICKY / START_REDELIVER_INTENT
 * 세 상수값을 반환하는 것에 따라서, 각각 서비스가 강제종료되었을때, 그 이후 반응이
 * 달라지게 됩니다. 
 * 먼저 START_STICKY 를 반환하게 되면, 강제 서비스 종료시 시스템이 다시 서비스를
 * 재시작 하지만, 당시에 넘어왔었던 인텐트 값이 NULL 로 초기화 됩니다. 즉 현재 서비스가 호출될때
 * 인텐트 값을 상실하게 됩니다.
 * START_NOT_STICKY 를 반환하게 되면, 서비스가 강제종료되어도, 시스템이 자동으로 다시 이 서비스를
 * 시작하지 않습니다. 그런 동작이 필요하다면 이 값을 onStartCommand 에서 반환하시면 됩니다.
 * START_REDELIVER_INTENT 상수값을 반환하게 되면, 시스템이 서비스가 강제종료 되었을때 재시작
 * 하면서, 서비스 시작당시 넘어온 인텐트 값을 재시작 시에도 다시 넘겨주게 됩니다.
 * 즉 인텐트 값이 재시작 시에도 유지가 됩니다.
 * 서비스를 시작할때 인텐트에 추가 데이터를 입력하여 넘겨주어 그 값을 옵션처리로 하는 경우에는,
 * 이 상수값을 넘겨서 다시 서비스가 시작하더라도 정상적으로 작동하도록 하실 수 있습니다.
 */
public class ServiceStartArguments extends Service {
    private NotificationManager mNM;
    private Intent mInvokeIntent;
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    
    /*
     * 쓰레드 실행시 이 클래스의 인스턴스 변수값을 넘겨서,
     * 메시지 방식으로 처리를 하도록 합니다. 
     */
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }
        
        @Override
        public void handleMessage(Message msg) {
            /*
             * 넘어온 메시지값에서 넘어온 값을 추출하여 처리를 하고있습니다.
             */
         Bundle arguments = (Bundle)msg.obj;
            
            String txt = arguments.getString("name");
            
            Log.i("ServiceStartArguments", "Message: " + msg + ", "
                    + arguments.getString("name"));
        
            if ((msg.arg2&Service.START_FLAG_REDELIVERY) == 0) {
                txt = "New cmd #" + msg.arg1 + ": " + txt;
            } else {
                txt = "Re-delivered #" + msg.arg1 + ": " + txt;
            }
            
            /*
             * 알람을 표시합니다.
             */
            showNotification(txt);
        
            // Normally we would do some work here...  for our sample, we will
            // just sleep for 5 seconds.
            // 5초간 실행을 보류시킵니다.(5초간 대기)
            long endTime = System.currentTimeMillis() + 5*1000;
            while (System.currentTimeMillis() < endTime) {
                synchronized (this) {
                    try {
                        wait(endTime - System.currentTimeMillis());
                    } catch (Exception e) {
                    }
                }
            }
            
            /*
             * 위에서 5초 지연후에 알림표시했던것을 제거합니다.
             */
            hideNotification();
            
            Log.i("ServiceStartArguments", "Done with #" + msg.arg1);
            stopSelf(msg.arg1);
        }
    };
    
    @Override
    public void onCreate() {
     /*
      * 알림 매니저를 시스템으로부터 가져옵니다.
      */
        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        
        Toast.makeText(this, R.string.service_created,
                Toast.LENGTH_SHORT).show();
        
        // This is who should be launched if the user selects our persistent
        // notification.
        // 알림항목을 클릭하면, 이 서비스를 호출하는 액티비티로 가도록 설정합니다.
        mInvokeIntent = new Intent(this, Controller.class);
        
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread thread = new HandlerThread("ServiceStartArguments",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        
        // 위에서 처리내용을 정의한 클래스를 쓰레드 루프로 시작합니다.
        // 메시지를 넘길때마다 핸들러 메소드가 실행됩니다.
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    /*
     * (non-Javadoc)
     * @see android.app.Service#onStartCommand(android.content.Intent, int, int)
     * startService 메소드에 의해서 명시적으로 서비스를 호출할때마다 이 메소드가 호출됩니다.
     * Controller 액티비티 클래스에서 인텐트에 추가값을 입력하여 보내고,
     * 여기에서 값을 받아서 임의로 처리를 하고 있습니다.
     * 
     * startId : 서비스 명시적 호출시마다 고유한 정수값이 넘어옵니다.
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("ServiceStartArguments",
                "Starting #" + startId + ": " + intent.getExtras());
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.arg2 = flags;
        // 서비스 호출시 번들객체값을 인텐트에 입력한것을 받아옵니다.
        msg.obj = intent.getExtras();
        // 위에서 정의한 핸들로 클래스 객체를 실행합니다.
        mServiceHandler.sendMessage(msg);
        Log.i("ServiceStartArguments", "Sending: " + msg);
        
        // For the start fail button, we will simulate the process dying
        // for some reason in onStartCommand().
        // 인텐트에 fail 값을 true 로 넘기면, 현재 서비스의 프로세스를
        // 강제종료 합니다.
        if (intent.getBooleanExtra("fail", false)) {
            // Don't do this if we are in a retry... the system will
            // eventually give up if we keep crashing.
            if ((flags&START_FLAG_RETRY) == 0) {
                // Since the process hasn't finished handling the command,
                // it will be restarted with the command again, regardless of
                // whether we return START_REDELIVER_INTENT.
             // 서비스 호출시 재시작 옵션을 주었으면 현재 서비스의 프로세스를 종료합니다.
                Process.killProcess(Process.myPid());
            }
        }
        
        // Normally we would consistently return one kind of result...
        // however, here we will select between these two, so you can see
        // how they impact the behavior.  Try killing the process while it
        // is in the middle of executing the different commands.
        /*
   * 서비스 실행중에 강제 종료되었을때 여기시 반환되는 값에 따라서 이후에 반응내용이 달라집니다.
   * 반환값은 상수값으로 START_STICKY / START_NOT_STICKY / START_REDELIVER_INTENT 가 있습니다.
   * 먼저 START_STICKY 를 반환하게 되면, 강제 서비스 종료시 시스템이 다시 서비스를
   * 재시작 하지만, 당시에 넘어왔었던 인텐트 값이 NULL 로 초기화 됩니다. 
   * 즉 현재 서비스가 호출될때 인텐트 값을 상실하게 됩니다.
   * START_NOT_STICKY 를 반환하게 되면, 서비스가 강제종료되어도, 시스템이 자동으로 다시 이 서비스를
   * 시작하지 않습니다. 그런 동작이 필요하다면 이 값을 onStartCommand 에서 반환하시면 됩니다.
   * START_REDELIVER_INTENT 상수값을 반환하게 되면, 시스템이 서비스가 강제종료 되었을때 재시작
   * 하면서, 서비스 시작당시 넘어온 인텐트 값을 재시작 시에도 다시 넘겨주게 됩니다.
   * 즉 인텐트 값이 재시작 시에도 유지가 됩니다.
   * 서비스를 시작할때 인텐트에 추가 데이터를 입력하여 넘겨주어 그 값을 옵션처리로 하는 경우에는,
   * 이 상수값을 넘겨서 다시 서비스가 시작하더라도 정상적으로 작동하도록 하실 수 있습니다.
   * 
   * 여기서는 이 서비스를 호출하는 액티비티에서 인텐트에 redeliver 이름으로 불린값을 심어서,
   * 어떤식으로 강제종료시 이후에 반응할 것인지를 컨트롤 하고 있습니다.
         */
        return intent.getBooleanExtra("redeliver", false)
                ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }
    /*
     * (non-Javadoc)
     * @see android.app.Service#onDestroy()
     * 서비스 종료시에, 쓰레드 루프와 알림을 종료합니다.
     */
    @Override
    public void onDestroy() {
        mServiceLooper.quit();
        hideNotification();
        // Tell the user we stopped.
        Toast.makeText(ServiceStartArguments.this, R.string.service_destroyed,
                Toast.LENGTH_SHORT).show();
    }
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    /**
     * Show a notification while this service is running.
     * 텍스트를 알림으로 표시합니다.
     */
    private void showNotification(String text) {
        // 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.service_start_arguments_label),
                       text, contentIntent);
        // We show this for as long as our service is processing a command.
        notification.flags |= Notification.FLAG_ONGOING_EVENT;
        
        // Send the notification.
        // We use a string id because it is a unique number.  We use it later to cancel.
        mNM.notify(R.string.service_created, notification);
    }
    
    private void hideNotification() {
        mNM.cancel(R.string.service_created);
    }
    
    // ----------------------------------------------------------------------
    /**
     * Example of explicitly starting the {@link ServiceStartArguments}.
     * 
     * <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.service_start_arguments_controller);
            
            // Watch for button clicks.
            // 각각 마다 startService 메소드로 서비스를 시작할때,
            // 인텐트에 추가값을 입력하고 있습니다.
            // 이 값이 서비스로 넘어가서, 서비스의 onStartCommand 메소드가
            // 호출되면서 인텐트도 넘어가서 넘긴 값에 따라서 임의의
            // 처리를 합니다.
            Button button = (Button)findViewById(R.id.start1);
            button.setOnClickListener(mStart1Listener);
            button = (Button)findViewById(R.id.start2);
            button.setOnClickListener(mStart2Listener);
            button = (Button)findViewById(R.id.start3);
            button.setOnClickListener(mStart3Listener);
            button = (Button)findViewById(R.id.startfail);
            button.setOnClickListener(mStartFailListener);
            button = (Button)findViewById(R.id.kill);
            button.setOnClickListener(mKillListener);
        }
        private OnClickListener mStart1Listener = new OnClickListener() {
            public void onClick(View v) {
                startService(new Intent(Controller.this,
                        ServiceStartArguments.class)
                                .putExtra("name", "One"));
            }
        };
        private OnClickListener mStart2Listener = new OnClickListener() {
            public void onClick(View v) {
                startService(new Intent(Controller.this,
                        ServiceStartArguments.class)
                                .putExtra("name", "Two"));
            }
        };
        private OnClickListener mStart3Listener = new OnClickListener() {
            public void onClick(View v) {
                startService(new Intent(Controller.this,
                        ServiceStartArguments.class)
                                .putExtra("name", "Three")
                                .putExtra("redeliver", true));
            }
        };
        private OnClickListener mStartFailListener = new OnClickListener() {
            public void onClick(View v) {
                startService(new Intent(Controller.this,
                        ServiceStartArguments.class)
                                .putExtra("name", "Failure")
                                .putExtra("fail", true));
            }
        };
        private OnClickListener mKillListener = new OnClickListener() {
            public void onClick(View v) {
                // This is to simulate the service being killed while it is
                // running in the background.
                Process.killProcess(Process.myPid());
            }
        };
    }
}


/res/drawable-hdpi/ 폴더에 stat_sample.png 파일을 안드로이드 샘플폴더에서 복사해 옵니다.

/res/layout/service_start_arguments_controller.xml ServiceStartArguments.Controller 액티비티 클래스의 레이아웃 파일을 추가해줍니다.

<?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.ServiceStartArguments.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/service_start_arguments_controller"/>
    <Button android:id="@+id/start1"
        android:layout_width="wrap_content" android:layout_height="wrap_content" 
        android:text="@string/start1_service">
        <requestFocus />
    </Button>
    <Button android:id="@+id/start2"
        android:layout_width="wrap_content" android:layout_height="wrap_content" 
        android:text="@string/start2_service">
    </Button>
    <Button android:id="@+id/start3"
        android:layout_width="wrap_content" android:layout_height="wrap_content" 
        android:text="@string/start3_service">
    </Button>
    <Button android:id="@+id/startfail"
        android:layout_width="wrap_content" android:layout_height="wrap_content" 
        android:text="@string/startfail_service">
    </Button>
    <Button android:id="@+id/kill"
        android:layout_width="wrap_content" android:layout_height="wrap_content" 
        android:text="@string/kill_process">
    </Button>
</LinearLayout>


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


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, Test012!</string>
    <string name="app_name">Test012</string>
    
    <string name="activity_service_start_arguments_controller">App/Service/Service
        Start Arguments Controller
    </string>
    <string name="service_start_arguments_controller">This demonstrates how
        service can be started with arguments, and run until all arguments are
        processed.
    </string>
    <string name="start1_service">Start \"One\" no redeliver</string>
    <string name="start2_service">Start \"Two\" no redeliver</string>
    <string name="start3_service">Start \"Three\" w/redeliver</string>
    <string name="startfail_service">Start failed delivery</string>
    <string name="service_created">Service created.</string>
    <string name="service_destroyed">Service destroyed.</string>
    
    <string name="kill_process">Kill Process</string>
    
    <string name="service_start_arguments_label">Sample Service Start Arguments</string>
    
</resources>

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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="korsoft.net.Test012"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        
        <service android:name=".ServiceStartArguments" />
  
        <activity android:name=".ServiceStartArguments$Controller"
                android:label="@string/activity_service_start_arguments_controller"
                android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
  
    </application>
</manifest>

반응형