Android应用开发学习笔记之Service

作者:刘昊昱 

博客:http://blog.csdn.net/liuhaoyutz

 

Android Service分为两种,一是StartedService,另一种是Bound Service。下面来看Android官方文档对这两种Service的定义:

A service canessentially take two forms:

Started

A service is "started" when anapplication component (such as an activity) starts it by calling startService(). Once started, a service can run in thebackground indefinitely, even if the component that started it is destroyed.Usually, a started service performs a single operation and does not return aresult to the caller. For example, it might download or upload a file over thenetwork. When the operation is done, the service should stop itself.

Bound

A service is "bound" when anapplication component binds to it by calling bindService(). A bound service offers a client-serverinterface that allows components to interact with the service, send requests,get results, and even do so across processes with interprocess communication(IPC). A bound service runs only as long as another application component isbound to it. Multiple components can bind to the service at once, but when allof them unbind, the service is destroyed.

 

一、StartedService的实现

 

创建Started Service有两种方法,一是继承IntentService类,二是继承Service类。前者我们只需要实现onHandleIntent()函数和一个无参数的构造函数,代码简单,但是这种方法一次只能处理一个客户端请求。后者需要我们实现多个成员函数,但是自主性较大,可以同时处理多个客户请求。

我们先来看一个通过继承IntentService实现Started Service的例子,该程序运行效果如下:

Android应用开发学习笔记之Service_第1张图片

点击“显示当前时间”按钮后,LogCat会显示如下信息:

先来看主布局文件,其内容如下:

<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
   <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:text="@string/hello" />
   <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:textSize="20dp"
        android:text="显示当前时间"/>
 
</LinearLayout>


再来看主Activity文件,其内容如下:

package com.liuhaoyu;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
 
public classMainActivity extends Activity {
   /** Called when the activity is firstcreated. */
   @Override
   publicvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        Button button =(Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener(){
           
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                startService(new Intent(MainActivity.this, CurrentTimeService.class));
            }
        });
   }
}


下面看service的实现:

package com.liuhaoyu;
 
import android.app.IntentService;
import android.content.Intent;
import android.text.format.Time;
import android.util.Log;
 
public classCurrentTimeService extends IntentService {
 
   publicCurrentTimeService() {
        super("CurrentTimeService");
   }
 
   @Override
   protectedvoidonHandleIntent(Intent intent) {
        Time time = new Time();
        time.setToNow();
        String currentTime = time.format("%Y-%m-%d %H:%M:%S");
        Log.i("CurrentTimeService", currentTime);
   }
}


最后,需要注意我们要在AndroidManifest.xml文件中声明Service,如下:

        <service android:name=".CurrentTimeService">
        </service>


注意上面这个程序,每次点击按钮,LogCat只会显示一条时间信息,也就是说Service的onHandleIntent()函数只会执行一次。而且我们并没有调用stopSelf()等函数停止Service,这是因为IntentService已经在幕后替我们完成了许多工作,我们就不用亲自做了,来看Android官方文档的描述:

The IntentService doesthe following:

·        Creates a default worker threadthat executes all intents delivered to onStartCommand() separate from your application's main thread.

·        Creates a work queue thatpasses one intent at a time to your onHandleIntent() implementation, so you never have to worry aboutmulti-threading.

·        Stops the service after allstart requests have been handled, so you never have to call stopSelf().

·        Provides default implementationof onBind() that returns null.

·        Provides a defaultimplementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent() implementation.

 

下面我们来看一个通过继承Service实现Started Service的例子,该程序运行效果如下:

Android应用开发学习笔记之Service_第2张图片

点击“显示当前时间”按钮后,LogCat显示如下内容:

先来看主布局文件,其内容如下:

<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
   <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:text="@string/hello" />
   <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:textSize="20dp"
        android:text="显示当前时间" />
 
</LinearLayout>


下面来看主Activity文件,其内容如下:

package com.liuhaoyu;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
 
public class MainActivity extends Activity {
    /**Called when the activity is first created. */
   @Override
    publicvoid onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
       
       Button button = (Button)findViewById(R.id.button);
       button.setOnClickListener(new View.OnClickListener() {
           
            @Override
            publicvoid onClick(View v) {
                //TODO Auto-generated method stub
                startService(newIntent(MainActivity.this, CurrentTimeService.class));
            }
        });
    }
}


下面看Service的实现:

package com.liuhaoyu;
 
import android.app.Service;
import android.content.Intent;
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.text.format.Time;
import android.util.Log;
import android.widget.Toast;
 
