Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> [Android 基礎系列]Service、IntentService和習以為常的誤解

[Android 基礎系列]Service、IntentService和習以為常的誤解

編輯:關於Android編程

前言:

也許是低門檻原因,最初接觸Android的人寫了很多書、博文,創造了一個邪論:Activity就是弄界面的,Service就是弄後台的,進而將“播放音樂”這種演變為“耗時操作”,進而演繹成:“耗時的、長時間運行的都需要使用service”。只想說:MDZZ! 原意是想全文自己寫,但看了一眼API文檔,整理的實在是太好了,所以本文會摘錄API的內容並結合重點寫一點內容。  

正文:

Service:

API文檔中的概述如下:
A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding  declaration in its package's AndroidManifest.xml. Services can be started with Context.startService() andContext.bindService().
Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.
  給大家蹩腳的翻譯一下: Service是一個應用組件,代表著:“在不和用戶交互的情況下,進行‘持續’運行的應用需求”或者“為其他應用提供功能性服務” 。每個Service類(當然是你要用的)都需要在Manifest文件中聲明(利用節點),Service有兩種方式啟動:Context.startService() 和 Context.bindService() 函數。 注意注意注意!service和其他應用組件一樣,運行在宿主進程的主線程中,這意味著如果你的service需要做 頻繁的CPU計算操作(例如播放MP3)或者阻塞的操作(例如訪問網絡),那應該創建獨立的線程(就是讓你規避主線程)。 然後提到了IntentService是一個規范的實現,它在子線程中處理工作(言下之意你不用手動開辟子線程)。   請注意我上面用了一個詞:“持續”運行,這並不准確,但我不想使用長時間運行這個詞,總會被誤解為“阻塞”。   OK,再看看Service是個什麼鬼,API說你所疑惑的可以通過排除法解決:
?	A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.
?	A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).
Service不是一個分隔獨立的進程 Service不是線程,不代表它脫離主線程工作   把兩點合一起:service是運行在啟動它的應用程序進程中的,應用程序這個進程有主線程(俗稱UI線程),service不脫離主線程工作,所以你不能阻塞service的主線程,否則這會帶來ANR。   我們先不看生命周期(其實大家對此是比較明白的) 我們看API中提到的Local Service使用,順便引出一個疑惑。   寫一個Service:
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);
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_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);

        // 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, LocalServiceActivities.Controller.class), 0);

        // Set the info for the views that show in the notification panel.
        notification.setLatestEventInfo(this, getText(R.string.local_service_label),
                       text, contentIntent);

        // 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();
}

我們看到了使用bindService 綁定並啟動service,並在Activity銷毀時解除綁定。所以有人疑惑了:我並不想我的service這時候被結束掉,但不解除綁定是不規范的、甚至擔心是否會影響到Activity的銷毀、回收。   OK,我們必須再回憶一下Service的介紹,沒有一句話說它是為Activity提供服務的,它本身是一個服務沒錯,它也只是一個服務,而不是為Activity提供服務的服務。   前面提到了兩種“啟動”service的方法 bindService和startService,嚴格的說:startService是啟動,而且是Context中定義的方法,從來沒有說是Activity中定義的;bindService是綁定service,綁定時如果沒有啟動會幫助你啟動,但本質是綁定。 存在這樣的差異: 啟動:我只是把你弄活了,然後沒有半毛錢關系。 綁定:你要跟著我混!我不要你了你就可以走了。   Context類中對unbindService的描述是:
Disconnect from an application service. You will no longer receive calls as the service is restarted, and the service is now allowed to stop at any time.
斷開應用服務連接,你不會再收到service重啟的消息(回調),這個service也可以在任意時間被系統終止了。   這裡我們做一個簡單的測試,讓兩個Activity綁定同一個Service,看解除綁定對service的影響:
package com.example.testservice;

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.util.Log;
import android.view.View;
import android.view.View.OnClickListener;

public class MainActivity extends Activity {
	
	private final static String TAG = "MainActivity";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Log.d(TAG, "onCreate");
		doBindService();
		
