Android的廣播機制非常靈活,Android的每一個應用程序都可以對自己感興趣的廣播進行注冊,這樣該程序就只會接收到自己所關心的廣播內容,這些廣播可能是來自系統的,也可能是來自于其他應用程序的。
Android提供了一套完整的API,允許應用程序自由地發送和接收廣播。發送廣播借助Intent,而接收廣播的方法利用廣播接收器Broadcast Receiver。
Android 內置了很多系統級別的廣播,我們可以在應用程序中通過監聽這些廣播來得到各種系統的狀態信息。比如手機開機完成后會發出一條廣播,電池的電量發生變化會發出一條廣播,時間或時區發生改變也會發出一條廣播等。而想要接收到這些廣播,需要使用廣播接收器。
動態注冊的廣播接收器雖然可以自由地控制注冊和注銷,但是必須在程序啟動之后才能接收到廣播。如果想讓程序在未啟動的情況下就能接收到廣播,就需要靜態注冊了?,F在我們讓程序接收一條開機廣播,當收到這個條廣播時就可以在onReceive()方法里執行相應的邏輯,從而實現開機啟動的功能。
使用AS提供快捷方式來創建一個廣播接收器,右擊com.example.broadcasttest包——New——Ohter——Broadcast Receiver,會彈出一個窗口,在Class Name里面輸入廣播接收器的名字BootCompleteReceiver,Exported屬性表示是否允許這個廣播接收器接收本程序以外的廣播,Enabled屬性表示是否啟用這個廣播接收器,勾選這兩個屬性,點擊Finish完成創建。修改BootCompleteReceiver中的代碼:public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"Boot Complete",Toast.LENGTH_LONG).show(); }}靜態的廣播接收器一定要在AndroidManifest.xml中注冊才可以使用,不過由于先前是使用AS快捷方式創建的廣播接收器,因此注冊這一步已經被自動完成了。會發現application標簽內多了如下代碼: <receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"></receiver>* 說明:在標簽內出現了一個新的標簽,所有靜態的廣播接收器都是在這里進行注冊的。它的用法其實和標簽很相似,都是通過android:name來指定具體注冊哪一個廣播接收器,而 enaled和exported屬性則是根據我們剛才勾選的狀態自動生成的。
<receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"></receiver>不過目前BootCompleteReceiver還是不能接收到開機廣播的,還需要對廣播進行限定,添加廣播過濾器和申請權限。 <receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver><uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>由于Android系統啟動完成后會發出一條值為android.permission.RECEIVE_BOOT_COMPLETED的廣播,因此我們 標簽里添加了相應的action。 監聽系統開機廣播也是需要聲明權限的,我們使用標簽又加入一條android.permission.RECEIVE_BOOT_COMPLETED權限。將模擬器重新啟動就可以收到開機廣播了。需要額外注意的是,不要在onReceive()方法中添加過多的邏輯或者進行任何的耗時操作,因為在廣播接收器中是不允許開啟線程的,當onReceive方法運行較長時間而沒有結束時,程序就會報錯。所以廣播接收器更多的是扮演一種打開程序其他組件的角色,比如創建一條狀態欄通知,或者啟動一個服務等。以上學習的是系統廣播,現在我們學習如何在程序中發送自定義廣播。廣播分為標準廣播和有序廣播,本節我們就將通過實踐的方式來看一下這兩種廣播具體的區別。
* 這里讓MyBroadcastReceiver接收一條值為com.beidou.broadcasttest.MY_BROADCAST的廣播,因此待會在發送廣播的時候,我們需要發出這樣的一條廣播。
修改activity_main.xml以及MainActivity中的代碼: Button button = (Button) findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent("com.beidou.broadcasttest.MY_BROADCAST"); sendBroadcast(intent); }});<Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="send broadcast!" />* 在點擊事件中,我們加入了發送自定義廣播的邏輯。 1. 首先構建出了一個Intent對象,并把要發送的廣播的值傳入。 2. 然后調用了Context的sendBroadcast方法將廣播發送出去,這樣所有監聽com.com.beidou.broadcasttest.MY_BROADCAST這條廣播的廣播接收器就會受到信息。 3. 由于廣播是使用Intent進行傳遞的,因此你還可以在Intent中攜帶數據傳遞給廣播接收器。
1.新創建 BroadcastTest2 項目,點擊AS-File-New-New Project進行創建. 2.新建AnotherBroadcastReceiver,代碼:
public class AnotherBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show(); }}3.修改配置文件
<receiver android:name=".AnotherBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.beidou.broadcasttest.MY_BROADCAST"></action> </intent-filter></receiver>4.證明了應用程序發出的廣播是可以被其他的程序接收到的。
發送有序廣播,修改MainActivity代碼: Intent intent = new Intent("com.beidou.broadcasttest.MY_BROADCAST");sendOrderedBroadcast(intent,null);1.發送有序廣播只需改動一行代碼,即將sendBroadcast()方法改成sendOrderedBroadcast方法。接收兩個參數,第一個參數仍然是Intent,第二個參數是一個與權限相關的字符串,這里傳入null即可。 2.重新運行程序,兩個應用程序都可以接收到這條廣播,但是這個時候的廣播接收器是有先后順序的,而且前面的廣播接收器還可以將廣播截斷,以阻止其繼續廣播。 3.設定廣播接收器的先后順序,在配置文件里面
<receiver android:name=".MyBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"> <action android:name="com.beidou.broadcasttest.MY_BROADCAST"></action> </intent-filter></receiver>* 通過android:priority=”100”,給MyBroadcastReceiver的優先級設成了100,以保證它一定會在AnotherBroadcastReceiver之前收到廣播. 4. MyBroadcastReceiver獲得了接收廣播的優先權,那么MyBroadcastReceiver就可以選擇是否允許廣播繼續傳遞了。修改MyBroadcastReceiver代碼:
public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"received in MyBroadcastReceiver",Toast.LENGTH_LONG). show(); abortBroadcast(); }}* 在onReceive方法中調用了abortBroadcast方法,就表示將這條廣播截斷,后面的廣播接收器將無法再接收到這條廣播。
前面我們發送和接收的廣播全部屬于系統全局廣播,即發出的廣播既可以被其他任何應用程序接收到,并且我們可以接收到來自于其他任何應用程序的廣播。這樣就存在安全性問題。
比如:我們發送的一些攜帶關鍵性數據的廣播有可能被其他應用程序截獲,或者其他的程序不停地向我們的廣播接收器里發送各種垃圾廣播。
為此,Android引入了一套本地廣播機制:使用這個廣播機制發出的廣播只能夠在應用程序的內部進行傳遞,并且廣播接收器也只能接收來自本應用程序發出的廣播。
用法:主要是使用了一個LocalBroadcastManager來對廣播進行管理,并提供了發送廣播和注冊廣播接收器的方法。修改MainActivity的代碼:public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private LocalReceiver localReceiver; private LocalBroadcastManager localBroadcastManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); localBroadcastManager = LocalBroadcastManager.getInstance(this); //獲取實例 Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent("com.beidou.broadcasttest.LOCAL_BROADCAST"); localBroadcastManager.sendBroadcast(intent);//發送廣播 } }); intentFilter = new IntentFilter(); intentFilter.addAction("com.beidou.broadcasttest.LOCAL_BROADCAST"); localReceiver = new LocalReceiver(); localBroadcastManager.registerReceiver(localReceiver, intentFilter);//注冊廣播 } class LocalReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT). show(); } } @Override protected void onDestroy() { super.onDestroy(); localBroadcastManager.unregisterReceiver(localReceiver);//注銷廣播 }}以上代碼和動態注冊廣播接收器以及發送廣播的代碼是一樣的。只不過現在首先是通過LocalBroadcastManager的getInstance()方法獲取它的一個實例,然后在注冊廣播接收器的時候調用的是LocalBroadcastManager的registerReceiver()方法,在發送廣播的時候調用的是LocalBroadcastManager的sendBroadcast()方法。注意點:本地廣播是無法靜態注冊的方式來接收的。主要是因為靜態注冊主要就是為了讓程序在未啟動的情況下也能收到廣播,而發送本地廣播時,我們的程度肯定是已經啟動的,因此也 不需要使用靜態注冊的功能。本地廣播的優勢: ① 可以明確地知道正在發送的廣播不會離開我們的程序,因此不必擔心泄密。 ② 其他的程序無法將廣播發送到我們程序的內部,因此不需要擔心會有安全漏洞。 ③ 發送本地廣播比發送系統廣播將會更加高效。強制下線功能比較常見:比如QQ在別處登錄了,就會將你強制擠下線。
邏輯:只需要在界面上彈出一個對話框,讓用戶無法進行任何其他操作,必須要點擊對話框中的確定按鈕,然后回到登錄界面即可。
疑問:當我們被通知需要強制下線時可能正處于任何一個界面,難道需要在每個界面上都編寫一個彈出對話框的邏輯?那到不用,借助廣播知識,可以輕松實現這一個功能。
強制下線功能需要先關閉掉所有的活動,然后回到登錄界面。創建BroadcastBestPractice項目,創建一個ActivityCollector類用于管理所有的活動:public class ActivityCollector { public static List<Activity> activities = new ArrayList<>(); public static void addActivity(Activity activity) { activities.add(activity); } public static void removeActivity(Activity activity) { activities.remove(activity); } public static void finishAll() { for (Activity activity : activities) { if (!activity.isFinishing()) { activity.finish(); } } }}創建BaseActivity類作為所有活動的父類。public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityCollector.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); }}創建一個登錄頁面的活動,新建LoginActivity,編輯布局activity_login.xml:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="60dp" android:orientation="horizontal"> <TextView android:layout_width="90dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="Account:" android:textSize="18sp" /> <EditText android:id="@+id/account" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="60dp" android:orientation="horizontal"> <TextView android:layout_width="90dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="PassWord:" android:textSize="18sp" /> <EditText android:id="@+id/password" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1" android:inputType="textPassword" /> </LinearLayout> <Button android:id="@+id/login" android:layout_width="match_parent" android:layout_height="60dp" android:text="login" /></LinearLayout>public class LoginActivity extends BaseActivity { private EditText accountEdit; private EditText passwordEdit; private Button login; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); accountEdit = (EditText) findViewById(R.id.account); passwordEdit = (EditText) findViewById(R.id.password); login = (Button) findViewById(R.id.login); login.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String account = accountEdit.getText().toString(); String password = passwordEdit.getText().toString(); //如果賬號是admin 且密碼是123456,就認為登錄成功 if (account.equals("admin") && password.equals("123456")) { startActivity(new Intent(LoginActivity.this, MainActivity.class)); finish(); } else { Toast.makeText(LoginActivity.this, "account or password is invalid", Toast.LENGTH_LONG).show(); } } }); }}修改activity_main.xml和MainActivity:<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:id="@+id/force_offline" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Send force offline broadcast" /></LinearLayout>public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button forceoffline = (Button) findViewById(R.id.force_offline); forceoffline.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent("com.beidou.broadcastbestpractice. FORCE_OFFLINE"); sendBroadcast(intent); } }); }}在BaseActivity中動態注冊一個廣播接收器就可以了。public class BaseActivity extends AppCompatActivity { private ForceOfflineReceiver receiver; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityCollector.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); } @Override protected void onResume() { super.onResume(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("com.beidou.broadcastbestpractice.FORCE_OFFLINE"); receiver = new ForceOfflineReceiver(); registerReceiver(receiver,intentFilter); } @Override protected void onPause() { super.onPause(); if (receiver!=null){ unregisterReceiver(receiver); receiver = null; } } class ForceOfflineReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, Intent intent) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("Warning"); builder.setMessage("You are forced to be offline. Please try to login again"); builder.setCancelable(false); builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { ActivityCollector.finishAll(); //銷毀所有的活動 Intent intent = new Intent(context,LoginActivity.class); context.startActivity(intent);//重新啟動LoginActivity } }); builder.show(); } }}修改AndroidManifest.xml文件:<application android:allowBackup="true" android:icon="@m* 綜上:當賬號和密碼正確的時候調轉到主頁面MainActivity,在主頁面里面發出一個廣播(BaseActivity里面已經加入了注冊和取消廣播的代碼),所以廣播發出后,BaseActivity里面的廣播接收器就會接收到廣播,與此同時彈出對話框,點擊OK,進行強制所有活動下線的操作,并重新打開登錄頁面。1.配置身份 git config –global user.name “FUkaiqiang” git config –global user.email “18201685396@163.com 2.驗證身份 git config –global user.name git config –global user.emai 3.創建代碼倉庫(進入項目存放的目錄) cd D: cd Test cd BroadcastBestPractice git init * 此時會生成一個.git文件夾,用來記錄所有的Git操作的 4.查看列表 ls -al 5. 提交本地代碼(先用add把想要的代碼先添加進來,而conmit則是真正地去執行提交操作) 提交單個文件 git add build.gradle 提交整個目錄 git add . 提交:git commit -m “First commit” (m不可少)
新聞熱點
疑難解答