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