		findViewById(R.id.button).setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				startActivity(new Intent(MainActivity.this,SecondActivity.class));
			}
		});
	}
	
	private LocalService mBoundService;

	private ServiceConnection mConnection = new ServiceConnection() {
	    public void onServiceConnected(ComponentName className, IBinder service) {
	        mBoundService = ((LocalService.LocalBinder)service).getService();

	        // Tell the user about this for our demo.
	        Log.d(TAG, "service bind");
	    }

	    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;
	        Log.d(TAG, "service unbind");
	    }
	};

	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).
		Log.d(TAG, "do service bind");
	    bindService(new Intent(this, 
	            LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
	    mIsBound = true;
	}
	
	private boolean mIsBound  = false;

	void doUnbindService() {
	    if (mIsBound) {
	        // Detach our existing connection.
	    	Log.d(TAG, "do service unbind");
	        unbindService(mConnection);
	        mIsBound = false;
	    } else {
	    	Log.d(TAG, "no necessary to unbind,it is not binding");
	    }
	}

	@Override
	protected void onDestroy() {
		Log.d(TAG, "onDestroy");
	    super.onDestroy();
	    doUnbindService();
	}
}
package com.example.testservice;

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.util.Log;

public class SecondActivity extends Activity {
	
	private final static String TAG = "SecondActivity";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_second);
		doBindService();
	}
	
	private LocalService mBoundService;

	private ServiceConnection mConnection = new ServiceConnection() {
	    public void onServiceConnected(ComponentName className, IBinder service) {
	        mBoundService = ((LocalService.LocalBinder)service).getService();

	        // Tell the user about this for our demo.
	        Log.d(TAG, "service bind");
	    }

	    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;
	        Log.d(TAG, "service unbind");
	    }
	};

	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).
		Log.d(TAG, "do service bind");
	    bindService(new Intent(this, 
	            LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
	    mIsBound = true;
	}
	
	private boolean mIsBound  = false;

	void doUnbindService() {
	    if (mIsBound) {
	        // Detach our existing connection.
	    	Log.d(TAG, "do service unbind");
	        unbindService(mConnection);
	        mIsBound = false;
	    } else {
	    	Log.d(TAG, "no necessary to unbind,it is not binding");
	    }
	}

	@Override
	protected void onDestroy() {
		Log.d(TAG, "onDestroy");
	    super.onDestroy();
	    doUnbindService();
	}
}
package com.example.testservice;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

/** 
 * @ClassName: LocalService 
 * @Description: TODO
 * @date 2016年4月27日 下午3:43:57
 *  
 * @author leobert.lan
 * @version 1.0
 */
public class LocalService extends Service {
	
	private final static String TAG = "LocalService";

    /**
     * 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() {
    	Log.i(TAG, "service onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "Received start id " + startId + ": " + intent);
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
    	Log.i(TAG, "service destroy");
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
    	Log.i(TAG, "service onBind");
        return mBinder;
    }

    // This is the object that receives interactions from clients.  See
    // RemoteService for a more complete example.
    private final IBinder mBinder = new LocalBinder();

}
我截取了一下logcat,但是eclipse的背景。。。 \

\

我們看到,不是解除綁定就關閉service的,是service不再被綁定的時候,它會被干掉。   正如API提供的demo中注釋所說的一樣:
// 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.
bindService可以獲得service對象,並且和它進行交互(說白了就是想調用他的方法)。 我們還有另外一個有逼格的東西:IntentService。

IntentService:

文檔這樣描述它:
IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

This "work queue processor" pattern is commonly used to offload tasks from an application's main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.

All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.
IntentService是service的子類,設計用來處理異步請求,通過startService函數的調用來發送請求,吧啦吧啦一堆。   要使用它的話需要實現onHandlerIntent方法,這個方法的描述:
This method is invoked on the worker thread with a request to process. Only one Intent is processed at a time, but the processing happens on a worker thread that runs independently from other application logic. So, if this code takes a long time, it will hold up other requests to the same IntentService, but it will not hold up anything else. When all requests have been handled, the IntentService stops itself, so you should not call stopSelf().
Intent是有序到達、處理的,先來的先處理,耗時的話後面的等著,不會阻塞主線程之類的,干完活自己干死自己,所以你不要調用stopSelf().   然而我們經常覺得沒啥卵用。  

多線程:

這裡話分兩頭,前面我們知道service不是thread,耗時操作應該規避主線程的,所以要開辟子線程做,IntentService做了一定的封裝,幫助開辟了子線程。 在Android使用多線程要知道一件事情:Dalvik畢竟能提供的資源有限,意味著回收很重要,開辟線程會提高資源使用程度,但也有被系統干死回收的可能,依托Activity開辟子線程(初衷一定是為了耗時的code、阻塞的code)有Activity面臨關閉而子線程操作沒有完成的可能,例如日記本應用。依托Service的生命力頑強一點。  
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved