Android的廣播機(jī)制非常靈活,Android的每一個(gè)應(yīng)用程序都可以對(duì)自己感興趣的廣播進(jìn)行注冊(cè),這樣該程序就只會(huì)接收到自己所關(guān)心的廣播內(nèi)容,這些廣播可能是來自系統(tǒng)的,也可能是來自于其他應(yīng)用程序的。
Android提供了一套完整的API,允許應(yīng)用程序自由地發(fā)送和接收廣播。發(fā)送廣播借助Intent,而接收廣播的方法利用廣播接收器Broadcast Receiver。
Android 內(nèi)置了很多系統(tǒng)級(jí)別的廣播,我們可以在應(yīng)用程序中通過監(jiān)聽這些廣播來得到各種系統(tǒng)的狀態(tài)信息。比如手機(jī)開機(jī)完成后會(huì)發(fā)出一條廣播,電池的電量發(fā)生變化會(huì)發(fā)出一條廣播,時(shí)間或時(shí)區(qū)發(fā)生改變也會(huì)發(fā)出一條廣播等。而想要接收到這些廣播,需要使用廣播接收器。
動(dòng)態(tài)注冊(cè)的廣播接收器雖然可以自由地控制注冊(cè)和注銷,但是必須在程序啟動(dòng)之后才能接收到廣播。如果想讓程序在未啟動(dòng)的情況下就能接收到廣播,就需要靜態(tài)注冊(cè)了?,F(xiàn)在我們讓程序接收一條開機(jī)廣播,當(dāng)收到這個(gè)條廣播時(shí)就可以在onReceive()方法里執(zhí)行相應(yīng)的邏輯,從而實(shí)現(xiàn)開機(jī)啟動(dòng)的功能。
使用AS提供快捷方式來創(chuàng)建一個(gè)廣播接收器,右擊com.example.broadcasttest包——New——Ohter——Broadcast Receiver,會(huì)彈出一個(gè)窗口,在Class Name里面輸入廣播接收器的名字BootCompleteReceiver,Exported屬性表示是否允許這個(gè)廣播接收器接收本程序以外的廣播,Enabled屬性表示是否啟用這個(gè)廣播接收器,勾選這兩個(gè)屬性,點(diǎn)擊Finish完成創(chuàng)建。修改BootCompleteReceiver中的代碼:public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"Boot Complete",Toast.LENGTH_LONG).show(); }}靜態(tài)的廣播接收器一定要在AndroidManifest.xml中注冊(cè)才可以使用,不過由于先前是使用AS快捷方式創(chuàng)建的廣播接收器,因此注冊(cè)這一步已經(jīng)被自動(dòng)完成了。會(huì)發(fā)現(xiàn)application標(biāo)簽內(nèi)多了如下代碼: <receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"></receiver>* 說明:在標(biāo)簽內(nèi)出現(xiàn)了一個(gè)新的標(biāo)簽,所有靜態(tài)的廣播接收器都是在這里進(jìn)行注冊(cè)的。它的用法其實(shí)和標(biāo)簽很相似,都是通過android:name來指定具體注冊(cè)哪一個(gè)廣播接收器,而 enaled和exported屬性則是根據(jù)我們剛才勾選的狀態(tài)自動(dòng)生成的。
<receiver android:name=".BootCompleteReceiver" android:enabled="true" android:exported="true"></receiver>不過目前BootCompleteReceiver還是不能接收到開機(jī)廣播的,還需要對(duì)廣播進(jìn)行限定,添加廣播過濾器和申請(qǐng)權(quán)限。 <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系統(tǒng)啟動(dòng)完成后會(huì)發(fā)出一條值為android.permission.RECEIVE_BOOT_COMPLETED的廣播,因此我們 標(biāo)簽里添加了相應(yīng)的action。 監(jiān)聽系統(tǒng)開機(jī)廣播也是需要聲明權(quán)限的,我們使用標(biāo)簽又加入一條android.permission.RECEIVE_BOOT_COMPLETED權(quán)限。將模擬器重新啟動(dòng)就可以收到開機(jī)廣播了。需要額外注意的是,不要在onReceive()方法中添加過多的邏輯或者進(jìn)行任何的耗時(shí)操作,因?yàn)樵趶V播接收器中是不允許開啟線程的,當(dāng)onReceive方法運(yùn)行較長時(shí)間而沒有結(jié)束時(shí),程序就會(huì)報(bào)錯(cuò)。所以廣播接收器更多的是扮演一種打開程序其他組件的角色,比如創(chuàng)建一條狀態(tài)欄通知,或者啟動(dòng)一個(gè)服務(wù)等。以上學(xué)習(xí)的是系統(tǒng)廣播,現(xiàn)在我們學(xué)習(xí)如何在程序中發(fā)送自定義廣播。廣播分為標(biāo)準(zhǔn)廣播和有序廣播,本節(jié)我們就將通過實(shí)踐的方式來看一下這兩種廣播具體的區(qū)別。
* 這里讓MyBroadcastReceiver接收一條值為com.beidou.broadcasttest.MY_BROADCAST的廣播,因此待會(huì)在發(fā)送廣播的時(shí)候,我們需要發(fā)出這樣的一條廣播。
修改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!" />* 在點(diǎn)擊事件中,我們加入了發(fā)送自定義廣播的邏輯。 1. 首先構(gòu)建出了一個(gè)Intent對(duì)象,并把要發(fā)送的廣播的值傳入。 2. 然后調(diào)用了Context的sendBroadcast方法將廣播發(fā)送出去,這樣所有監(jiān)聽com.com.beidou.broadcasttest.MY_BROADCAST這條廣播的廣播接收器就會(huì)受到信息。 3. 由于廣播是使用Intent進(jìn)行傳遞的,因此你還可以在Intent中攜帶數(shù)據(jù)傳遞給廣播接收器。
1.新創(chuàng)建 BroadcastTest2 項(xiàng)目,點(diǎn)擊AS-File-New-New Project進(jìn)行創(chuàng)建. 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.證明了應(yīng)用程序發(fā)出的廣播是可以被其他的程序接收到的。
發(fā)送有序廣播,修改MainActivity代碼: Intent intent = new Intent("com.beidou.broadcasttest.MY_BROADCAST");sendOrderedBroadcast(intent,null);1.發(fā)送有序廣播只需改動(dòng)一行代碼,即將sendBroadcast()方法改成sendOrderedBroadcast方法。接收兩個(gè)參數(shù),第一個(gè)參數(shù)仍然是Intent,第二個(gè)參數(shù)是一個(gè)與權(quán)限相關(guān)的字符串,這里傳入null即可。 2.重新運(yùn)行程序,兩個(gè)應(yīng)用程序都可以接收到這條廣播,但是這個(gè)時(shí)候的廣播接收器是有先后順序的,而且前面的廣播接收器還可以將廣播截?cái)啵宰柚蛊淅^續(xù)廣播。 3.設(shè)定廣播接收器的先后順序,在配置文件里面
<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的優(yōu)先級(jí)設(shè)成了100,以保證它一定會(huì)在AnotherBroadcastReceiver之前收到廣播. 4. MyBroadcastReceiver獲得了接收廣播的優(yōu)先權(quán),那么MyBroadcastReceiver就可以選擇是否允許廣播繼續(xù)傳遞了。修改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方法中調(diào)用了abortBroadcast方法,就表示將這條廣播截?cái)?,后面的廣播接收器將無法再接收到這條廣播。
前面我們發(fā)送和接收的廣播全部屬于系統(tǒng)全局廣播,即發(fā)出的廣播既可以被其他任何應(yīng)用程序接收到,并且我們可以接收到來自于其他任何應(yīng)用程序的廣播。這樣就存在安全性問題。
比如:我們發(fā)送的一些攜帶關(guān)鍵性數(shù)據(jù)的廣播有可能被其他應(yīng)用程序截獲,或者其他的程序不停地向我們的廣播接收器里發(fā)送各種垃圾廣播。
為此,Android引入了一套本地廣播機(jī)制:使用這個(gè)廣播機(jī)制發(fā)出的廣播只能夠在應(yīng)用程序的內(nèi)部進(jìn)行傳遞,并且廣播接收器也只能接收來自本應(yīng)用程序發(fā)出的廣播。
用法:主要是使用了一個(gè)LocalBroadcastManager來對(duì)廣播進(jìn)行管理,并提供了發(fā)送廣播和注冊(cè)廣播接收器的方法。修改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); //獲取實(shí)例 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);//發(fā)送廣播 } }); intentFilter = new IntentFilter(); intentFilter.addAction("com.beidou.broadcasttest.LOCAL_BROADCAST"); localReceiver = new LocalReceiver(); localBroadcastManager.registerReceiver(localReceiver, intentFilter);//注冊(cè)廣播 } 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);//注銷廣播 }}以上代碼和動(dòng)態(tài)注冊(cè)廣播接收器以及發(fā)送廣播的代碼是一樣的。只不過現(xiàn)在首先是通過LocalBroadcastManager的getInstance()方法獲取它的一個(gè)實(shí)例,然后在注冊(cè)廣播接收器的時(shí)候調(diào)用的是LocalBroadcastManager的registerReceiver()方法,在發(fā)送廣播的時(shí)候調(diào)用的是LocalBroadcastManager的sendBroadcast()方法。注意點(diǎn):本地廣播是無法靜態(tài)注冊(cè)的方式來接收的。主要是因?yàn)殪o態(tài)注冊(cè)主要就是為了讓程序在未啟動(dòng)的情況下也能收到廣播,而發(fā)送本地廣播時(shí),我們的程度肯定是已經(jīng)啟動(dòng)的,因此也 不需要使用靜態(tài)注冊(cè)的功能。本地廣播的優(yōu)勢(shì): ① 可以明確地知道正在發(fā)送的廣播不會(huì)離開我們的程序,因此不必?fù)?dān)心泄密。 ② 其他的程序無法將廣播發(fā)送到我們程序的內(nèi)部,因此不需要擔(dān)心會(huì)有安全漏洞。 ③ 發(fā)送本地廣播比發(fā)送系統(tǒng)廣播將會(huì)更加高效。強(qiáng)制下線功能比較常見:比如QQ在別處登錄了,就會(huì)將你強(qiáng)制擠下線。
邏輯:只需要在界面上彈出一個(gè)對(duì)話框,讓用戶無法進(jìn)行任何其他操作,必須要點(diǎn)擊對(duì)話框中的確定按鈕,然后回到登錄界面即可。
疑問:當(dāng)我們被通知需要強(qiáng)制下線時(shí)可能正處于任何一個(gè)界面,難道需要在每個(gè)界面上都編寫一個(gè)彈出對(duì)話框的邏輯?那到不用,借助廣播知識(shí),可以輕松實(shí)現(xiàn)這一個(gè)功能。
強(qiáng)制下線功能需要先關(guān)閉掉所有的活動(dòng),然后回到登錄界面。創(chuàng)建BroadcastBestPractice項(xiàng)目,創(chuàng)建一個(gè)ActivityCollector類用于管理所有的活動(dòng):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(); } } }}創(chuàng)建BaseActivity類作為所有活動(dòng)的父類。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); }}創(chuàng)建一個(gè)登錄頁面的活動(dòng),新建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(); //如果賬號(hào)是admin 且密碼是123456,就認(rèn)為登錄成功 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中動(dòng)態(tài)注冊(cè)一個(gè)廣播接收器就可以了。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(); //銷毀所有的活動(dòng) Intent intent = new Intent(context,LoginActivity.class); context.startActivity(intent);//重新啟動(dòng)LoginActivity } }); builder.show(); } }}修改AndroidManifest.xml文件:<application android:allowBackup="true" android:icon="@m* 綜上:當(dāng)賬號(hào)和密碼正確的時(shí)候調(diào)轉(zhuǎn)到主頁面MainActivity,在主頁面里面發(fā)出一個(gè)廣播(BaseActivity里面已經(jīng)加入了注冊(cè)和取消廣播的代碼),所以廣播發(fā)出后,BaseActivity里面的廣播接收器就會(huì)接收到廣播,與此同時(shí)彈出對(duì)話框,點(diǎn)擊OK,進(jìn)行強(qiáng)制所有活動(dòng)下線的操作,并重新打開登錄頁面。1.配置身份 git config –global user.name “FUkaiqiang” git config –global user.email “18201685396@163.com 2.驗(yàn)證身份 git config –global user.name git config –global user.emai 3.創(chuàng)建代碼倉庫(進(jìn)入項(xiàng)目存放的目錄) cd D: cd Test cd BroadcastBestPractice git init * 此時(shí)會(huì)生成一個(gè).git文件夾,用來記錄所有的Git操作的 4.查看列表 ls -al 5. 提交本地代碼(先用add把想要的代碼先添加進(jìn)來,而conmit則是真正地去執(zhí)行提交操作) 提交單個(gè)文件 git add build.gradle 提交整個(gè)目錄 git add . 提交:git commit -m “First commit” (m不可少)
新聞熱點(diǎn)
疑難解答
圖片精選