Android 四大組件之再論BroadCast

来源:http://www.cnblogs.com/deman/archive/2016/11/13/5989169.html
-Advertisement-
Play Games

BroadCast 是android提供的跨進程通訊的有一利器。 1.非同步執行onReceiver 這是context裡面註冊廣播的API,duplex2個我們不常用的東東。 我們分別來討論這2個東西。 先討論非同步handler。 如果我們傳入一個handler,會怎樣?我們所有的onReceive ...


BroadCast 是android提供的跨進程通訊的有一利器。

1.非同步執行onReceiver

    @Nullable
    public abstract Intent registerReceiver(BroadcastReceiver receiver,
            IntentFilter filter, @Nullable String broadcastPermission,
            @Nullable Handler scheduler);

這是context裡面註冊廣播的API,duplex2個我們不常用的東東。

我們分別來討論這2個東西。

先討論非同步handler。

如果我們傳入一個handler,會怎樣?我們所有的onReceiver是運行在主線程嗎?

package com.joyfulmath.samples.broadcastsample;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

import com.joyfulmath.servicesample.R;
import com.joyfulmath.servicesample.TraceLog;

import org.androidannotations.annotations.AfterViews;
import org.androidannotations.annotations.Click;
import org.androidannotations.annotations.EActivity;

/**
 * Created by Administrator on 2016/10/23 0023.
 */
@EActivity(R.layout.activity_main)
public class BcSampleActivity extends Activity {

    HandlerThread mThread = null;
    Handler nHandler = null;
    BroadCastAAA aReceiver = new BroadCastAAA();

    @Override
    protected void onResume() {
        super.onResume();
        TraceLog.i();


    }

    @Override
    protected void onPause() {
        super.onPause();
        TraceLog.i();
        unregisterReceiver(aReceiver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        aReceiver = null;
    }

    @AfterViews
    void initViews() {
        TraceLog.i();
        mThread = new HandlerThread("samples") {
            @Override
            protected void onLooperPrepared() {
                nHandler = new Handler(getLooper()) {
                    @Override
                    public void dispatchMessage(Message msg) {
                        TraceLog.i(msg.toString());
                        super.dispatchMessage(msg);
                    }

                    @Override
                    public void handleMessage(Message msg) {
                        TraceLog.i();
                        dispatchMsg(msg);
                    }
                };
                IntentFilter filter = new IntentFilter("com.joyfulmath.sample.broadcast.action");
                registerReceiver(aReceiver, filter, null, nHandler);
            }
        };
        mThread.start();
    }

    @Click(R.id.btn_connect)
    void onBtnClick() {
        TraceLog.i();
//        nHandler.sendEmptyMessage(0x01);
        sendBroadcast(new Intent("com.joyfulmath.sample.broadcast.action"));
    }

    void dispatchMsg(Message msg) {
        switch (msg.what) {
            case 0x1:
                TraceLog.i();
                break;
        }
        TraceLog.i();
    }

    public class BroadCastAAA extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            TraceLog.i();
        }
    }
}
broadcastsample

通過上述代碼,我們添加了一個運行在handlerThread裡面的nHandler。

log如下:

10-22 22:32:21.332 16889-16889/com.joyfulmath.servicesample I/BcSampleActivity$override: onBtnClick:  [at (BcSampleActivity.java:79)]
10-22 22:32:21.347 16889-21599/com.joyfulmath.servicesample I/BcSampleActivity$1$1$override: dispatchMessage: { when=-2ms callback=android.app.LoadedApk$ReceiverDispatcher$Args target=com.joyfulmath.samples.broadcastsample.BcSampleActivity$1$1 } [at (BcSampleActivity.java:59)]
10-22 22:32:21.348 16889-21599/com.joyfulmath.servicesample I/BcSampleActivity$BroadCastAAA: onReceive:  [at (BcSampleActivity.java:100)]

可以看到,onReceive沒有運行在主線程,運行在哪裡?就是nHandler的線程。

