2025-05-25

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的例子,該程序運行效果如下:

 

點擊“顯示當前時間”按鈕後, 

 

 

 

先來看主佈局文件,其內容如下:

 

[html]  

<?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文件,其內容如下:

 

[java]  

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的實現:

 

[java]  

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,如下:

 

[html]  

<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的例子, 

 

 

 

點擊“顯示當前時間”按鈕後, 

 

 

 

先來看主佈局文件,其內容如下:

 

[html]  

<?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文件,其內容如下:

 

[java]  

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的實現:

 

[java]  

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:

 

[html]  

<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接口, 

 

 

 

先來看主佈局文件,其內容如下:

 

[html]  

<?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文件,其內容如下:

 

[java] 

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的實現,其內容如下:

 

[java] 

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,如下:

 

[html]  

<service android:name=".LocalService">  

</service>  

 

 

 

 

下面我們再來看一個創建並使用Bound Service的例子,這個例子中通過Messenger類取得IBinder接口, 

 

 

 

先來看主佈局文件,其內容如下:

 

[html]  

<?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文件,其內容如下:

 

[java] 

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的實現,其代碼如下:

 

[java] 

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,如下:

 

[html] 

<service android:name=".MessengerSer

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *