현재 하고자 하는 것은 BOOT_COMPLETED action이 브로드캐스팅 됨과 동시에 백그라운드에서 서비스를 하나 돌리고,

그 서비스를 여러 앱이 사용할 수 있는 환경으로 만들어주고자 합니다.

그래서 다음과 같이 코딩을 했는데, bindService는 true를 반환하는데 onServiceConnected()가 호출이 안되는지 ServiceConnection 인스턴스를 사용하려고 하면 NullPointerException이 뜨네요.ㅠㅠ

혹시 이유를 아시는 분 계실까요..

제 코드는 다음과 같습니다.

 

서비스 소스코드들과 aidl, manifest입니다.

 

1. TERMMAServiceManager.java : 부팅시 TERMMAMiddleware 서비스를 등록해주는 코드입니다.

 

 package kr.ac.kaist.term;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class TERMMAServiceManager extends BroadcastReceiver{
 @Override
 public void onReceive(Context context, Intent intent) {
  Log.v("[TERMMAServiceManager]","onReceive!");
  if(intent.getAction().equalsIgnoreCase("android.intent.action.BOOT_COMPLETED")){
   Log.v("[TERMMAServiceManager]","BOOT Complete!!");
   ComponentName cName = new ComponentName(context.getPackageName(), TERMMAMiddleware.class.getName());
   ComponentName service = context.startService(new Intent().setComponent(cName));
   if(service == null){
    Log.e("[TERMMAServiceManager]","Could not start services TERMMAMiddleware");    
   }
  }else{
   Log.e("[TERMMAServiceManager]","Received unexpected intent " + intent.toString());
  }
 }
}