還有一個:callback=android.app.LoadedApk$ReceiverDispatcher$Args 這個是什麼,看名字可以猜測,這個就是分發Receiver的東西。

可以看這個registerReceiver的註釋:

Handler identifying the thread that will receive
     *      the Intent.  If null, the main thread of the process will be used.

所以onReceiver 是可以運行在子線程的。這裡有個問題:public class BroadCastAAA extends BroadcastReceiver

是內部類,如果運行在子線程,會不會導致記憶體泄露?

這些問題後續在分析.

2.Permission

 許可權管理分2塊:

public abstract void sendBroadcast(Intent intent,
            @Nullable String receiverPermission);

這裡有個permission

還有就是上一節提到的permission。

第一種:

        sendBroadcast(new Intent("com.joyfulmath.sample.broadcast.action"), Contants.BROADCAST_ACTION);
Permission Denial: receiving Intent { act=com.joyfulmath.sample.broadcast.action flg=0x10 } to ProcessRecord{39c0edf1 2286:com.joyfulmath.servicesample/u0a62} (pid=2286, uid=10062) requires com.joyfulmath.samples.broadcast.action due to sender com.joyfulmath.servicesample (uid 10062)

可以看到報permission denial。就是沒有許可權。報的錯誤是基於進程的。

permission定義在發送app,接受app需要userpermission。

當然解決這個錯誤就是在manifest裡面use這條permission。

第二種:

registerReceiver(aReceiver, filter, "com.joyfulmath.samples.broadcast.permission", nHandler);

靜態註冊也一樣。

這條語句的意思是:只有含有permisson的progress發送的廣播才能被收到。

permission定義在receiver中,發送app需要userpermission才能收到。

3.Local Broadcast:App應用內廣播(此處的App應用以App應用進程為界)

相比於全局廣播,App應用內廣播優勢體現在:

1.安全性更高;

2.更加高效。

為此,Android v4相容包中給出了封裝好的LocalBroadcastManager類,用於統一處理App應用內的廣播問題,使用方式上與通常的全局廣播幾乎相同,只是註冊/取消註冊廣播接收器和發送廣播時將主調context變成了LocalBroadcastManager的單一實例。

4.不同註冊方式的廣播接收器回調onReceive(context, intent)中的context具體類型

1).對於靜態註冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是ReceiverRestrictedContext;

2).對於全局廣播的動態註冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是Activity Context;

3).對於通過LocalBroadcastManager動態註冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是Application Context。

註:對於LocalBroadcastManager方式發送的應用內廣播,只能通過LocalBroadcastManager動態註冊的ContextReceiver才有可能接收到(靜態註冊或其他方式動態註冊的ContextReceiver是接收不到的)。

 

5.不同Android API版本中廣播機制相關API重要變遷

1).Android5.0/API level 21開始粘滯廣播和有序粘滯廣播過期,以後不再建議使用;

2).”靜態註冊的廣播接收器即使app已經退出,主要有相應的廣播發出,依然可以接收到,但此種描述自Android 3.1開始有可能不再成立“

Android 3.1開始系統在Intent與廣播相關的flag增加了參數,分別是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES。

FLAG_INCLUDE_STOPPED_PACKAGES:包含已經停止的包(停止:即包所在的進程已經退出)

FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已經停止的包

主要原因如下:

自Android3.1開始,系統本身則增加了對所有app當前是否處於運行狀態的跟蹤。在發送廣播時,不管是什麼廣播類型,系統預設直接增加了值為FLAG_EXCLUDE_STOPPED_PACKAGES的flag,導致即使是靜態註冊的廣播接收器,對於其所在進程已經退出的app,同樣無法接收到廣播。

詳情參加Android官方文檔:http://developer.android.com/about/versions/android-3.1.html#launchcontrols

由此,對於系統廣播,由於是系統內部直接發出,無法更改此intent flag值,因此,3.1開始對於靜態註冊的接收系統廣播的BroadcastReceiver,如果App進程已經退出,將不能接收到廣播。

