• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • Android -- Service官方文檔簡譯

    標簽: Service  Android Service  Service基礎

    Android -- Service官方文檔簡譯




    Service是一個應用程序組件,它既可以代表應用程序處理當無需和用戶交互時的長耗時操作,也能為其他應用程序提供接口支持。每一個Service類都必須在包的Androidmanifest.xml中存在對應的聲明。我們可以通過Context.startService()/Context.bindService()啟動一個服務。


    要注意,Service像其他程序對象一樣,運行在它坐在進程的主線程中。這也就意味著,如果你的Service要做任何耗CPU(如播放MP3)或阻塞(如網絡請求)操作時,它都應該創建一個新線程來處理。IntentService是可獲取的Service的一個標準實現,它擁有自己的線程來處理這些耗時工作。


    什么是服務


    • Service并不是一個單獨的的進程。一個Service對象不意味著它運行在自己的進程中;除非有額外明確信息,否則它作為應用程序的一部分和它運行在同一個進程中。
    • Service并不是一個線程。它并不意外這自己會在主線程之外處理自己的工作。

    因此Service本身是非常簡單的,它提供了兩個很主要的特征:

    • 它是一個提供給應用程序告知系統它想要在后臺(甚至當用戶并不直接和應用交互時)做一些處理的工具。這與Context.startService()調用對應,它會告知系統要啟動該Service;Service會一直運行到有服務或其他角色顯式停止它為止。
    • 他是一個提供給應用對外暴露自己接口給其他應用程序使用的工具。這與Context.bindService()調用對應,為了與該服務交互,該接口允許應用與該服務之間創建一個長保持的連接。

    當一個Service組件被創建后,基于以上任何一個原因,系統會做的工作就是初始化組件并在主線程中調用onCreate()和任何其他合適的回調。這取決Service借助合適的操作來實現這些內容,例如,創建一個操作處理的次級線程(對主線程而言)。


    要注意,Service是非常簡單的,我們能通過我們所想,以盡量簡單或復雜的方式去和它交互。通過把它看成本地Java對象,我們可以借助它來調用以AIDL形式提供的所有接口。


    服務生命周期


    Service的生命周期圖示如下:


    我們有兩種方式來讓系統運行一個Service。如果調用了Context.startService(),那么系統就會去啟動一個Service(需要的話,創建并調用它的onCreate()方法),并附帶Client提供的參數去調用它的onStartCommand(Intent, int, int)方法。此時,除非調用了Context.stopService()或stopSelf()方法,那么服務將一直運行下去。要注意多次調用Context.startService()并不會嵌套生效(盡管這確實會導致onStartCommand()被相應地多次調用),所以無論你多少次啟動一個Service,它只會在Context.onStop()/Context.stopSelf()被調用時停止一次;同時,Service可以使用它們的stopSelf(int)方法來確保在啟動服務的Intent被處理之前,服務不會被停止。


    客戶端可以調用Context.bindService()來獲取和此Service之間的一個長連接。如果此時服務沒有運行的話,它同樣會創建服務(調用onCreate()),但是不會調用onStartCommand()。客戶端將會在它的onBinder(Intent)方法中接收到一個服務返回的IBinder對象,這允許你通過該對象回調到服務中。只要這個連接一建立,服務就會一直運行(不管客戶端是否保持對服務IBinder的引用)。通常情況下,IBinder對象的返回是為了使用在AIDL中定義的那些復雜接口。


    一個服務既能被啟動,也能同時連接并被綁定。這種場景下,只要服務被啟動,或者至少有一個帶有Context.BIND_AUTO_CREATE標志的連接被建立,系統就會保持該服務持續運行。一旦這些情況都不成立,服務的onDestroy()方法就會被調用,而且該服務也會被有效地終止。從onDestroy()返回后,Service相關的所有清理工作(停止線程,注銷廣播接收者)都應該完成。


    進程生命周期


    只要一個服務被啟動或者有客戶端綁定它后,系統就會嘗試去保護該服務所在的進程繼續運行。當設備運行在內存極低、系統需要殺死當前存在進程的情況下時,持有服務的進程的優先級在接下來的幾種可能性中會是較高的:

    • 如果Service正在執行onCreate()、onStartCommand()或onDestroy()接口中的代碼,那么該Service的宿主進程將被認為是前臺進程,系統不會殺死它以確保這些代碼被執行。
    • 如果一個服務已經被啟動,那么該服務的宿主進程會被認為比當前任何在屏幕上對用戶可見的那些進程(即直接和用戶交互的進程)的重要性低,但比那些不可見的進程的優先級要高。這意味著除了在內存低的條件下,這類服務都不應該被殺死。但是,由于用戶并不會直接注意到這些服務,在那種狀況下(指內存低),這類服務都是合法的被殺死的候選對象;我們應該對這類情形做一些準備。特別是,那些長期運行的服務被殺死的可能性會越來越高;如果它們運行的時間已經足夠長,系統則會保證它們被殺死(或被重啟,如果允許的話)。
    • 如果有客戶端綁定到一個服務,那么該服務的宿主進程的重要性永遠不會比最重要的客戶端進程的重要性低多少。也就是說,如果客戶端進程對用戶可見,那么該服務進程也會被認為是對用戶可見。一種由客戶端重要性影響服務重要性的方式是通過BIND_ABOVE_CLIENT,BIND_ALLOW_OOM_MANAGEMENT,BIND_WAIVE_PRIORTY,BIND_IMPORTANT和BIND_ADJUST_WITH_ACTIVITY來調節。
    • 一個已經啟動的Service可以通過使用startForeground(int, Notifcation)API讓它進入前臺(foreground)狀態,此時系統會認為用戶已經主動注意到該服務了,所以當內存很低的時候,它的所屬進程將不會成為那個將要被殺死的候選對象。(理論上,在內存極端低的壓力下,一個處于前臺進程中的Service仍然有可能被殺死,但實際上這不應該看做是個問題。)

    需要注意,這意味著絕大多數時候,你的服務都會運行,但當系統的內存壓力很大時,它也許會被系統殺死。如果發生了這種情況,系統會在之后嘗試重啟該服務。這種情形下的一個重要結果就是如果你實現了onStartCommand()方法讓所要完成的工作被異步處理或者運行在另外一個線程中,你也許會想使用START_FLAG_REDELIVER標志,讓系統重新給你遞送一個Intent,這樣即使Service在處理它時被殺,也不會丟失這個Intent。


    其他作為服務(例如一個Activity)運行在同一進程中的應用程序組件不僅僅可以提升它服務本身的重要性,也可以提升整個進程的重要性。


    本地服務示例


    服務最常見的一個用途就是作為一個次級組件和應用程序的其他部分一起運行在同一個進程中。除非有明確說明的情況下,否則apk中的所有組件都運行在同一個進程,所以這是一個典型的情況。這樣使用時,假設所有組件都運行在同一進程,此時你可以大大簡化組件之間的交互:服務的客戶端可以簡單地將它從服務得到的IBinder對象轉換成一個由服務發布的具體類型。


    這里展示一個這樣使用服務的例子。首先是服務自身,它被綁定時會發布一個特殊的類:

    public class LocalService extends Service {
        private NotificationManager mNM;
    
        // Unique Identification Number for the Notification.
        // We use it on Notification start, and to cancel it.
        private int NOTIFICATION = R.string.local_service_started;
    
        /**
         * Class for clients to access.  Because we know this service always
         * runs in the same process as its clients, we don't need to deal with
         * IPC.
         */
        public class LocalBinder extends Binder {
            LocalService getService() {
                return LocalService.this;
            }
        }
    
        @Override
        public void onCreate() {
            mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
    
            // Display a notification about us starting.  We put an icon in the status bar.
            showNotification();
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("LocalService", "Received start id " + startId + ": " + intent);
            return START_NOT_STICKY;
        }
    
        @Override
        public void onDestroy() {
            // Cancel the persistent notification.
            mNM.cancel(NOTIFICATION);
    
            // Tell the user we stopped.
            Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
        // This is the object that receives interactions from clients.  See
        // RemoteService for a more complete example.
        private final IBinder mBinder = new LocalBinder();
    
        /**
         * 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.local_service_started);
    
            // The PendingIntent to launch our activity if the user selects this notification
            PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                    new Intent(this, LocalServiceActivities.Controller.class), 0);
    
            // Set the info for the views that show in the notification panel.
            Notification notification = new Notification.Builder(this)
                    .setSmallIcon(R.drawable.stat_sample)  // the status icon
                    .setTicker(text)  // the status text
                    .setWhen(System.currentTimeMillis())  // the time stamp
                    .setContentTitle(getText(R.string.local_service_label))  // the label of the entry
                    .setContentText(text)  // the contents of the entry
                    .setContentIntent(contentIntent)  // The intent to send when the entry is clicked
                    .build();
    
            // Send the notification.
            mNM.notify(NOTIFICATION, notification);
        }
    }

    那部分完成后,我們可以直接寫出訪問服務端的客戶端代碼,例如:

    private LocalService mBoundService;
    
    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.  Because we have bound to a explicit
            // service that we know is running in our own process, we can
            // cast its IBinder to a concrete class and directly access it.
            mBoundService = ((LocalService.LocalBinder)service).getService();
    
            // Tell the user about this for our demo.
            Toast.makeText(Binding.this, R.string.local_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.
            // Because it is running in our same process, we should never
            // see this happen.
            mBoundService = null;
            Toast.makeText(Binding.this, R.string.local_service_disconnected,
                    Toast.LENGTH_SHORT).show();
        }
    };
    
    void doBindService() {
        // Establish a connection with the service.  We use an explicit
        // class name because we want a specific service implementation that
        // we know will be running in our own process (and thus won't be
        // supporting component replacement by other applications).
        bindService(new Intent(Binding.this, 
                LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
    }
    
    void doUnbindService() {
        if (mIsBound) {
            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        doUnbindService();
    }


    遠程Messenger服務示例


    如果你需要寫一個能和遠端進程中的客戶端進行復雜溝通的服務(除了簡單使用Context.startService()給它發送命令外),這時你就應該使用Messenger類型來代替編寫完整的AIDL文件。


    下面是展示的一個使用Messenger來做為客戶端接口的服務示例。首先是服務自身,當它被綁定時會發布一個指向服務內部Handler的Messenger:

    public class MessengerService extends Service {
        /** For showing and hiding our notification. */
        NotificationManager mNM;
        /** Keeps track of all current registered clients. */
        ArrayList<Messenger> mClients = new ArrayList<Messenger>();
        /** Holds last value set by a client. */
        int mValue = 0;
    
        /**
         * Command to the service to register a client, receiving callbacks
         * from the service.  The Message's replyTo field must be a Messenger of
         * the client where callbacks should be sent.
         */
        static final int MSG_REGISTER_CLIENT = 1;
    
        /**
         * Command to the service to unregister a client, ot stop receiving callbacks
         * from the service.  The Message's replyTo field must be a Messenger of
         * the client as previously given with MSG_REGISTER_CLIENT.
         */
        static final int MSG_UNREGISTER_CLIENT = 2;
    
        /**
         * Command to service to set a new value.  This can be sent to the
         * service to supply a new value, and will be sent by the service to
         * any registered clients with the new value.
         */
        static final int MSG_SET_VALUE = 3;
    
        /**
         * Handler of incoming messages from clients.
         */
        class IncomingHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_REGISTER_CLIENT:
                        mClients.add(msg.replyTo);
                        break;
                    case MSG_UNREGISTER_CLIENT:
                        mClients.remove(msg.replyTo);
                        break;
                    case MSG_SET_VALUE:
                        mValue = msg.arg1;
                        for (int i=mClients.size()-1; i>=0; i--) {
                            try {
                                mClients.get(i).send(Message.obtain(null,
                                        MSG_SET_VALUE, mValue, 0));
                            } catch (RemoteException e) {
                                // The client is dead.  Remove it from the list;
                                // we are going through the list from back to front
                                // so this is safe to do inside the loop.
                                mClients.remove(i);
                            }
                        }
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        }
    
        /**
         * Target we publish for clients to send messages to IncomingHandler.
         */
        final Messenger mMessenger = new Messenger(new IncomingHandler());
    
        @Override
        public void onCreate() {
            mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
    
            // Display a notification about us starting.
            showNotification();
        }
    
        @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();
        }
    
        /**
         * When binding to the service, we return an interface to our messenger
         * for sending messages to the service.
         */
        @Override
        public IBinder onBind(Intent intent) {
            return mMessenger.getBinder();
        }
    
        /**
         * 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);
    
            // 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 notification = new Notification.Builder(this)
                    .setSmallIcon(R.drawable.stat_sample)  // the status icon
                    .setTicker(text)  // the status text
                    .setWhen(System.currentTimeMillis())  // the time stamp
                    .setContentTitle(getText(R.string.local_service_label))  // the label of the entry
                    .setContentText(text)  // the contents of the entry
                    .setContentIntent(contentIntent)  // The intent to send when the entry is clicked
                    .build();
    
            // 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);
        }
    }

    如果想讓服務運行在遠端進程中,我們可以在它的服務聲明標簽中使用"android:process"來明確這一點:

    <service android:name=".app.MessengerService"
            android:process=":remote" />
    注意,此處使用的名稱"remote"是任意的,如果你需要其他的進程,則使用其他的名字即可。":"后綴會將這個名字附加到包的標準進程名稱。


    那些完成之后,客戶端可以綁定服務并向它發送消息了。注意,這同時也允許客戶端注冊它的Messenger以用來接收消息:

    /** Messenger for communicating with service. */
    Messenger mService = null;
    /** Flag indicating whether we have called bind on the service. */
    boolean mIsBound;
    /** Some text view we are using to show state information. */
    TextView mCallbackText;
    
    /**
     * Handler of incoming messages from service.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MessengerService.MSG_SET_VALUE:
                    mCallbackText.setText("Received from service: " + msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    
    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());
    
    /**
     * Class for interacting with the main interface of the 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 = new Messenger(service);
            mCallbackText.setText("Attached.");
    
            // We want to monitor the service for as long as we are
            // connected to it.
            try {
                Message msg = Message.obtain(null,
                        MessengerService.MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mService.send(msg);
    
                // Give it some value as an example.
                msg = Message.obtain(null,
                        MessengerService.MSG_SET_VALUE, this.hashCode(), 0);
                mService.send(msg);
            } 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;
            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();
        }
    };
    
    void doBindService() {
        // Establish a connection with the service.  We use an explicit
        // class name because there is no reason to be able to let other
        // applications replace our component.
        bindService(new Intent(Binding.this, 
                MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);
        mIsBound = true;
        mCallbackText.setText("Binding.");
    }
    
    void doUnbindService() {
        if (mIsBound) {
            // If we have received the service, and hence registered with
            // it, then now is the time to unregister.
            if (mService != null) {
                try {
                    Message msg = Message.obtain(null,
                            MessengerService.MSG_UNREGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                } catch (RemoteException e) {
                    // There is nothing special we need to do if the service
                    // has crashed.
                }
            }
    
            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
            mCallbackText.setText("Unbinding.");
        }
    }
    (這里需要注意Messenger的用法,它可以指定接收者和發送者)。

    PS:會持續潤色。


    版權聲明:本文為csdn_of_coder原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/csdn_of_coder/article/details/75731902

    智能推薦

    Android動畫簡析

    Android動畫可以分為三種: View動畫,幀動畫和屬性動畫。 其實幀動畫也屬于View動畫,只不過它的平移,旋轉等常用的View動畫在表現上略有不同而已。View動畫通過對場景里的對象不斷做圖像變換(平移,縮放,旋轉,透明度)從而產生動畫效果,他是一種漸進式動畫,并且View動畫支持自定義。幀動畫通過順序播放一系列圖像從而產生動畫效果,可以簡單理解為圖片切換動畫,很顯然,如果圖片過大就會導致...

    Android Drawable 簡析

    Drawable 是開發中經常用到的一個概念,我們經常用它去設置 View 的背景,背景可以一個顏色值,也可以是一張資源圖片,還可以是一個自定義的 Drawable等等。這篇文章就簡單說下 Drawable 與 View 的關系,同時結合代碼,簡要分析一下 Drawable 如何作用于 View。 Drawable 介紹 官方介紹 A Drawable is a general abstracti...

    Android SurfaceView簡例

    Android SurfaceView簡例 Android中各的SurfaceView和View有很大的不同,兩者應用場景不同。大多數View能做的事情SurfaceView也可以,但是SurfaceView效率更高。Android的View繪制過程由Android系統控制,刷新機制開發者比較難以控制。而SurfaceView支持高頻、多線程繪制。SurfaceView不存在是否在Android ...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    猜你喜歡

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    精品国产乱码久久久久久蜜桃不卡