2. TERMMAMiddleware.java : 서비스파트가 구현된 코드입니다.

 package kr.ac.kaist.term;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class TERMMAMiddleware extends Service{
 private Queue<Message> mQueue;
 private Iterator it;
 private Timer timer;
 private TimerTask task;
 private long lastCheckOut; 
 private long tCnt;
 
 public void onCreate() {
     super.onCreate();
     Log.v("[TERMMAMiddleware]", "TERMMAMiddleware Service started!!!");
     
     mQueue = new LinkedList<Message>();
     Log.v("[TERMMAMiddleware]","queue assigned");
     it = mQueue.iterator();
     Log.v("[TERMMAMiddleware]","iterator assigned");
     timer = new Timer();
     Log.v("[TERMMAMiddleware]","timer created");
     task = new TimerTask(){
   public void run(){
    Log.v("[TERMMAMiddleware]","in run");
    dequeueAll();
   }
  };
  Log.v("[TERMMAMiddleware]","Task assigned!!");
 }
  
 @Override
 public IBinder onBind(Intent intent) {
  Log.v("[TERMMAMiddleware]","TERMMAMiddleware service is bounded!!!");
  return mBinder;
 }
 private final TERMMAInterface.Stub mBinder = new TERMMAInterface.Stub() {  
  public void send(String mString, String destIP, int destPort, boolean faultTolerant, int timeout)
    throws RemoteException {
   Log.v("[TERMMAMiddleware]","send!!!");
   Message m = new Message(mString, destIP, destPort, faultTolerant, timeout);
   if(m.isFaultTolerant()==true){
    enqueue(m);
   }else{
    sendMessage(m);
    dequeueAll();
   }
  }
 };
 
 private void enqueue(Message m){
  Log.v("[TERMMAMiddleware]","enqueue!!");
  mQueue.add(m);
  
  //set-up timer
  if(mQueue.size()==0){ 
   lastCheckOut = System.currentTimeMillis();
   tCnt = m.getTimeout();
   timer.schedule(task, m.getTimeout());
  }else{
   if(tCnt-(System.currentTimeMillis()-lastCheckOut) > m.getTimeout()){
    timer.cancel();
    timer.schedule(task, m.getTimeout());
   }
  } 
 }
 
 private void dequeueAll(){
  Log.v("[TERMMAMiddleware]","dequeueAll!!");
  while(it.hasNext()){
   printMessage(mQueue.poll());
   //sendMessage(mQueue.poll());
  }
 }
 
 private void sendMessage(Message m){
  Log.v("[TERMMAMiddleware]","sendMessage!!");
  try {
   Socket clientSocket = new Socket(m.getDestIP(), m.getDestPort());
   DataOutputStream out = new DataOutputStream(clientSocket.getOutputStream());
   out.writeBytes(m.getStr()+'\n');
   clientSocket.close();
  } catch (UnknownHostException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
 
 public void testString(String str){
  Log.v("[TERMMAMiddleware]","The given string is, "+str);
 }
 
 public void printMessage(Message m){
  Log.v("[TERMMAMiddleware]", "Message Info.");
  Log.v("[TERMMAMiddleware]", "Destination IP address = "+m.getDestIP());
  Log.v("[TERMMAMiddleware]", "Destination port = "+m.getDestPort());
  Log.v("[TERMMAMiddleware]", "Message string = "+m.getStr());
  Log.v("[TERMMAMiddleware]", "Message faultTolerant = "+m.isFaultTolerant());
  Log.v("[TERMMAMiddleware]", "Message timeout = "+m.getTimeout());
 }
}


3. AndroidManifest.xml : 서비스의 Manifest 파일입니다.

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kr.ac.kaist.term"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk android:minSdkVersion="10" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
 <application android:icon="@drawable/ic_launcher"
     android:label = "@string/app_name">
     <service 
         android:name="kr.ac.kaist.term.TERMMAMiddleware"
         android:process=":remote"
         android:enabled="true"
         android:exported="true">
   <intent-filter>
    <action android:name="kr.ac.kaist.term.TERMMAInterface" />
   </intent-filter>
     </service>
     <receiver android:name=".TERMMAServiceManager"
         android:enabled="true"
         android:exported="true"
         android:label="TERMMAServiceManager">
         <intent-filter>
             <action android:name="android.intent.action.BOOT_COMPLETED"/>
         </intent-filter>
     </receiver>
 </application>
</manifest>

========= 이제부터는 Client side의 소스코드입니다.----------------

 

1. TestAActivity.java :서비스를 바인딩해서 사용하는 코드입니다.

 

 package kr.ac.kaist.testa;
import kr.ac.kaist.term.TERMMAInterface;
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.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class TestAActivity extends Activity {
 TERMMAInterface TERMMAService = null;
 Button bindButton;
 Button unbindButton;
 TERMMAThread t1;
 boolean isBound;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
     Log.v("[TESTAActivity]","");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        bindButton = (Button)findViewById(R.id.button1);        
        bindButton.setOnClickListener(mBindListener);        
        unbindButton = (Button)findViewById(R.id.button2);        
        unbindButton.setOnClickListener(mUnbindListener);
    }
    
    private ServiceConnection mConnection = new ServiceConnection(){
  public void onServiceConnected(ComponentName className, IBinder service) {
   Log.v("[TESTAActivity]","onServiceConnected");
   TERMMAService = TERMMAInterface.Stub.asInterface(service);
   Toast.makeText(TestAActivity.this, "Connected", Toast.LENGTH_SHORT).show(); 
  }
  public void onServiceDisconnected(ComponentName arg0) {
   Log.v("[TESTAActivity]","onServiceDisconnected");
   TERMMAService = null;
   Toast.makeText(TestAActivity.this, "DisConnected", Toast.LENGTH_SHORT).show();
  }     
    };
    
    private OnClickListener mBindListener = new OnClickListener(){
     public void onClick(View v){
      Log.v("[TESTAActivity]","mBindListener onClick!");
      /*
       * bindService() method below returns false, as it's using the higher level application context
       * isBound = bindService(new Intent(TERMMAInterface.class.getName()), mConnection, Context.BIND_AUTO_CREATE);
       * therefore, getApplicationContext().bindService() returns true
       */
      isBound = getApplicationContext().bindService(new Intent(TERMMAInterface.class.getName()), mConnection, Context.BIND_AUTO_CREATE);
      Toast.makeText(TestAActivity.this, "Binding", Toast.LENGTH_SHORT).show();
      Log.v("[TESTAActivity]","isBound = "+isBound);
      Log.v("[TESTAActivity]","TERMMAInterface.class.getName() = "+TERMMAInterface.class.getName());
      Log.v("[TESTAActivity]","mConnection.toString = "+mConnection.toString());
      
      t1 = new TERMMAThread();
      t1.run();
     }
    };
    
    private OnClickListener mUnbindListener = new OnClickListener(){
     public void onClick(View v){
      Log.v("[TESTAActivity]","mUnbindListener onClick!");
      t1.stop();
      
      unbindService(mConnection);
      Toast.makeText(TestAActivity.this, "Unbinding", Toast.LENGTH_SHORT).show();
     }
    };
    
    public class TERMMAThread extends Thread{
     public void run(){
      Log.v("[TESTAActivity]","TERMMAThread run");
      try {
       int cnt = 0;
       
       while(true){
        sleep(5000);
        if(TERMMAService==null){
         Log.v("[TestAActivity]","TERMMAService is null!!");
        }
        TERMMAService.send("Hello", "dest_ip", ++cnt, true, 1000000);
        Toast.makeText(TestAActivity.this, "Message Sent", 2500).show();
       }    
   } catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
     }
    }
}

2. AndroidManifest.xml : 클라이언트사이드의 Manifest 파일입니다.

 <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kr.ac.kaist.testa"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk android:minSdkVersion="10" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".TestAActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

아.. 구글링을 해도 저에게 맞는 답을 찾기가 힘들고 답답하네요.ㅠㅠ...