編輯:關於Android編程
這個文章主要是講Android開發的四大組件,本文主要分為
一、Activity詳解
二、Service詳解
三、Broadcast Receiver詳解
四、Content Provider詳解
外加一個重要組件 intent的詳解。
一、Activity詳解
Activty的生命周期的也就是它所在進程的生命周期。
一個Activity的啟動順序:
onCreate()——>onStart()——>onResume()
當另一個Activity啟動時:
第一個Activity onPause()——>第二個Activity onCreate()——>onStart()——>onResume()
——>第一個Activity onStop()
當返回到第一個Activity時:
第二個Activity onPause()——>第一個Activity onRestart()——>onStart()——>onResume()
——>第二個Activity onStop()——>onDestroy()
一個Activity的銷毀順序:
(情況一)onPause()——>
(情況二)onPause()——>onStop()——>
(情況三)onPause()——>onStop()——>onDestroy()
每一個活動( Activity )都處於某一個狀態,對於開發者來說,是無法控制其應用程序處於某一個狀態的,這些均由系統來完成。
但是當一個活動的狀態發生改變的時候,開發者可以通過調用 onXX() 的方法獲取到相關的通知信息。
在實現 Activity 類的時候,通過覆蓋( override )這些方法即可在你需要處理的時候來調用。
一、 onCreate:當活動第一次啟動的時候,觸發該方法,可以在此時完成活動的初始化工作。
onCreate 方法有一個參數,該參數可以為空( null ),也可以是之前調用 onSaveInstanceState ()方法保存的狀態信息。
二、onStart:該方法的觸發表示所屬活動將被展現給用戶。
三、onResume:當一個活動和用戶發生交互的時候,觸發該方法。
四、onPause:當一個正在前台運行的活動因為其他的活動需要前台運行而轉入後台運行的時候,觸發該方法。這時候需要將活動的狀態持久化,比如正在編輯的數據庫記錄等。
五、onStop:當一個活動不再需要展示給用戶的時候,觸發該方法。如果內存緊張,系統會直接結束這個活動,而不會觸發 onStop 方法。 所以保存狀態信息是應該在onPause時做,而不是onStop時做。活動如果沒有在前台運行,都將被停止或者Linux管理進程為了給新的活動預留足夠的存儲空間而隨時結束這些活動。因此對於開發者來說,在設計應用程序的時候,必須時刻牢記這一原則。在一些情況下,onPause方法或許是活動觸發的最後的方法,因此開發者需要在這個時候保存需要保存的信息。
六、onRestart:當處於停止狀態的活動需要再次展現給用戶的時候,觸發該方法。
七、 onDestroy:當活動銷毀的時候,觸發該方法。和 onStop 方法一樣,如果內存緊張,系統會直接結束這個活動而不會觸發該方法。
· onSaveInstanceState :系統調用該方法,允許活動保存之前的狀態,比如說在一串字符串中的光標所處的位置等。
通常情況下,開發者不需要重寫覆蓋該方法,在默認的實現中,已經提供了自動保存活動所涉及到的用戶界面組件的所有狀態信息。
Activity棧
上面提到開發者是無法控制Activity的狀態的,那Activity的狀態又是按照何種邏輯來運作的呢?這就要知道 Activity 棧。
每個Activity的狀態是由它在Activity棧(是一個後進先出LIFO,包含所有正在運行Activity的隊列)中的位置決定的。
當一個新的Activity啟動時,當前的活動的Activity將會移到Activity棧的頂部。
如果用戶使用後退按鈕返回的話,或者前台的Activity結束,活動的Activity就會被移出棧消亡,而在棧上的上一個活動的Activity將會移上來並變為活動狀態。如下圖所示: 一個應用程序的優先級是受最高優先級的Activity影響的。當決定某個應用程序是否要終結去釋放資源,Android內存管理使用棧來決定基於Activity的應用程序的優先級。
Activity狀態
一般認為Activity有以下四種狀態:
活動的:當一個Activity在棧頂,它是可視的、有焦點、可接受用戶輸入的。Android試圖盡最大可能保持它活動狀態,殺死其它Activity來確保當前活動Activity有足夠的資源可使用。當另外一個Activity被激活,這個將會被暫停。
暫停:在很多情況下,你的Activity可視但是它沒有焦點,換句話說它被暫停了。有可能原因是一個透明或者非全屏的Activity被激活。
當被暫停,一個Activity仍會當成活動狀態,只不過是不可以接受用戶輸入。在極特殊的情況下,Android將會殺死一個暫停的Activity來為活動的Activity提供充足的資源。當一個Activity變為完全隱藏,它將會變成停止。
停止:當一個Activity不是可視的,它“停止”了。這個Activity將仍然在內存中保存它所有的狀態和會員信息。盡管如此,當其它地方需要內存時,它將是最有可能被釋放資源的。當一個Activity停止後,一個很重要的步驟是要保存數據和當前UI狀態。一旦一個Activity退出或關閉了,它將變為待用狀態。
待用: 在一個Activity被殺死後和被裝在前,它是待用狀態的。待用Acitivity被移除Activity棧,並且需要在顯示和可用之前重新啟動它。
activity的四種加載模式
在android的多activity開發中,activity之間的跳轉可能需要有多種方式,有時是普通的生成一個新實例,有時希望跳轉到原來某個activity實例,而不是生成大量的重復的activity。加載模式便是決定以哪種方式啟動一個跳轉到原來某個Activity實例。
在android裡,有4種activity的啟動模式,分別為:
·standard: 標准模式,一調用startActivity()方法就會產生一個新的實例。
·singleTop: 如果已經有一個實例位於Activity棧的頂部時,就不產生新的實例,而只是調用Activity中的newInstance()方法。如果不位於棧頂,會產生一個新的實例。
·singleTask: 會在一個新的task中產生這個實例,以後每次調用都會使用這個,不會去產生新的實例了。
·singleInstance: 這個跟singleTask基本上是一樣,只有一個區別:在這個模式下的Activity實例所處的task中,只能有這個activity實例,不能有其他的實例。
這些啟動模式可以在功能清單文件AndroidManifest.xml中進行設置,中的launchMode屬性。
相關的代碼中也有一些標志可以使用,比如我們想只啟用一個實例,則可以使用 Intent.FLAG_ACTIVITY_REORDER_TO_FRONT 標志,這個標志表示:如果這個activity已經啟動了,就不產生新的activity,而只是把這個activity實例加到棧頂來就可以了。
Intent intent = new Intent(ReorderFour.this, ReorderTwo.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
復制代碼
Activity的加載模式受啟動Activity的Intent對象中設置的Flag和manifest文件中Activity的元素的特性值交互控制。
下面是影響加載模式的一些特性
核心的Intent Flag有:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
FLAG_ACTIVITY_SINGLE_TOP
核心的特性有:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
四種加載模式的區別
所屬task的區別
一般情況下,“standard”和”singleTop”的activity的目標task,和收到的Intent的發送者在同一個task內,就相當於誰調用它,它就跟誰在同一個Task中。
除非Intent包括參數FLAG_ACTIVITY_NEW_TASK。如果提供了FLAG_ACTIVITY_NEW_TASK參數,會啟動到別的task裡。
“singleTask”和”singleInstance” 總是把要啟動的activity作為一個task的根元素,他們不會被啟動到一個其他task裡。
是否允許多個實例
“standard”和”singleTop”可以被實例化多次,並且是可以存在於不同的task中;這種實例化時一個task可以包括一個activity的多個實例;
“singleTask”和”singleInstance”則限制只生成一個實例,並且是task的根元素。
singleTop 要求如果創建intent的時候棧頂已經有要創建的Activity的實例,則將intent發送給該實例,而不創建新的實例。
是否允許其它activity存在於本task內
“singleInstance”獨占一個task,其它activity不能存在那個task裡;
如果它啟動了一個新的activity,不管新的activity的launch mode 如何,新的activity都將會到別的task裡運行(如同加了FLAG_ACTIVITY_NEW_TASK參數)。
而另外三種模式,則可以和其它activity共存。
是否每次都生成新實例
“standard”對於每一個啟動Intent都會生成一個activity的新實例;
“singleTop”的activity如果在task的棧頂的話,則不生成新的該activity的實例,直接使用棧頂的實例,否則,生成該activity的實例。
比如:
現在task棧元素為A-B-C-D(D在棧頂),這時候給D發一個啟動intent,如果D是 “standard”的,則生成D的一個新實例,棧變為A-B-C-D-D。
如果D是singleTop的話,則不會生產D的新實例,棧狀態仍為A-B-C-D
如果這時候給B發Intent的話,不管B的launchmode是”standard” 還是 “singleTop” ,都會生成B的新實例,棧狀態變為A-B-C-D-B。
“singleInstance”是其所在棧的唯一activity,它會每次都被重用。
“singleTask” 如果在棧頂,則接受intent,否則,該intent會被丟棄,但是該task仍會回到前台。 當已經存在的activity實例處理新的intent時候,會調用onNewIntent()方法,如果收到intent生成一個activity實例,那麼用戶可以通過back鍵回到上一個狀態;如果是已經存在的一個activity來處理這個intent的話,用戶不能通過按back鍵返回到這之前的狀態。
-----------------------------------
二、Service詳解
service可以在和多場合的應用中使用,比如播放多媒體的時候用戶啟動了其他Activity這個時候程序要在後台繼續播放,比如檢測SD卡上文件的變化,再或者在後台記錄你地理信息位置的改變等等,總之服務嘛,總是藏在後頭的。
Service是在一段不定的時間運行在後台,不和用戶交互應用組件。每個Service必須在manifest中 通過來聲明。可以通過contect.startservice和contect.bindserverice來啟動。
Service和其他的應用組件一樣,運行在進程的主線程中。這就是說如果service需要很多耗時或者阻塞的操作,需要在其子線程中實現。
service的兩種模式(startService()/bindService()不是完全分離的):
本地服務 Local Service 用於應用程序內部。
它可以啟動並運行,直至有人停止了它或它自己停止。在這種方式下,它以調用Context.startService()啟動,而以調用Context.stopService()結束。它可以調用Service.stopSelf() 或 Service.stopSelfResult()來自己停止。不論調用了多少次startService()方法,你只需要調用一次stopService()來停止服務。
用於實現應用程序自己的一些耗時任務,比如查詢升級信息,並不占用應用程序比如Activity所屬線程,而是單開線程後台執行,這樣用戶體驗比較好。
遠程服務 Remote Service 用於android系統內部的應用程序之間。
它可以通過自己定義並暴露出來的接口進行程序操作。客戶端建立一個到服務對象的連接,並通過那個連接來調用服務。連接以調用Context.bindService()方法建立,以調用 Context.unbindService()關閉。多個客戶端可以綁定至同一個服務。如果服務此時還沒有加載,bindService()會先加載它。
可被其他應用程序復用,比如天氣預報服務,其他應用程序不需要再寫這樣的服務,調用已有的即可。
生命周期
使用context.startService() 啟動Service是會會經歷:
context.startService() ->onCreate()- >onStart()->Service running
context.stopService() | ->onDestroy() ->Service stop
如果Service還沒有運行,則android先調用onCreate()然後調用onStart();如果Service已經運行,則只調用onStart(),所以一個Service的onStart方法可能會重復調用多次。
stopService的時候直接onDestroy,如果是調用者自己直接退出而沒有調用stopService的話,Service會一直在後台運行。該Service的調用者再啟動起來後可以通過stopService關閉Service。
所以調用startService的生命周期為:onCreate --> onStart(可多次調用) --> onDestroy
使用使用context.bindService()啟動Service會經歷:
context.bindService()->onCreate()->onBind()->Service running
onUnbind() -> onDestroy() ->Service stop
onBind將返回給客戶端一個IBind接口實例,IBind允許客戶端回調服務的方法,比如得到Service運行的狀態或其他操作。這個時候把調用者(Context,例如Activity)會和Service綁定在一起,Context退出了,Srevice就會調用onUnbind->onDestroy相應退出。
所以調用bindService的生命周期為:onCreate --> onBind(只一次,不可多次綁定) --> onUnbind --> onDestory。
在Service每一次的開啟關閉過程中,只有onStart可被多次調用(通過多次startService調用),其他onCreate,onBind,onUnbind,onDestory在一個生命周期中只能被調用一次。
而啟動service,根據onStartCommand的返回值不同,有兩個附加的模式:
1. START_STICKY 用於顯示啟動和停止service。
2. START_NOT_STICKY或START_REDELIVER_INTENT用於有命令需要處理時才運行的模式。
服務不能自己運行,需要通過調用Context.startService()或Context.bindService()方法啟動服務。這兩個方法都可以啟動Service,但是它們的使用場合有所不同。
1. 使用startService()方法啟用服務,調用者與服務之間沒有關連,即使調用者退出了,服務仍然運行。
如果打算采用Context.startService()方法啟動服務,在服務未被創建時,系統會先調用服務的onCreate()方法,接著調用onStart()方法。
如果調用startService()方法前服務已經被創建,多次調用startService()方法並不會導致多次創建服務,但會導致多次調用onStart()方法。
采用startService()方法啟動的服務,只能調用Context.stopService()方法結束服務,服務結束時會調用onDestroy()方法。
2. 使用bindService()方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。
onBind()只有采用Context.bindService()方法啟動服務時才會回調該方法。該方法在調用者與服務綁定時被調用,當調用者與服務已經綁定,多次調用Context.bindService()方法並不會導致該方法被多次調用。
采用Context.bindService()方法啟動服務時只能調用onUnbind()方法解除調用者與服務解除,服務結束時會調用onDestroy()方法。
看看官方給出的比較流程示意圖:
官方文檔告訴我們,一個service可以同時start並且bind,在這樣的情況,系統會一直保持service的運行狀態如果service已經start了或者BIND_AUTO_CREATE標志被設置。如果沒有一個條件滿足,那麼系統將會調用onDestory方法來終止service.所有的清理工作(終止線程,反注冊接收器)都在onDestory中完成。
擁有service的進程具有較高的優先級
官方文檔告訴我們,Android系統會盡量保持擁有service的進程運行,只要在該service已經被啟動(start)或者客戶端連接(bindService)到它。當內存不足時,需要保持,擁有service的進程具有較高的優先級。
1. 如果service正在調用onCreate,onStartCommand或者onDestory方法,那麼用於當前service的進程則變為前台進程以避免被killed。
2. 如果當前service已經被啟動(start),擁有它的進程則比那些用戶可見的進程優先級低一些,但是比那些不可見的進程更重要,這就意味著service一般不會被killed.
3. 如果客戶端已經連接到service (bindService),那麼擁有Service的進程則擁有最高的優先級,可以認為service是可見的。
4. 如果service可以使用startForeground(int, Notification)方法來將service設置為前台狀態,那麼系統就認為是對用戶可見的,並不會在內存不足時killed。
如果有其他的應用組件作為Service,Activity等運行在相同的進程中,那麼將會增加該進程的重要性。
本地service
1.不需和Activity交互的本地服務
public class LocalService extends Service {
private static final String TAG = "LocalService";
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return null;
}
@Override
public void onCreate() {
Log.i(TAG, "onCreate");
super.onCreate();
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
super.onDestroy();
}
@Override
public void onStart(Intent intent, int startId) {
Log.i(TAG, "onStart");
super.onStart(intent, startId);
}
}
復制代碼 Activity:
public class ServiceActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.servicedemo);
((Button) findViewById(R.id.startLocalService)).setOnClickListener(
new View.OnClickListener(){
@Override
public void onClick(View view) {
// TODO Auto-generated method stub
startService(new Intent("com.demo.SERVICE_DEMO"));
}
});
((Button) findViewById(R.id.stopLocalService)).setOnClickListener(
new View.OnClickListener(){
@Override
public void onClick(View view) {
// TODO Auto-generated method stub
stopService(new Intent("com.demo.SERVICE_DEMO"));
}
});
}
}
復制代碼 在AndroidManifest.xml添加:
復制代碼 否則啟動服務時會提示new Intent找不到"com.demo.SERVICE_DEMO"。
對於這類不需和Activity交互的本地服務,是使用startService/stopService的最好例子。
運行時可以發現第一次startService時,會調用onCreate和onStart,在沒有stopService前,無論點擊多少次startService,都只會調用onStart。而stopService時調用onDestroy。再次點擊stopService,會發現不會進入service的生命周期的,即不會再調用onCreate,onStart和onDestroy。
而onBind在startService/stopService中沒有調用。
2.本地服務和Activity交互
對於這種case,官方的sample(APIDemo\app.LocalService)是最好的例子:
/**
* This is an example of implementing an application service that runs locally
* in the same process as the application. The {@link LocalServiceController}
* and {@link LocalServiceBinding} classes show how to interact with the
* service.
*
*
Notice the use of the {@link NotificationManager} when interesting things
* happen in the service. This is generally how background services should
* interact with the user, rather than doing something more disruptive such as
* calling startActivity().
*/
public class LocalService extends Service {
private NotificationManager mNM;
/**
* Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with
* IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
@Override
public void onCreate() {
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Display a notification about us starting. We put an icon in the status bar.
showNotification();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
@Override
public void onDestroy() {
// Cancel the persistent notification.
mNM.cancel(R.string.local_service_started);
// Tell the user we stopped.
Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
/**
* Show a notification while this service is running.
*/
private void showNotification() {
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.local_service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.stat_sample, text,
System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
new Intent(this, LocalServiceController.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, getText(R.string.local_service_label),
text, contentIntent);
// Send the notification.
// We use a layout id because it is a unique number. We use it later to cancel.
mNM.notify(R.string.local_service_started, notification);
}
}
復制代碼 這裡可以發現onBind需要返回一個IBinder對象。也就是說和上一例子LocalService不同的是,
1. 添加了一個public內部類繼承Binder,並添加getService方法來返回當前的Service對象;
2. 新建一個IBinder對象--new那個Binder內部類;
3. onBind方法返還那個IBinder對象。
Activity:
/**
*
Example of binding and unbinding to the {@link LocalService}.
* This demonstrates the implementation of a service which the client will
* bind to, receiving an object through which it can communicate with the service.
*/
public class LocalServiceBinding extends Activity {
private boolean mIsBound;
private LocalService mBoundService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.local_service_binding);
// Watch for button clicks.
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
mBoundService = ((LocalService.LocalBinder)service).getService();
// Tell the user about this for our demo.
Toast.makeText(LocalServiceBinding.this, R.string.local_service_connected,
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
// Because it is running in our same process, we should never
// see this happen.
mBoundService = null;
Toast.makeText(LocalServiceBinding.this, R.string.local_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(LocalServiceBinding.this,
LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
};
}
復制代碼
明顯看出這裡面添加了一個名為ServiceConnection類,並實現了onServiceConnected(從IBinder獲取Service對象)和onServiceDisconnected(set Service to null)。
而bindService和unbindService方法都是操作這個ServiceConnection對象的。
AndroidManifest.xml裡添加:
復制代碼 這裡沒什麼特別的,因為service沒有需要什麼特別的action,所以只是聲明service而已,而activity和普通的沒差別。
運行時,發現調用次序是這樣的:
bindService:
1.LocalService : onCreate
2.LocalService : onBind
3.Activity: onServiceConnected
unbindService: 只是調用onDestroy
可見,onStart是不會被調用的,而onServiceDisconnected沒有調用的原因在上面代碼的注釋有說明。
------------------------
三、Broadcast Receiver詳解
BroadcastReceiver 用於異步接收廣播Intent。主要有兩大類,用於接收廣播的:
·正常廣播 Normal broadcasts(用 Context.sendBroadcast()發送)是完全異步的。它們都運行在一個未定義的順序,通常是在同一時間。這樣會更有效,但意味著receiver不能包含所要使用的結果或中止的API。
·有序廣播 Ordered broadcasts(用 Context.sendOrderedBroadcast()發送)每次被發送到一個receiver。所謂有序,就是每個receiver執行後可以傳播到下一個receiver,也可以完全中止傳播--不傳播給其他receiver。 而receiver運行的順序可以通過matched intent-filter 裡面的android:priority來控制,當priority優先級相同的時候,Receiver以任意的順序運行。
要注意的是,即使是Normal broadcasts,系統在某些情況下可能會恢復到一次傳播給一個receiver。 特別是receiver可能需要創建一個進程,為了避免系統超載,只能一次運行一個receiver。
Broadcast Receiver 並沒有提供可視化的界面來顯示廣播信息。可以使用Notification和Notification Manager來實現可視化的信息的界面,顯示廣播信息的內容,圖標及震動信息。
生命周期
一個BroadcastReceiver 對象只有在被調用onReceive(Context, Intent)的才有效的,當從該函數返回後,該對象就無效的了,結束生命周期。
因此從這個特征可以看出,在所調用的onReceive(Context, Intent)函數裡,不能有過於耗時的操作,不能使用線程來執行。對於耗時的操作,請start service來完成。因為當得到其他異步操作所返回的結果時,BroadcastReceiver 可能已經無效了。
發送廣播
事件的廣播比較簡單,構建Intent對象,可調用sendBroadcast(Intent)方法將廣播發出。另外還有sendOrderedBroadcast(),sendStickyBroadcast()等方法,請查閱API Doc。
1.new Intent with action name
Intent intent = new Intent(String action);
或者 只是new Intent, 然後
intent.setAction(String action);
2.set data等准備好了後,in activity,
sendBroadcast(Intent); // 發送廣播
接收廣播
通過定義一個繼承BroadcastReceiver類來實現,繼承該類後覆蓋其onReceiver方法,並在該方法中響應事件。
public class SMSReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// get data from SMS intent
Bundle bundle = intent.getExtras();
if (bundle != null){
// get message by "pdus"
Object[] objArray = (Object[]) bundle.get("pdus");
// rebuild SMS
SmsMessage[] messages = new SmsMessage[objArray.length];
for (int i=0; i < objArray.length; i++){
messages[i] = SmsMessage.createFromPdu((byte[])objArray[i]);
StringBuilder str = new StringBuilder("from: ");
str.append(messages[i].getDisplayOriginatingAddress());
str.append("\nmessage:\n");
str.append(messages[i].getDisplayMessageBody());
Toast.makeText(context, str.toString(), Toast.LENGTH_LONG)
.show();
}
}
}
}
復制代碼 注冊Receiver
注冊有兩種方式:
1. 靜態方式,在AndroidManifest.xml的application裡面定義receiver並設置要接收的action。
復制代碼 2.動態方式, 在activity裡面調用函數來注冊,和靜態的內容差不多。一個形參是receiver,另一個是IntentFilter,其中裡面是要接收的action。
public class HelloDemo extends Activity {
private BroadcastReceiver receiver;
@Override
protected void onStart() {
super.onStart();
receiver = new CallReceiver();
registerReceiver(receiver, new IntentFilter("android.intent.action.PHONE_STATE"));
}
@Override
protected void onStop() {
unregisterReceiver(receiver);
super.onStop();
}
}
復制代碼 一個receiver可以接收多個action的,即可以有多個intent-filter,需要在onReceive裡面對intent.getAction(action name)進行判斷。
個人推薦使用靜態注冊方式,由系統來管理receiver,而且程序裡的所有receiver,可以在xml裡面一目了然。而動態注冊方式,隱藏在代碼中,比較難發現。
而且動態注冊,需要特別注意的是,在退出程序前要記得調用Context.unregisterReceiver()方法。一般在activity的onStart()裡面進行注冊, onStop()裡面進行注銷。官方提醒,如果在Activity.onResume()裡面注冊了,就必須在Activity.onPause()注銷。
Permission權限
要接收某些action,需要在AndroidManifest.xml裡面添加相應的permission。例如接收SMS:
復制代碼 下面給出動態注冊的接收來電的廣播處理的CallReceiver的代碼:
一種方式是直接讀取intent.getStringExtra("incoming_number")來獲取來電號碼:
public class CallReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
TelephonyManager teleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
switch(teleManager.getCallState()){
case TelephonyManager.CALL_STATE_RINGING: //響鈴
Toast.makeText(context, "Ringing: " + intent.getStringExtra("incoming_number"), Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK: //接聽
Toast.makeText(context, "OffHook: " + intent.getStringExtra("incoming_number"), Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_IDLE: //掛斷
Toast.makeText(m_context, "Idle: " + incomingNumber, Toast.LENGTH_LONG).show();
break;
}
}
}
復制代碼 在運行時,發現除了響鈴時可以獲取來電號碼,接聽和掛斷都不能成功獲取的,顯示為null。
另一種方式是通過PhoneStateListener的onCallStateChanged來監聽狀態的變化:
public class CallReceiver extends BroadcastReceiver {
private Context m_context;
@Override
public void onReceive(Context context, Intent intent) {
m_context = context;
TelephonyManager teleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
teleManager.listen(new PhoneStateListener(){
@Override
public void onCallStateChanged(int state, String incomingNumber) {
switch(state){
case TelephonyManager.CALL_STATE_RINGING: //響鈴
Toast.makeText(m_context, "Ringing: " + incomingNumber, Toast.LENGTH_LONG)
.show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK: //接聽
Toast.makeText(m_context, "OffHook: " + incomingNumber, Toast.LENGTH_LONG)
.show();
break;
case TelephonyManager.CALL_STATE_IDLE: //掛斷
Toast.makeText(m_context, "Idle: " + incomingNumber, Toast.LENGTH_LONG)
.show();
break;
}
}}, PhoneStateListener.LISTEN_CALL_STATE);
}
}
復制代碼 運行時也發現incomingNumber在接聽和掛斷時獲取為blank。
因為這裡監聽的是通話的狀態變化,所以這個receiver會被調用3次。
監聽通話狀態需要加上權限:
復制代碼 ===========
小結:
1. 對於sendBroadCast的intent對象,需要設置其action name;
2. 推薦使用顯式指明receiver,在配置文件AndroidManifest.xml指明;
3. 一個receiver可以接收多個action;
4. 每次接收廣播都會重新生成一個接收廣播的對象,再次調用onReceive;
5. 在BroadCast 中盡量不要處理太多邏輯問題,建議復雜的邏輯交給Activity 或者 Service 去處理。
--------------------------------------------
四、Content Provider詳解
ContentProvider(內容提供者)是Android中的四大組件之一。主要用於對外共享數據,也就是通過ContentProvider把應用中的數據共享給其他應用訪問,其他應用可以通過ContentProvider對指定應用中的數據進行操作。ContentProvider分為系統的和自定義的,系統的也就是例如聯系人,圖片等數據。
android中對數據操作包含有:
file, sqlite3, Preferences, ContectResolver與ContentProvider前三種數據操作方式都只是針對本應用內數據,程序不能通過這三種方法去操作別的應用內的數據。
android中提供ContectResolver與ContentProvider來操作別的應用程序的數據。
使用方式:
一個應用實現ContentProvider來提供內容給別的應用來操作,
一個應用通過ContentResolver來操作別的應用數據,當然在自己的應用中也可以。
以下這段是Google Doc中對ContentProvider的大致概述:
內容提供者將一些特定的應用程序數據供給其它應用程序使用。內容提供者繼承於ContentProvider 基類,為其它應用程序取用和存儲它管理的數據實現了一套標准方法。然而,應用程序並不直接調用這些方法,而是使用一個 ContentResolver 對象,調用它的方法作為替代。ContentResolver可以與任意內容提供者進行會話,與其合作來對所有相關交互通訊進行管理。
1.ContentProvider
Android提供了一些主要數據類型的ContentProvider,比如音頻、視頻、圖片和私人通訊錄等。可在android.provider包下面找到一些Android提供的ContentProvider。通過獲得這些ContentProvider可以查詢它們包含的數據,當然前提是已獲得適當的讀取權限。
主要方法:
public boolean onCreate() 在創建ContentProvider時調用
public Cursor query(Uri, String[], String, String[], String) 用於查詢指定Uri的ContentProvider,返回一個Cursor
public Uri insert(Uri, ContentValues) 用於添加數據到指定Uri的ContentProvider中
public int update(Uri, ContentValues, String, String[]) 用於更新指定Uri的ContentProvider中的數據
public int delete(Uri, String, String[]) 用於從指定Uri的ContentProvider中刪除數據
public String getType(Uri) 用於返回指定的Uri中的數據的MIME類型
*如果操作的數據屬於集合類型,那麼MIME類型字符串應該以vnd.android.cursor.dir/開頭。
例如:要得到所有person記錄的Uri為content://contacts/person,那麼返回的MIME類型字符串為"vnd.android.cursor.dir/person"。
*如果要操作的數據屬於非集合類型數據,那麼MIME類型字符串應該以vnd.android.cursor.item/開頭。
例如:要得到id為10的person記錄的Uri為content://contacts/person/10,那麼返回的MIME類型字符串應為"vnd.android.cursor.item/person"。
2.ContentResolver
當外部應用需要對ContentProvider中的數據進行添加、刪除、修改和查詢操作時,可以使用ContentResolver類來完成,要獲取ContentResolver對象,可以使用Context提供的getContentResolver()方法。
ContentResolver cr = getContentResolver();
ContentResolver提供的方法和ContentProvider提供的方法對應的有以下幾個方法。
public Uri insert(Uri uri, ContentValues values) 用於添加數據到指定Uri的ContentProvider中。
public int delete(Uri uri, String selection, String[] selectionArgs) 用於從指定Uri的ContentProvider中刪除數據。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) 用於更新指定Uri的ContentProvider中的數據。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 用於查詢指定Uri的ContentProvider。
3.Uri
Uri指定了將要操作的ContentProvider,其實可以把一個Uri看作是一個網址,我們把Uri分為三部分。
第一部分是"content://"。可以看作是網址中的"http://"。
第二部分是主機名或authority,用於唯一標識這個ContentProvider,外部應用需要根據這個標識來找到它。可以看作是網址中的主機名,比如"blog.csdn.net"。
第三部分是路徑名,用來表示將要操作的數據。可以看作網址中細分的內容路徑。
---------------------------------
支付寶有上線一項繳費服務新功能——ETC繳費功能,用戶可以通過支付寶對武漢地區的ETC進行繳費,這個功能暫時面對武漢市的廣大司機朋友
感覺用到的次數無比多,要是要把它記下來,免得要用的時候又要重來一遍(個人記性太差)先看效果圖接下來,說說要怎麼寫1.首先在.gradle中添加一個jar包gradle-w
文件存儲是 Android 中最基本的一種數據存儲方式,它不對存儲的內容進行任何的格式化處理,所有數據都是原封不動的保存到文件當中的。概述文件存取的核心就是輸入流和輸出流
MavLink是輕量級的通訊協議,主要應用於終端與小型無人載具間的通訊。由於它的通用性,MavLink可以被翻譯成各種語言的代碼應用於各種不同的環境。具體如何通過工具來生