安卓四大組件之廣播

来源:http://www.cnblogs.com/huangjie123/archive/2016/11/10/6052104.html
-Advertisement-
Play Games

廣播是一種廣泛運用的在應用程式之間傳輸信息的機制,Android 為了將系統運行時的各種“事件”通知給其他應用,因此內置了多種廣播。廣播機制最大的特點就是發送方並不關心接收方是否接到數據,也不關心接收方是如何處理數據的。Android 中的每個應用程式都可以對自己感興趣的廣播進行註冊,這樣該程式就只 ...


      廣播是一種廣泛運用的在應用程式之間傳輸信息的機制,Android 為了將系統運行時的各種“事件”通知給其他應用,因此內置了多種廣播。廣播機制最大的特點就是發送方並不關心接收方是否接到數據,也不關心接收方是如何處理數據的。Android 中的每個應用程式都可以對自己感興趣的廣播進行註冊,這樣該程式就只會接收到自己所關心的廣播內容,這些廣播可能是來自於系統的,也可能是來自於其他應用程式的,前者是系統廣播,後者是自定義廣播。廣播在具體的項目中應用場景並不多,但一旦使用會使得程式變得精簡很多,因此本片文章就簡單介紹一下安卓系統的廣播機制。

      首先,簡單的介紹一下安卓的廣播機制。BroadCastReceiver是對發送出來的Broadcast 進行過濾接受並響應的一類組件,是Android四大組件之一,主要用於接收系統或者app發送的廣播事件。在我們的項目中經常使用廣播接收者接收系統通知,比如開機啟動、sd掛載、低電量、外播電話、鎖屏等。 如果我們做的是播放器,那麼監聽到用戶鎖屏後我們應該將我們的播放之暫停等。android的四大組件本質上就是為了實現移動或者說嵌入式設備上的MVC架構,它們之間有時候是一種相互依存的關係, 有時候又是一種補充關係,引入廣播機制可以方便幾大組件的信息和數據交互。廣播有利於程式間互通消息,例如在自己的應用程式內監聽系統來電。 

       BroadCastReceiver的一般編寫步驟:

        1. 寫一個類繼承BroadCastReceiver;

        2. 重寫oncreat()方法;

        3. 註冊廣播。動態註冊和靜態註冊,前者在java代碼中實現,後這在清單文件中編寫。動態註冊需要寫BroadCastReceiver的實現類和過濾器,靜態註冊除了寫過濾器外還要在receiver標簽的name屬性上添加包名和類名。

       按照廣播的屬性來分,廣播分兩種:有序廣播和無序廣播。

      無序廣播:又叫普通廣播,完全非同步,不會被某個廣播接收者終止,邏輯上可以被任何廣播接收者接收到,在廣播發出之後,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,因此它們之間沒有任何先後順序可言。優點是效率較高。缺點是一個接收者不能將處理結果傳遞給下一個接收者,並無法終止廣播intent的傳播。Context.sendBroadcast() 發送的是普通廣播,所有訂閱者都有機會獲得併進行處理。

      有序廣播:按照被接收者的優先順序順序,在被接收者中依次傳播。比如有三個廣播接收者A,B,C,優先順序是A > B > C。那這個消息先傳給A,再傳給B,最後傳給C。,因此通常需要在AndroidManifest.xml 中進行註冊,優先順序別聲明在intent-filter 元素的android:priority 屬性中,數越大優先順序別越高,取值範圍:-1000 到1000,優先順序別也可以調用IntentFilter 對象的setPriority()進行設置。有序廣播的接收者可以終止廣播的傳播,廣播的傳播一旦終止,後面的接收者就無法接收到廣播,有序廣播的接收者可以將數據傳遞給下一個接收者,如:A 得到廣播後,可以往它的結果對象中存入數據,當廣播傳給B 時,B 可以從A 的結果對象中得到A 存入的數據。Context.sendOrderedBroadcast() 發送的是有序廣播。Bundlebundle = getResultExtras(true))可以獲取上一個接收者存入在結果對象中的數據。

      對於有序廣播有一個小細節,那就是優先順序高的廣播可以終結一個廣播。終止一個有序廣播:abortBroadcast()。終止有序廣播只需要一句代碼,該代碼是BroadCastReceiver 類中的方法,因此這裡可以直接使用。這裡需要註意的是如果abortBroadCast 是在一個無序廣播中執行的,那麼就會報如下異常:

          java.lang.RuntimeException:

          BroadcastReceiver trying to return result during a non-ordered broadcast

      在低版本的手機上比如Android2.3 上是不會報這樣的異常的,安卓工程師認為終止無序廣播是不合法的操作,因此在Android2.3之後的版本,終止無序廣播都是非法操作。為了防止我們終止一個無序廣播導致報異常,我們可以先判斷接收到的廣播類型。優化後的代碼如下:

       if (isOrderedBroadcast()) {

              abortBroadcast();

       }

       isOrderedBroadcast 方法是BroadCastReceiver 類提供的,用於判斷當前的廣播類型。返回true 為有序廣播,返回false 為無序廣播。

       之後,再來講一下廣播的註冊機制。廣播註冊方式有兩種,動態註冊和靜態註冊。在清單文件中註冊廣播接收者稱為靜態註冊,在代碼中註冊稱為動態註冊。靜態註冊的廣播接收者只要app在系統中運行則一直可以接收到廣播消息,動態註冊的廣播接收者當註冊的Activity或者Service銷毀了那麼就接收不到廣播了。

       靜態註冊:在清單文件中進行如下配置

<receiver android:name="包名+類名" > 
   <intent-filter>
       <action android:name="android.intent.action.CALL" > </action> 
   </intent-filter> 
</receiver>

 

      動態註冊:在代碼中進行如下註冊

//廣播發送者:
Intent intent = new Intent();
intent.setAction(Uri);
intent.putExtra("packname", packname);
sendBroadcast(intent);

//自定義廣播接受中:
receiver = new BroadcastReceiver();
  IntentFilter intentFilter = new IntentFilter(); 
intentFilter.addAction(Uri);
context.registerReceiver(receiver, intentFilter);

        

        按照廣播的編寫方式來分,又可以分為系統廣播和自定義廣播兩種。

       系統廣播的應用較為普遍,比如文章開頭所講的:系統電量的改變、屏幕的鎖屏、網路狀態的改變、接收到新的簡訊、撥打電話事件、sdcard 的掛載和移除、應用的安裝和卸載等等。比如我們開發的線上播放視頻類的APP,那麼我們就有必要監聽網路轉態改變的事件廣播,如果用戶的網路狀態從wifi 改變為了4G 上網,那麼應該提示用戶是否使用4G 網路繼續播放視頻,如果不提示用戶,那麼就可能導致用戶流量被大量使用,一會兒功夫,用戶可能就要停機了。這些都是通過系統廣播完成。Android給許多系統服務廣播Intent。你可以使用這些基於系統事件的消息來給自己的工程增添一些功能,這些事件如時區變更、數據連接狀態、SMS消息或電話呼叫。系統廣播的編寫也較為簡單,通過查看底層源碼獲取相關的action節點及其他參數,然後把這些寫在清單文件中或動態寫在java代碼中,就可以在BroadCastReceiver的實現類中接受廣播了。文章最後給出一個屏幕點亮和熄滅的案例,再次就不在贅述。       

       給出幾種常用的系統廣播監聽事件中清單文件的寫法。當然若是動態註冊,只需要在java代碼中添加過濾器即可,節點參數類似。

<--!監聽SD卡狀態的receiver節點:-->
<receiver android:name="包名+類名" > 
<intent-filter> 
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/> 
<data android:scheme="file"></data> 
</intent-filter> 
</receiver>


<--!開機監聽:-->
<receiver android:name="包名+類名"> 
<intent-filter>
 <action android:name="android.intent.action.BOOT_COMPLETED" > </action>
</intent-filter>
 </receiver>

<--!電話監聽:-->
<receiver android:name="包名+類名"> 
  <intent-filter> <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
   </intent-filter> 
</receiver>

<--!程式安裝和卸載的監聽:-->
<receiver android:name="包名+類名"> 
  <intent-filter >
    <action android:name="android.intent.action.PACKAGE_ADDED"/> 
   <action android:name="android.intent.action.PACKAGE_REMOVED"/> 
    <data android:scheme="package"></data> 
  </intent-filter> 
</receiver>


<--!簡訊監聽:-->
<receiver android:name="包名+類名" >
    <intent-filter android:priority="1000" >
      <action android:name="android.provider.Telephony.SMS_RECEIVED" /> 
    </intent-filter>
 </receiver>

 

        上面列出來的是幾種較為常見的系統廣播的參數寫法,當然還有一些並未寫出,比如,電量低、日期更改等等,因此可以在需要編寫時查閱相關的底層源碼獲取。

        再來講一下自定義廣播。

        自定義廣播的應用場景通常是,兩個程式間發送數據。例如,用看門狗程式實現程式鎖,當密碼登錄界面上密碼輸入正確時,立即通知看門狗程式跳過輪訓,這樣就可以進入程式。自定義的廣播同樣可以分為有序關播和無序廣播兩種,下麵分別給出自定義的無序廣播和自定義的有序廣播兩個案例。

       自定義無序廣播,定義兩個程式,利用廣播機制發送數據。

       廣播發送者:

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    /**
     * 發送關播
     * @param view
     */
    public void click(View view){
        Intent intent = new Intent();
        intent.setAction("com.example.sendbroadcast.mysend");  //可以自定義action的值
        intent.putExtra("data", "用廣播傳遞的數據");
        sendBroadcast(intent);
    }
}

         廣播接受者:

import android.os.Bundle;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.view.Menu;

