第九章 四大組件的工作過程
四大組件分別是Activity,Service,BroadcastReceiver,ContentProvider。如何使用是最基礎的,但我們在本章想談更多:(1)對四大組件的運行狀態和工作方式做一個概括性的描述;(2)對四大組件的運行過程進行分析。
(一)四大組件的運行狀態
除了BroadcastReceiver之外,其他三個組件都必須在AndroidManifest.xml中註冊;BroadcastReceiver有動態註冊(在代碼中定義)和靜態註冊(XML兩種)。除了ContentProvider不需要藉助Intent,其他三者都需要Intent。
1.1.Activity的運行狀態
Activity是一種展示型組件,主要作用是展示一個界面並和用戶交互,扮演的是前臺界面的角色。用戶可感知,Activity由Intent觸發,可以用顯示Intent明確指定某個Activity或者多個Activity組件,也可以用隱式Intent去指定某個Activity或者多個Activity組件,一個Activity也存在特定的啓動模式(Standard、SingleTop、SingleTask、SingleInstance)。
1.2.Service的運行狀態
Service是一種計算型組件,用於在後臺執行一系列計算任務。Service工作於後臺,無法直接感知。Activity有一種工作狀態,而Service有兩種狀態:啓動狀態和綁定狀態。
Service在啓動狀態時,可以做一些後臺計算並且不需要與外界直接交互,但是運行在主線程中,所以儘量不要耗時,或者開闢線程去處理耗時任務。但當Service處於綁定狀態的時候,內部同樣可以做後臺計算,並且還可以與外界靈活交互,Service組件停止的話就需要先stopService並且unBindService。
1.3.BroadcastReceiver的運行狀態
BroadcastReceiver是一種消息型組件,用於在不同的組件乃至不同的應用之間傳遞消息,同樣無法被用戶感知。被稱爲廣播。廣播有兩種註冊方式,靜態和動態註冊。靜態註冊需要在XML中註冊,並在應用安裝時被系統解析,無需應用啓動就可收到廣播;動態註冊廣播需要Context.registerReceiver來註冊,並且在不需要的時候通過Context.unregisterReceiver來解除廣播。必須啓動才能註冊解除廣播,通過sendBroadCast進行傳播。標準廣播(異步執行的廣播,無先後順序,無法截斷),有序廣播(同步執行的廣播,又先後順序,可被截斷)。OnReceiver方法。
1.4.ContentProvider的運行狀態
ContentProvider是一種數據共享型組件,用於向其他組件乃至其他應用共享數據,無法被用戶直接感知。它的內部需要實現增刪查改四個操作,在他的內部維持着一份數據集合,這個數據集合既可以通過數據庫來實現,也可以採用其他類型來實現,ContentProvider內部的inset、update、delete和query方法需要處理好線程同步,因爲這幾個方法都是在線程池中被調用的。ContentResolver方法。
(二)Activity的工作過程
步驟一:顯示調用啓動,startActivity開始啓動,調用了startActivityForResult方法。
Intent intent = new Intent(this, TestActivity.class);
startActivity(intent);
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
步驟二:startActivityForResult中調用了execStartActivity方法。然後再調用execStartActivity的 ActivityManager.getDefault().startActivity方法,該通過Binder實現ActivityManagerService啓動。
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
//ActivityThread的ApplicationThread在啓動過程中發揮着很重要的作用,接下來我們看mInstrumentation.execStartActivity這個方法
.....
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, android.app.Activity target,
Intent intent, int requestCode, Bundle options) {
...
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
//ActivityManager.getDefault()實際上是AMS,啓動過程轉移至AMS中,
int result = ActivityManager.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
//假設XML中沒有定義該Activity,在這裏進行檢查報錯。
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
步驟三:emmm..經過了複雜的過程,Activity的啓動過程最終還是回到了ActivityThread中,performLaunchActivity方法最終完成了對象的創建和啓動。最終執行了onResume方法。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
...
}
....
}
需要說明的是:performLaunchActivity方法主要完成如下幾個事件:1.從ActivityClientRecord中獲取待啓動的Activity組件信息;2.通過instrumentation的newActivity方法使用類加載器創建Activity對象;(activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);)3.通過LoadedApk的makeApplicatiton方法來創建Application對象(public Application makeApplication(...) {...});4.創建ContextImpl對象並通過Activity的attach方法來完成一些重要數據的初始化。5.調用Activity的onCreate方法。
(三)後臺默默的勞動者-服務
Android從一開始就支持後臺功能,使得應用程序即使在關閉的情況下仍然可以在後臺運行。譬如:一邊打電話,一邊後臺掛着QQ。下面主要聚焦在Android多線程編程(解析異步消息處理機制<Handler、Message的處理機制>、異步通信任務AsynacTask)、服務的基本用法、生命週期以及高階技巧(前臺服務與IntentService),
3.1.服務是什麼?
Android實現程序後臺運行的解決方案,適合去執行不需要和用戶交互還要求長期運行的任務。服務運行不依賴於任何用戶界面,當程序被切換至後臺或者打開另一個App,服務依然可以正常運行。
服務不是獨立進程,而依賴於創建服務所在App進程,當App進程被殺掉時,所有依賴於該進程的服務也會停止。服務並不會自動開啓線程,所有線程都是默認運行在主線程的,所以存在被阻塞住的情況。
3.2.Android多線程編程
耗時任務譬如發送網絡請求,需要放在子線程中運行,如果放在主線程運行,則會有阻塞住的風險。
3.2.1.線程的基本用法
與Java類似,使用相同語法。
定義並啓動一個線程。
方法一:新建類繼承自Thread並重寫父類的run()方法,在其中執行耗時邏輯。如何調用?new出一個示例並調用它的start方法。
//啓動剛纔定義的線程
new MyThread().run();
//定義一個線程,繼承自Thread類並實現其run方法
class MyThread extends Thread{
@Override
public void run() {
//處理具體耗時邏輯。
}
}
方法二:繼承的方式耦合性高,可以使用實現Runnable接口的方式來定義一個線程。
//啓動剛纔定義的實現Runnable接口的線程。
MyThread01 myThread01 = new MyThread01();
//Thread構造函數接受一個Runnable參數並傳至Thread構造函數中。
new Thread(myThread01).start();
//實現Runnable接口會導致較低的耦合性,
class MyThread01 implements Runnable{
@Override
public void run() {
//處理具體邏輯
}
}
方法三:匿名類的方式進行實現運行一個線程。
new Thread(new Runnable() {
@Override
public void run() {
//處理耗時具體邏輯
}
}).start();
3.2.2.在子線程中更新UI
Andreoid UI線程是不安全的。如果需要更新App的UI元素,必須在主線程中進行,否則就會出現異常。子線程更新UI會報錯:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
場景:點擊按鈕在子線程完成耗時任務,在主線程更新UI。點擊Button在子線程中通過異步消息處理機制改變UI的內容。
解決方案:
步驟一:在子線程中創建一個Message對象,並在這裏對具體的Message進行處理,執行一些耗時操作;然後將handler.sendMessage將Message對象發送出去;
步驟二:新建Handler對象,並重寫它的handleMessage方法,在這裏對剛纔傳遞過來的具體的Message進行處理。handleMessage方法中的代碼是在主線程中運行的,在這裏進行UI操作。
代碼示例:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn_click;
private TextView tv_content;
//整型常量UPDATE_TEXT用來更新TextView這個操作
public static final int UPDATE_TEXT = 1;
//新建Handler對象,並重寫它的handleMessage方法,在這裏對具體的Message進行處理。
private Handler handler = new Handler(){
//handleMessage方法中的代碼是在主線程中運行的,在這裏進行UI操作。
@Override
public void handleMessage(Message msg) {
//如果發現Message的what字段等於UPDATE_TEXT,修改TextView的內容
switch (msg.what){
case UPDATE_TEXT:
//在這裏可以進行UI操作
tv_content.setText("Nice to meet u");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_click = (Button) findViewById(R.id.btn_click);
tv_content = (TextView) findViewById(R.id.text);
btn_click.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_click:
new Thread(new Runnable() {
@Override
public void run() {
//創建一個Message對象,並在這裏對具體的Message進行處理
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);//將Message對象傳遞出去。
}
}).start();
}
}
}
3.2.3.解析異步消息處理機制
Android異步消息處理主要有四個部分:Message、Handler、MessageQueue、Looper。上一小節講了Message和Handler。
3.2.3.1.概念介紹:
Message:線程之間傳遞的消息。內部可攜帶少量的信息,可用於不同線程之間交互。譬如:Message的what字段,此外還有arg1、arg2字段攜帶整形數據;obj字段攜帶Object對象。
Handler:處理者。用於發送和處理消息。發送消息一般是Handler的sendMessage方法,經過一系列處理後,最終會傳遞到Handler的handleMessage方法中。
MessageQueue:消息隊列。存放所有通過Handler發送的消息,這部分消息存儲於消息隊列中,等待被處理。每個線程只會有一個MessageQueue對象。
Looper:每個線程中MessageQueue的管家,調用Looper的loop方法後,就會進入到一個無線循環中,每當發現MessageQueue存在一條消息,就會將它去除,並傳遞到Handler的handleMessage方法中,每個線程同樣也只有一個Looper。
3.2.3.2.工作流程:
步驟一:主線程中創建Handler對象,重寫handleMessage方法,當子線程需要更新UI,構建Message對象,通過Handle發送出去;
步驟二:該消息被添加進MessageQueue隊列中等待被處理,Looper方法一直嘗試從MessageQueue中讀取待處理消息,並通過dispatchMessage方法分發給handleMessage方法;
步驟三:handleMessage方法在主線程操作,可以在這裏操作UI。
3.2.4.AsyncTask的用法
爲了更方便在子線程中更新UI,Android提供了AsyncTask,更簡單的從子線程到主線程。背後的處理機制也是基於異步消息處理機制的。
AsyncTask是一個抽象類,並制定三個泛型參數,這三個參數的用途如下:1.Params:執行AsyncTask需要傳入的參數,可用於在後臺任務中使用;2.Progress:後臺任務執行時,如果需要界面上顯示進度,此爲進度單位;3.Result:任務執行結束後,需要對結果返回。
3.2.4.1.幾個關鍵API方法:
1.onPreExecute在後臺任務開始執行之前進行調用,用於界面的初始化操作,譬如顯示一個進度條對話框等。
2.doInBackground(處理具體耗時任務):會在子線程中運行,在這裏處理所有耗時任務,在這裏是不可以更新UI元素,如果需要反饋當前任務的執行進度,可以調用publishProgress(Progress...)方法來完成。
3.onProgressUpdate(進行UI操作):後臺調用了publishProgress(Progress...)方法後,onProgressUpdate將會被很快調用,其方法中攜帶的參數是從後臺任務中傳遞過來的,在這裏對UI進行更新,利用參數中的數值可以對界面元素進行相應的更新。
4.onPostExecute(任務收尾工作):當後臺任務執行完畢後並使用return語句返回後,該方法很快被調用。返回數據會作爲參數傳遞到此方法中。可利用返回數據來進行UI操作。譬如:提醒任務執行結果,以及關閉進度條對話框等。
3.2.4.2.代碼示例:
//第一個參數是Void,執行AsyncTask無需傳入參數給後臺任務;第二個泛型參數是Integer,使用整形數據作爲進度顯示單位;
//第三個參數是使用布爾類型用來反饋執行結果。
public class DownLoadTask extends AsyncTask<Void,Integer,Boolean>{
//1.onPreExecute在後臺任務開始執行之前進行調用,用於界面的初始化操作,譬如顯示一個進度條對話框等。
@Override
protected void onPreExecute() {
super.onPreExecute();//顯示進度條
}
//2.該方法中的所有代碼都會在子線程中運行,在這裏處理所有耗時任務,在這裏是不可以更新UI元素,如果需要反饋當前任務的執行進度,
// 可以調用publishProgress(Progress...)方法來完成。
@Override
protected Boolean doInBackground(Void... params) {
return null;//執行後臺耗時任務
}
//3.後臺調用了publishProgress(Progress...)方法後,onProgressUpdate將會被很快調用,其方法中攜帶的參數是從後臺任務中傳遞過來的,
// 在這裏對UI進行更新,利用參數中的數值可以對界面元素進行相應的更新。
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);//在這裏顯示下載的進度
}
//4.當後臺任務執行完畢後並使用return語句返回後,該方法很快被調用。返回數據會作爲參數傳遞到此方法中。可利用返回數據來進行UI操作。
//譬如:提醒任務執行結果,以及關閉進度條對話框等。
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);//關閉進度對話框並提示下載結果
}
}
3.2.4.3.啓動這個任務
new DowloadTask().execute();
使用publishProgress方法完成子線程到主線程的切換。
3.3.服務的基本用法:
3.3.1.定義一個服務
右擊包名->New->Service->Service。Exported表示是允許除了當前App之外的其他程序訪問;Enabled屬性表示是否啓用該服務。
MyService繼承自Service,是一個服務。每一個服務都需要註冊在XML中的。複寫以下的方法。onCreate:在服務創建的時候調用;onStartCommand:每次服務啓動的時候調用,希望服務一旦啓動就立刻執行某個動作,則將其寫在onStartCommand中;onDestroy:服務銷燬的時候調用,回收不再使用的資源;onBind:唯一的抽一的抽象方法,需要在子類中實現,暫時忽略。
3.3.2.啓動和停止服務
啓動服務:
//構建Intent對象
Intent startIntent = new Intent(this,MyService.class);
//調用startService來啓動MyService這個任務。定義於Context類中的,所以可以直接調用startService方法。
startService(startIntent);//啓動服務
結束服務:
Intent stopIntent = new Intent(this,MyService.class);
//除了這種調用方式外,還有那些停止方法?MyService內部的任何一處位置調用StopSelf即可讓該服務停止。
stopService(stopIntent);//停止服務
執行的操作:startService->stopService->startService->startService,截圖如下所示,理解其生命週期。
3.3.3.活動與服務之間通信
上一節類似於活動通知服務:“可以幹了!”但幹了什麼,什麼時候搞的咋樣活動完全不知道。那麼如何讓服務活動和服務之間更緊密一些?onBind方法瞭解一下。
場景:在MyService模擬一個簡單的下載功能。
步驟一:創建一個專門的Binder對象來對下載功能進行管理。新建DowbLoadBinder繼承自Binder,提供了開始下載和查看下載的方法,並提供打印日誌。onBind方法中返回這個實例。
public class MyService extends Service {
private DowloadBinder mBinder = new DowloadBinder();
private final static String TAG = "MyService" ;
public MyService() {
}
....
//唯一的抽一的抽象方法,需要在子類中實現,暫時忽略。
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
class DowloadBinder extends Binder{
public void startDownload(){
Log.d(TAG, "startDownload exec: ");
}
public int getProgress(){
Log.d(TAG, "getProgress exec: ");
return 0;
}
}
}
步驟二:創建ServieConnection內部匿名類,在裏面重寫了onServiceConnected和onServiceDisconnected兩個方法,會在活動綁定與服務綁定以及解除綁定的時候調用。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MyService.DowloadBinder dowloadBinder;
private Button btn_start, btn_stop,btn_bind_service,btn_unbind_service;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//向下轉型得到DownloadBinder實例,有了這個實例,活動和服務之間建立了密切的關係。
dowloadBinder = (MyService.DowloadBinder) service;
//可根據不同場景具體調用dowloadBinder中的不同方法,指揮服務去幹什麼
dowloadBinder.startDownload();
dowloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
.....
btn_bind_service = (Button) findViewById(R.id.btn_bind_service);
btn_unbind_service = (Button) findViewById(R.id.btn_unbind_service);
....
btn_bind_service.setOnClickListener(this);
btn_unbind_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
....
case R.id.btn_unbind_service:
unbindService(connection);
break;
default:
break;
}
}
}
3.3.4.活動與服務之間通信的所有代碼:
MyService.java:
//繼承自Service,是一個服務。每一個服務都是註冊在XML中的。
public class MyService extends Service {
private DowloadBinder mBinder = new DowloadBinder();
private final static String TAG = "MyService" ;
public MyService() {
}
//服務創建的時候調用
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate exec: ");
}
//每次服務啓動的時候調用,希望服務一旦啓動就立刻執行某個動作,則將其寫在onStartCommand中。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand exec: ");
return super.onStartCommand(intent, flags, startId);
}
//服務銷燬的時候調用,回收不再使用的資源。
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy exec: ");
super.onDestroy();
}
//唯一的抽一的抽象方法,需要在子類中實現,暫時忽略。
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return mBinder;
}
class DowloadBinder extends Binder{
public void startDownload(){
Log.d(TAG, "startDownload exec: ");
}
public int getProgress(){
Log.d(TAG, "getProgress exec: ");
return 0;
}
}
}
MainActivity.java:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MyService.DowloadBinder dowloadBinder;
private Button btn_start, btn_stop,btn_bind_service,btn_unbind_service;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//向下轉型得到DownloadBinder實例,有了這個實例,活動和服務之間建立了密切的關係。
dowloadBinder = (MyService.DowloadBinder) service;
//可根據不同場景具體調用dowloadBinder中的不同方法,指揮服務去幹什麼
dowloadBinder.startDownload();
dowloadBinder.getProgress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start = (Button) findViewById(R.id.btn_start);
btn_stop = (Button) findViewById(R.id.btn_stop);
btn_bind_service = (Button) findViewById(R.id.btn_bind_service);
btn_unbind_service = (Button) findViewById(R.id.btn_unbind_service);
btn_start.setOnClickListener(this);
btn_stop.setOnClickListener(this);
btn_bind_service.setOnClickListener(this);
btn_unbind_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_start:
//構建Intent對象
Intent startIntent = new Intent(this,MyService.class);
//調用startService來啓動MyService這個任務。定義於Context類中的,所以可以直接調用startService方法。
startService(startIntent);//啓動服務
break;
case R.id.btn_stop:
Intent stopIntent = new Intent(this,MyService.class);
//除了這種調用方式外,還有那些停止方法?MyService內部的任何一處位置調用StopSelf即可讓該服務停止。
stopService(stopIntent);//停止服務
break;
case R.id.btn_bind_service:
//構建Intent對象,調用bindService實現Activity和Service的綁定
Intent binIntent = new Intent(this,MyService.class);
//第一個參數是剛構建出來的Intent對象,第二個參數是前面創建的ServiceConnection實例;
//第三個參數BIND_AUTO_CREATE是活動和服務綁定後自動創建服務。會使得onCreate方法得以執行但onStartCommand不會執行。
bindService(binIntent,connection,BIND_AUTO_CREATE);//綁定服務
break;
case R.id.btn_unbind_service:
unbindService(connection);
break;
default:
break;
}
}
}
3.4.服務的生命週期
項目中任何位置調用Context的startService後的生命週期:
onCreate(如果未創建,創建時使用)->onStartCommand(每調用一次startService執行一次)->onDestroy(但調用stopService或者stopSelf時候會停止,因爲不管調用了多少次startService,系統中只會創建一個實例)
Context中使用bindService來獲取服務的持久連接:
onCreate->onBind(獲取IBinder對象的實例,活動服務開始通信)->onUnbind(解綁服務)->onDestroy(銷燬服務)
3.5.服務的更多技巧
3.5.1.使用前臺服務
服務在後臺很容易被殺死回收掉,如果希望服務一直保持運行狀態,不會因爲系統內存不足導致被回收,就考慮使用前臺服務吧。前臺服務與普通服務最大區別就是:它一直有一個正在運行的圖標在系統的狀態欄顯示,下拉狀態欄後可以看到詳細信息,類似於通知的效果。如何創建前臺服務?
public class MyService extends Service {
.....
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate exec: ");
Intent intent = new Intent(MyService.this,MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
//構建Notification對象及相應的PendingIntent方法。
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("This is content title")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round))
.setContentIntent(pi)
.build();
//並沒有使用建NotificationManager讓他顯示出來,而是使用startForeground讓MyService變成一個前臺服務。
startForeground(2,notification);
}
....
}
3.5.2.使用IntentService
服務中的代碼都是默認在主線程中的,如果直接在服務中處理耗時任務,會導致ANR。解決方法:在onStartCommad方法裏開啓子線程,處理耗時邏輯。
但是這種服務一旦啓動,會一直處於運行狀態,必須調用stopService或者stopSelf使其停止下來。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand exec: ");
new Thread(new Runnable() {
@Override
public void run() {
//處理具體邏輯
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
這種寫法不復雜,但程序員們老忘。所以Android提供了一個IntrentService解決了忘記開啓線程、忘記調用stopSelf的尷尬。
步驟一:建立MyIntentService繼承自IntentService;提供無參構造函數,必須在其內部調用父類有參構造方法;子類實現在onHandleIntent方法中,用來處理耗時任務。打印當前線程的ID。XML中記得註冊。
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService(){
super("MyIntentService");//調用有參構造函數
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the wqwidong1orker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.i(TAG, "onHandleIntent MyIntentService: Thread is:"+Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy MyIntentService: ");
}
}
步驟二:啓動Service,與之前一樣。
Log.d("MainActivity", "Main Thread is: "+Thread.currentThread().getId());
Intent intentservice = new Intent(this,MyIntentService.class);
startService(intentservice);