編輯:關於android開發
今天我們來講一下Android中BroadcastReceiver的相關知識。
BroadcastReceiver也就是“廣播接收者”的意思,顧名思義,它就是用來接收來自系統和應用中的廣播。
在Android系統中,廣播體現在方方面面,例如當開機完成後系統會產生一條廣播,接收到這條廣播就能實現開機啟動服務的功能;當網絡狀態改變時系統會產生一條廣播,接收到這條廣播就能及時地做出提示和保存數據等操作;當電池電量改變時,系統會產生一條廣播,接收到這條廣播就能在電量低時告知用戶及時保存進度,等等。
Android中的廣播機制設計的非常出色,很多事情原本需要開發者親自操作的,現在只需等待廣播告知自己就可以了,大大減少了開發的工作量和開發周期。而作為應用開發者,就需要數練掌握Android系統提供的一個開發利器,那就是BroadcastReceiver。下面我們就對BroadcastReceiver逐一地分析和演練,了解和掌握它的各種功能和用法。
首先,我們來演示一下創建一個BroadcastReceiver,並讓這個BroadcastReceiver能夠根據我們的需要來運行。
要創建自己的BroadcastReceiver對象,我們需要繼承android.content.BroadcastReceiver,並實現其onReceive方法。下面我們就創建一個名為MyReceiver廣播接收者:
packagecom.scott.receiver; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.util.Log; publicclassMyReceiverextendsBroadcastReceiver{ privatestaticfinalStringTAG="MyReceiver"; @Override publicvoidonReceive(Contextcontext,Intentintent){ Stringmsg=intent.getStringExtra("msg"); Log.i(TAG,msg); } }在onReceive方法內,我們可以獲取隨廣播而來的Intent中的數據,這非常重要,就像無線電一樣,包含很多有用的信息。
在創建完我們的BroadcastReceiver之後,還不能夠使它進入工作狀態,我們需要為它注冊一個指定的廣播地址。沒有注冊廣播地址的BroadcastReceiver就像一個缺少選台按鈕的收音機,雖然功能俱備,但也無法收到電台的信號。下面我們就來介紹一下如何為BroadcastReceiver注冊廣播地址。
靜態注冊
靜態注冊是在AndroidManifest.xml文件中配置的,我們就來為MyReceiver注冊一個廣播地址:
配置了以上信息之後,只要是android.intent.action.MY_BROADCAST這個地址的廣播,MyReceiver都能夠接收的到。注意,這種方式的注冊是常駐型的,也就是說當應用關閉後,如果有廣播信息傳來,MyReceiver也會被系統調用而自動運行。
動態注冊
動態注冊需要在代碼中動態的指定廣播地址並注冊,通常我們是在Activity或Service注冊一個廣播,下面我們就來看一下注冊的代碼:
MyReceiverreceiver=newMyReceiver(); IntentFilterfilter=newIntentFilter(); filter.addAction("android.intent.action.MY_BROADCAST"); registerReceiver(receiver,filter); 注意,registerReceiver是android.content.ContextWrapper類中的方法,Activity和Service都繼承了ContextWrapper,所以可以直接調用。在實際應用中,我們在Activity或Service中注冊了一個BroadcastReceiver,當這個Activity或Service被銷毀時如果沒有解除注冊,系統會報一個異常,提示我們是否忘記解除注冊了。所以,記得在特定的地方執行解除注冊操作: @Override protectedvoidonDestroy(){ super.onDestroy(); unregisterReceiver(receiver); }執行這樣行代碼就可以解決問題了。注意,這種注冊方式與靜態注冊相反,不是常駐型的,也就是說廣播會跟隨程序的生命周期。
我們可以根據以上任意一種方法完成注冊,當注冊完成之後,這個接收者就可以正常工作了。我們可以用以下方式向其發送一條廣播:
publicvoidsend(Viewview){ Intentintent=newIntent("android.intent.action.MY_BROADCAST"); intent.putExtra("msg","helloreceiver."); sendBroadcast(intent); }注意,sendBroadcast也是android.content.ContextWrapper類中的方法,它可以將一個指定地址和參數信息的Intent對象以廣播的形式發送出去。
點擊發送按鈕,執行send方法,控制台打印如下:
看到這樣的打印信息,表明我們的廣播已經發出去了,並且被MyReceiver准確無誤的接收到了。
上面的例子只是一個接收者來接收廣播,如果有多個接收者都注冊了相同的廣播地址,又會是什麼情況呢,能同時接收到同一條廣播嗎,相互之間會不會有干擾呢?這就涉及到普通廣播和有序廣播的概念了。
普通廣播(Normal Broadcast)
普通廣播對於多個接收者來說是完全異步的,通常每個接收者都無需等待即可以接收到廣播,接收者相互之間不會有影響。對於這種廣播,接收者無法終止廣播,即無法阻止其他接收者的接收動作。
為了驗證以上論斷,我們新建三個BroadcastReceiver,演示一下這個過程,FirstReceiver、SecondReceiver和ThirdReceiver的代碼如下:
packagecom.scott.receiver; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.util.Log; publicclassFirstReceiverextendsBroadcastReceiver{ privatestaticfinalStringTAG="NormalBroadcast"; @Override publicvoidonReceive(Contextcontext,Intentintent){ Stringmsg=intent.getStringExtra("msg"); Log.i(TAG,"FirstReceiver:"+msg); } } publicclassSecondReceiverextendsBroadcastReceiver{ privatestaticfinalStringTAG="NormalBroadcast"; @Override publicvoidonReceive(Contextcontext,Intentintent){ Stringmsg=intent.getStringExtra("msg"); Log.i(TAG,"SecondReceiver:"+msg); } } publicclassThirdReceiverextendsBroadcastReceiver{ privatestaticfinalStringTAG="NormalBroadcast"; @Override publicvoidonReceive(Contextcontext,Intentintent){ Stringmsg=intent.getStringExtra("msg"); Log.i(TAG,"ThirdReceiver:"+msg); } } 然後再次點擊發送按鈕,發送一條廣播,控制台打印如下:看來這三個接收者都接收到這條廣播了,我們稍微修改一下三個接收者,在onReceive方法的最後一行添加以下代碼,試圖終止廣播:
abortBroadcast(); 再次點擊發送按鈕,我們會發現,控制台中三個接收者仍然都打印了自己的日志,表明接收者並不能終止廣播。有序廣播(Ordered Broadcast)
有序廣播比較特殊,它每次只發送到優先級較高的接收者那裡,然後由優先級高的接受者再傳播到優先級低的接收者那裡,優先級高的接收者有能力終止這個廣播。
為了演示有序廣播的流程,我們修改一下上面三個接收者的代碼,如下:
packagecom.scott.receiver; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.os.Bundle; importandroid.util.Log; publicclassFirstReceiverextendsBroadcastReceiver{ privatestaticfinalStringTAG="OrderedBroadcast"; @Override publicvoidonReceive(Contextcontext,Intentintent){ Stringmsg=intent.getStringExtra("msg"); Log.i(TAG,"FirstReceiver:"+msg); Bundlebundle=newBundle(); bundle.putString("msg",msg+"@FirstReceiver"); setResultExtras(bundle); } }代碼改完之後,我們需要為三個接收者注冊廣播地址,我們修改一下AndroidMainfest.xml文件:
我們看到,現在這三個接收者的
現在,我們需要修改一下發送廣播的代碼,如下:
publicvoidsend(Viewview){ Intentintent=newIntent("android.intent.action.MY_BROADCAST"); intent.putExtra("msg","helloreceiver."); sendOrderedBroadcast(intent,"scott.permission.MY_BROADCAST_PERMISSION"); } 注意,使用sendOrderedBroadcast方法發送有序廣播時,需要一個權限參數,如果為null則表示不要求接收者聲明指定的權限,如果不為null,則表示接收者若要接收此廣播,需聲明指定權限。這樣做是從安全角度考慮的,例如系統的短信就是有序廣播的形式,一個應用可能是具有攔截垃圾短信的功能,當短信到來時它可以先接受到短信廣播,必要時終止廣播傳遞,這樣的軟件就必須聲明接收短信的權限。
所以我們在AndroidMainfest.xml中定義一個權限:
android:name="scott.permission.MY_BROADCAST_PERMISSION"/> 然後聲明使用了此權限:關於這部分如果有不明白的地方可以參考我之前寫過的一篇文章:Android聲明和使用權限
然後我們點擊發送按鈕發送一條廣播,控制台打印如下:
我們看到接收是按照順序的,第一個和第二個都在結果集中加入了自己的標記,並且向優先級低的接收者傳遞下去。
既然是順序傳遞,試著終止這種傳遞,看一看效果如何,我們修改FirstReceiver的代碼,在onReceive的最後一行添加以下代碼:
abortBroadcast(); 然後再次運行程序,控制台打印如下:
此次,只有第一個接收者執行了,其它兩個都沒能執行,因為廣播被第一個接收者終止了。
上面就是BroadcastReceiver的介紹,下面我將會舉幾個常見的例子加深一下大家對廣播的理解和應用:
1.開機啟動服務
我們經常會有這樣的應用場合,比如消息推送服務,需要實現開機啟動的功能。要實現這個功能,我們就可以訂閱系統“啟動完成”這條廣播,接收到這條廣播後我們就可以啟動自己的服務了。我們來看一下BootCompleteReceiver和MsgPushService的具體實現:
packagecom.scott.receiver; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.util.Log; publicclassBootCompleteReceiverextendsBroadcastReceiver{ privatestaticfinalStringTAG="BootCompleteReceiver"; @Override publicvoidonReceive(Contextcontext,Intentintent){ Intentservice=newIntent(context,MsgPushService.class); context.startService(service); Log.i(TAG,"BootComplete.StartingMsgPushService..."); } } packagecom.scott.receiver; importandroid.app.Service; importandroid.content.Intent; importandroid.os.IBinder; importandroid.util.Log; publicclassMsgPushServiceextendsService{ privatestaticfinalStringTAG="MsgPushService"; @Override publicvoidonCreate(){ super.onCreate(); Log.i(TAG,"onCreatecalled."); } @Override publicintonStartCommand(Intentintent,intflags,intstartId){ Log.i(TAG,"onStartCommandcalled."); returnsuper.onStartCommand(intent,flags,startId); } @Override publicIBinderonBind(Intentarg0){ returnnull; } } 然後我們需要在AndroidManifest.xml中配置相關信息:
如果我們查看已運行的服務就會發現,MsgPushService已經運行起來了。
2.網絡狀態變化
在某些場合,比如用戶浏覽網絡信息時,網絡突然斷開,我們要及時地提醒用戶網絡已斷開。要實現這個功能,我們可以接收網絡狀態改變這樣一條廣播,當由連接狀態變為斷開狀態時,系統就會發送一條廣播,我們接收到之後,再通過網絡的狀態做出相應的操作。下面就來實現一下這個功能:
packagecom.scott.receiver; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.net.ConnectivityManager; importandroid.net.NetworkInfo; importandroid.util.Log; importandroid.widget.Toast; publicclassNetworkStateReceiverextendsBroadcastReceiver{ privatestaticfinalStringTAG="NetworkStateReceiver"; @Override publicvoidonReceive(Contextcontext,Intentintent){ Log.i(TAG,"networkstatechanged."); if(!isNetworkAvailable(context)){ Toast.makeText(context,"networkdisconnected!",0).show(); } } /** *網絡是否可用 * *@paramcontext *@return */ publicstaticbooleanisNetworkAvailable(Contextcontext){ ConnectivityManagermgr=(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[]info=mgr.getAllNetworkInfo(); if(info!=null){ for(inti=0;i
3.電量變化
如果我們閱讀軟件,可能是全屏閱讀,這個時候用戶就看不到剩余的電量,我們就可以為他們提供電量的信息。要想做到這一點,我們需要接收一條電量變化的廣播,然後獲取百分比信息,這聽上去挺簡單的,我們就來實現以下:
packagecom.scott.receiver; importandroid.content.BroadcastReceiver; importandroid.content.Context; importandroid.content.Intent; importandroid.os.BatteryManager; importandroid.util.Log; publicclassBatteryChangedReceiverextendsBroadcastReceiver{ privatestaticfinalStringTAG="BatteryChangedReceiver"; @Override publicvoidonReceive(Contextcontext,Intentintent){ intcurrLevel=intent.getIntExtra(BatteryManager.EXTRA_LEVEL,0);//當前電量 inttotal=intent.getIntExtra(BatteryManager.EXTRA_SCALE,1);//總電量 intpercent=currLevel*100/total; Log.i(TAG,"battery:"+percent+"%"); } } 然後再注冊一下廣播接地址信息就可以了:
當然,有些時候我們是要立即獲取電量的,而不是等電量變化的廣播,比如當閱讀軟件打開時立即顯示出電池電量。我們可以按以下方式獲取:
IntentbatteryIntent=getApplicationContext().registerReceiver(null, newIntentFilter(Intent.ACTION_BATTERY_CHANGED)); intcurrLevel=batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL,0); inttotal=batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE,1); intpercent=currLevel*100/total; Log.i("battery","battery:"+percent+"%");Android中實時預覽UI和編寫UI的各種技巧,androidui一、啰嗦 之前有讀者反饋說,你搞這個所謂的最佳實踐,每篇文章最後就給了一個庫,感覺不是很高大上。其實,
Android 中關於Fragment嵌套Fragment的問題,androidfragment轉載請注明出處:http://www.cnblogs.com/Joanna
Android Ndk中C與JAVA之間的互相調用 本例實現一個簡單的場景: 1、使用C編寫一個方法,接收1個參數,方法中調用Java的方法。 2、Java類中編寫一
Android ViewPager滑動背景漸變,androidviewpager原理