目錄:
1. 前言
本文是對(duì) IntentService 的深入學(xué)習(xí)跪帝,包含其基本使用方法搂蜓、IntentService更新處理UI工作回论,以及對(duì) IntentService 的源碼分析蹦漠。
1.1 定義
IntentService 是 Service 的子類,繼承于 Service 類雨涛,用于處理后臺(tái)異步請(qǐng)求任務(wù)。
用戶通過調(diào)用 Context.StartService(Intent) 發(fā)送請(qǐng)求懦胞,服務(wù)根據(jù)請(qǐng)求啟動(dòng)替久,使用工作線程依次處理每個(gè) Intent任務(wù)請(qǐng)求,并在處理完所有任務(wù)請(qǐng)求后自身停止服務(wù)躏尉。
使用時(shí)蚯根,擴(kuò)展 IntentService ,即實(shí)現(xiàn)它的子類并具體實(shí)現(xiàn) onHandleIntent(android.content.Intent) 方法醇份。IntentService 接收 Intent稼锅,啟動(dòng)工作線程,并在適當(dāng)時(shí)機(jī)停止服務(wù)僚纷。
所有的請(qǐng)求都在同一個(gè)工作線程上處理矩距,一次處理一個(gè)請(qǐng)求,所以處理完所以的請(qǐng)求可能會(huì)花費(fèi)很長(zhǎng)的時(shí)間怖竭,但由于 IntentService 是另外創(chuàng)建的線程來工作锥债,所以保證不會(huì)阻止App 的主線程,防止 App 出現(xiàn) ANR痊臭。
1.2 使用場(chǎng)景
用于處理后臺(tái)長(zhǎng)時(shí)間的耗時(shí)操作哮肚,如:下載文件、播放音樂...
2. 使用方法
由于 IntentService 是抽象類广匙,所以在實(shí)際使用中允趟,我們需要?jiǎng)?chuàng)建一個(gè) IntentService 子類來具體實(shí)現(xiàn)。
使用步驟分為兩步:
- 創(chuàng)建 IntentService 子類鸦致,并在清單文件中注冊(cè)潮剪。
- 在 Activity 中通過調(diào)用 startService(Intent) 方法發(fā)送任務(wù)請(qǐng)求涣楷。
例子:
我們模擬在后臺(tái)進(jìn)行下載以及讀取文件的耗時(shí)操作,觀察打印出來的 Log 信息抗碰。
如下所示:
IntentService 的執(zhí)行流程為:onCreate() -> onStartCommand() -> onStart() -> onHandleIntent -> ···處理完所有請(qǐng)求··· -> onDestroy()
具體代碼如下所示:分 Java & Kotlin 兩個(gè)版本展示狮斗。
2.1 Java版本
步驟一:創(chuàng)建 MyIntentService
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public static final String DOWNLOAD_ACTION = "DOWNLOAD_ACTION";
public static final String READ_ACTION = "READ_ACTION";
public static final String TEST_AUTHOR = "TEST_AUTHOR";
public MyIntentService() {
super("MyIntentService");
}
/**
* 進(jìn)行一些耗時(shí)操作
* @param intent 通過startService(Intent intent)方法傳入
*/
@Override
protected void onHandleIntent(Intent intent) {
Log.e(TAG, "onHandleIntent: ");
if (intent != null) {
final String action = intent.getAction();
String author = intent.getExtras().getString(TEST_AUTHOR);
//模擬下載動(dòng)作
if (DOWNLOAD_ACTION.equals(action)) {
for (int i = 0; i < 5; i++) {
try {
//線程等待1s,模擬耗時(shí)操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, author + " " + action + " " + i);
}
}
//模擬讀操作
if (READ_ACTION.equals(action)) {
for (int i = 0; i < 5; i++) {
try {
//線程等待2s弧蝇,模擬耗時(shí)操作
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, author + " " + action + " " + i);
}
}
}
}
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onCreate: ");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Log.e(TAG, "onStart: ");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy: ");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
}
}
并記得在清單文件中注冊(cè)碳褒,這一步你也可以通過 Android Studio 來幫我們完成
<service
android:name=".intentservice.MyIntentService"
android:exported="false">
</service>
步驟二:通過 startService(Intent) 發(fā)送任務(wù)請(qǐng)求
public class IntentServiceActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service);
//模擬 Jere 做下載動(dòng)作
Intent intent = new Intent(this, MyIntentService.class);
intent.setAction(MyIntentService.DOWNLOAD_ACTION);
intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere");
startService(intent);
//模擬 James 做讀取動(dòng)作
Intent jamesIntent = new Intent(this, MyIntentService.class);
jamesIntent.setAction(MyIntentService.READ_ACTION);
jamesIntent.putExtra(MyIntentService.TEST_AUTHOR, "James");
startService(jamesIntent);
}
}
2.2 Koltin版本
步驟一:創(chuàng)建 MyIntentService
class MyIntentService : IntentService("MyIntentService") {
companion object {
private val TAG: String = "MyIntentService"
val DOWNLOAD_ACTION = "DOWNLOAD_ACTION"
val READ_ACTION = "READ_ACTION"
val TEST_AUTHOR = "TEST_AUTHOR"
}
override fun onHandleIntent(intent: Intent?) {
val action: String? = intent?.action
val author: String? = intent?.extras?.getString(TEST_AUTHOR)
when (intent?.action) {
DOWNLOAD_ACTION -> {
for (i in 0..10) {
Thread.sleep(1000)
Log.e(TAG, "$author $action $I")
}
}
READ_ACTION -> {
for (j in 0..10) {
Thread.sleep(2000)
Log.e(TAG, "$author $action $j")
}
}
}
}
override fun onCreate() {
super.onCreate()
Log.e(TAG, "onCreate")
Toast.makeText(this, "conCreate", Toast.LENGTH_SHORT).show()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.e(TAG, "onStartCommand")
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
Log.e(TAG, "onDestroy")
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show()
}
}
在清單文件中注冊(cè):
<service
android:name=".multiplethread.intentservice.MyIntentService"
android:exported="false">
</service>
步驟二:通過 startService(Intent) 發(fā)送任務(wù)請(qǐng)求
class TestIntentServiceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_intent_service)
val intent = Intent()
intent.setClass(this, MyIntentService::class.java)
intent.action = MyIntentService.DOWNLOAD_ACTION
intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere")
startService(intent)
val jamesIntent = Intent()
jamesIntent.setClass(this, MyIntentService::class.java)
jamesIntent.action = MyIntentService.READ_ACTION
jamesIntent.putExtra(MyIntentService.TEST_AUTHOR, "James")
startService(jamesIntent)
}
}
2.3 IntentService更新處理UI工作
思考: 在上面的例子中,我們是通過在打印出來的日志信息來觀察后臺(tái)異步任務(wù)的執(zhí)行情況看疗,那 IntentService 可以處理 UI 工作嗎沙峻?
答: IntentService 是服務(wù)端,肯定是不可以處理 UI 工作的两芳,但是他可以通過其對(duì)應(yīng)的客戶端专酗,也就是 Activity 來更新處理 UI 工作。
客戶端與服務(wù)端之間的通信盗扇,我們使用 Messenger 祷肯,關(guān)于 Messenger 內(nèi)容請(qǐng)看另一篇博客 Android Messenger初探,具體實(shí)現(xiàn)如下所示:
例子: 我們?cè)?IntentService 模擬下載任務(wù)疗隶,通過 ProgressBar 顯示下載進(jìn)度佑笋。
效果圖如下所示:
具體代碼如下所示:分Java & Kotlin 兩個(gè)版本展示。
2.3.1 Java版本
- 在 Activity 中發(fā)送請(qǐng)求時(shí)斑鼻,新建一個(gè) Messenger 對(duì)象蒋纬,傳遞給 IntentService。
public class IntentServiceActivity extends AppCompatActivity {
private ProgressBar downloadProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service);
downloadProgressBar = findViewById(R.id.download_progress_bar);
//用于創(chuàng)建 Messenger坚弱,接收 IntentService 回復(fù)的消息
MessengerHandler messengerHandler = new MessengerHandler(this);
//模擬 Jere 做下載動(dòng)作
Intent intent = new Intent(this, MyIntentService.class);
intent.setAction(MyIntentService.DOWNLOAD_ACTION);
intent.putExtra(MyIntentService.TEST_AUTHOR, "Jere");
//將 Messenger 傳遞給 IntentService蜀备,讓其回復(fù)消息回來
intent.putExtra(MyIntentService.TEST_MESSENGER, new Messenger(messengerHandler));
startService(intent);
}
/**
* 用于創(chuàng)建 Messenger 對(duì)象
*
* 靜態(tài)內(nèi)部類,防止內(nèi)存泄漏
*/
public static class MessengerHandler extends Handler {
private WeakReference<IntentServiceActivity> weakReference;
MessengerHandler(IntentServiceActivity activity) {
weakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
//msg 為 IntentService 回復(fù)的消息荒叶,包含 Bundle 等信息碾阁。
Bundle bundle = msg.getData();
//獲取 IntentService 傳遞過來的 下載進(jìn)度 參數(shù)
int downloadProgressBarValue = bundle.getInt(MyIntentService.DOWNLOAD_PROGRESS_VALUE_KEY);
//將下載進(jìn)度設(shè)置成 ProgressBar 的進(jìn)度,顯示出來些楣。
IntentServiceActivity activity = weakReference.get();
if (activity != null && !activity.isFinishing()) {
activity.downloadProgressBar.setProgress(downloadProgressBarValue);
}
}
}
}
- 在 IntentService 中獲取 Messenger 對(duì)象脂凶,并回復(fù)消息給 Activity。
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public static final String DOWNLOAD_ACTION = "DOWNLOAD_ACTION";
public static final String READ_ACTION = "READ_ACTION";
public static final String TEST_AUTHOR = "TEST_AUTHOR";
public static final String TEST_MESSENGER = "TEST_MESSENGER";
public static final String DOWNLOAD_PROGRESS_VALUE_KEY = "DOWNLOAD_PROGRESS_VALUE";
public MyIntentService() {
super("MyIntentService");
}
/**
* 進(jìn)行一些耗時(shí)操作
* @param intent 通過startService(Intent intent)方法傳入
*/
@Override
protected void onHandleIntent(Intent intent) {
Log.e(TAG, "onHandleIntent: ");
if (intent != null) {
final String action = intent.getAction();
String author = intent.getExtras().getString(TEST_AUTHOR);
//模擬下載動(dòng)作
if (DOWNLOAD_ACTION.equals(action)) {
for (int i = 0; i <= 6; i++) {
try {
//線程等待1s愁茁,模擬耗時(shí)操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, author + " " + action + " " + i);
//獲取從 Activity 傳入的 Messenger
Messenger messenger = (Messenger) intent.getExtras().get(TEST_MESSENGER);
//新建消息蚕钦,設(shè)置下載進(jìn)度參數(shù)
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putInt(DOWNLOAD_PROGRESS_VALUE_KEY, i);
msg.setData(bundle);
try {
//回復(fù)消息
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onCreate: ");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
super.onStart(intent, startId);
Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show();
Log.e(TAG, "onStart: ");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy: ");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
}
}
這樣就實(shí)現(xiàn)了 IntentService 更新處理 UI 工作,具體效果看上圖 IntentService更新處理UI工作鹅很。
2.3.2 Kotlin版本
同樣嘶居,Kotlin的實(shí)現(xiàn)方式為:
- 在 Activity 中發(fā)送請(qǐng)求時(shí),新建一個(gè) Messenger 對(duì)象促煮,傳遞給 IntentService邮屁。
class TestIntentServiceActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_intent_service)
val intent = Intent()
intent.setClass(this, MyIntentService::class.java)
intent.action = MyIntentService.DOWNLOAD_ACTION_KEY
intent.putExtra(MyIntentService.TEST_AUTHOR_KEY, "Jere")
//將 Messenger 傳遞給 IntentService, 使其傳遞消息回來胸蛛,實(shí)現(xiàn)客戶端與服務(wù)端之間進(jìn)行溝通
intent.putExtra(MyIntentService.TEST_MESSENGER_KEY, Messenger(MessengerHandler(this)))
startService(intent)
}
/**
* 此 Handler 用于創(chuàng)建 Messenger 對(duì)象,接收 IntentService 回復(fù)回來的消息
*
* 靜態(tài)內(nèi)部類樱报,防止內(nèi)存泄漏
*/
class MessengerHandler(activity: TestIntentServiceActivity) : Handler() {
var weakReference: WeakReference<TestIntentServiceActivity> = WeakReference(activity)
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
//msg 為 IntentService 回復(fù)的消息,包含 Bundle 等信息泞当。
val bundle: Bundle = msg.data
//獲取 IntentService 傳遞過來的 下載進(jìn)度 參數(shù)
val downloadProgressBarValue: Int =
bundle.get(MyIntentService.DOWNLOAD_PROGRESS_BAR_VALUE_KEY) as Int
val activity: TestIntentServiceActivity? = weakReference.get()
//將下載進(jìn)度設(shè)置成 ProgressBar 的進(jìn)度迹蛤,顯示出來。
if (activity != null && !activity.isFinishing) {
activity.intentServiceDownloadProgressBar.progress = downloadProgressBarValue
}
}
}
}
- 在 IntentService 中獲取 Messenger 對(duì)象襟士,并回復(fù)消息給 Activity盗飒。
class MyIntentService : IntentService("MyIntentService") {
companion object {
private val TAG: String = "MyIntentService"
val DOWNLOAD_ACTION_KEY = "DOWNLOAD_ACTION"
val READ_ACTION_KEY = "READ_ACTION"
val TEST_AUTHOR_KEY = "TEST_AUTHOR"
val TEST_MESSENGER_KEY = "TEST_MESSENGER"
val DOWNLOAD_PROGRESS_BAR_VALUE_KEY = "DOWNLOAD_PROGRESS_BAR_VALUE"
}
override fun onHandleIntent(intent: Intent?) {
val action: String? = intent?.action
val author: String? = intent?.extras?.getString(TEST_AUTHOR_KEY)
when (intent?.action) {
DOWNLOAD_ACTION_KEY -> {
for (i in 0..6) {
Thread.sleep(1000)
Log.e(TAG, "$author $action $I")
//獲取從 Activity 中傳入的 Messenger 對(duì)象
val messenger: Messenger = intent.extras?.get(TEST_MESSENGER_KEY) as Messenger
//新建一個(gè) Message 對(duì)象
val msg: Message = Message()
//為 Message 對(duì)象設(shè)置 下載進(jìn)度 參數(shù)
val bundle: Bundle = Bundle()
bundle.putInt(DOWNLOAD_PROGRESS_BAR_VALUE_KEY, i)
msg.data = bundle
//Messenger 回復(fù)消息給 Activity
messenger.send(msg)
}
}
READ_ACTION_KEY -> {
for (j in 0..10) {
Thread.sleep(2000)
Log.e(TAG, "$author $action $j")
}
}
}
}
override fun onCreate() {
super.onCreate()
Log.e(TAG, "onCreate")
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.e(TAG, "onStartCommand")
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show()
return super.onStartCommand(intent, flags, startId)
}
override fun onStart(intent: Intent?, startId: Int) {
super.onStart(intent, startId)
Log.e(TAG, "onStart")
Toast.makeText(this, "onStart", Toast.LENGTH_SHORT).show()
}
override fun onDestroy() {
super.onDestroy()
Log.e(TAG, "onDestroy")
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show()
}
}
具體效果看上圖 IntentService更新處理UI工作。
3. 源碼分析
上面介紹了 IntentService 的使用方法陋桂,接下來逆趣,我們來看一下 IntentService 的源碼。如果你理解了 Hander嗜历、Looper宣渗、MessageQueue三者之間的關(guān)系,那么理解 IntentService 的源代碼十分容易梨州。(如果不理解痕囱,請(qǐng)看博客 Android Handler深入學(xué)習(xí)(源碼分析))
根據(jù)上面的例子,我們知道了 IntentService 的執(zhí)行順序?yàn)椋?/p>
onCreate() -> onStartCommand() -> onStart() -> onHandleIntent -> ···處理完所有請(qǐng)求··· -> onDestroy()
IntentService 源碼如下:
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
//創(chuàng)建一個(gè)內(nèi)部類暴匠,繼承于 Handler 類鞍恢。
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
/**
* IntentService 構(gòu)造函數(shù),在你的子類構(gòu)造函數(shù)中調(diào)用(在子類中通過 super(name) 方法調(diào)用)
*
* @param 參數(shù) name 用于命名工作線程
*
*/
public IntentService(String name) {
super();
mName = name;
}
@Override
public void onCreate() {
super.onCreate();
//創(chuàng)建一個(gè) HandlerThread 對(duì)象
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
//啟動(dòng) HandlerThread 線程
thread.start();
//獲取 HandlerThread 中創(chuàng)建的 Looper 對(duì)象每窖,賦值給全局變量 mServiceLooper帮掉。
mServiceLooper = thread.getLooper();
//創(chuàng)建一個(gè) Handler 對(duì)象,并關(guān)聯(lián)剛剛通過 HandlerThread 創(chuàng)建的 Looper 對(duì)象窒典,即關(guān)聯(lián) mServiceLooper
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
//發(fā)送消息給 mServiceHanlder蟆炊,
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* 你不應(yīng)該在你的 IntentService 類中復(fù)寫 onStartCommand() 方法。
* 相反瀑志,你應(yīng)該復(fù)寫 onHandleIntent() 方法盅称,該方法會(huì)在 IntentService 接收到啟動(dòng)請(qǐng)求時(shí)被調(diào)用。
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
//退出 Looper 消息循環(huán)
mServiceLooper.quit();
}
/**
* 除非是綁定服務(wù)后室,否則你不需要實(shí)現(xiàn)此方法缩膝,因?yàn)榇朔椒ǖ哪J(rèn)實(shí)現(xiàn)返回 null.
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* 此方法在工作線程中被調(diào)用用于處理請(qǐng)求,一次只能處理一個(gè)請(qǐng)求岸霹,處理工作運(yùn)行于獨(dú)立于其他應(yīng)用程序的工作線程中疾层。
* 因此,如果該段代碼花費(fèi)了很長(zhǎng)的一段時(shí)間贡避,它會(huì)阻塞同一個(gè) IntentService 的其他請(qǐng)求痛黎,但不會(huì)阻塞其他任何東西予弧。
* 當(dāng)所有的請(qǐng)求都被處理完,該 IntentService 就會(huì)停止運(yùn)行湖饱,所以你不用調(diào)用 stopSelf() 方法掖蛤。
*
* @param intent 通過 startService(Intent) 方法傳入。如果服務(wù)在其進(jìn)程消失后重新啟動(dòng)井厌,則此值可能為空;
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
總結(jié):
- 在 onCreate() 方法中蚓庭,我們新建一個(gè) HandlerThread 對(duì)象,然后啟用它仅仆,獲取其創(chuàng)建的 Looper 對(duì)象器赞,然后新創(chuàng)建一個(gè) Looper 對(duì)象關(guān)聯(lián)該 Looper 對(duì)象。關(guān)于 HandlerThread 的知識(shí)點(diǎn)請(qǐng)看另一篇博客 Android HandlerThread詳解(源碼分析)
- 然后我們?cè)?onStart() 方法中墓拜,通過 Message港柜,將 Intent任務(wù)請(qǐng)求 發(fā)送給 mServiceHandler,Intent任務(wù)請(qǐng)求就是我們?cè)?Activity 中通過 startService(Intent) 傳入的 Intent咳榜。
- mServiceHanlder 接受到任務(wù)請(qǐng)求夏醉,調(diào)用 onHandleIntent() 方法處理任務(wù)請(qǐng)求,處理完所有請(qǐng)求后涌韩,調(diào)用 stopSelf() 結(jié)束 IntentService授舟。
至此 HandlerThread 的源碼也就分析結(jié)束了。
其實(shí)分享文章的最大目的正是等待著有人指出我的錯(cuò)誤贸辈,如果你發(fā)現(xiàn)哪里有錯(cuò)誤释树,請(qǐng)毫無保留的指出即可,虛心請(qǐng)教擎淤。
另外奢啥,如果你覺得文章不錯(cuò),對(duì)你有所幫助嘴拢,請(qǐng)給我點(diǎn)個(gè)贊桩盲,就當(dāng)鼓勵(lì),謝謝~Peace~席吴!