public classCurrentTimeService extends Service {
   
    private Looper mServiceLooper;
    private ServiceHandler mServiceHandler;
 
    // Handler that receives messages from the thread
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looperlooper) {
            super(looper);
        }
       
        @Override
        public void handleMessage(Messagemsg) {
            // Normally we would do some work here, like download afile.
            // For our sample, we just sleep for 5 seconds.
            long endTime = System.currentTimeMillis()+ 5*1000;
            while (System.currentTimeMillis()< endTime) {
                synchronized (this) {
                    try {
                        wait(endTime - System.currentTimeMillis());
                        }catch(Exception e) {
                           
                        }
                }
            }
           
            Timetime = newTime();
            time.setToNow();
            String currentTime = time.format("%Y-%m-%d %H:%M:%S");
            Log.i("CurrentTimeService", currentTime);
           
            // Stop the service using the startId, so that we don'tstop
            // the service in the middle of handling another job
            stopSelf(msg.arg1);
        }
    }
 
    @Override
    public IBinder onBind(Intentintent) {
        // TODO Auto-generated method stub
        // We don't provide binding, so return null
        return null;
    }
 
    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
       
        // Start up the thread running the service.  Note that we create a
        // separatethread because the service normally runs in the process's
        // main thread,which we don't want to block.  We alsomake it
        // backgroundpriority so CPU-intensive work will not disrupt our UI.
        HandlerThread thread = new HandlerThread("ServiceStartArguments",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
       
        // Get theHandlerThread's Looper and use it for our Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = newServiceHandler(mServiceLooper);
    }
 
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        Toast.makeText(this, "service done",Toast.LENGTH_SHORT).show();
    }
 
    @Override
    public int onStartCommand(Intentintent, intflags, intstartId) {
        // TODO Auto-generated method stub
        Toast.makeText(this, "service starting",Toast.LENGTH_SHORT).show();
       
        // For each start request, send a message to start a joband deliver the
        // start ID so we know which request we're stopping whenwe finish the job
        Messagemsg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        mServiceHandler.sendMessage(msg);
       
        // If we get killed, after returning from here, restart
        return START_STICKY;
    }
   
}


最后,我们要在AndroidManifest.xml文件中声明CurrentTimeService:

        <service android:name=".CurrentTimeService">
        </service>


 

二、 BoundService的实现

 

我们先来看Android官方文档上对BoundService的描述:

A boundservice is an implementation of the Service class that allows other applications to bindto it and interact with it. To provide binding for a service, you mustimplement the onBind() callback method. This method returns an IBinder object that defines the programming interfacethat clients can use to interact with the service.

A client canbind to the service by calling bindService(). When it does, it must provide animplementation ofServiceConnection, which monitors the connection with theservice. The bindService() method returns immediately without a value,but when the Android system creates the connection between the client andservice, it callsonServiceConnected() on the ServiceConnection, to deliver the IBinder that the client can use to communicate withthe service.

Multiple clients canconnect to the service at once. However, the system calls your service's onBind() methodto retrieve the IBinder onlywhen the first client binds. The system then delivers the same IBinder toany additional clients that bind, without calling onBind() again.

When the last clientunbinds from the service, the system destroys the service (unless the servicewas also started bystartService()).

When you implement yourbound service, the most important part is defining the interface that your onBind() callbackmethod returns. There are a few different ways you can define your service's IBinder interfaceand the following section discusses each technique.

如上所述,创建Bound Service最重要的是定义IBinder接口,Client就是通过IBinder接口与Service进行交互。Android提供了3种方法定义IBinder接口:

(1) 继承Binder类。如果BoundService是一个应用程序私有的,并且Bound Service和Client运行在同一个进程中,那么继承Binder类来创建IBinder接口是最好的选择。

(2) 使用Messenger。如果BoundService和Client运行在不同的进程中,那么我们可以使用Messenger创建IBinder接口。通过Messenger.getBinder()可以返回IBinder接口给Client。在这种方式中,BoundService需要创建一个Handler对象来处理各种Message,并将该Handler对象传递给Messenger构造函数。

(3) 使用AIDL。如果希望BinderService能同时处理多个Client请求,可以使用AIDL声明IBinder接口,但是要BinderService注意处理多线程安全问题。

通过继承Binder类实现IBinder接口,进而创建BoundService的步骤如下:

<1>在Service中,继承Binder类创建一个Binder子类,该子类能够提供如下3个功能之一即可:

l  该Binder子类包含Client能够调用的公共方法。

l  该Binder子类提供返回当前Service实例的方法,当前Service实例包含client能调用的公共方法。

l  该Binder子类提供返回service管理的其它类的实例的方法,其中包含client能调用的公共方法。

<2>在Bound Service的onBind()函数中返回IBinder接口。

<3>client从onServiceConnected()方法接收IBinder接口,并通过BoundService提供的公共方法对Bound Service进行访问。

下面我们来看一个创建并使用Bound Service的例子,这个例子中通过继承Binder类,实现IBinder接口,程序运行效果如下图所示:

Android应用开发学习笔记之Service_第3张图片

先来看主布局文件,其内容如下:

<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
   <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:text="@string/hello" />
   
   <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:onClick="onButtonClick"
        android:textSize="20dp"
        android:text="点击获取随机数" />
 
</LinearLayout>


下面看主Activity文件,其内容如下:

package com.liuhaoyu;
 
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.view.View;
import android.widget.Toast;
 
import com.liuhaoyu.LocalService.LocalBinder;
 
public class MainActivity extends Activity {
   LocalService mService;
   boolean mBound = false; 
   