但是對於自定義的廣播,可以通過覆寫此flag為FLAG_INCLUDE_STOPPED_PACKAGES,使得靜態註冊的BroadcastReceiver,即使所在App進程已經退出,也能能接收到廣播,並會啟動應用進程,但此時的BroadcastReceiver是重新新建的。

1 Intent intent = new Intent();
2 intent.setAction(BROADCAST_ACTION);
3 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
4 intent.putExtra("name", "qqyumidi");
5 sendBroadcast(intent);

註1:對於動態註冊類型的BroadcastReceiver,由於此註冊和取消註冊實在其他組件(如Activity)中進行,因此,不受此改變影響。

註2:在3.1以前,相信不少app可能通過靜態註冊方式監聽各種系統廣播,以此進行一些業務上的處理(如即時app已經退出,仍然能接收到,可以啟動service等..),3.1後,靜態註冊接受廣播方式的改變,將直接導致此類方案不再可行。於是,通過將Service與App本身設置成不同的進程已經成為實現此類需求的可行替代方案。

 

 


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

-Advertisement-
Play Games
更多相關文章
  • retrofit 英文名字是改裝的意思,也就是說他是對網路請求的一種改裝,他不負責進行網路請求,他是對請求方式的一種封裝。真正進行網路請求的是okhttp。 以下所有內容在Android Studio已經導入retrofit為基礎。導入方式如下: 利用Retrofit進行簡單的GET請求 retro ...
  • 現在的cocoapods與之前比較。有很多不一樣的地方。自己試了一試,終於搞定。現在大概紀錄一下。 1.首先查看ruby是否是最新版的。 ruby是通過rvm安裝的所以需要先安裝rvm後查看ruby是否是最新的(通過查看ruby版本列表來判斷),在終端需要中輸入以下命令: (1)安裝RVM.RVM: ...
  • 1.設置電池狀態 1_監聽電量變化廣播 2_電量刷新方法 2.得到系統時間並且更新顯示 1_得到系統時間 2_在hanlder設置更新 3.傳遞播放列表 序列化 4.監聽播放完成和播放上一個視頻 1_設置監聽播放完成 2_點擊下一個按鈕 5.播放器控制面板控制面 1_手勢識別器 2_隱藏和顯示控制面 ...
  • https://www.buzzingandroid.com/tools/android-layout-finder/ ...
  • 筆者長期從事於資料庫的開發,算了,不提當年了,因為一直用的是小語種(PowerBuilder),還是來說說這兩個最常見的控制項吧! RadioButton(單選)和CheckBox(多選) 先來看看繼承關係吧 兩個還是親兄弟,是View的第四代傳人,是View的玄孫,好小呀! RadioButton必 ...
  • 1.MediaPlayer和VideoView介紹 Android 系統中提供開發者開發多媒體應用(音視頻方面) 一,MediaPlayer, 解碼的是底層,MediaPlayer負責和底層打交道,封裝了很多方法 start,pause,stop ,播放視頻的類 這個MediaPlayer可以播放本 ...
  • 這周過的實在是艱辛,自打這周二起我的本本就開始鬧“罷工”,最後還是重裝系統了事. . . 只是可憐了我的那些被格了的軟體(悲傷辣麽大)! 往事不要再提,人生幾度風雨... 簡單的說一下(這不是介紹)Android對話框吧 就這樣吧,我要去整理一下我的“傷心事”了。 另外:麻煩有懂 sublime的大 ...
  • 因為開發微信支付需要IOS9.0版本,但是本來的XCode是6.0版本,所以最高的是8.3版本,所以要進行升級更新,但是打開更新中心發現沒有更新提示,所以到AppStore裡面進行下載,因為本機已經安裝了Xcode,所以點擊下載就變成更新,但是點擊更新卻沒有反應,死活沒有反應,後來以為是因為有了Xc ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...