Android 簡訊的收發及在android模擬器之間實踐(一)

2021-01-08 開源中國

本文通過運行兩個Android模擬器,介紹在Android中如何實現簡訊服務(SMS,short message service)的功能。通過這個例子,我想帶給大家的是:更加熟悉之前介紹過的Android應用程式的概念及技術細節,且通過實例調度大家的興趣。我之所以選擇SMS為例子,主要原因是SMS已經非常成熟了,從中可以發掘更多的信息和技術細節,而且我相信大部分人發簡訊比打電話多。

本文的主要內容如下:

1、溫故知新 2、準備工作:SMS涉及的主要類SmsManager 3、簡單的SMS發送程序 3.1、運行SMS程序給另一個android模擬器發短 4、SMS增強(一) 5、SMS增強(二) 6、SMS接收程序(下篇) 7、emulator工具(下篇) 8、…
1、溫故知新

廣播接收者:一個廣播接收者是這樣一個組件,它不做什麼事,僅是接受廣播公告並作出相應的反應。許多廣播源自於系統代碼,例如公告時區的改變、電池電量低、已採取圖片、用戶改變了語言偏好。應用程式也可以發起廣播,例如為了他其他程序知道某些數據已經下載到設備且他們可以使用這些數據

BroadcastReceiver類:是接受sendBroadcast()發送的意圖(intents)的基類。可以用Context.registerReceiver()動態地註冊這個類的實例,或者通過AndroidManifest.xml中<receiver>標籤靜態發布。

廣播接收者不顯示一個用戶界面。然而,它們啟動一個活動去響應收到的信息,或者他們可能使用NotificationManager去通知用戶。通知可以使用多種方式獲得用戶的注意——閃爍的背光、振動設備、播放聲音等等。典型的是放在一個持久的圖標在狀態欄,用戶可以打開獲取信息。

2、準備工作:SMS涉及的主要類SmsManager

實現SMS主要用到SmsManager類,該類繼承自java.lang.Object類,下面我們介紹一下該類的主要成員。

公有方法:

ArrayList<String> divideMessage(String text)
當簡訊超過SMS消息的最大長度時,將簡訊分割為幾塊。
參數:text——初始的消息,不能為空
返回值:有序的ArrayList<String>,可以重新組合為初始的消息 static SmsManager getDefault()
獲取SmsManager的默認實例。
返回值:SmsManager的默認實例 void SendDataMessage(String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)
發送一個基於SMS的數據到指定的應用程式埠。
參數:
1)、destinationAddress——消息的目標地址
2)、scAddress——服務中心的地址or為空使用當前默認的SMSC 3)destinationPort——消息的目標埠號
4)、data——消息的主體,即消息要發送的數據
5)、sentIntent——如果不為空,當消息成功發送或失敗這個PendingIntent就廣播。結果代碼是Activity.RESULT_OK表示成功,或RESULT_ERROR_GENERIC_FAILURE、RESULT_ERROR_RADIO_OFF、RESULT_ERROR_NULL_PDU之一表示錯誤。對應RESULT_ERROR_GENERIC_FAILURE,sentIntent可能包括額外的「錯誤代碼」包含一個無線電廣播技術特定的值,通常只在修復故障時有用。
每一個基於SMS的應用程式控制檢測sentIntent。如果sentIntent是空,調用者將檢測所有未知的應用程式,這將導致在檢測的時候發送較小數量的SMS。
6)、deliveryIntent——如果不為空,當消息成功傳送到接收者這個PendingIntent就廣播。
異常:如果destinationAddress或data是空時,拋出IllegalArgumentException異常。 void sendMultipartTextMessage(String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliverIntents)
發送一個基於SMS的多部分文本,調用者應用已經通過調用divideMessage(String text)將消息分割成正確的大小。
參數:
1)、destinationAddress——消息的目標地址
2)、scAddress——服務中心的地址or為空使用當前默認的SMSC
3)、parts——有序的ArrayList<String>,可以重新組合為初始的消息
4)、sentIntents——跟SendDataMessage方法中一樣,只不過這裡的是一組PendingIntent
5)、deliverIntents——跟SendDataMessage方法中一樣,只不過這裡的是一組PendingIntent
異常:如果destinationAddress或data是空時,拋出IllegalArgumentException異常。 void sendTextMessage(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent)
發送一個基於SMS的文本。參數的意義和異常前面的已存在的一樣,不再累述。