    /**Called when the activity is first created. */
   @Override
    publicvoid onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
    }
 
   @Override
   protected void onStart() {
       super.onStart();
        //Bind to LocalService
       Intent intent = new Intent(this, LocalService.class);
       bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
 
   @Override
   protected void onStop() {
       super.onStop();
        //Unbind from the service
        if(mBound) {
           unbindService(mConnection);
           mBound = false;
        }
    }
 
    /**Called when a button is clicked (the button in the layout file attaches to
      *this method with the android:onClick attribute) */
    publicvoid onButtonClick(View v) {
        if(mBound) {
           // Call a method from the LocalService.
           // However, if this call were something that might hang, then thisrequest should
           // occur in a separate thread to avoid slowing down the activityperformance.
           int num = mService.getRandomNumber();
           Toast.makeText(this, "number: " + num,Toast.LENGTH_SHORT).show();
        }
    }
 
    /**Defines callbacks for service binding, passed to bindService() */
   private ServiceConnection mConnection = new ServiceConnection() {
 
       @Override
       public void onServiceConnected(ComponentName className,
               IBinder service) {
           // We've bound to LocalService, cast the IBinder and get LocalServiceinstance
           LocalBinder binder = (LocalBinder) service;
           mService = binder.getService();
           mBound = true;
        }
 
       @Override
       public void onServiceDisconnected(ComponentName arg0) {
           mBound = false;
        }
    };
}


下面看LocalService的实现,其内容如下:

package com.liuhaoyu;
 
import java.util.Random;
 
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
 
public classLocalService extends Service {
   // Binder given to clients
   privatefinalIBinder mBinder= newLocalBinder();
   // Random number generator
   privatefinalRandom mGenerator= newRandom();
 
   /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients,we don't need to deal with IPC.
     */
   publicclassLocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods
            return LocalService.this;
        }
   }
 
   @Override
   publicIBinder onBind(Intent intent) {
        return mBinder;
   }
 
   /** method for clients */
   publicintgetRandomNumber() {
     returnmGenerator.nextInt(100);
   }
}


最后,我们要在AndroidManifest.xml文件中声明LocalService,如下:

    <service android:name=".LocalService">
    </service>


 

下面我们再来看一个创建并使用Bound Service的例子,这个例子中通过Messenger类取得IBinder接口,程序运行效果如下图所示:

Android应用开发学习笔记之Service_第4张图片

先来看主布局文件,其内容如下:

<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
 
   <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:text="@string/hello" />
   
   <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button"
        android:onClick="onButtonClick"
        android:textSize="20dp"
        android:text="发送Message" />
 
</LinearLayout>


下面来看主Activity文件,其内容如下:

package com.liuhaoyu;
 
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.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.view.View;
 
public classMainActivity extends Activity {
   /** Messenger for communicating with theservice. */
   Messenger mService= null;
 
   /** Flag indicating whether we have calledbind on the service. */
   booleanmBound;
   
   /** Called when the activity is firstcreated. */
   @Override
   publicvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
   }
   
   /** Called when a button is clicked (thebutton in the layout file attaches to
     * this method with the android:onClickattribute) */
  publicvoidonButtonClick(View v) {
       if (mBound) {
       sayHello();
       }
  }
 
   /**
     * Class for interacting with the maininterface of the service.
     */
   privateServiceConnection mConnection = newServiceConnection() {
        public void onServiceConnected(ComponentName className,IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  Weare communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            mBound = true;
        }
 
        public void onServiceDisconnected(ComponentNameclassName) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mBound = false;
        }
   };
 
   publicvoidsayHello() {
        if (!mBound) return;
        // Create and send a message to the service, using a supported 'what'value
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO,0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
   }
 
   @Override
   protectedvoidonStart() {
        super.onStart();
        // Bind to the service
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
   }
 
   @Override
   protectedvoidonStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
   }
}


下面我们来看Service的实现,其代码如下:

package com.liuhaoyu;
 
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.widget.Toast;
 
public class MessengerService extends Service {
    /**Command to the service to display a message */
    staticfinal int MSG_SAY_HELLO = 1;
 
    /**
     *Handler of incoming messages from clients.
     */
    classIncomingHandler extends Handler {
       @Override
       public void handleMessage(Message msg) {
           switch (msg.what) {
               case MSG_SAY_HELLO:
                   Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break;
               default:
                    super.handleMessage(msg);
           }
        }
    }
 
    /**
     *Target we publish for clients to send messages to IncomingHandler.
     */
    finalMessenger mMessenger = new Messenger(new IncomingHandler());
 
    /**
     *When binding to the service, we return an interface to our messenger
     * forsending messages to the service.
     */
   @Override
    publicIBinder onBind(Intent intent) {
       Toast.makeText(getApplicationContext(), "binding",Toast.LENGTH_SHORT).show();
       return mMessenger.getBinder();
    }
}


最后,我们需要在AndroidManifest.xml文件中声明MessengerService,如下:

        <service android:name=".MessengerService">   
        </service>


你可能感兴趣的:(android)