編輯:關於Android編程
本文主要講述了:
一、BroadcastReceiver概述:
二、BroadcastReceiver事件分類
三、BroadcastReceiver事件的編程流程
四、兩類BroadcastReceiver
五、普通廣播和有序廣播
六、Service與BroadcastReceiver如何交互?
七、開機自動運行service
八、BroadcastReceiver的生命周期
一、BroadcastReceiver概述:
1、廣播接收器是一個專注於接收廣播通知信息,並做出對應處理的組件。很多廣播是源自於系統代碼的──比如,通知時區改變、電池電量低、拍攝了一張照片或者用戶改變了語言選項。應用程序也可以進行廣播──比如說,通知其它應用程序一些數據下載完成並處於可用狀態。1、注冊廣播事件:注冊方式有兩種,
一種是靜態注冊,就是在 AndroidManifest.xml文件中定義,注冊的廣播接收器必須要繼承BroadcastReceiver類;
在AndroidManifest.xml中用標簽生命注冊,並在標簽內用標簽設置過濾器。
//繼承BroadcastReceiver,重寫onReceiver方法 //使用過濾器,接收指定action廣播
IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(String); //為BroadcastReceiver指定action,使之用於接收同action的廣播 registerReceiver(BroadcastReceiver,intentFilter);
發送廣播消息:Context.sendBroadcast(intent )
3、 接收廣播事件:當發送的廣播被接收器監聽到後,會調用它的onReceive()方法,並將包含消息的Intent對象傳給它。onReceive中代碼的執行時間不要超過5s,否則Android會彈出超時dialog。
四、兩類BroadcastReceiver
1、正常廣播 Normal broadcasts(用 Context.sendBroadcast()發送)是完全異步的。它們都運行在一個未定義的順序,通常是在同一時間。這樣會更有效,但意味著receiver不能包含所要使用的結果或中止的API。
2、有序廣播 Ordered broadcasts(用 Context.sendOrderedBroadcast()發送)每次被發送到一個receiver。所謂有序,就是每個receiver執行後可以傳播到下一個receiver,也可以完全中止傳播——不傳播給其他receiver。 而receiver運行的順序可以通過matched intent-filter 裡面的android:priority來控制,當priority優先級相同的時候,Receiver以任意的順序運行。
PS:
下面舉例說明了4種情況的廣播事件:靜態注冊的系統廣播事件、靜態注冊的用戶自定義廣播事件、動態注冊的系統廣播事件和動態注冊的用戶自定義廣播事件。
1、創建廣播接受者
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class MyReceiver extends BroadcastReceiver { private static final String TAG = "MyReceiver"; @Override public void onReceive(Context context, Intent intent) { String msg = intent.getStringExtra("msg"); Log.i(TAG, msg); } }2、廣播注冊
1)靜態注冊
靜態注冊是在AndroidManifest.xml文件中配置的,我們就來為MyReceiver注冊一個廣播地址:
配置了以上信息之後,只要是android.intent.action.MY_BROADCAST這個地址的廣播,MyReceiver都能夠接收的到。
2)動態注冊
動態注冊需要在代碼中動態的指定廣播地址並注冊,通常我們是在Activity或Service注冊一個廣播,下面我們就來看一下注冊的代碼:
MyReceiver receiver = new MyReceiver(); IntentFilter filter = new IntentFilter(); filter.addAction("android.intent.action.MY_BROADCAST"); registerReceiver(receiver, filter);
public void send(View view) { Intent intent = new Intent("android.intent.action.MY_BROADCAST"); intent.putExtra("msg", "hello receiver."); sendBroadcast(intent); }
下面簡單介紹下系統廣播
下面是android系統中定義了很多標准的Broadcast Action來響應系統的廣播事件(只列出一部分)
①ACTION_TIME_CHANGED(時間改變時觸發)
②ACTION_BOOT_COMPLETED(系統啟動完成後觸發)--比如有些程序開機後啟動就是用這種方式來實現的
③ACTION_PACKAGE_ADDED(添加包時觸發)
④ACTION_BATTERY_CHANGED(電量低時觸發)
/** * 系統靜態注冊廣播消息接收器 * * @author zuolongsnail * */ public class SystemReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_BATTERY_LOW)) { Log.e("SystemReceiver", "電量低提示"); Toast.makeText(context, "您的手機電量偏低,請及時充電", Toast.LENGTH_SHORT).show(); } } }
五、普通廣播和有序廣播
上面的例子只是一個接收者來接收廣播,如果有多個接收者都注冊了相同的廣播地址,又會是什麼情況呢,能同時接收到同一條廣播嗎,相互之間會不會有干擾呢?
這就涉及到普通廣播和有序廣播的概念了。
1、Normal Broadcast(普通廣播):Normal Broadcast是完全異步的,可以在同一時刻(邏輯上)被所有接收者接收到,消息傳遞的效率比較高。但缺點是接受者不能將處理結果傳遞給下一個接收者,並且無法終止Broadcast Intent的廣播。
2、Ordered Broadcast(有序廣播):Ordered Broadcast的接收者將按預先聲明的優先級依次接受Broadcast。如:A的級別高於B、B的級別高於C,那麼Broadcast先傳給A,再傳給B,最後傳給C。優先級別聲明在
3、context提供的如下兩個方法用於發送廣播:
sendBroadcast():發送Normal Broadcast
sendOrderedBroadcast():發送OrderedBroadcast。
4、對於OrderedBroadcast而言,系統會根據接收者生命的優先級別順序逐個執行接收者,優先接收到Broadcast的接收者可以終止Broadcast,調用BroadcastReceiver的abortBroadcast()方法即可終止Broadcast。如果Broadcast被前面的接收者終止,後面的接收者就再也無法獲取到Broadcast。
5、不僅如此,對於OrderBroadcast而言,優先接收到Broadcast的接收者可以通過setResultExtras(Bundle)方法將處理結果存入Broadcast中,然後傳給下一個接收者,下一個接收者通過代碼: Bundle bundle=getResultExtras(true)可以獲取上一個接收者存入的數據。
實例:點擊按鈕,兩個Receiver接收同一條廣播,在logcat中打印出數據(按照Receiver的優先順序,Receiver2先,Receiver1後)
Manifest:
//發送廣播,bundle綁上key為a的數據 import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class C48_BroadcastActivity extends Activity { /** Called when the activity is first created. */ Button button; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); button=(Button)findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Intent intent=new Intent("com.song.123"); Bundle bundle=new Bundle(); bundle.putString("a", "aaa"); intent.putExtras(bundle); //有序廣播 sendOrderedBroadcast(intent, null); } }); } }Receiver2
package com.song; //優先接到廣播,bundle綁上key為b的數據 import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; public class MyReceiver2 extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub System.out.println("receiver2"); // context.getSystemService(name); Bundle bundle=intent.getExtras(); bundle.putString("b", "bbb"); System.out.println("a="+bundle.get("a")); setResultExtras(bundle); //切斷廣播 // abortBroadcast(); } }
package com.song; //接收從receiver2傳來的廣播,包含key為a和b的數據 import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; public class MyReceiver1 extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub System.out.println("receiver1"); //要不要接受上一個廣播接收器receiver2傳來的的數據 Bundle bundle=getResultExtras(true); System.out.println("a="+bundle.getString("a")+",b="+bundle.getString("b")); } }
六、Service與BroadcastReceiver如何交互?
我們之前都是先啟動了一個Activity,然後在Activity中啟動服務。如果是這樣,在啟動服務時必須要先啟動一個Activity。在很多時候這樣做有些多余,我們現在可以利用Broadcast Receiver在Android系統啟動時運行一個Activity。也許我們會從中得到一些啟發,既然可以在Broadcast Receiver中啟動Activity,為什麼不能啟動Service呢?說做就做,現在讓我們來驗證一下這個想法。
先編寫一個服務類,這個服務類沒什麼特別的,仍然使用前面兩節編寫的MyService類即可。在AndroidManifest.xml文件中配置MyService類的代碼也相同。
下面來完成最關鍵的一步,就是建立一個BroadcastReceiver,代碼如下:
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class StartupReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 啟動一個Service Intent serviceIntent = new Intent(context, MyService.class); context.startService(serviceIntent); Intent activityIntent = new Intent(context, MessageActivity.class); // 要想在Service中啟動Activity,必須設置如下標志 activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(activityIntent); } }
AndroidManifest.xml文件的完整代碼。
PS:
開機自動運行service
我們經常會有這樣的應用場合,比如消息推送服務,需要實現開機啟動的功能。要實現這個功能,我們就可以訂閱系統“啟動完成(BOOT_COMPLETED)”這條廣播,接收到這條廣播後我們就可以啟動自己的服務了;上面實例其實和開機啟動服務差不多了,下面我們在說說開機啟動服務。
Receiver :
public class BootCompleteReceiver extends BroadcastReceiver { private static final String TAG = "BootCompleteReceiver"; @Override public void onReceive(Context context, Intent intent) { Intent service = new Intent(context, MsgPushService.class); context.startService(service); Log.i(TAG, "Boot Complete. Starting MsgPushService..."); } }
Service :
public class MsgPushService extends Service { private static final String TAG = "MsgPushService"; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate called."); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand called."); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent arg0) { return null; } }AndroidManifest
最後在說說Broadcast的生命周期
1、一個BroadcastReceiver 對象只有在被調用onReceive(Context, Intent)的才有效的,當從該函數返回後,該對象就無效的了,結束生命周期。
因此從這個特征可以看出,在所調用的onReceive(Context, Intent)函數裡,不能有過於耗時的操作,不能使用線程來執行。對於耗時的操作,請start service來完成。因為當得到其他異步操作所返回的結果時,BroadcastReceiver 可能已經無效了。
2、一個Broadcast receiver只有一個回調方法:
void onReceive(Context curContext, Intent broadcastMsg)
當Broadcast receiver接收到一條廣播信息,android會調用它的onReceive()方法,並傳遞給它一個包含廣播信息的intent對象。當Broadcast receiver在執行這個方法時可以認為它是活動的,onReceive()方法返回時,它便終止了。
一個包含活動Broadcast receiver的進程會被系統保護以避免被終止。但是如果進程不包含任何活動組件,那麼當它占用的內存要用於其它進程時,系統任何時候都可以終止運行該進程。
當應答一條廣播信息十分耗時,而另一個線程必須執行某個任務時會出現一個問題。試想一下,如果onReceive()方法產生一個線程然後返回,那麼整個進程,包括新的線程會被認為是不活動的(除非在進程中還有其它活動的組件),該進程就有被系統終止運行的危險。解決這個問題的辦法就是在onReceive()方法中啟動一個服務,讓這個服務做那樣的工作,這樣系統就會知道進程中有活動的組件而不會停止進程。
項目中需要在應用從後台切換到前台時做操作,自己實現了功能,但對這塊的機制不太了解,So.找了相關的資料來學習總結下。!!!部分資料來源https://github.com
tinyalsa位於Android源碼的external/tinyalsa位置。關於tinyalsa,tinyalsa是Google在Android 4.0之後推的基於a
SlidingMenu——方式11、初始化SlidingMenu對象2、設置SlidingMenu的菜單模式。 只有左側菜單,只有右側菜單,或者雙
Android有很多種drawable類型,除了前幾篇詳細講解的shape、selector、layer-list,還有上一篇提到的color、bitmap、