Android組件之服務(wù)的詳解
一、服務(wù)的概念
1、服務(wù)是實(shí)現(xiàn)程序后臺(tái)運(yùn)行的解決方案,主要執(zhí)行那些不需要和用戶交互而且還要求長期運(yùn)行的任務(wù)
2、服務(wù)是依賴于創(chuàng)建服務(wù)時(shí)所在的應(yīng)用程序進(jìn)程而存在的,而不是運(yùn)行在一個(gè)獨(dú)立的進(jìn)程當(dāng)中
3、服務(wù)的后臺(tái)≠創(chuàng)建子線程,需要在服務(wù)的內(nèi)部手動(dòng)創(chuàng)建子線程,并且在這里執(zhí)行具體的任務(wù),否則可能會(huì)出現(xiàn)主線程被堵塞的情況
二、Android的多線程編程
2.1 線程的基本用法
- 線程聲明方式一:
//定義方式 class myThread extends Thread{ @Override public void run(){ //具體的處理邏輯 } } //啟動(dòng)方式 new myThread().start();
- 線程聲明方式二:
//定義方式 class myThread implements Runnable{ @Override public void run(){ //具體的處理邏輯 } } //啟動(dòng)方式 myThread my = new myThread(); new Thread(my).start();
- 線程聲明方式三:
new Thread(new Runnable() { @Override public void run() { } }).start();
2.2 在子線程中更新UI
更新方式一
利用runOnUiThread()方法,該方法可以要執(zhí)行的操作回調(diào)到主線程中進(jìn)行操作,使用方法:
以更新TextView為例:
@Override protected void onCreate(Bundle savedInstanceState) { ..... change.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { new Thread(new Runnable() { @Override public void run() { changeUI("next to meet you"); } }).start(); } }); } public void changeUI(String date){ runOnUiThread(new Runnable() { @Override public void run() { text.setText(date); } }); }
1、我們?cè)谧泳€程中調(diào)用changeUI()方法,并將更改后的數(shù)據(jù)傳過去
2、在changeUI()方法中,調(diào)用runOnUiThread()方法將進(jìn)程調(diào)回到主線程中,然后調(diào)用setText()方法進(jìn)行更新UI操作
具體使用方法,博客:理解 Activity.runOnUiThread
更新方式二
利用Android自帶的異步消息處理機(jī)制
public class MainActivity extends AppCompatActivity { public static final int UPDATE_TEXT = 1; private TextView text; private Handler handler = new Handler(){ public void handleMessage(Message msg){ switch (msg.what){ case UPDATE_TEXT: //對(duì)UI進(jìn)行操作 text.setText("Nice to meet you"); break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { .... change.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { new Thread(new Runnable() { @Override public void run() { Message message = new Message(); message.what = UPDATE_TEXT; handler.sendMessage(message); } }).start(); } }); } }
1、首先定義一個(gè)字段UPDATE_TEXT表示更新的具體動(dòng)作
2、創(chuàng)建Handler的對(duì)象,并重寫父類的handleMessage()方法,在該方法中對(duì)Message進(jìn)行處理
3、判斷Message的what的值,進(jìn)行相應(yīng)的UI操作
4、回到子線程,首先獲取一個(gè)Message 的對(duì)象,對(duì)what字段賦值
5、調(diào)用Handler對(duì)象的sendMessage()將這個(gè)Message 發(fā)送出去
6、在Handler收到這個(gè)消息以后就會(huì)調(diào)用handleMessage()對(duì)消息進(jìn)行處理,且現(xiàn)在已經(jīng)是在主線程中處理不會(huì)出現(xiàn)問題
2.3 解析異步消息處理機(jī)制
1、處理機(jī)制組成:Message、Handler、MessageQueue、Looper
2、Message:
- 1)作用:在線程之間傳遞信息
- 2)可以在內(nèi)部攜帶少量信息,利用其字段,如what,arg1,rg2,obj字段存儲(chǔ),用于在不同線程之間交換數(shù)據(jù)
3、Handler:
- 1)作用:主要用于發(fā)送和處理消息
- 2)發(fā)送消息使用Handler的sendMessage()方法
- 3)發(fā)出的消息經(jīng)過處理以后,傳遞到Handler的handleMessage()方法中
4、MessageQueue:
- 1)作用:主要用于存放所有通過Handler發(fā)送的消息。每個(gè)線程中只有一個(gè)MessageQueue對(duì)象
5、Looper:
- 1)作用:每個(gè)線程中MessageQueue的管家
- 2)在調(diào)用其loop()方法后,進(jìn)入無限循環(huán),在循環(huán)中每發(fā)現(xiàn)MessageQueue中存在一條消息就將其取出,傳遞到Handler的handleMessage()方法中進(jìn)行處理
- 3)每個(gè)線程中只有一個(gè)Looper對(duì)象
6、整個(gè)異步流程:
- 1)首先主線程中創(chuàng)建一個(gè)Handler的對(duì)象,并重寫父類的handleMessage()方法
- 2)在子線程要進(jìn)行UI操作時(shí),創(chuàng)建一個(gè)Message對(duì)象,通過Handler的sendMessage()方法出去
- 3)這條消息會(huì)被添加到MessageQueue隊(duì)列中等待
- 4)Looper從MessageQueue中取出一條消息,傳遞到Handler的handleMessage()方法中進(jìn)行處理
2.4 使用AsyncTask
1、AsyncTask的實(shí)現(xiàn)原理是基于異步消息處理機(jī)制的
2、AsyncTask是一個(gè)抽象類,在子類繼承這個(gè)類時(shí)指定3個(gè)泛型參數(shù):
- Params:在執(zhí)行AsyncTask時(shí)需傳入的參數(shù),可用于在后臺(tái)任務(wù)中使用
- Progress:后臺(tái)任務(wù)執(zhí)行時(shí),若需要在界面上顯示當(dāng)前進(jìn)度,這里就指定的泛型作為進(jìn)度單位
- Result:當(dāng)任務(wù)執(zhí)行完畢,若需要對(duì)結(jié)果進(jìn)行返回,則使用這里指定的泛型作為返回值類型
- 如:
public class DownloadTask extends AsyncTask<Void,Integer,Boolean> { .... }
第一個(gè)泛型參數(shù)指定為Void,表示在執(zhí)行AsyncTask時(shí)不需要傳入?yún)?shù)給后臺(tái)任務(wù)
第二個(gè)參數(shù)指定為Integer,表示使用整型數(shù)據(jù)來作為進(jìn)度顯示單位
第三個(gè)參數(shù)指定為Boolean,表示用boolean類型數(shù)據(jù)來反饋執(zhí)行結(jié)果
3、在繼承后,還需重寫如下方法
- onPreExecute() :在后臺(tái)任務(wù)開始執(zhí)行之前調(diào)用,用于進(jìn)行一些界面上的初始化操作,如顯示一個(gè)進(jìn)度條對(duì)話框等
- doInBackground(Params… ) :該方法中所有的代碼都會(huì)在子線程中運(yùn)行。后臺(tái)任務(wù)一旦完成通過return語句將任務(wù)的執(zhí)行結(jié)果返回,若AsyncTask第三個(gè)參數(shù)指定為Void,則可以不返回任務(wù)執(zhí)行結(jié)果。若需要更新UI元素,則調(diào)用publishProgress(Progress…)方法來完成
- onProgressUpdate(Progress… ):當(dāng)在后臺(tái)任務(wù)中調(diào)用publishProgress(Progress…)方法,該方法就會(huì)被調(diào)用,該方法所攜帶的參數(shù)就是在后臺(tái)任務(wù)中傳遞過來的,在這個(gè)方法中利用參數(shù)中的數(shù)值可以對(duì)UI進(jìn)行操作
- onPostExecute(Result) :后臺(tái)任務(wù)執(zhí)行完畢并返回return語句進(jìn)行返回時(shí),這個(gè)方法就會(huì)被調(diào)用,返回的數(shù)據(jù)作為參數(shù)會(huì)傳遞到此方法中,可以利用返回的數(shù)據(jù)來進(jìn)行一些UI操作,如提醒任務(wù)執(zhí)行的結(jié)果,或關(guān)閉進(jìn)度條對(duì)話框等具體框架:
public class DownloadTask extends AsyncTask<Void,Integer,Boolean> { @Override protected void onPreExecute() { progressDialog.show();//顯示進(jìn)度對(duì)話框 } @Override protected Boolean doInBackground(Void... voids) {//在該方法里執(zhí)行下載任務(wù) try{ while(true){ int percent = doDownload();//虛構(gòu)一個(gè)方法,返回一個(gè)值表示下載進(jìn)度 publishProgress(percent);//調(diào)用publishProgress方法,并將進(jìn)度數(shù)值傳進(jìn)去 if(percent >= 100) break; } }catch (Exception e){ e.printStackTrace(); } return true; } @Override protected void onProgressUpdate(Integer... values) {//該方法所帶的參數(shù)就是publishProgress()方法的進(jìn)度數(shù)值 progressDialog.setMessage("Downloaded" + values[0] + "%"); } @Override protected void onPostExecute(Boolean aBoolean) {//后臺(tái)任務(wù)完成后該方法就會(huì)被調(diào)用 progressDialog.dismiss();//關(guān)閉進(jìn)度對(duì)話框 //提示下載結(jié)果 if (aBoolean){ Toast.makeText(context,"Succeeded",Toast.LENGTH_SHORT).show(); }else { Toast.makeText(context,"Failed",Toast.LENGTH_SHORT).show(); } } }
4、啟動(dòng)方法:
new DownloadTask().execute();
三、服務(wù)的基本用法
3.1 首先定義一個(gè)服務(wù)
在上述界面中:Exported表示是否允許除了當(dāng)前程序之外的其他程序訪問這個(gè)服務(wù);Enabled表示是否啟動(dòng)這個(gè)服務(wù)
3.2 MyService類里重寫幾個(gè)方法
public class MyService extends Service { public MyService() { } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } @Override public void onCreate() { super.onCreate(); }//服務(wù)被創(chuàng)建時(shí)調(diào)用 @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); }//每次服務(wù)啟動(dòng)時(shí)調(diào)用,若需要服務(wù)一旦啟動(dòng)就立刻執(zhí)行某個(gè)動(dòng)作,就可以將邏輯卸載這個(gè)方法里面 @Override public void onDestroy() { super.onDestroy(); }//服務(wù)被銷毀時(shí)調(diào)用 }
1、onCreate()方法在服務(wù)被創(chuàng)建時(shí)調(diào)用
2、onStartCommand()方法在每次服務(wù)啟動(dòng)時(shí)調(diào)用,若需要服務(wù)一旦啟動(dòng)就立刻執(zhí)行某個(gè)動(dòng)作,就可以將邏輯卸載這個(gè)方法里面
3、onDestroy()方法在服務(wù)被銷毀時(shí)調(diào)用
4、onCreate()方法在服務(wù)第一次創(chuàng)建時(shí)調(diào)用,onStartCommand()方法是在每次啟動(dòng)服務(wù)時(shí)調(diào)用(若在一次服務(wù)中調(diào)用了onDestroy()方法,那么這個(gè)服務(wù)就無了,再點(diǎn)開始按鈕就會(huì)調(diào)用onCreate()方法)
3.3 在注冊(cè)文件中完成對(duì)服務(wù)的注冊(cè)
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testservices" > <application ..... <service android:name=".MyService" android:enabled="true" android:exported="true" > </service> ..... </application> </manifest>
3.4 啟動(dòng)和停止服務(wù)
首先要?jiǎng)?chuàng)建一個(gè)活動(dòng)和兩個(gè)用于啟動(dòng)服務(wù)的按鈕,創(chuàng)建完成后通過Intent實(shí)現(xiàn)活動(dòng)和服務(wù)之間的“橋梁”搭建
Button start = findViewById(R.id.start_service); Button stop = findViewById(R.id.stop_service); start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent startIntent = new Intent(MainActivity2.this,MyService.class); startService(startIntent);//啟動(dòng)服務(wù) } }); stop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent stopIntent = new Intent(MainActivity2.this,MyService.class); stopService(stopIntent);//停止服務(wù) } });
關(guān)于讓服務(wù)停止,如果不點(diǎn)擊停止按鈕服務(wù)會(huì)一直處于運(yùn)行狀態(tài)。可以通過在MyService中指定的地方調(diào)用 stopSelf()
方法 實(shí)現(xiàn)服務(wù)的自動(dòng)停止
然后在MyService里定義一些日志
public class MyService extends Service { final static String TAG = "MyService"; .... @Override public void onCreate() { super.onCreate(); Log.d(TAG,"onCreate executed"); }//服務(wù)被創(chuàng)建時(shí)調(diào)用 @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG,"onStartCommand executed"); return super.onStartCommand(intent, flags, startId); }//每次服務(wù)啟動(dòng)時(shí)調(diào)用,若需要服務(wù)一旦啟動(dòng)就立刻執(zhí)行某個(gè)動(dòng)作,就可以將邏輯卸載這個(gè)方法里面 @Override public void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestroy executed"); }//服務(wù)被銷毀時(shí)調(diào)用 }
運(yùn)行結(jié)果:
3.5 活動(dòng)和服務(wù)進(jìn)行通信
以下載功能為例:在服務(wù)中提供一個(gè)下載功能,在活動(dòng)中決定何時(shí)開始下載以及隨時(shí)查看下載進(jìn)度
1、首先在MyService中創(chuàng)建一個(gè)Binder的子類內(nèi)部類,對(duì)下載功能進(jìn)行管理:
class DownloadBinder extends Binder{ public void startDownload(){ Log.d(TAG,"startDownload executed"); }//開始下載的模擬方法 public int getProgress(){ Log.d(TAG,"getProgress executed"); return 0; }//查看下載進(jìn)度的模擬方法 }
2、然后同樣地,在MyService里獲取到DownloadBinder 的實(shí)例,并且通過 onBind()方法 返回這個(gè)實(shí)例(最關(guān)鍵的一步)
private DownloadBinder binder = new DownloadBinder(); ..... @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. return binder; }
3、在活動(dòng)中定義兩個(gè)按鈕,分別用于綁定服務(wù)和服務(wù)解綁
4、在活動(dòng)中創(chuàng)建一個(gè)ServiceConnection的匿名類,并重寫onServiceConnected()、onServiceDisconnected()方法,這兩個(gè)方法會(huì)在活動(dòng)與服務(wù)成功綁定和斷開時(shí)調(diào)用
public class MainActivity2 extends AppCompatActivity { private MyService.DownloadBinder binder; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { binder = (MyService.DownloadBinder) iBinder; binder.startDownload(); binder.getProgress(); //在這里通過轉(zhuǎn)型獲取到Binder實(shí)例,通過這個(gè)實(shí)例,就可以在活動(dòng)中根據(jù)具體的場景來調(diào)用DownloadBinder中任何public的方法 } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onCreate(Bundle savedInstanceState) { ..... } }
5、在按鈕事件里完成活動(dòng)與服務(wù)之間的綁定和解綁
bind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent bindIntent = new Intent(MainActivity2.this,MyService.class); bindService(bindIntent,connection,BIND_AUTO_CREATE);//綁定服務(wù) //BIND_AUTO_CREATE是一個(gè)標(biāo)志位,該標(biāo)志位表示在活動(dòng)和服務(wù)進(jìn)行綁定后自動(dòng)創(chuàng)建服務(wù),即MyService里的onCreate()方法會(huì)得到執(zhí)行,onStartCommand()方法不會(huì)得到執(zhí)行 } }); unbind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { unbindService(connection);//解除綁定 } });
四、服務(wù)的生命周期
五、服務(wù)的更多技巧
5.1 使用前臺(tái)服務(wù)
1、前臺(tái)服務(wù)的作用:可以使服務(wù)一直保持運(yùn)行狀態(tài),而不會(huì)由于系統(tǒng)內(nèi)存不足的原因?qū)е卤换厥?br />
2、跟普通服務(wù)的區(qū)別:一直有一個(gè)正在運(yùn)行的圖標(biāo)在系統(tǒng)的狀態(tài)欄顯示,下拉狀態(tài)欄后可以看到更詳細(xì)的信息
3、具體實(shí)現(xiàn):
public class MyService extends Service { ..... @Override public void onCreate() { super.onCreate(); Log.d(TAG,"onCreate executed"); Intent intent = new Intent(this,MainActivity2.class); PendingIntent pi = PendingIntent.getActivity(this,0,intent,0); NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); Notification notification = new NotificationCompat.Builder(this,"YonC") .setContentTitle("This is content title") .setContentText("his is content text") .setWhen(System.currentTimeMillis()) .setContentIntent(pi) .build(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("YonC", "YonC",NotificationManager.IMPORTANCE_HIGH); manager.createNotificationChannel(channel); } manager.notify(1,notification); startForeground(1,notification); }//服務(wù)被創(chuàng)建時(shí)調(diào)用 ..... }
1、這里利用PendingIntent 和Notification 創(chuàng)建一個(gè)通知的形式創(chuàng)建了一個(gè)前臺(tái)活動(dòng)
2、首先通過notify()方法將該通知顯現(xiàn)出來
3、然后調(diào)用startForeground()方法讓MyService變成一個(gè)前臺(tái)服務(wù),該方法接收兩個(gè)參數(shù),第一個(gè)參數(shù)是通知的id,
類似于notify()方法的第一個(gè)參數(shù);第二個(gè)參數(shù)為構(gòu)建出的Notification對(duì)象
4、注意還需要申請(qǐng)權(quán)限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
5.2 服務(wù)中的多線程問題&IntentService
1、ANR:即Application Not Responding,直接在服務(wù)中處理一些耗時(shí)的邏輯就會(huì)出現(xiàn)此類情況
2、IntentService是服務(wù)的子類之一,在該類中的onHandleIntent()方法中的邏輯都是在子線程中執(zhí)行,因此可以將服務(wù)中一些耗時(shí)的邏輯都放在這個(gè)方法中從而避免ANR問題
3、具體實(shí)現(xiàn):
public class MyIntentService extends IntentService { public MyIntentService(){ super("MyIntentService"); }//這個(gè)無參的構(gòu)造函數(shù)和調(diào)用父類有參構(gòu)造函數(shù)是必須有的 @Override protected void onHandleIntent(@Nullable Intent intent) { //該方法里的邏輯都是在子線程中執(zhí)行的 } }
son.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(MainActivity2.this,MyIntentService.class); startService(intent); } });
在按鈕注冊(cè)事件中引用Intent “建橋”,再調(diào)用startService()方法開啟服務(wù)
<service android:name=".MyIntentService"/>
完成該服務(wù)的注冊(cè)
到此這篇關(guān)于Android組件之服務(wù)的詳解的文章就介紹到這了,更多相關(guān)Android組件服務(wù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析android res 運(yùn)行錯(cuò)誤的問題
本篇文章是對(duì)android中res運(yùn)行錯(cuò)誤的問題進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06PopupWindow自定義位置顯示的實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了PopupWindow自定義位置顯示,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10務(wù)必掌握的Android十六進(jìn)制狀態(tài)管理最佳實(shí)踐
這篇文章主要為大家介紹了務(wù)必掌握的Android十六進(jìn)制狀態(tài)管理最佳實(shí)踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09如何利用adb卸載手機(jī)預(yù)裝軟件(系統(tǒng)軟件)
對(duì)于Android手機(jī)通常有很多不必要的預(yù)置軟件,但是又無法卸載,占用桌面有很難受,所以本次使用adb工具來實(shí)現(xiàn)從電腦命令來卸載或停用軟件,下面這篇文章主要給大家介紹了關(guān)于如何利用adb卸載手機(jī)預(yù)裝軟件(系統(tǒng)軟件)的相關(guān)資料,需要的朋友可以參考下2022-09-09Android 自定義閃屏頁廣告倒計(jì)時(shí)view效果
這篇文章主要介紹了Android 自定義閃屏頁廣告倒計(jì)時(shí)view效果,需要的朋友可以參考下2017-05-05