常量:

public static final int RESULT_ERROR_GENERIC_FAILURE
表示普通錯誤,值為1(0x00000001) public static final int RESULT_ERROR_NO_SERVICE
表示服務當前不可用,值為4 (0x00000004) public static final int RESULT_ERROR_NULL_PDU
表示沒有提供pdu,值為3 (0x00000003) public static final int RESULT_ERROR_RADIO_OFF
表示無線廣播被明確地關閉,值為2 (0x00000002) public static final int STATUS_ON_ICC_FREE
表示自由空間,值為0 (0x00000000) public static final int STATUS_ON_ICC_READ
表示接收且已讀,值為1 (0x00000001) public static final int STATUS_ON_ICC_SENT
表示存儲且已發送,值為5 (0x00000005) public static final int STATUS_ON_ICC_UNREAD
表示接收但未讀,值為3 (0x00000003) public static final int STATUS_ON_ICC_UNSENT
表示存儲但為發送,值為7 (0x00000007) 3、簡單的SMS發送程序

1)、首先,編輯布局文件res/layout/main.xml,達到我們想要的結果,界面如下:

圖1、程序運行界面

對應的xml代碼如下:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/txtPhoneNo"/> <!-- text's value define in res/values/strings.xml --> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/edtPhoneNo"/> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/txtContent"/> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:minLines="3" android:id="@+id/edtContent"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/btnText" android:id="@+id/btnSend"/></LinearLayout>

相應的要在res/values/strings.xm中添加上面定義的視圖的text的值,如下:

<?xml version="1.0" encoding="utf-8"?><resources> <string name="txtPhoneNo">Please input phone NO:</string> <string name="txtContent">Please input SMS\'s content:</string> <string name="btnText">send!</string> <string name="app_name">SMS</string></resources>

2)、做完這些準備工作之後,我麼要開始編寫代碼實現簡單的簡訊發送了。

通過第一步我們構建好界面之後,現在要在上面的基礎上編寫業務邏輯了。大致過程為:在java源文件中,獲取用戶在edtPhoneNo中輸入的電話號碼,edtContent中輸入要發送的內容;然後點擊btnSend按鈕發送簡訊,要達到這個目的我們要設置btnSend的OnClickListener以達到當點擊它觸發發送簡訊的功能,而且要發送簡訊就要用到我們前面介紹的SmsManager類提供的方法接口。

設置btnSend的OnClickListener的代碼如下:

btnSend.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { String phoneNo = edtPhoneNo.getText().toString(); String message = edtContent.getText().toString(); if (phoneNo.length() > 0 && message.length() > 0){ //call sendSMS to send message to phoneNo sendSMS(phoneNo, message); } else Toast.makeText(getBaseContext(), "Please enter both phone number and message.", Toast.LENGTH_SHORT).show(); }});

發送簡訊的功能的代碼如下:

 

private void sendSMS(String phoneNumber, String message) { // ---sends an SMS message to another device--- SmsManager sms = SmsManager.getDefault(); PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent(this,TextMessage.class), 0); //if message's length more than 70 , //then call divideMessage to dive message into several part //and call sendTextMessage() //else direct call sendTextMessage() if (message.length() > 70) { ArrayList<String> msgs = sms.divideMessage(message); for (String msg : msgs) { sms.sendTextMessage(phoneNumber, null, msg, pi, null); } } else { sms.sendTextMessage(phoneNumber, null, message, pi, null); } Toast.makeText(TextMessage.this, "簡訊發送完成", Toast.LENGTH_LONG).show();}

如果你已經看了第2節介紹的SmsManager類的介紹,代碼應該很好理解。在這裡要說明的是,sendTextMessage方法中的第4個和第5個參數PendingIntent設為null,這樣的話不能根據簡訊發出之後的狀態做相應的事情,如簡訊發送失敗後的提醒、接收者成功接收後的回執……完整的流程源碼如下:

package skynet.com.cnblogs.www;import java.util.ArrayList;import android.app.Activity;import android.app.PendingIntent;import android.content.Intent;import android.os.Bundle;import android.telephony.SmsManager;import android.view.View;import android.widget.*;public class TextMessage extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btnSend = (Button) findViewById(R.id.btnSend); edtPhoneNo = (EditText) findViewById(R.id.edtPhoneNo); edtContent = (EditText) findViewById(R.id.edtContent); btnSend.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { String phoneNo = edtPhoneNo.getText().toString(); String message = edtContent.getText().toString(); if (phoneNo.length() > 0 && message.length() > 0) { // call sendSMS to send message to phoneNo sendSMS(phoneNo, message); } else Toast.makeText(getBaseContext(), "Please enter both phone number and message.", Toast.LENGTH_SHORT).show(); } }); } private Button btnSend; private EditText edtPhoneNo; private EditText edtContent; private void sendSMS(String phoneNumber, String message) { // ---sends an SMS message to another device--- SmsManager sms = SmsManager.getDefault(); PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent(this, TextMessage.class), 0); // if message's length more than 70 , // then call divideMessage to dive message into several part ,and call // sendTextMessage() // else direct call sendTextMessage() if (message.length() > 70) { ArrayList<String> msgs = sms.divideMessage(message); for (String msg : msgs) { sms.sendTextMessage(phoneNumber, null, msg, pi, null); } } else { sms.sendTextMessage(phoneNumber, null, message, pi, null); } Toast.makeText(TextMessage.this, "簡訊發送完成", Toast.LENGTH_LONG).show(); }}

3)運行前,還要在清單文件AndroidManifest.xml中加入允許發送簡訊的權限:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="skynet.com.cnblogs.www" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".TextMessage" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.SEND_SMS"/></manifest>

3.1、運行SMS程序給另一個android模擬器發簡訊

運行上面我們編寫的TextMessage程序,另外在Windows的命令行下切換到tools目錄下,並輸入emulator –data smsReceiver,我的如下:

這樣就會啟動一個android模擬器,如下所示:(注意它的編號:5556,就是用這個編號與它通信的)

圖2、通過emulator啟動一個android模擬器

通過我們TextMessage程序啟動的android模擬器,編寫簡訊:

圖3、TextMessage程序個5556模擬器發簡訊

點擊發送之後,通過命令行啟動的5556號android模擬器會收到我們剛才發送的簡訊,如下所示:

圖4、收到簡訊的提示

tips:

如果通過命令行的emulator啟動android模擬器提示「NO DNS servers found!」,這時我們發的簡訊模擬器是收不到的。

在Windows下,如果電腦沒有介入網絡,即找不DNS伺服器的話會出現這種情況!

在Mac下,如果提示這個警告的話,可以這樣解決:檢查你是否有 /etc/resolv.conf文件,如果沒有的話,通過下面的命令行

ln -s /private/var/run/resolv.conf /etc/resolv.conf可以解決。

4、SMS增強(一)

上面我們實現了一個簡單的SMS程序,下面我們要對它進行增強!你肯定已經注意到了,我們上面的SMS程序的sendTextMessage方法中的第4個和第5個參數PendingIntent設為null,即sentIntent和deliveryIntent。

第4個參數-sendIntent,當消息成功發送或發送失敗都將被觸發。廣播接收者的結果碼,Activity.RESULT_OK表示成功,或RESULT_ERROR_GENERIC_FAILURE、RESULT_ERROR_RADIO_OFF、RESULT_ERROR_NULL_PDU之一表示錯誤。對應RESULT_ERROR_GENERIC_FAILURE,sentIntent可能包括額外的「錯誤代碼」包含一個無線電廣播技術特定的值,通常只在修復故障時有用。第5個參數-deliveryIntent,僅當目標接收到你的SMS消息才觸發。

為了跟蹤發出的簡訊的狀態,實現和註冊Broadcast Receiver(廣播接收者)監聽傳遞給sendTextMessage方法的參數Pending Intents。下面我們就實現和註冊這個廣播接收者:

String SENT_SMS_ACTION="SENT_SMS_ACTION";String DELIVERED_SMS_ACTION="DELIVERED_SMS_ACTION";//create the sentIntent parameter Intent sentIntent=new Intent(SENT_SMS_ACTION);PendingIntent sentPI=PendingIntent.getBroadcast( this, 0, sentIntent, 0);//create the deilverIntent parameter Intent deliverIntent=new Intent(DELIVERED_SMS_ACTION);PendingIntent deliverPI=PendingIntent.getBroadcast( this, 0, deliverIntent, 0);//register the Broadcast Receivers registerReceiver(new BroadcastReceiver(){ @Override public void onReceive(Context _context,Intent _intent) { switch(getResultCode()){ case Activity.RESULT_OK: Toast.makeText(getBaseContext(), "SMS sent success actions", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: Toast.makeText(getBaseContext(), "SMS generic failure actions", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_RADIO_OFF: Toast.makeText(getBaseContext(), "SMS radio off failure actions", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NULL_PDU: Toast.makeText(getBaseContext(), "SMS null PDU failure actions", Toast.LENGTH_SHORT).show(); break; } }},new IntentFilter(SENT_SMS_ACTION));registerReceiver(new BroadcastReceiver(){ @Override public void onReceive(Context _context,Intent _intent) { Toast.makeText(getBaseContext(), "SMS delivered actions", Toast.LENGTH_SHORT).show(); }},new IntentFilter(DELIVERED_SMS_ACTION));

在基本完成了要做的工作,接下來要做的就是將sendTextMessage的第4個和第5個參數改為sentPI、deliverPI,這樣工作基本完成,修改後的sendSMS方法如下:

private void sendSMS(String phoneNumber, String message) { // ---sends an SMS message to another device--- SmsManager sms = SmsManager.getDefault(); String SENT_SMS_ACTION = "SENT_SMS_ACTION"; String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION"; // create the sentIntent parameter Intent sentIntent = new Intent(SENT_SMS_ACTION); PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, sentIntent, 0); // create the deilverIntent parameter Intent deliverIntent = new Intent(DELIVERED_SMS_ACTION); PendingIntent deliverPI = PendingIntent.getBroadcast(this, 0, deliverIntent, 0); // register the Broadcast Receivers registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context _context, Intent _intent) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(getBaseContext(), "SMS sent success actions", Toast.LENGTH_SHORT) .show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: Toast.makeText(getBaseContext(), "SMS generic failure actions", Toast.LENGTH_SHORT) .show(); break; case SmsManager.RESULT_ERROR_RADIO_OFF: Toast .makeText(getBaseContext(), "SMS radio off failure actions", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NULL_PDU: Toast.makeText(getBaseContext(), "SMS null PDU failure actions", Toast.LENGTH_SHORT) .show(); break; } } }, new IntentFilter(SENT_SMS_ACTION)); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context _context, Intent _intent) { Toast.makeText(getBaseContext(), "SMS delivered actions", Toast.LENGTH_SHORT).show(); } }, new IntentFilter(DELIVERED_SMS_ACTION)); // if message's length more than 70 , // then call divideMessage to dive message into several part ,and call // sendTextMessage() // else direct call sendTextMessage() if (message.length() > 70) { ArrayList<String> msgs = sms.divideMessage(message); for (String msg : msgs) { sms.sendTextMessage(phoneNumber, null, msg, sentPI, deliverPI); } } else { sms.sendTextMessage(phoneNumber, null, message, sentPI, deliverPI); }}

運行之後的,發送簡訊成功的話就可以看到如下界面:

圖5、增強SMS(一)

閱讀:Android 簡訊的收發及在android模擬器之間實踐(二)

相關焦點

  • Android模擬器adb命令介B
    2.安裝apk程序到模擬器則執行adb install android123.apk,這樣名為android123的安b包就會安b到Android模擬器中,前提是android123.apk文件需要放到SDK/Tools目錄下。
  • android模擬器 用葉子豬手遊模擬器
    android模擬器 用葉子豬手遊模擬器  關於葉子豬手遊模擬器  android模擬器 用葉子豬手遊模擬器 葉子豬手遊模擬器是葉子豬遊戲網獨自研發的一款在電腦上玩安卓手機遊戲的應用程式。
  • 解決Android 模擬器無法上網的問題
    一、本文引用地址:http://www.eepw.com.cn/article/201610/305384.htmWindows下,配置好Adroid環境變量後(如將d:/android-sdk-windows-1.0_r1/tools加入系統變量
  • Android模擬器執行ARM應用
    打開APP Android模擬器執行ARM應用 網際網路科技密 發表於 2020-04-13 09:34:03 過去,開發者需要通過模擬器鏡系統映像搭建一個完整的 ARM 環境,才能繞過這個限制並在 x86 機器上執行 ARM 應用。但是,把整個系統的 ARM 指令都轉換成 x86 指令會造成過高的性能負荷,因此與基於 x86 的系統映像相比,完整的 ARM 系統映像在 x86 宿主機上的運行速度會慢很多,而且它還無法使用 x86 處理器提供的硬體加速和 CPU 虛擬化技術。
  • android系統上的WII,PS2模擬器讓IOS用戶羨慕不已
    android系統上的WII,PS2模擬器讓IOS用戶羨慕不已 在很久很久以前就想過萬一PS2模擬器登陸智慧型手機會怎麼樣,就在去年一個實用化的
  • 使用全新 Android 模擬器工具進行持續測試
    可調試性: 跟蹤來自 Android 模擬器遠程實例的錯誤。了解 Android 模擬器https://developer.android.google.cn/studio/run/emulatorAndroid Emulator Container Scripthttps://github.com/google/android-emulator-container-scripts
  • Android 模擬器大幅優化
    來自Android開發者博客的消息,Android模擬器現在已經有了多項改進和優化,可以讓開發者們更方便的進行應用開發。
  • 乾貨分享 | Android 存儲空間的最佳實踐 (上)
    分區存儲改變了應用在外置存儲中保存和訪問文件的方式,為了幫您遷移應用並支持分區存儲,我們概括了常見用例的最佳實踐並分享給大家。本文分為上下兩篇,分別為您介紹處理媒體文件和非媒體文件的用例和最佳實踐,供您參考。分區存儲:https://developer.android.
  • 谷歌推出「Android 信息網頁版」:在電腦上收發手機信息
    谷歌推出「Android 信息網頁版」:在電腦上收發手機信息 站長之家(ChinaZ.com) 6 月 19 日消息,谷歌於今天正式了推出「Android 信息
  • 圖解Google Android內核編譯教程
    Android作為Google公司推出的一款手機開發平臺,其本身是基於linux內核的。Google提供的內核原始碼中除了linux部分外,有很大一部分是與虛擬處理器Qemu和模擬硬體平臺Goldfish相關的。
  • Android安全幾道入門題目
    本文通過幾個題目可以讓你基本了解android中簡單的但比較經典的漏洞、以及簡單的android註冊機開發的思路。閱讀本文,你可能需要了解android逆向的基本知識和常用工具、非常簡單的java語言、smali的語法知識。本文適合android入門初學者,最基本的東西。大佬請無視!
  • 一文入門Android逆向
    然後選擇`Asia→Shanghai`,然後重啟即可。4、更所以有些應用是基於ARM架構編譯的就無法安裝,出現如下提示:2、解決方法安裝ARM Translation tool下載與Genymotion
  • Android 模擬器現已支持 AMD 處理器和 Hyper-V
    Linux系統若您正在使用 Linux 進行 Android 應用開發,Android 模擬器將繼續使用原生 KVM 虛擬技術管理工具為英特爾以及 AMD 設備提供高速、高性能的虛擬化解決方案。Android 模擬器 v27.3.8 新增加快照用戶界面,並在性能、穩定性和資源利用方面的表現更為出色。
  • 谷歌 Android Studio 4.1 正式版發布
    IT之家10月22日消息 谷歌官方表示嗎,近期發布了 Android Studio 4.1 穩定版,為大家帶來一系列針對常見的編輯、調試和優化工作的功能。4.1 版本的重點訴求之一是幫助您在使用 Android Jetpack 庫 (即 Android 的開發庫套件) 時遵循最佳實踐和提升代碼編寫效率。基於大家的反饋,直接在 IDE 中集成了諸多常用的 Android 庫,從而改善了編寫代碼的體驗。
  • 乾貨分享 | Android 存儲空間的最佳實踐 (下)
    為了提高文件的規整程度並讓用戶可以更好地控制他們的文件,android 10 為應用引入了名為 &34; 的新範式。分區存儲改變了應用在外置存儲中保存和訪問文件的方式,為了幫您遷移應用並支持分區存儲,我們概括了常見用例的最佳實踐並分享給大家。
  • ​【移動安全】一文入門Android逆向
    1.5、常用工具1、命令工具tmux: 可以關閉窗口將程序放在後臺運行jnettop: 監測網絡流量,得到通訊IP、埠、URL、速率信息netstat -tunlp:埠對應進程號、監聽、收發包埠2、四大組件與系統架構2.1、Android四大組件1、Activity1、一個Activity通常就是一個單獨的窗口2、Activity之間通過
  • Android Studio 4.1 發布,全方位提升開發體驗
    4.1 版本的重點訴求之一是幫助您在使用 Android Jetpack 庫 (即 Android 的開發庫套件) 時遵循最佳實踐和提升代碼編寫效率。基於大家的反饋,我們直接在 IDE 中集成了諸多常用的 Android 庫,從而改善了編寫代碼的體驗。
  • Android SDK更新 模擬器支持X86架構
    最令安卓愛好者興奮的莫過於這次更新之後模擬器將支持X86架構,也就是說你可以在Windows和OS X環境下運行安卓模擬器了。在這之前在電腦上用過安卓模擬器的人都知道這是多麼痛苦的一件事,因為模擬器的運行速度不是一般的慢。
  • Android Studio 4.1發布:可直接運行安卓模擬器、支持Dagger導航和TensorFlow Lite模型
    今天我們很高興地發布了穩定版的 Android Studio 4.1,其中包含針對常見的編輯、調試和優化用例的一系列特性。此版本的一大主題是幫助你在使用 Android Jetpack 庫(這是 Android 的庫套件,旨在幫助開發人員遵循最佳實踐並更快地編寫代碼)時提高工作效率。根據大家的反饋,我們對代碼編輯體驗以及流行 Android 庫的 IDE 集成做了許多改進。
  • Android夜間模式最佳實踐
    下面簡要描述下幾種方案的實現原理:一、通過切換theme來實現夜間模式首先在attrs.xml中,為需要隨theme變化的內容定義屬性<?xml version="1.0" encoding="utf-8"?