前言
寫著一篇文章的原因,主要是因為在面試中,服務這個關鍵詞的出現頻率非常高。很多時候,面試官會問你,Service中能否進行耗時操作? 我們稍後就會揭曉那麼這個答案。
思維導圖
生命週期
由圖中可以直觀的看出幾點。
啟動方式 | 存在方式 |
---|---|
startService() | 獨立於Activity執行,自行結束或被叫停 |
bindService() | 繫結於Activity執行,Activity結束時,會被叫停 |
涉及方法 | 用途 |
---|---|
onCreate() | |
onDestroy() | |
onStartCommand() | 用於計數,服務被呼叫的次數 |
onBind() | 與Activity元件繫結 |
onUnbind() | 與Activity元件解綁 |
使用方法
Service
方法需要在AndroidManifest.xml
中進行註冊
// 第一步:在AndroidManifest.xml中進行註冊
<service android:name=".LocalService"/>
// 第二步:啟動
① startService(Intent);
② bindService(Intent, ServiceConnection, Int);
// 第三步:解綁(使用方法② 啟動時操作)
unBindService(ServiceConnection);
// 第四步:暫停
stopService(Intent);
複製程式碼
Activity和Service的通訊
Activity
和Service
的通訊其實就是基於IBinder
來進行實現的。但是IBinder
其實是一個介面,對我們而言一般使用他的實現類Binder
並通過強制轉換來完成操作。
/**
* Service方法繼承
* onBind()是一個抽象方法。
*/
public class LocalService extends Service {
private final IBinder binder = new ServiceBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public class ServiceBinder extends Binder {
LocalService getLocalService(){
return LocalService.this;
}
}
}
複製程式碼
以上程式碼,是一個用於通訊的基礎版本。
既然需要通訊,那我們總需要知道對方是誰,如果使用的是startService()
,上文已經提到他是獨立於Activity
的,所以勢必使用的是bindService()
。
在上文的使用方法中已經提到了bindService()
使用到的引數,Intent
、ServiceConnection
、Int
。
ServiceConnection
/**
* bindService()方法中的引數之一。
* 用於對service進行操作
*/
ServiceConnection connection = new ServiceConnection() {
// Activity和Service繫結時呼叫
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
// 基於Binder拿到我們要的Service
service = ((LocalService.ServiceBinder)binder).getLocalService();
// 幹你需要乾的事情
}
// Activity和Service解綁時呼叫
@Override
public void onServiceDisconnected(ComponentName name) {
service = null;
}
};
複製程式碼
Int
BIND_AUTO_CREATE
:收到繫結需求,如果Service尚未建立,則立即建立。BIND_DEBUG_UNBIND
:用於測試使用,對unbind呼叫不匹配的除錯幫助。BIND_NOT_FOREGROUND
:不允許此繫結將目標服務的程式提升到前臺排程優先順序
這是一個已經存在於Service
類中的值,這裡並不全部例舉,一般來說都是使用BIND_AUTO_CREATE
。
必須要呼叫的unbindService(ServiceConnection)
為什麼我們一定要呼叫這個方法,如果我們不解綁會出現什麼樣的問題?
經過測試,Logcat中爆出了這樣的錯誤Activity has leaked ServiceConnection that was originally bound here
。也就是說ServiceConnection
記憶體洩漏了。這也是為什麼我們一直說需要解綁的原因。
IntentService
public class LocalIntentService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public LocalIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
}
}
複製程式碼
先看一段我們的繼承程式碼,和Service
不同的地方就是,必須重寫的方法是onHandleIntent(Intent intent)
。
那我們也和之前一樣做一個原始碼導讀好了。
IntentService原始碼導讀
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
@UnsupportedAppUsage
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
}
複製程式碼
其實從整個程式碼的變數我們已經可以做一個猜測了。Looper
+Handler
+Service
的組成成分。那它的處理過程勢必依賴於一個Handler
的通訊機制。另外看到了ServiceHandler
中的stopSelf()
方法,我們也就清楚了一個問題為什麼我們不需要去控制IntentService
的暫停。
接下來從生命週期的角度來看看這個IntentService
,因為Binder
機制上是一致的,所以分析主線就是onCreate() --> onStartCommand() --> onDestroy()
。
onCreate()
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
複製程式碼
建立了一個HandlerThread
,去初始化了Looper
和Handler
,也就說明服務在內部處理。
onStartCommand()
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId); // 1 -->
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; // 2 -->
}
複製程式碼
onStart()
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
複製程式碼
其他都是和Handler
一致的,整體流程也就是Message
封裝,再通過Handler
進行一個傳送。
mRedelivery
這個變數是幹什麼的?
/**
* Constant to return from {@link #onStartCommand}: if this service's
* process is killed while it is started (after returning from
* {@link #onStartCommand}), and there are no new start intents to
* deliver to it, then take the service out of the started state and
* don't recreate until a future explicit call to
* {@link Context#startService Context.startService(Intent)}. The
* service will not receive a {@link #onStartCommand(Intent, int, int)}
* call with a null Intent because it will not be restarted if there
* are no pending Intents to deliver.
*
* <p>This mode makes sense for things that want to do some work as a
* result of being started, but can be stopped when under memory pressure
* and will explicit start themselves again later to do more work. An
* example of such a service would be one that polls for data from
* a server: it could schedule an alarm to poll every N minutes by having
* the alarm start its service. When its {@link #onStartCommand} is
* called from the alarm, it schedules a new alarm for N minutes later,
* and spawns a thread to do its networking. If its process is killed
* while doing that check, the service will not be restarted until the
* alarm goes off.
*/
public static final int START_NOT_STICKY = 2;
/**
* Constant to return from {@link #onStartCommand}: if this service's
* process is killed while it is started (after returning from
* {@link #onStartCommand}), then it will be scheduled for a restart
* and the last delivered Intent re-delivered to it again via
* {@link #onStartCommand}. This Intent will remain scheduled for
* redelivery until the service calls {@link #stopSelf(int)} with the
* start ID provided to {@link #onStartCommand}. The
* service will not receive a {@link #onStartCommand(Intent, int, int)}
* call with a null Intent because it will only be restarted if
* it is not finished processing all Intents sent to it (and any such
* pending events will be delivered at the point of restart).
*/
public static final int START_REDELIVER_INTENT = 3;
複製程式碼
一大段冗長的英文很煩,更何況我也就低分飄過6級的水平呢,哈哈哈哈!!
就不折磨你們了,直接做出一個解釋吧。
- START_NOT_STICKY:預設模式,這是一個容許被殺的模式,隨時允許被叫停
- START_REDELIVER_INTENT:告訴系統在崩潰後重新啟動服務,並重新傳遞在崩潰時存在的意圖
好了,以上基本就是整個IntentService
的介紹了。
總結
- 在ANR機制中,
Service
的響應時長不能超過20s,其實也可以比較直觀的看出,Service
其實並不能進行所謂耗時操作。但是如果加上了Thread
進行非同步處理,那麼其實他還是可以進行耗時操作的。(具體看你怎麼進行回答,主要還是一個知識點,Service
執行在主執行緒) Service
存在的原因是Activity
是一個經常會被銷燬的元件,雖然我們同樣可以通過Thread
進行非同步操作,但是當Activity
例項被銷燬時,相應的捆綁在Activity
生命週期內的Thread
例項我們也沒有能力再去尋找了。
以上就是我的學習成果,如果有什麼我沒有思考到的地方或是文章記憶體在錯誤,歡迎與我分享。
相關文章推薦: