Android組件之服務(wù)的詳解
一、服務(wù)的概念
1、服務(wù)是實(shí)現(xiàn)程序后臺運(yùn)行的解決方案,主要執(zhí)行那些不需要和用戶交互而且還要求長期運(yùn)行的任務(wù)
2、服務(wù)是依賴于創(chuàng)建服務(wù)時所在的應(yīng)用程序進(jìn)程而存在的,而不是運(yùn)行在一個獨(dú)立的進(jìn)程當(dāng)中
3、服務(wù)的后臺≠創(chuàng)建子線程,需要在服務(wù)的內(nèi)部手動創(chuàng)建子線程,并且在這里執(zhí)行具體的任務(wù),否則可能會出現(xiàn)主線程被堵塞的情況
二、Android的多線程編程
2.1 線程的基本用法
- 線程聲明方式一:
//定義方式 class myThread extends Thread{ @Override public void run(){ //具體的處理邏輯 } } //啟動方式 new myThread().start();
- 線程聲明方式二:
//定義方式 class myThread implements Runnable{ @Override public void run(){ //具體的處理邏輯 } } //啟動方式 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、我們在子線程中調(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: //對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、首先定義一個字段UPDATE_TEXT表示更新的具體動作
2、創(chuàng)建Handler的對象,并重寫父類的handleMessage()方法,在該方法中對Message進(jìn)行處理
3、判斷Message的what的值,進(jìn)行相應(yīng)的UI操作
4、回到子線程,首先獲取一個Message 的對象,對what字段賦值
5、調(diào)用Handler對象的sendMessage()將這個Message 發(fā)送出去
6、在Handler收到這個消息以后就會調(diào)用handleMessage()對消息進(jìn)行處理,且現(xiàn)在已經(jīng)是在主線程中處理不會出現(xiàn)問題
2.3 解析異步消息處理機(jī)制
1、處理機(jī)制組成:Message、Handler、MessageQueue、Looper
2、Message:
- 1)作用:在線程之間傳遞信息
- 2)可以在內(nèi)部攜帶少量信息,利用其字段,如what,arg1,rg2,obj字段存儲,用于在不同線程之間交換數(shù)據(jù)
3、Handler:
- 1)作用:主要用于發(fā)送和處理消息
- 2)發(fā)送消息使用Handler的sendMessage()方法
- 3)發(fā)出的消息經(jīng)過處理以后,傳遞到Handler的handleMessage()方法中
4、MessageQueue:
- 1)作用:主要用于存放所有通過Handler發(fā)送的消息。每個線程中只有一個MessageQueue對象
5、Looper:
- 1)作用:每個線程中MessageQueue的管家
- 2)在調(diào)用其loop()方法后,進(jìn)入無限循環(huán),在循環(huán)中每發(fā)現(xiàn)MessageQueue中存在一條消息就將其取出,傳遞到Handler的handleMessage()方法中進(jìn)行處理
- 3)每個線程中只有一個Looper對象
6、整個異步流程:
- 1)首先主線程中創(chuàng)建一個Handler的對象,并重寫父類的handleMessage()方法
- 2)在子線程要進(jìn)行UI操作時,創(chuàng)建一個Message對象,通過Handler的sendMessage()方法出去
- 3)這條消息會被添加到MessageQueue隊(duì)列中等待
- 4)Looper從MessageQueue中取出一條消息,傳遞到Handler的handleMessage()方法中進(jìn)行處理
2.4 使用AsyncTask
1、AsyncTask的實(shí)現(xiàn)原理是基于異步消息處理機(jī)制的
2、AsyncTask是一個抽象類,在子類繼承這個類時指定3個泛型參數(shù):
- Params:在執(zhí)行AsyncTask時需傳入的參數(shù),可用于在后臺任務(wù)中使用
- Progress:后臺任務(wù)執(zhí)行時,若需要在界面上顯示當(dāng)前進(jìn)度,這里就指定的泛型作為進(jìn)度單位
- Result:當(dāng)任務(wù)執(zhí)行完畢,若需要對結(jié)果進(jìn)行返回,則使用這里指定的泛型作為返回值類型
- 如:
public class DownloadTask extends AsyncTask<Void,Integer,Boolean> { .... }
第一個泛型參數(shù)指定為Void,表示在執(zhí)行AsyncTask時不需要傳入?yún)?shù)給后臺任務(wù)
第二個參數(shù)指定為Integer,表示使用整型數(shù)據(jù)來作為進(jìn)度顯示單位
第三個參數(shù)指定為Boolean,表示用boolean類型數(shù)據(jù)來反饋執(zhí)行結(jié)果
3、在繼承后,還需重寫如下方法
- onPreExecute() :在后臺任務(wù)開始執(zhí)行之前調(diào)用,用于進(jìn)行一些界面上的初始化操作,如顯示一個進(jìn)度條對話框等
- doInBackground(Params… ) :該方法中所有的代碼都會在子線程中運(yùn)行。后臺任務(wù)一旦完成通過return語句將任務(wù)的執(zhí)行結(jié)果返回,若AsyncTask第三個參數(shù)指定為Void,則可以不返回任務(wù)執(zhí)行結(jié)果。若需要更新UI元素,則調(diào)用publishProgress(Progress…)方法來完成
- onProgressUpdate(Progress… ):當(dāng)在后臺任務(wù)中調(diào)用publishProgress(Progress…)方法,該方法就會被調(diào)用,該方法所攜帶的參數(shù)就是在后臺任務(wù)中傳遞過來的,在這個方法中利用參數(shù)中的數(shù)值可以對UI進(jìn)行操作
- onPostExecute(Result) :后臺任務(wù)執(zhí)行完畢并返回return語句進(jìn)行返回時,這個方法就會被調(diào)用,返回的數(shù)據(jù)作為參數(shù)會傳遞到此方法中,可以利用返回的數(shù)據(jù)來進(jìn)行一些UI操作,如提醒任務(wù)執(zhí)行的結(jié)果,或關(guān)閉進(jìn)度條對話框等具體框架:
public class DownloadTask extends AsyncTask<Void,Integer,Boolean> { @Override protected void onPreExecute() { progressDialog.show();//顯示進(jìn)度對話框 } @Override protected Boolean doInBackground(Void... voids) {//在該方法里執(zhí)行下載任務(wù) try{ while(true){ int percent = doDownload();//虛構(gòu)一個方法,返回一個值表示下載進(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) {//后臺任務(wù)完成后該方法就會被調(diào)用 progressDialog.dismiss();//關(guān)閉進(jìn)度對話框 //提示下載結(jié)果 if (aBoolean){ Toast.makeText(context,"Succeeded",Toast.LENGTH_SHORT).show(); }else { Toast.makeText(context,"Failed",Toast.LENGTH_SHORT).show(); } } }
4、啟動方法:
new DownloadTask().execute();
三、服務(wù)的基本用法
3.1 首先定義一個服務(wù)
在上述界面中:Exported表示是否允許除了當(dāng)前程序之外的其他程序訪問這個服務(wù);Enabled表示是否啟動這個服務(wù)
3.2 MyService類里重寫幾個方法
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)建時調(diào)用 @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); }//每次服務(wù)啟動時調(diào)用,若需要服務(wù)一旦啟動就立刻執(zhí)行某個動作,就可以將邏輯卸載這個方法里面 @Override public void onDestroy() { super.onDestroy(); }//服務(wù)被銷毀時調(diào)用 }
1、onCreate()方法在服務(wù)被創(chuàng)建時調(diào)用
2、onStartCommand()方法在每次服務(wù)啟動時調(diào)用,若需要服務(wù)一旦啟動就立刻執(zhí)行某個動作,就可以將邏輯卸載這個方法里面
3、onDestroy()方法在服務(wù)被銷毀時調(diào)用
4、onCreate()方法在服務(wù)第一次創(chuàng)建時調(diào)用,onStartCommand()方法是在每次啟動服務(wù)時調(diào)用(若在一次服務(wù)中調(diào)用了onDestroy()方法,那么這個服務(wù)就無了,再點(diǎn)開始按鈕就會調(diào)用onCreate()方法)
3.3 在注冊文件中完成對服務(wù)的注冊
<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 啟動和停止服務(wù)
首先要創(chuàng)建一個活動和兩個用于啟動服務(wù)的按鈕,創(chuàng)建完成后通過Intent實(shí)現(xiàn)活動和服務(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);//啟動服務(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ù)會一直處于運(yùn)行狀態(tài)??梢酝ㄟ^在MyService中指定的地方調(diào)用 stopSelf()
方法 實(shí)現(xiàn)服務(wù)的自動停止
然后在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)建時調(diào)用 @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG,"onStartCommand executed"); return super.onStartCommand(intent, flags, startId); }//每次服務(wù)啟動時調(diào)用,若需要服務(wù)一旦啟動就立刻執(zhí)行某個動作,就可以將邏輯卸載這個方法里面 @Override public void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestroy executed"); }//服務(wù)被銷毀時調(diào)用 }
運(yùn)行結(jié)果:
3.5 活動和服務(wù)進(jìn)行通信
以下載功能為例:在服務(wù)中提供一個下載功能,在活動中決定何時開始下載以及隨時查看下載進(jìn)度
1、首先在MyService中創(chuàng)建一個Binder的子類內(nèi)部類,對下載功能進(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()方法 返回這個實(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、在活動中定義兩個按鈕,分別用于綁定服務(wù)和服務(wù)解綁
4、在活動中創(chuàng)建一個ServiceConnection的匿名類,并重寫onServiceConnected()、onServiceDisconnected()方法,這兩個方法會在活動與服務(wù)成功綁定和斷開時調(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í)例,通過這個實(shí)例,就可以在活動中根據(jù)具體的場景來調(diào)用DownloadBinder中任何public的方法 } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onCreate(Bundle savedInstanceState) { ..... } }
5、在按鈕事件里完成活動與服務(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是一個標(biāo)志位,該標(biāo)志位表示在活動和服務(wù)進(jìn)行綁定后自動創(chuàng)建服務(wù),即MyService里的onCreate()方法會得到執(zhí)行,onStartCommand()方法不會得到執(zhí)行 } }); unbind.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { unbindService(connection);//解除綁定 } });
四、服務(wù)的生命周期
五、服務(wù)的更多技巧
5.1 使用前臺服務(wù)
1、前臺服務(wù)的作用:可以使服務(wù)一直保持運(yùn)行狀態(tài),而不會由于系統(tǒng)內(nèi)存不足的原因?qū)е卤换厥?br />
2、跟普通服務(wù)的區(qū)別:一直有一個正在運(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)建時調(diào)用 ..... }
1、這里利用PendingIntent 和Notification 創(chuàng)建一個通知的形式創(chuàng)建了一個前臺活動
2、首先通過notify()方法將該通知顯現(xiàn)出來
3、然后調(diào)用startForeground()方法讓MyService變成一個前臺服務(wù),該方法接收兩個參數(shù),第一個參數(shù)是通知的id,
類似于notify()方法的第一個參數(shù);第二個參數(shù)為構(gòu)建出的Notification對象
4、注意還需要申請權(quán)限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
5.2 服務(wù)中的多線程問題&IntentService
1、ANR:即Application Not Responding,直接在服務(wù)中處理一些耗時的邏輯就會出現(xiàn)此類情況
2、IntentService是服務(wù)的子類之一,在該類中的onHandleIntent()方法中的邏輯都是在子線程中執(zhí)行,因此可以將服務(wù)中一些耗時的邏輯都放在這個方法中從而避免ANR問題
3、具體實(shí)現(xiàn):
public class MyIntentService extends IntentService { public MyIntentService(){ super("MyIntentService"); }//這個無參的構(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); } });
在按鈕注冊事件中引用Intent “建橋”,再調(diào)用startService()方法開啟服務(wù)
<service android:name=".MyIntentService"/>
完成該服務(wù)的注冊
到此這篇關(guān)于Android組件之服務(wù)的詳解的文章就介紹到這了,更多相關(guān)Android組件服務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
PopupWindow自定義位置顯示的實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了PopupWindow自定義位置顯示,具有一定的參考價值,感興趣的小伙伴們可以參考一下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)軟件)
對于Android手機(jī)通常有很多不必要的預(yù)置軟件,但是又無法卸載,占用桌面有很難受,所以本次使用adb工具來實(shí)現(xiàn)從電腦命令來卸載或停用軟件,下面這篇文章主要給大家介紹了關(guān)于如何利用adb卸載手機(jī)預(yù)裝軟件(系統(tǒng)軟件)的相關(guān)資料,需要的朋友可以參考下2022-09-09