Android Service服務(wù)詳細(xì)介紹及使用總結(jié)
Android Service服務(wù)詳解
一.Service簡(jiǎn)介
Service是android 系統(tǒng)中的四大組件之一(Activity、Service、BroadcastReceiver、 ContentProvider),它跟Activity的級(jí)別差不多,但不能頁(yè)面顯示只能后臺(tái)運(yùn)行,并且可以和其他組件進(jìn)行交互。service可以在很多場(chǎng)合的應(yīng)用中使用,比如播放多媒體的時(shí)候用戶(hù)啟動(dòng)了其他Activity這個(gè)時(shí)候程序要在后臺(tái)繼續(xù)播放,比如檢測(cè)SD卡上文件的變化,再或者在后臺(tái)記錄你地理信息位置的改變等等,總之服務(wù)總是藏在后臺(tái)的,例如,一個(gè)service可能處理網(wǎng)絡(luò) 事物,播放音樂(lè),執(zhí)行文件I/O,或與一個(gè)內(nèi)容提供者交互,所有這些都在后臺(tái)進(jìn)行。
我們一定要知道的是這里Service的后臺(tái)運(yùn)行并不是子線程。Service的運(yùn)行是在主線程中進(jìn)行的,只是它沒(méi)有界面顯示而已,它的耗時(shí)操作同樣需要開(kāi)啟子線程,否者會(huì)跟Activity一樣出現(xiàn)ANR(application not response–程序沒(méi)有響應(yīng))。
我們要知道的是主線程的內(nèi)容包括UI和后臺(tái)。只要程序中的UI或后臺(tái)其中一個(gè)在跑,程序都算是在運(yùn)行狀態(tài)。
(一)Service的創(chuàng)建和注冊(cè)1.Service服務(wù)的創(chuàng)建
必須要實(shí)現(xiàn)重寫(xiě)其中的onBind方法,可以在里面做各種操作,也可以接收傳遞過(guò)來(lái)的Intent的數(shù)據(jù)做處理。
public class MyService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { System.out.println("MyService.onBind"); return null; } }
2.Service服務(wù)的注冊(cè),在AndroidManifest中注冊(cè)
<service android:name=".MyService" />
服務(wù)的注冊(cè)是四大組件中最簡(jiǎn)單的一個(gè),一般只要設(shè)置name屬性就可以了。但是如果有其他需求還是要設(shè)置其他的屬性值的。
對(duì)Service服務(wù)做好創(chuàng)建和注冊(cè)后,就可以操作服務(wù)了。
(二)Service兩種啟動(dòng)模式
Service的啟動(dòng)有兩種方式:Context.startService() 和 Context.bindService()。這里的Context是上下文的意思。
1.startService()方式啟動(dòng)時(shí)的生命周期回調(diào)方法
(1)啟動(dòng)服務(wù)startService : –>onCreate()–> onStart()
(2)停止服務(wù)stopService : –>onDestroy()
如果調(diào)用者直接退出而沒(méi)有停止Service,則Service 會(huì)一直在后臺(tái)運(yùn)行。這里的退走只是關(guān)閉了UI界面。
startService()方法啟動(dòng)服務(wù),在服務(wù)未被創(chuàng)建時(shí),系統(tǒng)會(huì)先調(diào)用服務(wù)的onCreate()方 法,接著調(diào)用onStart()方法。如果調(diào)用startService()方法前服務(wù)已經(jīng)被創(chuàng)建,多次調(diào)用 startService()方法并不會(huì)導(dǎo)致多次創(chuàng)建服務(wù),但會(huì)導(dǎo)致多次調(diào)用onStart()方法。采用 startService()方法啟動(dòng)的服務(wù),只能調(diào)用stopService()方法結(jié)束服務(wù),服務(wù)結(jié)束時(shí) 會(huì)調(diào)用生命周期的onDestroy()方法。
2.bindService()方式啟動(dòng)時(shí)的生命周期回調(diào)方法
(1)綁定bindService : –> onCreate() –> onBind()
(2)解綁unbindService: –>onUnbind()
(3)正常停止程序服務(wù)的方法是先解綁unbindService,再停止服務(wù)stopService。
(4)如果綁定后調(diào)用stopService 方法,這時(shí)是不能停止服務(wù)的,如果這時(shí)再調(diào)用解綁unbindService,程序后先解綁,后停止服務(wù)。
用bindService()方法啟動(dòng)服務(wù),在服務(wù)未被創(chuàng)建時(shí),系統(tǒng)會(huì)先調(diào)用服務(wù)的onCreate() 方法,接著調(diào)用onBind()方法。這個(gè)時(shí)候調(diào)用者和服務(wù)綁定在一起,調(diào)用者退出了,系統(tǒng)就會(huì)
先調(diào)用服務(wù)的onUnbind()方法,接著調(diào)用onDestroy()方法。如果調(diào)用 bindService()方法前服務(wù)已經(jīng)被綁定,多次調(diào)用bindService()方法并不會(huì)導(dǎo)致多次創(chuàng)建服務(wù)及綁定(也就是說(shuō) onCreate()和onBind()方法并不會(huì)被多次調(diào)用)。如果調(diào)用者希望與正在綁定的服務(wù)解除綁 定,可以調(diào)用unbindService()方法,調(diào)用該方法也會(huì)導(dǎo)致系統(tǒng)調(diào)用服務(wù)的onUnbind()->onDestroy()方法。
綁定Service方法:bindService(intent, conn, Service.BIND_AUTO_CREATE);
三個(gè)參數(shù)的說(shuō)明:
第一個(gè):Intent對(duì)象
第二個(gè):ServiceConnection對(duì)象,創(chuàng)建該對(duì)象要實(shí)現(xiàn)它的onServiceConnected()和 on ServiceDisconnected()來(lái)判斷連接成功或者是斷開(kāi)連接
第三個(gè):創(chuàng)建Service的模式,一般指定綁定的時(shí)候自動(dòng)創(chuàng)建
(三)Service的五個(gè)生命周期的回調(diào)方法
1.周期命名(1)onCreate()
(2)onStart()
(3)onBind()
(4)onUnBind()
(5)onDestroy()
2.生命周期圖解
上面展示的是沒(méi)有綁定服務(wù)和有綁定服務(wù)的生命周期的不同情況的過(guò)程。
3.關(guān)于幾個(gè)方法的說(shuō)明
(1)onCreate()說(shuō)明服務(wù)第一次被創(chuàng)建
(2)onStartComand()說(shuō)明服務(wù)開(kāi)始工作
(3)onBind()說(shuō)明服務(wù)已經(jīng)綁定
(4)onUnBind()說(shuō)明服務(wù)已經(jīng)解綁
(5)onDestroy()說(shuō)明服務(wù)已經(jīng)停止
正如上面說(shuō)的啟動(dòng)服務(wù)有兩種方式,一個(gè)是使用startService,另一個(gè)方法是使用bindService方法;使用bindService方法沒(méi)有回調(diào)到startCommand方法;
也可以先啟動(dòng)服務(wù)用startService,再綁定服務(wù)用bindService,這時(shí)的Service的回調(diào)方法的順序是:
–>onCreate()–>onStartCommand()–>onBind()
二.IntentService
普通的Service要?jiǎng)?chuàng)建一個(gè)線程去完成耗時(shí)操作,因?yàn)槠溥\(yùn)行在主線程,並且要手動(dòng)停止IntentService是繼承于Service并處理異步請(qǐng)求的一個(gè)類(lèi),在IntentService內(nèi)有一個(gè)工作線程 來(lái)處理耗時(shí)操作,啟動(dòng)IntentService的方式和啟動(dòng)傳統(tǒng)Service一樣,同時(shí),當(dāng)任務(wù)執(zhí)行完 后,IntentService會(huì)自動(dòng)停止,而不需要我們?nèi)ナ謩?dòng)控制。
另外,可以啟動(dòng)IntentService多次,而每一個(gè)耗時(shí)操作會(huì)以工作隊(duì)列的方式在IntentService的onHandleIntent回調(diào)方法中執(zhí)行,并且,每次只會(huì)執(zhí)行一個(gè)工作線程,執(zhí)行完第一個(gè)再執(zhí)行第二個(gè),以此類(lèi)推。 而且,所有請(qǐng)求都在一個(gè)單線程中,不會(huì)阻塞應(yīng)用程序的主線程(UI Thread),同一時(shí)間只處理一個(gè)請(qǐng)求。
那么,用IntentService有什么好處呢?
首先,我們省去了在Service中手動(dòng)開(kāi)線程的麻煩,
第二,當(dāng)操作完成時(shí),我們不用手動(dòng)停止Service IntentService,一個(gè)方便我們處理業(yè)務(wù)流程的類(lèi),它是一個(gè)Service,但是比Service更智能
三.查看Service的生命周期回調(diào)方法的簡(jiǎn)單示例
本示例只是用來(lái)看看Service在服務(wù)開(kāi)啟時(shí),停止時(shí),綁定時(shí),解綁時(shí),生命周期方法的回調(diào)情況加深對(duì)Service生命周期的印象。
(一)創(chuàng)建MyService類(lèi)(繼承Service)
package com.lwz.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.support.annotation.Nullable; /** * 服務(wù)的創(chuàng)建, * 測(cè)試生命周期的過(guò)程和先后 * 五個(gè)生命周期: * onCreate * onStartCommand * onDestroy * onBind * onUnBind */ public class MyService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { System.out.println("MyService.onBind"); return null; } @Override public void onCreate() { System.out.println("MyService.onCreate"); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { System.out.println("MyService.onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { System.out.println("MyService.onDestroy"); super.onDestroy(); } @Override public boolean onUnbind(Intent intent) { System.out.println("MyService.onUnbind"); return super.onUnbind(intent); } }
(二)在AndroidManifest中注冊(cè)服務(wù)
<service android:name=".MyService" />
(三)設(shè)計(jì)控制服務(wù)開(kāi)啟、停止、綁定、解綁狀態(tài)的代碼
package com.lwz.service; import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.support.v7.app.AppCompatActivity; import android.view.View; /** * 服務(wù)的創(chuàng)建和使用 * 注意這里的服務(wù)不依賴(lài)于Activity頁(yè)面,即使頁(yè)面關(guān)閉了,服務(wù)沒(méi)有主動(dòng)去停止,是不會(huì)關(guān)閉的 * Service也是在主線程中執(zhí)行任務(wù)的,但是為什么不會(huì)造成主線程阻塞?? * 因?yàn)樽龅牟皇呛臅r(shí)操作,如果做耗時(shí)操作一樣會(huì)造成ANR。。。 * 這里點(diǎn)擊綁定服務(wù)后,點(diǎn)擊停止服務(wù)按鈕是無(wú)效的,要先解綁后,才能停止服務(wù)。 * 正常情況下,從綁定狀態(tài)到解綁狀態(tài)是不會(huì)停止服務(wù)的。只是一種狀態(tài)改變而已。 * 這里點(diǎn)擊綁定服務(wù)后,點(diǎn)擊停止服務(wù)按鈕是無(wú)效的,但是解綁后,會(huì)馬上停止服務(wù)。 */ public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } //開(kāi)啟服務(wù) public void startService(View view) { //開(kāi)啟服務(wù)需要Intent對(duì)象,和Activity跳轉(zhuǎn)類(lèi)似 startService(new Intent(this, MyService.class)); } //停止服務(wù) public void stopService(View view) { //停止服務(wù)的方法 stopService(new Intent(this, MyService.class)); } //綁定服務(wù) public void bindService(View view) { //綁定服務(wù) bindService(new Intent(this, MyService.class), conn, flags); } //解綁服務(wù) public void unBindService(View view) { //防止在沒(méi)有綁定的情況下,去解除綁定,拋出異常 try { //解除綁定 unbindService(conn); } catch (Exception e) { System.out.println("MainActivity.unBindService" + e); } } //服務(wù)綁定的連接對(duì)象 private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { } @Override public void onServiceDisconnected(ComponentName name) { } }; //服務(wù)綁定的標(biāo)識(shí) //BIND_AUTO_CREATE 綁定的同時(shí),啟動(dòng)Service private int flags = Service.BIND_AUTO_CREATE; }
上面是使用四個(gè)按鈕來(lái)實(shí)現(xiàn)服務(wù)的幾種狀態(tài)的改變。
(四)布局文件的設(shè)計(jì)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="Service" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="startService" android:text="啟動(dòng)服務(wù)" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="stopService" android:text="停止服務(wù)" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="bindService" android:text="綁定服務(wù)" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="unBindService" android:text="解綁服務(wù)" /> </LinearLayout>
上面布局文件代碼比較簡(jiǎn)單的,只是用四個(gè)按鈕搞定!
程序運(yùn)行后顯示的界面:
點(diǎn)擊“啟動(dòng)服務(wù)”按鈕后的Log信息:
這時(shí)有兩個(gè)回調(diào)方法執(zhí)行。onCreate和onStartCommand.
點(diǎn)擊“停止服務(wù)”按鈕后的Log信息:
執(zhí)行了一個(gè)回調(diào)方法:onDestroy;這時(shí)服務(wù)已經(jīng)停止,相當(dāng)于程序剛運(yùn)行的狀態(tài)!
點(diǎn)擊“綁定服務(wù)”按鈕后的Log信息:
這里執(zhí)行了兩個(gè)回調(diào)方法,先啟動(dòng)服務(wù),再綁定服務(wù)!
在綁定服務(wù)的情況下是不能停止服務(wù)的,要解綁服務(wù)才能停止服務(wù)。
在程序的服務(wù)啟動(dòng)/綁定了的情況下,再點(diǎn)擊啟動(dòng)服務(wù),只會(huì)回調(diào)onStartCommand方法,也就是說(shuō)一個(gè)服務(wù)在一個(gè)生命周期內(nèi)只會(huì)回調(diào)一次onCreate方法。
點(diǎn)擊“解綁服務(wù)”按鈕后顯示的Log信息:
執(zhí)行了兩個(gè)回調(diào)方法:onUnBind和onDestroy
如果是用服務(wù)做簡(jiǎn)單 的事情,使用第一種方法來(lái)啟動(dòng)服務(wù)就可以了,但是如果需要涉及到比較復(fù)雜的數(shù)據(jù)處理和操作就用綁定服務(wù)的方法來(lái)啟動(dòng)服務(wù)。
四.IntentService的使用示例
程序設(shè)計(jì):查找手機(jī)SD卡中的所有圖片顯示在UI界面上。
//(一)布局文件activity_main.xml文件實(shí)際 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/main_lv" android:layout_width="match_parent" android:layout_height="match_parent"></ListView> </RelativeLayout>
非常簡(jiǎn)單的布局設(shè)計(jì),使用List View來(lái)顯示所有的圖片
(二)遍歷文件的工具類(lèi)的設(shè)計(jì)
package com.lwz.intentservice; import android.os.Environment; import android.os.StatFs; import java.io.File; import java.util.ArrayList; import java.util.List; /** * SD卡的路徑:Environment.getExternalStorageDirectory() */ public class FileUtils { /** * 獲得指定目錄下的所有的圖片 */ public static final ArrayList<File> getAllPicture(File dir) { ArrayList<File> files = getAllFile(dir); ArrayList<File> imgList = new ArrayList<>(); for (File file : files) { if (file.getName().endsWith(".png") || file.getName().endsWith(".jpg")) imgList.add(file); } return imgList; } /** * 遞歸遍歷文件夾的方法 */ public static final void getFileFromDir(File dir, List<File> fileList) { File[] files = dir.listFiles(); if (files == null) return; for (File file : files) { if (file.isDirectory()) getFileFromDir(file, fileList); fileList.add(file); } } /** * 獲得根目錄下的所有圖片 */ public static final ArrayList<File> getAllPicture() { return getAllPicture(Environment.getExternalStorageDirectory()); } }
(三)簡(jiǎn)化BaseAdapter的一個(gè)工具類(lèi)
package com.lwz.intentservice; import android.content.Context; import android.widget.BaseAdapter; import java.util.ArrayList; import java.util.List; /** * 這是一個(gè)簡(jiǎn)化BaseAdapter適配器的工具類(lèi) * 這是使用的是定義一個(gè)泛型T,使用時(shí)傳入什么數(shù)據(jù),T就是什么數(shù)據(jù) * 實(shí)際設(shè)計(jì)中除了getVIew方法外,其他的方法基本是差不多的 * 所以繼承這個(gè)工具類(lèi)后只要重寫(xiě)getView方法,就可以使用BaseAdapter了 */ public abstract class ListItemAdapter<T> extends BaseAdapter { List<T> list = new ArrayList<>(); Context context; ListItemAdapter(Context context, List<T> list) { this.context = context; this.list = list; } ListItemAdapter(Context context, T[] list) { this.context = context; for (T t : list) { this.list.add(t); } } @Override public int getCount() { return list == null ? 0 : list.size(); } @Override public T getItem(int position) { return list == null ? null : list.get(position); } @Override public long getItemId(int position) { return position; } }
(四)MyIntentService的創(chuàng)建
package com.lwz.intentservice; import android.app.IntentService; import android.content.Intent; import android.os.Message; import android.util.Log; import java.io.File; import java.util.ArrayList; /** * IntentService的使用 * IntentService是Service的子類(lèi),也需要在xml中注冊(cè) * 它有自定義的子線程的方法 * 這里主要需要解決的問(wèn)題是資源文件得到后怎么把數(shù)據(jù)傳遞給UI線程的Activity */ public class MyIntentService extends IntentService { /** * 通過(guò)構(gòu)造方法,傳入子線程的名字 * 但是這里必須要?jiǎng)?chuàng)建一個(gè)無(wú)參的構(gòu)造方法 */ public MyIntentService() { super("myService"); } /** * 這是在子線程中的執(zhí)行操作 */ @Override protected void onHandleIntent(Intent intent) { Log.e("TAG", "子線程開(kāi)始工作"); //遍歷文件夾獲取圖片 ArrayList<File> list = FileUtils.getAllPicture(); //使用handler發(fā)送信息 Message msg = Message.obtain(); //這里給handler對(duì)象傳遞一個(gè)對(duì)象 msg.obj = list; //發(fā)送廣播來(lái)傳遞數(shù)據(jù) Intent intent1 = new Intent("filefinish"); intent1.putExtra("file", list); sendBroadcast(intent1); } @Override public void onCreate() { super.onCreate(); Log.e("TAG", "onCreate"); } @Override public void onDestroy() { super.onDestroy(); Log.e("TAG", "onDestroy"); } }
這里的Intent Service的注冊(cè)和Intent的注冊(cè)是一樣的。
(五)MainActivity中的代碼設(shè)計(jì)
package com.lwz.intentservice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ListView; import java.io.File; import java.util.ArrayList; import java.util.List; /** * 這里使用服務(wù)來(lái)IntentService來(lái)遍歷文件夾 * 在程序創(chuàng)建的使用就要啟動(dòng)服務(wù) * 在頁(yè)面銷(xiāo)毀的時(shí)候就停止服務(wù) 但是Service執(zhí)行完任務(wù)后還有傳遞數(shù)據(jù)給MainActivity 在MainActivity中才能進(jìn)行UI界面的更新 這就涉及到Service和Activity的數(shù)據(jù)傳遞問(wèn)題了 這里使用的是用廣播來(lái)傳遞數(shù)據(jù) */ public class MainActivity extends AppCompatActivity { //定義布局內(nèi)的控件 ListView listView; //定義適配器的數(shù)據(jù)的集合 //一定要static??? static ArrayList<File> fileList; static MyBaseAdapter adapter; MyBroadcastReceiver mbcr; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mbcr = new MyBroadcastReceiver(); //動(dòng)態(tài)注冊(cè)一個(gè)廣播 IntentFilter filter = new IntentFilter(); filter.addAction("filefinish"); registerReceiver(mbcr, filter);// 注冊(cè) //創(chuàng)建適配器的對(duì)象 adapter = new MyBaseAdapter(this, fileList); //實(shí)例化布局內(nèi)的控件 listView = (ListView) findViewById(R.id.main_lv); //給listView設(shè)置適配器 listView.setAdapter(adapter); //啟動(dòng)服務(wù) startService(new Intent(this, MyIntentService.class)); } //創(chuàng)建適配器的類(lèi) class MyBaseAdapter extends ListItemAdapter<File> { MyBaseAdapter(Context context, List<File> list) { super(context, list); } @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView image = null; if (convertView == null) { image = new ImageView(getBaseContext()); convertView = image; } else { image = (ImageView) convertView; } //設(shè)置圖片資源和屬性 image.setImageURI(Uri.fromFile(fileList.get(position))); image.setScaleType(ImageView.ScaleType.FIT_XY); image.setAdjustViewBounds(true); return image; } } //停止服務(wù) public void stop() { stopService(new Intent(MainActivity.this, MyIntentService.class)); } @Override protected void onDestroy() { super.onDestroy(); //即使之前停止了服務(wù),再次停止服務(wù)也是不會(huì)報(bào)錯(cuò)的 stop(); //解除廣播 unregisterReceiver(mbcr); } //動(dòng)態(tài)創(chuàng)建廣播接收者 class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { //對(duì)接收到的廣播進(jìn)行處理,intent里面包含數(shù)據(jù) fileList = (ArrayList<File>) intent.getSerializableExtra("file"); //刷新適配器 adapter.notifyDataSetChanged(); //停止服務(wù),它的子線程也會(huì)停止 stop(); } } }
程序運(yùn)行前還記得加上SD卡的訪問(wèn)權(quán)限;
上面程序功能還是有點(diǎn)問(wèn)題!遍歷完文件后。頁(yè)面沒(méi)有馬上更新?退出程序再進(jìn)來(lái),頁(yè)面上馬上顯示SD卡的圖片。
一般的說(shuō)遍歷文件夾也不算是耗時(shí)操作,這里只是簡(jiǎn)單示范。
一般的耗時(shí)操作是從網(wǎng)絡(luò)下載數(shù)據(jù),或本地移動(dòng)大文件等等。
五.同一個(gè)程序中Service和Activity通信的一些方式,這里展示主要代碼。
這里組件不要忘記在AndroidManifest中注冊(cè)
(一)使用Intent來(lái)傳遞數(shù)據(jù)1.MainActivity中的代碼,發(fā)送數(shù)據(jù)
Intent intent = new Intent(this, MyService.class);
intent.putExtra("msg", "activity向service傳遞一個(gè)hello service");
startService(intent);
2.MyService中的代碼,接收數(shù)據(jù)
這里要使用onStartCommand的方法來(lái)接收Intent的數(shù)據(jù),如果上面使用的是bind的方法來(lái)啟動(dòng)服務(wù),這里可以在onBind方法中接收數(shù)據(jù)。
@Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e("onStartCommand", intent.getStringExtra(msg)); return super.onStartCommand(intent, flags, startId); }
(二)使用單例模式來(lái)傳遞數(shù)據(jù)
這個(gè)方法算是有點(diǎn)麻煩的吧!這里要在MyService類(lèi)中先做好單例,然后在Activity中調(diào)用MyService對(duì)象的方法
1.MyService中的代碼
//定義一個(gè)靜態(tài)的類(lèi)變量,單例的使用準(zhǔn)備 private static MyService instance; //靜態(tài)方法,返回的是一個(gè)本類(lèi)對(duì)象 //為了能讓另一邊的類(lèi)調(diào)用Myservice的方法 public static MyService getInstance() { return instance; } @Override public void onCreate() { super.onCreate(); //單例模式變量賦值 instance = this; } public void print(String msg) { Log.e("service", msg); }
其中print方法是在Activity中調(diào)用的,可以達(dá)到傳送數(shù)據(jù)給MyService,但是這里要先啟動(dòng)過(guò)服務(wù)后才能使用單例,因?yàn)檫@里是在MyService的onCreate方法中把對(duì)象賦值給instance,之后才能實(shí)現(xiàn)單例。
2.MainActivity中的代碼:
/** * 單例模式傳參 *MyService這里通過(guò)一個(gè)靜態(tài)方法,來(lái)獲得MyService的對(duì)象 這里通過(guò)MyService.getInstance()方法來(lái)獲得MyService對(duì)象 */ //必須保證Myservice對(duì)象不能為null //靜態(tài)的變量,最后釋放(不用的時(shí)候,手動(dòng)將static變量=null) if (MyService.getInstance() != null) { MyService.getInstance().print("使用單例從activity中調(diào)用service的方法"); }
(三)廣播傳參傳數(shù)據(jù)
弄兩個(gè)廣播接收者相互傳數(shù)據(jù)。
這里要在MyService和MyService中分別動(dòng)態(tài)的創(chuàng)建廣播接收者和動(dòng)態(tài)注冊(cè)廣播接收者,然后在MainActivity中發(fā)送廣播,在MyService中接收到廣播傳來(lái)遞數(shù)據(jù)后,在發(fā)送廣播,讓MainActivity接收廣播數(shù)據(jù)!
1.MyService中的代碼:
@Override public void onCreate() { super.onCreate(); //動(dòng)態(tài)注冊(cè)廣播接收者,要定義好接收的action屬性值 IntentFilter filter = new IntentFilter("service"); registerReceiver(serviceReceiver, filter); } //定義一個(gè)廣播接收者BroadcastReceiver BroadcastReceiver serviceReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.e("service", "接收到了activity發(fā)送的廣播:" + intent.getStringExtra("msg")); //發(fā)送廣播給MainActivity sendBroadcast(new Intent("activity").putExtra("msg", "發(fā)送給activity的消息")); } };
2.MainActivity中的代碼:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //注冊(cè)本類(lèi)內(nèi)的廣播,定義好action的屬性值 IntentFilter filter = new IntentFilter("activity"); registerReceiver(activityReceiver, filter); } /** * 通過(guò)廣播來(lái)傳遞數(shù)據(jù) */ public void sendBroadcast(View view) { //指明action屬性值 Intent intent = new Intent("service"); intent.putExtra("msg", "activity向廣播傳遞一個(gè)hello broadcast"); sendBroadcast(intent); } //定義一個(gè)內(nèi)部類(lèi)的廣播接收者,用于接收MyService傳遞過(guò)來(lái)的數(shù)據(jù) BroadcastReceiver activityReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.e("activity", intent.getStringExtra("msg")); } };
(四)MyService實(shí)例調(diào)用方法
這個(gè)方法是最最麻煩的方法了!涉及到一個(gè)Binder類(lèi)的使用!這里要被調(diào)用的方法其實(shí)不是MyService中的方法,而是里面的內(nèi)部接口的抽象方法,需要在MainActivity中去實(shí)現(xiàn)這個(gè)方法!但是,實(shí)際這個(gè)方法實(shí)在MyService中執(zhí)行的。
1.MyService中的代碼:
//定義一個(gè)接口 interface Callback { //定義兩個(gè)要實(shí)現(xiàn)的方法 void call(); void start(); } //定義一個(gè)接口對(duì)象 Callback callback; /** * 創(chuàng)建Binder類(lèi),很多很多的Service就是通過(guò)Binder機(jī)制來(lái)和客戶(hù)端通訊交互的。 */ class Mybinder extends Binder { public MyService getService() { return MyService.this; } //設(shè)置回調(diào)方法 public void setCallback(Callback callback) { MyService.this.callback = callback; } } //定義一個(gè)模擬開(kāi)始音樂(lè)播放的方法 //需要重寫(xiě)start里面的方法來(lái)開(kāi)始播放音樂(lè) public void startMusic() { //播放 Toast.makeText(this, "音樂(lè)開(kāi)始播放", Toast.LENGTH_SHORT).show(); callback.start(); } @Nullable @Override public IBinder onBind(Intent intent) { //要傳遞一個(gè)MyBinder對(duì)象給MainActivity return new MyBinder(); } //定義一個(gè)模擬開(kāi)始音樂(lè)播放的方法 //需要重寫(xiě)start里面的方法來(lái)開(kāi)始播放音樂(lè) public void startMusic() { //播放 Toast.makeText(this, "音樂(lè)開(kāi)始播放", Toast.LENGTH_SHORT).show(); //在MainActivity中實(shí)例化callback對(duì)象 callback.start(); }
上面的代碼中要開(kāi)始播放音樂(lè)要調(diào)用startMusic方法,并且要實(shí)例化里面的callback對(duì)象,而要實(shí)例化callback對(duì)象必須要調(diào)用內(nèi)部類(lèi)Mybinder的set Callback方法,而實(shí)現(xiàn)這個(gè)方法又必須實(shí)現(xiàn)這個(gè)接口的方法!
2.MainActivity中的代碼:
/** * 綁定服務(wù)Service 調(diào)用MyService的方法來(lái)調(diào)用內(nèi)部類(lèi)的接口方法 */ //定義一個(gè)MyService對(duì)象 MyService myService; public void bindService(View view) { bindService(new Intent(this, MyService.class), conn, BIND_AUTO_CREATE); myService.startMusic(); } //創(chuàng)建ServiceConnection對(duì)象 ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { //連接服務(wù)后,操作。。。 //獲取IBinder的bind對(duì)象,從MyService的onBinder中傳遞過(guò)來(lái)的 MyService.Mybinder bind = (MyService.Mybinder) service; //通過(guò)bind對(duì)象獲取Service對(duì)象 myService = bind.getService(); //設(shè)置監(jiān)聽(tīng)事件的回調(diào)方法,并實(shí)現(xiàn)里面的兩個(gè)方法 //這里的回調(diào)方法不是MyService中的,而是內(nèi)部類(lèi)Mybinder中的 bind.setCallback(new MyService.Callback() { @Override public void call() { Log.e("activity", "Service回調(diào)Activity"); } @Override public void start() { //比如在后臺(tái)播放音樂(lè);開(kāi)始播放音樂(lè) Log.e("action", "正在播放音樂(lè)"); //關(guān)閉頁(yè)面 finish(); } });
上面的關(guān)系確實(shí)是有點(diǎn)亂,我發(fā)現(xiàn)我有些注解還是有點(diǎn)問(wèn)題的?。?nbsp;
上面就是Service中的個(gè)方面的總結(jié)。
Service還可以用來(lái)做進(jìn)程間的數(shù)據(jù)傳遞,這里就涉及到AIDL(Android Interface Definition Language,安卓接口定義語(yǔ)言)進(jìn)程通信。這個(gè)相對(duì)來(lái)說(shuō)比較復(fù)雜,另作總結(jié)!
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
- android?Service基礎(chǔ)(啟動(dòng)服務(wù)與綁定服務(wù))
- Android服務(wù)Service教程
- Android8.0適配前臺(tái)定位服務(wù)service的示例代碼
- 淺談Android Service服務(wù)的高級(jí)技巧
- 說(shuō)說(shuō)在Android如何使用服務(wù)(Service)的方法
- Android實(shí)現(xiàn)Service在前臺(tái)運(yùn)行服務(wù)
- Android 判斷某個(gè)服務(wù)(service)是否運(yùn)行
- Android Service服務(wù)不被停止詳解及實(shí)現(xiàn)
- Android四大組件之Service服務(wù)詳細(xì)講解
相關(guān)文章
Android在多種設(shè)計(jì)下實(shí)現(xiàn)懶加載機(jī)制的方法
這篇文章主要介紹了Android在多種設(shè)計(jì)下實(shí)現(xiàn)懶加載機(jī)制的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06Android自定義帶增長(zhǎng)動(dòng)畫(huà)和點(diǎn)擊彈窗提示效果的柱狀圖DEMO
這篇文章主要介紹了Android自定義帶增長(zhǎng)動(dòng)畫(huà)和點(diǎn)擊彈窗提示效果的柱狀圖的相關(guān)資料,非常不錯(cuò)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11Android開(kāi)發(fā)中Flutter組件實(shí)用技巧
這篇文章主要為大家介紹了Android開(kāi)發(fā)中Flutter組件實(shí)用技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05Android開(kāi)發(fā)之imageView圖片按比例縮放的實(shí)現(xiàn)方法
這篇文章主要介紹了Android開(kāi)發(fā)之imageView圖片按比例縮放的實(shí)現(xiàn)方法,較為詳細(xì)的分析了Android中ImageView控件的scaleType屬性控制圖片縮放的具體用法,需要的朋友可以參考下2016-01-01Android利用CountDownTimer實(shí)現(xiàn)點(diǎn)擊獲取驗(yàn)證碼倒計(jì)時(shí)效果
這篇文章主要為大家詳細(xì)介紹了Android利用CountDownTimer實(shí)現(xiàn)點(diǎn)擊獲取驗(yàn)證碼倒計(jì)時(shí)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03Android Recyclerview實(shí)現(xiàn)水平分頁(yè)GridView效果示例
本篇文章主要介紹了Android Recyclerview實(shí)現(xiàn)水平分頁(yè)GridView效果示例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08Kotlin實(shí)現(xiàn)Android系統(tǒng)懸浮窗詳解
大家好,本篇文章主要講的是Kotlin實(shí)現(xiàn)Android系統(tǒng)懸浮窗詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12Android 微信6.1 tab欄圖標(biāo)和字體顏色漸變的實(shí)現(xiàn)
本文主要對(duì)微信6.1 tab 欄顏色漸變效果的實(shí)現(xiàn)全過(guò)程進(jìn)行分析介紹,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2016-12-12