public class MainActivity extends Activity {
    private myReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//         兩種註冊的方式,在java代碼中和清單文件中
        receiver = new myReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.example.sendbroadcast.mysend");
        registerReceiver(receiver, filter);
    }

    class myReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            System.out
                    .println("收到廣播發送者發來的數據: " + intent.getStringExtra("data"));
        }
    }
    /**
     * 程式關閉時銷毀廣播
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
        receiver = null;
    }
}

         上述程式還可以在清單文件中註冊。

        <receiver android:name="com.example.receivebroadcast.MyBroadcastReceiver" >
            <intent-filter>
                <action android:name="com.example.sendbroadcast.mysend" />
            </intent-filter>
        </receiver>

 

         自定義有序廣播。本文給出的案例是一個消息傳遞機制,有消息發佈者發佈一個消息,一次傳遞給四個人,優先順序高的接受者修改消息。

         消息發佈者:

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    
    public void click(View view){
        Intent intent = new Intent();
        intent.setAction("com.example.sendordermessage");
        //new MyReceiver()是一個監視程式,監聽最後廣播的數據
        sendOrderedBroadcast(intent, null, new MyReceiver(), null, 1, "我是消息發佈者, 我發佈的消息是: 街上有一隻狼", null);
    }
}

        另外在廣播發佈者中,寫一個監視程式,查看最後一個廣播接受者接受到的數據。

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
         System.out.println("我是內線程式,我收到的消息是: " + getResultData());
    }
}

 

        第一個廣播接受者:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class FirstReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
          System.out.println("我是第一個消息接受者,我聽說" + getResultData());
          setResultData("街上有5只狼");
    }
}

 

      第二個廣播接受者:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class SecondReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
          System.out.println("我是第二個消息接受者,我聽說" + getResultData());
          setResultData("街上有10只猴");
    }
}

       第三個廣播接受者:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class ThirldReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
          System.out.println("我是第三個消息接受者,我聽說" + getResultData());
          setResultData("街上有20只狼");
    }
}

       第四個廣播接受者:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class FourthReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
          System.out.println("我是第四個消息接受者,我聽說" + getResultData());
    }
}

       廣播接受者的清單文件的配置

        <receiver android:name="com.example.receiverorder.FirstReceiver" >
            <intent-filter android:priority="1000" >
                <action android:name="com.example.sendordermessage" />
            </intent-filter>
        </receiver>
        <receiver android:name="com.example.receiverorder.SecondReceiver" >
            <intent-filter android:priority="500" >
                <action android:name="com.example.sendordermessage" />
            </intent-filter>
        </receiver>
        <receiver android:name="com.example.receiverorder.ThirldReceiver" >
            <intent-filter android:priority="0" >
                <action android:name="com.example.sendordermessage" />
            </intent-filter>
        </receiver>
        <receiver android:name="com.example.receiverorder.FourthReceiver" >
            <intent-filter android:priority="-1000" >
                <action android:name="com.example.sendordermessage" />
            </intent-filter>
        </receiver>

 

      工程的目錄結構:

                  

          成果展示:

               

        最後補充一個小內容。在Android 中一些操作比較頻繁的事件,比如鎖屏解屏和電量的變化,也會發送特定的廣播。但是此類廣播的註冊是無法註冊在AndroidManifest.xml 中,只能在代碼中進行註冊。也就是說對於鎖屏解屏和電量變化的監聽只能通過動態註冊。亮屏和息屏的操作經常用於省電處理中,當屏幕熄滅時可以關閉掉一些耗時的服務,當屏幕點亮時再啟動服務。這樣既保證了用戶的使用體驗,同時減少了手機的耗電量,對於產品優化大有裨益。下麵給出屏幕廣播監視案例,寫一個內部類繼承BroadcastReceiver,在該類中接受關播信息。

        

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;

public class MainActivity extends Activity {

    private myReceiver myreceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        myreceiver = new myReceiver();
        registerReceiver(myreceiver, filter);
    }
    /**
     * 廣播實現類
     *
     */
    class myReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            System.out.println(intent.getAction().toString());
        }
    }
    
    /**
     * 反註冊廣播
     * 
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(myreceiver);
        myreceiver = null;
    }
}

        自此,廣播的內容全部講解完畢。

 

 

 

 

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 什麼時候需要 Looper Looper用於封裝了android線程中的消息迴圈,預設情況下一個線程是不存在消息迴圈(message loop)的,需要調用Looper.prepare()來給線程創建一個消息迴圈,調用Looper.loop()來使消息迴圈起作用,使用Looper.prepare() ...
  • 下載地址與說明http://www.androiddevtools.cn/#sdk-list 手動添加 SDK 這是Android開發所需的sdk,下載並解壓後,將解壓出的整個文件夾複製或者移動到 your sdk 路徑/platforms文件夾,然後打開SDK Manager,打開 Tools(工 ...
  • Android Weekly筆記, 本期內容包括: Mockito的擴展; ConstraintLayout的鏈式約束; Kotlin的Async-Await; RxJava2.0; 屏幕旋轉導致的Activity重建; Throwable類的設計問題; Espresso測試中如何等待非同步請求返回;... ...
  • 最近比較關註微信小程式,而且微信小程式的後臺必須強制要求https, https相對http成本要高很多了。 這裡我感覺有2個商機 (1)提供https 中轉伺服器 ,按流量來收費 (2) 微信小程式https 雲後臺 對於很多人學習微信小程式的人來說,需要一個免費的後臺來配合,那麼就可以搞一個 微 ...
  • 在分析Android問題的時候重要的手段之一就是分析log,在events.log中有很多系統log,其中有些log的含義並不是很瞭解,下麵就是從安卓源碼中得到的系統log的tag。 關於Tag的說明如下:以 “30005 am_create_activity (User|1|5),(Token|1 ...
  • 安裝 Node.js 安裝 weex-toolkit 安裝好node.js後,打開CMD工具現在安裝weex-toolkit,這是weex的集成環境。 有了weex-toolkit就可以使用weex命令了我使用的版本是如圖,這個可能變化的很快先看一下weex命令 如果你只是想調試某個we文件,那麼執 ...
  • 轉載請標明出處: "http://www.cnblogs.com/why168888/p/6051120.html" 本文出自: "【Edwin博客園】"         公司運維問我怎麼在windows上安裝模擬器,我 ...
  • 1.是否有邀請信息紅點的設置 2.註冊聯繫人邀請信息變化的廣播 3.邀請信息條目的點擊事件 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...