Android Service服務(wù)詳細(xì)介紹及使用總結(jié)
Android Service服務(wù)詳解
一.Service簡介
Service是android 系統(tǒng)中的四大組件之一(Activity、Service、BroadcastReceiver、 ContentProvider),它跟Activity的級別差不多,但不能頁面顯示只能后臺(tái)運(yùn)行,并且可以和其他組件進(jìn)行交互。service可以在很多場合的應(yīng)用中使用,比如播放多媒體的時(shí)候用戶啟動(dòng)了其他Activity這個(gè)時(shí)候程序要在后臺(tái)繼續(xù)播放,比如檢測SD卡上文件的變化,再或者在后臺(tái)記錄你地理信息位置的改變等等,總之服務(wù)總是藏在后臺(tái)的,例如,一個(gè)service可能處理網(wǎng)絡(luò) 事物,播放音樂,執(zhí)行文件I/O,或與一個(gè)內(nèi)容提供者交互,所有這些都在后臺(tái)進(jìn)行。
我們一定要知道的是這里Service的后臺(tái)運(yùn)行并不是子線程。Service的運(yùn)行是在主線程中進(jìn)行的,只是它沒有界面顯示而已,它的耗時(shí)操作同樣需要開啟子線程,否者會(huì)跟Activity一樣出現(xiàn)ANR(application not response–程序沒有響應(yīng))。
我們要知道的是主線程的內(nèi)容包括UI和后臺(tái)。只要程序中的UI或后臺(tái)其中一個(gè)在跑,程序都算是在運(yùn)行狀態(tài)。
(一)Service的創(chuàng)建和注冊1.Service服務(wù)的創(chuàng)建
必須要實(shí)現(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ù)的注冊是四大組件中最簡單的一個(gè),一般只要設(shè)置name屬性就可以了。但是如果有其他需求還是要設(shè)置其他的屬性值的。
對Service服務(wù)做好創(chuàng)建和注冊后,就可以操作服務(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)用者直接退出而沒有停止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ù)及綁定(也就是說 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ù)的說明:
第一個(gè):Intent對象
第二個(gè):ServiceConnection對象,創(chuàng)建該對象要實(shí)現(xiàn)它的onServiceConnected()和 on ServiceDisconnected()來判斷連接成功或者是斷開連接
第三個(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.生命周期圖解
上面展示的是沒有綁定服務(wù)和有綁定服務(wù)的生命周期的不同情況的過程。
3.關(guān)于幾個(gè)方法的說明
(1)onCreate()說明服務(wù)第一次被創(chuàng)建
(2)onStartComand()說明服務(wù)開始工作
(3)onBind()說明服務(wù)已經(jīng)綁定
(4)onUnBind()說明服務(wù)已經(jīng)解綁
(5)onDestroy()說明服務(wù)已經(jīng)停止
正如上面說的啟動(dòng)服務(wù)有兩種方式,一個(gè)是使用startService,另一個(gè)方法是使用bindService方法;使用bindService方法沒有回調(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并處理異步請求的一個(gè)類,在IntentService內(nèi)有一個(gè)工作線程 來處理耗時(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è),以此類推。 而且,所有請求都在一個(gè)單線程中,不會(huì)阻塞應(yīng)用程序的主線程(UI Thread),同一時(shí)間只處理一個(gè)請求。
那么,用IntentService有什么好處呢?
首先,我們省去了在Service中手動(dòng)開線程的麻煩,
第二,當(dāng)操作完成時(shí),我們不用手動(dòng)停止Service IntentService,一個(gè)方便我們處理業(yè)務(wù)流程的類,它是一個(gè)Service,但是比Service更智能
三.查看Service的生命周期回調(diào)方法的簡單示例
本示例只是用來看看Service在服務(wù)開啟時(shí),停止時(shí),綁定時(shí),解綁時(shí),生命周期方法的回調(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)建,
* 測試生命周期的過程和先后
* 五個(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中注冊服務(wù)
<service android:name=".MyService" />
(三)設(shè)計(jì)控制服務(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ù)沒有主動(dòng)去停止,是不會(huì)關(guān)閉的
* Service也是在主線程中執(zhí)行任務(wù)的,但是為什么不會(huì)造成主線程阻塞??
* 因?yàn)樽龅牟皇呛臅r(shí)操作,如果做耗時(shí)操作一樣會(huì)造成ANR。。。
* 這里點(diǎn)擊綁定服務(wù)后,點(diǎn)擊停止服務(wù)按鈕是無效的,要先解綁后,才能停止服務(wù)。
* 正常情況下,從綁定狀態(tài)到解綁狀態(tài)是不會(huì)停止服務(wù)的。只是一種狀態(tài)改變而已。
* 這里點(diǎn)擊綁定服務(wù)后,點(diǎn)擊停止服務(wù)按鈕是無效的,但是解綁后,會(huì)馬上停止服務(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)識(shí)
//BIND_AUTO_CREATE 綁定的同時(shí),啟動(dòng)Service
private int flags = Service.BIND_AUTO_CREATE;
}
上面是使用四個(gè)按鈕來實(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>
上面布局文件代碼比較簡單的,只是用四個(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方法,也就是說一個(gè)服務(wù)在一個(gè)生命周期內(nèi)只會(huì)回調(diào)一次onCreate方法。
點(diǎn)擊“解綁服務(wù)”按鈕后顯示的Log信息:
執(zhí)行了兩個(gè)回調(diào)方法:onUnBind和onDestroy
如果是用服務(wù)做簡單 的事情,使用第一種方法來啟動(dòng)服務(wù)就可以了,但是如果需要涉及到比較復(fù)雜的數(shù)據(jù)處理和操作就用綁定服務(wù)的方法來啟動(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>
非常簡單的布局設(shè)計(jì),使用List View來顯示所有的圖片
(二)遍歷文件的工具類的設(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());
}
}
(三)簡化BaseAdapter的一個(gè)工具類
package com.lwz.intentservice;
import android.content.Context;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import java.util.List;
/**
* 這是一個(gè)簡化BaseAdapter適配器的工具類
* 這是使用的是定義一個(gè)泛型T,使用時(shí)傳入什么數(shù)據(jù),T就是什么數(shù)據(jù)
* 實(shí)際設(shè)計(jì)中除了getVIew方法外,其他的方法基本是差不多的
* 所以繼承這個(gè)工具類后只要重寫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)造方法,傳入子線程的名字
* 但是這里必須要?jiǎng)?chuàng)建一個(gè)無參的構(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對象傳遞一個(gè)對象
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è)計(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ù)來IntentService來遍歷文件夾
* 在程序創(chuàng)建的使用就要啟動(dòng)服務(wù)
* 在頁面銷毀的時(shí)候就停止服務(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();
//動(dòng)態(tài)注冊一個(gè)廣播
IntentFilter filter = new IntentFilter();
filter.addAction("filefinish");
registerReceiver(mbcr, filter);// 注冊
//創(chuàng)建適配器的對象
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)建適配器的類
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) {
//對接收到的廣播進(jìn)行處理,intent里面包含數(shù)據(jù)
fileList = (ArrayList<File>) intent.getSerializableExtra("file");
//刷新適配器
adapter.notifyDataSetChanged();
//停止服務(wù),它的子線程也會(huì)停止
stop();
}
}
}
程序運(yùn)行前還記得加上SD卡的訪問權(quán)限;
上面程序功能還是有點(diǎn)問題!遍歷完文件后。頁面沒有馬上更新?退出程序再進(jìn)來,頁面上馬上顯示SD卡的圖片。
一般的說遍歷文件夾也不算是耗時(shí)操作,這里只是簡單示范。
一般的耗時(shí)操作是從網(wǎng)絡(luò)下載數(shù)據(jù),或本地移動(dòng)大文件等等。
五.同一個(gè)程序中Service和Activity通信的一些方式,這里展示主要代碼。
這里組件不要忘記在AndroidManifest中注冊
(一)使用Intent來傳遞數(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的方法來接收Intent的數(shù)據(jù),如果上面使用的是bind的方法來啟動(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);
}
(二)使用單例模式來傳遞數(shù)據(jù)
這個(gè)方法算是有點(diǎn)麻煩的吧!這里要在MyService類中先做好單例,然后在Activity中調(diào)用MyService對象的方法
1.MyService中的代碼
//定義一個(gè)靜態(tài)的類變量,單例的使用準(zhǔn)備
private static MyService instance;
//靜態(tài)方法,返回的是一個(gè)本類對象
//為了能讓另一邊的類調(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)過服務(wù)后才能使用單例,因?yàn)檫@里是在MyService的onCreate方法中把對象賦值給instance,之后才能實(shí)現(xiàn)單例。
2.MainActivity中的代碼:
/**
* 單例模式傳參
*MyService這里通過一個(gè)靜態(tài)方法,來獲得MyService的對象
這里通過MyService.getInstance()方法來獲得MyService對象
*/
//必須保證Myservice對象不能為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)注冊廣播接收者,然后在MainActivity中發(fā)送廣播,在MyService中接收到廣播傳來遞數(shù)據(jù)后,在發(fā)送廣播,讓MainActivity接收廣播數(shù)據(jù)!
1.MyService中的代碼:
@Override
public void onCreate() {
super.onCreate();
//動(dòng)態(tài)注冊廣播接收者,要定義好接收的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);
//注冊本類內(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向廣播傳遞一個(gè)hello broadcast");
sendBroadcast(intent);
}
//定義一個(gè)內(nèi)部類的廣播接收者,用于接收MyService傳遞過來的數(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類的使用!這里要被調(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è)接口對象
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;
}
}
//定義一個(gè)模擬開始音樂播放的方法
//需要重寫start里面的方法來開始播放音樂
public void startMusic() {
//播放
Toast.makeText(this, "音樂開始播放", Toast.LENGTH_SHORT).show();
callback.start();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//要傳遞一個(gè)MyBinder對象給MainActivity
return new MyBinder();
}
//定義一個(gè)模擬開始音樂播放的方法
//需要重寫start里面的方法來開始播放音樂
public void startMusic() {
//播放
Toast.makeText(this, "音樂開始播放", Toast.LENGTH_SHORT).show();
//在MainActivity中實(shí)例化callback對象
callback.start();
}
上面的代碼中要開始播放音樂要調(diào)用startMusic方法,并且要實(shí)例化里面的callback對象,而要實(shí)例化callback對象必須要調(diào)用內(nèi)部類Mybinder的set Callback方法,而實(shí)現(xiàn)這個(gè)方法又必須實(shí)現(xiàn)這個(gè)接口的方法!
2.MainActivity中的代碼:
/**
* 綁定服務(wù)Service
調(diào)用MyService的方法來調(diào)用內(nèi)部類的接口方法
*/
//定義一個(gè)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)方法,并實(shí)現(xiàn)里面的兩個(gè)方法
//這里的回調(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() {
//比如在后臺(tái)播放音樂;開始播放音樂
Log.e("action", "正在播放音樂");
//關(guān)閉頁面
finish();
}
});
上面的關(guān)系確實(shí)是有點(diǎn)亂,我發(fā)現(xiàn)我有些注解還是有點(diǎn)問題的!!
上面就是Service中的個(gè)方面的總結(jié)。
Service還可以用來做進(jìn)程間的數(shù)據(jù)傳遞,這里就涉及到AIDL(Android Interface Definition Language,安卓接口定義語言)進(jìn)程通信。這個(gè)相對來說比較復(fù)雜,另作總結(jié)!
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
- android?Service基礎(chǔ)(啟動(dòng)服務(wù)與綁定服務(wù))
- Android服務(wù)Service教程
- Android8.0適配前臺(tái)定位服務(wù)service的示例代碼
- 淺談Android Service服務(wù)的高級技巧
- 說說在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ī)制的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
Android自定義帶增長動(dòng)畫和點(diǎn)擊彈窗提示效果的柱狀圖DEMO
這篇文章主要介紹了Android自定義帶增長動(dòng)畫和點(diǎn)擊彈窗提示效果的柱狀圖的相關(guān)資料,非常不錯(cuò)具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
Android開發(fā)中Flutter組件實(shí)用技巧
這篇文章主要為大家介紹了Android開發(fā)中Flutter組件實(shí)用技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
Android開發(fā)之imageView圖片按比例縮放的實(shí)現(xiàn)方法
這篇文章主要介紹了Android開發(fā)之imageView圖片按比例縮放的實(shí)現(xiàn)方法,較為詳細(xì)的分析了Android中ImageView控件的scaleType屬性控制圖片縮放的具體用法,需要的朋友可以參考下2016-01-01
Android利用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-03
Android Recyclerview實(shí)現(xiàn)水平分頁GridView效果示例
本篇文章主要介紹了Android Recyclerview實(shí)現(xiàn)水平分頁GridView效果示例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08
Kotlin實(shí)現(xiàn)Android系統(tǒng)懸浮窗詳解
大家好,本篇文章主要講的是Kotlin實(shí)現(xiàn)Android系統(tǒng)懸浮窗詳解,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12
Android 微信6.1 tab欄圖標(biāo)和字體顏色漸變的實(shí)現(xiàn)
本文主要對微信6.1 tab 欄顏色漸變效果的實(shí)現(xiàn)全過程進(jìn)行分析介紹,具有很好的參考價(jià)值,下面跟著小編一起來看下吧2016-12-12

