四大組件:Activity, Service, Content provider, Broadcast receivehtml
通訊媒介:Intentjava
Ref: Android基礎入門教程linux
嫌SDK內置的AVD啓動速度,運行速度慢?android
Ref: 下載、安裝安卓模擬器Genymotiongit
Ref: Error-cxxabi-1-3-8-not-foundgithub
能夠任意調整大小的一種圖片格式 ***.9.png 。shell
詳見連接。數組
1). 界面原型設計android-studio
2). Android自帶DroidDraw工具設計Android界面瀏覽器
res目錄, # 在R.java文件下(能夠理解爲字典)生成對應的資源id,咱們能夠直接經過資源id訪問到對應的資源.
|---- assets目錄, # 須要咱們經過AssetManager以二進制流的形式來讀取.
mipmap目錄:
分辨率不一樣系統會根據屏幕分辨率來選擇hdpi,mdpi,xmdpi,xxhdpi下的對應圖片。
drawable: 存放各類位圖文件,(.png,.jpg,.9png,.gif等)除此以外多是一些其餘的drawable類型的XML文件 mipmap-hdpi: 高分辨率,通常咱們把圖片丟這裏 mipmap-mdpi: 中等分辨率,不多,除非兼容的的手機很舊 mipmap-xhdpi: 超高分辨率,手機屏幕材質愈來愈好,之後估計會慢慢往這裏過渡 mipmap-xxhdpi:超超高分辨率,這個在高端機上有所體現
原則是使用最接近的密度級別!禁止這種自動選取:
另外若是你想禁止Android不跟隨屏幕密度加載不一樣文件夾的資源,只需以下:
AndroidManifest.xml文件
android:anyDensity="false"
menu目錄:
在之前有物理菜單按鈕,即menu鍵的手機上,用的較多,如今用的並很少,菜單項相關的資源xml可在這裏編寫。
values目錄:
demens.xml:定義尺寸資源 string.xml:定義字符串資源 styles.xml:定義樣式資源 colors.xml:定義顏色資源 arrays.xml:定義數組資源 attrs.xml:自定義控件時用的較多,自定義控件的屬性! theme主題文件,和styles很類似,可是會對整個應用中的Actvitiy或指定Activity起做用,通常是改變窗口外觀的!
可在Java代碼中經過setTheme使用,或者在Androidmanifest.xml中爲<application...>添加theme的屬性!
PS:你可能看到過這樣的values目錄:values-w820dp,values-v11等,前者w表明平板設備,820dp表明屏幕寬度;而v11這樣表明在API(11),即android 3.0後纔會用到的!
raw目錄:
用於存放各類原生資源(音頻,視頻,一些XML文件等),咱們能夠經過openRawResource(int id)來得到資源的二進制流!
其實和Assets差很少,不過這裏面的資源會在R文件那裏生成一個資源id而已
動畫有兩種:屬性動畫和補間動畫:
animator:存放屬性動畫的XML文件
anim:存放補間動畫的XML文件
Java代碼中使用:
文字: txtName.setText(getResources().getText(R.string.name));
圖片: imgIcon.setBackgroundDrawableResource(R.drawable.icon);
顏色: txtName.setTextColor(getResouces().getColor(R.color.red));
佈局: setContentView(R.layout.main);
控件: txtName = (TextView)findViewById(R.id.txt_name);
XML代碼中使用:
<TextView android:text="@string/hello_world" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background = "@drawable/img_back"/>
三個比較重要的文件:
代碼下載:https://github.com/guolindev/booksource
├── chapter1
│ └── HelloWorld
├── chapter2
│ ├── ActivityLifeCycleTest
│ └── ActivityTest
參考:生命週期圖
package com.example.activitytest; import android.content.Intent; import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.Toast;
public class FirstActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { // ----> aa super.onCreate(savedInstanceState); Log.d("FirstActivity", "Task id is " + getTaskId()); setContentView(R.layout.first_layout);
Button button1 = (Button) findViewById(R.id.button_1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // ----> bb // Intent intent = new Intent(FirstActivity.this, SecondActivity.class); // startActivity(intent); SecondActivity.actionStart(FirstActivity.this, "data1", "data2"); // ----> cc } }); } @Override protected void onRestart() { super.onRestart(); Log.d("FirstActivity", "onRestart"); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.add_item: Toast.makeText(this, "You clicked Add", Toast.LENGTH_SHORT).show(); break; case R.id.remove_item: Toast.makeText(this, "You clicked Remove", Toast.LENGTH_SHORT).show(); break; default: } return true; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case 1: if (resultCode == RESULT_OK) { String returnedData = data.getStringExtra("data_return"); Log.d("FirstActivity", returnedData); } break; default: } } }
常見問題解答列表:
(aa.1) 理解bundle
在Activity之間使用Intent傳值和Bundle傳值的區別
Intent負責Activity之間的交互 本身是帶有一個Bundle的
Intent.putExtras(Bundle bundle) //設置本身內部的bundle
Intent.getExtras() //獲取Intent帶有的Bundle
intent --> bundle --> (key,value)
一個等價
intent.putExtra(key, value) Bundle bundle = intent.getExtras(); //intent裏的bundle帶上key&value bundle.putXXX(key, value); intent.putExtras(bundle);
又一個等價
intent.getXXXExtra(key) Bundle bundle = intent.getExtras(); bundle.getXXX(key);
(aa.2) 持久化activity的能力
@Override public void onCreate(Bundle savedInstanceState,PersistableBundle p) { super.onCreate(savedInstanceState,p); }
Activity就擁有了持久化的能力了,通常咱們會搭配另外兩個方法來使用:
public void onSaveInstanceState (Bundle outState, PersistableBundle outPersistentState) public void onRestoreInstanceState(Bundle savedInstanceState, PersistableBundle persistentState )
(bb) 理解bundle
省略了new View.OnClickListener()的顯示,由於沒有必要,是固定默認的。
(cc) 啓動一個Activity的幾種方式
經過intent切換activity的顯示:【這裏僅僅是初步複習】
顯式啓動:經過包名來啓動
①最多見的:
startActivity(new Intent(當前Act.this,要啓動的Act.class));
②經過Intent的ComponentName:
ComponentName cn = new ComponentName("當前Act的全限定類名", "啓動Act的全限定類名") ; Intent intent = new Intent() ; intent.setComponent(cn) ; startActivity(intent) ;
③初始化Intent時指定包名:
Intent intent = new Intent("android.intent.action.MAIN"); intent.setClassName("當前Act的全限定類名", "啓動Act的全限定類名");
startActivity(intent);
1. Activity間的數據傳遞
注意的是:Bunndle.get時,也不必定Intent裏面的數據類型,因此要有類型概念的(強制轉換似的)去value。
2. Activity給前一個迴應一個返回值
3.知曉當前是哪一個Activity
Ref: 拒絕BaseActivity,優雅的代替BaseActivity
4. 隨時關閉全部Activity
藉助鏈表關掉全部activities。
public class ActivityCollector { public static LinkedList<Activity> activities = new LinkedList<Activity>();
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(); } } } }
5. 甚至想殺死整個App
/** * 退出應用程序 */ public void AppExit(Context context) { try { ActivityCollector.finishAll(); ActivityManager activityMgr = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); activityMgr.killBackgroundProcesses(context.getPackageName()); System.exit(0);
} catch (Exception ignored) { } }
6. 雙擊退出程序的兩種方法
7. 爲Activity設置過場動畫
8. Bundle傳遞數據的限制
在使用Bundle傳遞數據時,要注意,Bundle的大小是有限制的 < 0.5MB,若是大於這個值 是會報TransactionTooLargeException異常的!!!
9. 查看當前全部Activity的命令
使用下述命令便可,前提是你爲SDK配置了環境變量:
adb shell dumpsys activity
10. 設置Activity全屏的方法
11. onWindowFocusChanged方法妙用
當Activity獲得或者失去焦點的時候,就會回調該方法! 若是咱們想監控Activity是否加載完畢,就能夠用到這個方法了
12. 定義對話框風格的Activity
在某些狀況下,咱們可能須要將Activity設置成對話框風格的,Activity通常是佔滿全屏的, 而Dialog則是佔據部分屏幕的!實現起來也很簡單!
直接設置下Activity的theme:
android:theme="@android:style/Theme.Dialog"
這樣就能夠了,固然你能夠再設置下標題,小圖標!
//設置左上角小圖標 requestWindowFeature(Window.FEATURE_LEFT_ICON); setContentView(R.layout.main); getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON, android.R.drawable.ic_lion_icon); //設置文字: setTitle(R.string.actdialog_title); //XML代碼中設置:android:label="@string/activity_dialog"
另外,系統給咱們提供的常見Activity:
//1.撥打電話 // 給移動客服10086撥打電話 Uri uri = Uri.parse("tel:10086"); Intent intent = new Intent(Intent.ACTION_DIAL, uri); startActivity(intent); //2.發送短信 // 給10086發送內容爲「Hello」的短信 Uri uri = Uri.parse("smsto:10086"); Intent intent = new Intent(Intent.ACTION_SENDTO, uri); intent.putExtra("sms_body", "Hello"); // putExtra, 放入本身要傳遞的數據 startActivity(intent); //3.發送彩信(至關於發送帶附件的短信) Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra("sms_body", "Hello"); Uri uri = Uri.parse("content://media/external/images/media/23"); intent.putExtra(Intent.EXTRA_STREAM, uri); intent.setType("image/png"); startActivity(intent); //4.打開瀏覽器: // 打開Google主頁 Uri uri = Uri.parse("http://www.baidu.com"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); //5.發送電子郵件:(閹割了Google服務的沒戲!!!!) // 給someone@domain.com發郵件 Uri uri = Uri.parse("mailto:someone@domain.com"); Intent intent = new Intent(Intent.ACTION_SENDTO, uri); startActivity(intent); // 給someone@domain.com發郵件發送內容爲「Hello」的郵件 Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_EMAIL, "someone@domain.com"); intent.putExtra(Intent.EXTRA_SUBJECT, "Subject"); intent.putExtra(Intent.EXTRA_TEXT, "Hello"); intent.setType("text/plain"); startActivity(intent); // 給多人發郵件 Intent intent = new Intent(Intent.ACTION_SEND); String[] tos = {"1@abc.com", "2@abc.com"}; // 收件人 String[] ccs = {"3@abc.com", "4@abc.com"}; // 抄送 String[] bccs = {"5@abc.com", "6@abc.com"}; // 密送 intent.putExtra(Intent.EXTRA_EMAIL, tos); intent.putExtra(Intent.EXTRA_CC, ccs); intent.putExtra(Intent.EXTRA_BCC, bccs); intent.putExtra(Intent.EXTRA_SUBJECT, "Subject"); intent.putExtra(Intent.EXTRA_TEXT, "Hello"); intent.setType("message/rfc822"); startActivity(intent); //6.顯示地圖: // 打開Google地圖中國北京位置(北緯39.9,東經116.3) Uri uri = Uri.parse("geo:39.9,116.3"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); //7.路徑規劃 // 路徑規劃:從北京某地(北緯39.9,東經116.3)到上海某地(北緯31.2,東經121.4) Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=39.9 116.3&daddr=31.2 121.4"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); //8.多媒體播放: Intent intent = new Intent(Intent.ACTION_VIEW); Uri uri = Uri.parse("file:///sdcard/foo.mp3"); intent.setDataAndType(uri, "audio/mp3"); startActivity(intent); //獲取SD卡下全部音頻文件,而後播放第一首=-= Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); //9.打開攝像頭拍照: // 打開拍照程序 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(intent, 0); // 取出照片數據 Bundle extras = intent.getExtras(); Bitmap bitmap = (Bitmap) extras.get("data"); //另外一種: //調用系統相機應用程序,並存儲拍下來的照片 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); time = Calendar.getInstance().getTimeInMillis(); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment .getExternalStorageDirectory().getAbsolutePath()+"/tucue", time + ".jpg"))); startActivityForResult(intent, ACTIVITY_GET_CAMERA_IMAGE); //10.獲取並剪切圖片 // 獲取並剪切圖片 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); intent.putExtra("crop", "true"); // 開啓剪切 intent.putExtra("aspectX", 1); // 剪切的寬高比爲1:2 intent.putExtra("aspectY", 2); intent.putExtra("outputX", 20); // 保存圖片的寬和高 intent.putExtra("outputY", 40); intent.putExtra("output", Uri.fromFile(new File("/mnt/sdcard/temp"))); // 保存路徑 intent.putExtra("outputFormat", "JPEG");// 返回格式 startActivityForResult(intent, 0); // 剪切特定圖片 Intent intent = new Intent("com.android.camera.action.CROP"); intent.setClassName("com.android.camera", "com.android.camera.CropImage"); intent.setData(Uri.fromFile(new File("/mnt/sdcard/temp"))); intent.putExtra("outputX", 1); // 剪切的寬高比爲1:2 intent.putExtra("outputY", 2); intent.putExtra("aspectX", 20); // 保存圖片的寬和高 intent.putExtra("aspectY", 40); intent.putExtra("scale", true); intent.putExtra("noFaceDetection", true); intent.putExtra("output", Uri.parse("file:///mnt/sdcard/temp")); startActivityForResult(intent, 0); //11.打開Google Market // 打開Google Market直接進入該程序的詳細頁面 Uri uri = Uri.parse("market://details?id=" + "com.demo.app"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); //12.進入手機設置界面: // 進入無線網絡設置界面(其它能夠觸類旁通) Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS); startActivityForResult(intent, 0); //13.安裝apk: Uri installUri = Uri.fromParts("package", "xxx", null); returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri); //14.卸載apk: Uri uri = Uri.fromParts("package", strPackageName, null); Intent it = new Intent(Intent.ACTION_DELETE, uri); startActivity(it); //15.發送附件: Intent it = new Intent(Intent.ACTION_SEND); it.putExtra(Intent.EXTRA_SUBJECT, "The email subject text"); it.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/eoe.mp3"); sendIntent.setType("audio/mp3"); startActivity(Intent.createChooser(it, "Choose Email Client")); //16.進入聯繫人頁面: Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setData(People.CONTENT_URI); startActivity(intent); //17.查看指定聯繫人: Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, info.id);//info.id聯繫人ID Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setData(personUri); startActivity(intent);
App橫豎屏切換的時候會銷燬當前的Activity而後從新建立一個。
橫豎屏切換時Act走下述生命週期:
onPause -> onStop -> onDestory -> onCreate -> onStart -> onResume
*** 如何禁止屏幕橫豎屏自動切換?
在AndroidManifest.xml中爲Act添加一個屬性:android:screenOrientation,
有下述可選值:
unspecified:默認值 由系統來判斷顯示方向.斷定的策略是和設備相關的,因此不一樣的設備會有不一樣的顯示方向。
landscape:橫屏顯示(寬比高要長)
portrait:豎屏顯示(高比寬要長)
user:用戶當前首選的方向
behind:和該Activity下面的那個Activity的方向一致(在Activity堆棧中的)
sensor:有物理的感應器來決定。若是用戶旋轉設備這屏幕會橫豎屏切換。
nosensor:忽略物理感應器,這樣就不會隨着用戶旋轉設備而更改了("unspecified"設置除外)。
*** 不由止的話?
1) 準備兩套不一樣的佈局,Android會本身根據橫豎屏加載不一樣佈局: 建立兩個佈局文件夾:layout-land橫屏,layout-port豎屏 而後把這兩套佈局文件丟這兩文件夾裏,文件名同樣,Android就會自行判斷,而後加載相應佈局了!
2) 本身在代碼中進行判斷,本身想加載什麼就加載什麼:
咱們通常是在onCreate()方法中加載佈局文件的,咱們能夠在這裏對橫豎屏的狀態作下判斷,關鍵代碼以下:
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
{ setContentView(R.layout.橫屏); } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{ setContentView(R.layout.豎屏); }
那麼,狀態保存問題,怎麼處理?
經過一個Bundle savedInstanceState參數便可完成! 三個核心方法:
onCreate(Bundle savedInstanceState);
onSaveInstanceState(Bundle outState);
onRestoreInstanceState(Bundle savedInstanceState);
參見:(aa.2)
(1) 首先參考:Activity與View的關係
(2) 其次,Activty的棧的概念:
Task是Activity的集合,是一個概念,實際使用的Back Stack來存儲Activity,能夠有多個Task,可是 同一時刻只有一個棧在最前面,其餘的都在後臺!那棧是如何產生的呢?
答:當咱們經過主屏幕,點擊圖標打開一個新的App,此時會建立一個新的Task!
舉個例子:
咱們經過點擊通訊錄APP的圖標打開APP,這個時候會新建一個棧1,而後開始把新產生的Activity添加進來,可能咱們在通信錄的APP中打開了短信APP的頁面,可是此時不會新建一個棧,而是繼續添加到棧1中。
這個時候假如咱們點擊Home鍵,回到主屏幕,此時棧1進入後臺,咱們可能有下述兩種操做:
1) 點擊菜單鍵(正方形那個按鈕),點擊打開剛剛的程序,而後棧1又回到前臺了! 又或者咱們點擊主屏幕上通訊錄的圖標,打開APP,此時也不會建立新的棧,棧1回到前臺!
2) 若是此時咱們點擊另外一個圖標打開一個新的APP,那麼此時則會建立一個新的棧2,棧2就會到前臺, 而棧1繼續呆在後臺;
3) 後面也是這樣...以此類推!
(3) Task的管理
實現 對Actvitiy的管理:
相關屬性值 - taskAffinity
1)當啓動 activity的Intent對象包含FLAG_ACTIVITY_NEW_TASK標記:
當傳遞給startActivity()的Intent對象包含 FLAG_ACTIVITY_NEW_TASK標記時,系統會爲須要啓動的Activity尋找與當前Activity不一樣Task。
2)allowTaskReparenting屬性設置爲true
那麼它能夠從一個Task(Task1)移到另一個有相同Affinity的Task(Task2)中(Task2帶到前臺時)。
若是一個.apk文件從用戶角度來看包含了多個"應用程序",你可能須要對那些 Activity賦不一樣的Affinity值。
相關屬性值 - launchMode
四個可選值,啓動模式咱們研究的核心,他們分別是:standard (default),singleTop,singleTask,singleInstance。----> (4)
相關屬性值 - 清空棧
當用戶長時間離開Task(當前task被轉移到後臺)時,系統會清除task中棧底Activity外的全部Activity 。這樣,當用戶返回到Task時,只留下那個task最初始的Activity了。
咱們能夠經過修改下面這些屬性來 改變這種行爲!
alwaysRetainTaskState: 若是棧底Activity的這個屬性被設置爲true,上述的狀況就不會發生。 Task中的全部activity將被長時間保存。
clearTaskOnLaunch:若是棧底activity的這個屬性被設置爲true,一旦用戶離開Task, 則 Task棧中的Activity將被清空到只剩下棧底activity。
這種狀況恰好與 alwaysRetainTaskState相反。即便用戶只是短暫地離開,task也會返回到初始狀態 (只剩下棧底acitivty)。
finishOnTaskLaunch:與clearTaskOnLaunch類似,但它只對單獨的activity操做,而不是整個Task。
它能夠結束任何Activity,包括棧底的Activity。 當它設置爲true時,當前的Activity只在當前會話期間做爲Task的一部分存在, 當用戶退出Activity再返回時,它將不存在。
(4) Activity的四種加載模式詳解 from Activity詳解四 activity四種加載模式
standard
默認模式,能夠不用寫配置。在這個模式下,都會默認建立一個新的實例。所以,在這種模式下,能夠有多個相同的實例,也容許多個相同Activity疊加。
例如:
若我有一個Activity名爲A1, 上面有一個按鈕可跳轉到A1。那麼若是我點擊按鈕,便會新啓一個Activity A1疊在剛纔的A1之上,再點擊,又會再新啓一個在它之上……
點back鍵會依照棧順序依次退出。
singleTop
能夠有多個實例,可是不容許多個相同Activity疊加。即,若是Activity在棧頂的時候,啓動相同的Activity,不會建立新的實例,而會調用其onNewIntent方法。
例如:
若我有兩個Activity名爲B1,B2,兩個Activity內容功能徹底相同,都有兩個按鈕能夠跳到B1或者B2,惟一不一樣的是B1爲standard,B2爲singleTop。
若我意圖打開的順序爲B1->B2->B2,則實際打開的順序爲B1->B2(後一次意圖打開B2,實際只調用了前一個的onNewIntent方法)
若我意圖打開的順序爲B1->B2->B1->B2,則實際打開的順序與意圖的一致,爲B1->B2->B1->B2。【不能有相連的便可】
singleTask
只有一個實例。在同一個應用程序中啓動他的時候,若Activity不存在,則會在當前task建立一個新的實例,若存在,則會把task中在其之上的其它Activity destory掉並調用它的onNewIntent方法。
若是是在別的應用程序中啓動它,則會新建一個task,並在該task中啓動這個Activity,singleTask容許別的Activity與其在一個task中共存,也就是說,若是我在這個singleTask的實例中再打開新的Activity,這個新的Activity仍是會在singleTask的實例的task中。
例如:
若個人應用程序中有三個Activity,C1,C2,C3,三個Activity可互相啓動,其中C2爲singleTask模式,那麼,不管我在這個程序中如何點擊啓動,如:C1->C2->C3->C2->C3->C1-C2,C1,C3可能存在多個實例,可是C2只會存在一個,而且這三個Activity都在同一個task裏面。
可是C1->C2->C3->C2->C3->C1-C2,這樣的操做過程實際應該是以下這樣的,由於singleTask會把task中在其之上的其它Activity destory掉。
操做:C1->C2 C1->C2->C3 C1->C2->C3->C2 C1->C2->C3->C2->C3->C1 C1->C2->C3->C2->C3->C1-C2
實際:C1->C2 C1->C2->C3 C1->C2 C1->C2->C3->C1 C1->C2
如果別的應用程序打開C2,則會新啓一個task。
如別的應用Other中有一個activity,taskId爲200,從它打開C2,則C2的taskIdI不會爲200,例如C2的taskId爲201,那麼再從C2打開C一、C3,則C二、C3的taskId仍爲201。
注意:若是此時你點擊home,而後再打開Other,發現這時顯示的確定會是Other應用中的內容,而不會是咱們應用中的C1 C2 C3中的其中一個。
singleInstance
只有一個實例,而且這個實例獨立運行在一個task中,這個task只有這個實例,不容許有別的Activity存在。
例如:
程序有三個ActivityD1,D2,D3,三個Activity可互相啓動,其中D2爲singleInstance模式。那麼程序從D1開始運行,假設D1的taskId爲200,那麼從D1啓動D2時,D2會新啓動一個task,即D2與D1不在一個task中運行。假設D2的taskId爲201,再從D2啓動D3時,D3的taskId爲200,也就是說它被壓到了D1啓動的任務棧中。
如果在別的應用程序打開D2,假設Other的taskId爲200,打開D2,D2會新建一個task運行,假設它的taskId爲201,那麼若是這時再從D2啓動D1或者D3,則又會再建立一個task,所以,若操做步驟爲other->D2->D1,這過程就涉及到了3個task了。
1) StartService(),啓動Service
2) BindService(),啓動Service
3) 啓動Service後,綁定Service
① 首次啓動會建立一個Service實例,,依次調用onCreate()和onStartCommand()方法,此時Service 進入運行狀態;
若是再次調用StartService啓動Service,將不會再建立新的Service對象, 系統會直接複用前面建立的Service對象,調用它的onStartCommand()方法!
② 但這樣的Service與它的調用者無必然的聯繫,就是說當調用者結束了本身的生命週期,可是隻要不調用stopService,那麼Service仍是會繼續運行的!
③ 不管啓動了多少次Service,只需調用一次StopService便可停掉Service。
驗證StartService啓動Service的調用順序,首先要註冊:
<!-- 配置Service組件,同時配置一個action --> <service android:name=".TestService1"> <intent-filter> <action android:name="com.jay.example.service.TEST_SERVICE1"/> </intent-filter> </service>
Service類的編寫:
public class TestService1 extends Service { private final String TAG = "TestService1"; //必需要實現的方法 @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind方法被調用!"); return null; } //Service被建立時調用 @Override public void onCreate() { Log.i(TAG, "onCreate方法被調用!"); super.onCreate(); } //Service被啓動時調用 @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand方法被調用!"); return super.onStartCommand(intent, flags, startId); } //Service被關閉以前回調 @Override public void onDestroy() { Log.i(TAG, "onDestory方法被調用!"); super.onDestroy(); } }
MainActivity類的編寫:
public class MainActivity extends Activity { private Button start; private Button stop; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); start = (Button) findViewById(R.id.btnstart); stop = (Button) findViewById(R.id.btnstop); //建立啓動Service的Intent,以及Intent屬性 final Intent intent = new Intent(); intent.setAction("com.jay.example.service.TEST_SERVICE1"); //爲兩個按鈕設置點擊事件,分別是啓動與中止service start.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startService(intent); } }); stop.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopService(intent); } }); } }
① 當首次使用bindService綁定一個Service時,系統會實例化一個Service實例,並調用其onCreate()和onBind()方法,
而後,調用者就能夠經過IBinder和Service進行交互了,
此後,若是再次使用bindService綁定Service,系統不會建立新的Sevice實例,也不會再調用onBind()方法,只會直接把IBinder對象傳遞給其餘後來增長的客戶端!
② 若是咱們解除與服務的綁定,只需調用unbindService(),此時onUnbind和onDestory方法將會被調用!這是一個客戶端的狀況。
假如是多個客戶端綁定同一個Service的話,狀況以下:
當一個客戶完成和service之間的互動後,它調用 unbindService() 方法來解除綁定。
當全部的客戶端都和service解除綁定後,系統會銷燬service。(除非service也被startService()方法開啓)
③ 另外,和上面狀況不一樣,bindService模式下的Service是與調用者相互關聯的,能夠理解爲 "一條繩子上的螞蚱",要死一塊兒死,在bindService後,一旦調用者銷燬,那麼Service也當即終止!
經過BindService調用Service時調用的Context的bindService的解析 bindService(Intent Service, ServiceConnection conn, int flags)
service:經過該intent指定要啓動的Service;
conn :ServiceConnection對象,用戶監聽訪問者與Service間的鏈接狀況。鏈接成功回調該對象中的onServiceConnected(ComponentName, IBinder)方法;
若是Service所在的宿主因爲異常終止或者其餘緣由終止,致使Service與訪問者間斷開 鏈接時調用onServiceDisconnected(CompanentName)方法,主動經過unBindService() 方法斷開並不會調用上述方法!
flags :指定綁定時是否自動建立Service(若是Service還未建立),參數能夠是0(不自動建立),BIND_AUTO_CREATE(自動建立)
在AndroidManifest.xml中對Service組件進行註冊:
<service android:name=".TestService2" android:exported="false"> <intent-filter> <action android:name = "com.jay.example.service.TEST_SERVICE2"/> </intent-filter> </service>
Service類的編寫:
public class TestService2 extends Service { private final String TAG = "TestService2"; private int count; private boolean quit; //定義onBinder方法所返回的對象
//Step1.在自定義的Service中繼承Binder,實現本身的IBinder對象 private MyBinder binder = new MyBinder(); public class MyBinder extends Binder { public int getCount() { return count; } } //必須實現的方法,綁定改Service時回調該方法
//Step2.經過onBind()方法返回本身的IBinder對象 @Override public IBinder onBind(Intent intent) { // 注意:返回的 IBinder對象會傳到ServiceConnection對象中的onServiceConnected的參數 Log.i(TAG, "onBind方法被調用!"); return binder; } //Service被建立時回調 @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate方法被調用!");
//建立一個線程動態地修改count的值 new Thread() { public void run() // Thread類中,重寫下run()方法 { while(!quit) { try { Thread.sleep(1000); }catch(InterruptedException e){
e.printStackTrace();
} count++; } }; }.start(); } //Service斷開鏈接時回調 @Override public boolean onUnbind(Intent intent) { Log.i(TAG, "onUnbind方法被調用!"); return true; } //Service被關閉前回調 @Override public void onDestroy() { super.onDestroy(); this.quit = true; Log.i(TAG, "onDestroyed方法被調用!"); } @Override public void onRebind(Intent intent) { Log.i(TAG, "onRebind方法被調用!"); super.onRebind(intent); } }
MainActivity類的編寫:
public class MainActivity extends Activity { private Button btnbind; private Button btncancel; private Button btnstatus; //保持所啓動的Service的IBinder對象,同時定義一個ServiceConnection對象 TestService2.MyBinder binder; private ServiceConnection conn = new ServiceConnection() { // Step3.定義一個ServiceConnection對象,重寫兩個方法 //Activity與Service斷開鏈接時回調該方法 @Override public void onServiceDisconnected(ComponentName name) { System.out.println("------Service DisConnected-------"); } //Activity與Service鏈接成功時回調該方法 @Override public void onServiceConnected(ComponentName name, IBinder service) { System.out.println("------Service Connected-------"); binder = (TestService2.MyBinder) service; } };
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
btnbind = (Button) findViewById(R.id.btnbind); btncancel = (Button) findViewById(R.id.btncancel); btnstatus = (Button) findViewById(R.id.btnstatus);
final Intent intent = new Intent(); intent.setAction("com.jay.example.service.TEST_SERVICE2");
btnbind.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { bindService(intent, conn, Service.BIND_AUTO_CREATE); // 綁定service } }); btncancel.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { unbindService(conn); // 解除service綁定 } }); btnstatus.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getApplicationContext(), "Service的count的值爲:" + binder.getCount(), Toast.LENGTH_SHORT).show(); } }); } }
若是Service已經由某個客戶端經過StartService()啓動,接下來由其餘客戶端 再調用bindService()綁定到該Service後調用unbindService()解除綁定。
最後在 調用bindService()綁定到Service的話,此時所觸發的生命週期方法以下:
onCreate( ) -> onStartCommand( ) -> onBind( ) -> onUnbind( ) -> onRebind( )
PS:前提是:onUnbind()方法返回true!!! 這裏或許部分讀者有疑惑了,調用了unbindService後Service不是應該調用 onDistory()方法麼!其實這是由於這個Service是由咱們的StartService來啓動的 ,因此你調用onUnbind()方法取消綁定,Service也是不會終止的!
得出的結論: 假如咱們使用bindService來綁定一個啓動的Service,注意是已經啓動的Service!!! 系統只是將Service的內部IBinder對象傳遞給Activity,並不會將Service的生命週期 與Activity綁定,所以調用unBindService( )方法取消綁定時,Service也不會被銷燬!
IBinder是Android給咱們提供的一個進程間通訊的一個接口,而咱們通常是不直接實現這個接口的, 而是經過繼承Binder類來實現進程間通訊!是Android中實現IPC(進程間通訊)的一種方式!
Android中的Binder機制由一系列系統組件構成: Client、Server、Service Manager和Binder驅動程序,流程以下:
--> Client調用某個代理接口中的方法時,代理接口的方法會將Client傳遞的參數打包成Parcel對象;
--> 而後代理接口把該Parcel對象發送給內核中的Binder driver;
--> 而後Server會讀取Binder Driver中的請求數據,假如是發送給本身的,解包Parcel對象, 處理並將結果返回;
NB:Client ==> 代理接口 { 打包Parcel成對象,發送出去} ==> Binder driver ==> Server讀取Binder driver中的請求數據 ==> 解包Parcel,並處理
PS:代理接口中的定義的方法和Server中定義的方法是一一對應的, 另外,整個調用過程是一個同步的,即Server在處理時,Client會被Block(鎖)住! 而這裏說的代理接口的定義就是等下要說的AIDL(Android接口描述語言)!
還在於可靠性、傳輸性、安全性。android創建了一套新的進程間通訊方式。
1.Service不是一個單獨的進程,它和它的應用程序在同一個進程中
2.Service不是一個線程,這樣就意味着咱們應該避免在Service中進行耗時操做
因而乎,Android給咱們提供瞭解決上述問題的替代品,就是下面要講的IntentService;
IntentService是繼承與Service並處理異步請求的一個類,在IntentService中有 一個工做線程來處理耗時操做,請求的Intent記錄會加入隊列。
工做流程:
客戶端經過startService(Intent)來啓動IntentService;
咱們並不須要手動控制IntentService,當任務執行完後,IntentService會自動中止;
能夠啓動IntentService屢次,每一個耗時操做會以工做隊列的方式在IntentService的 onHandleIntent回調方法中執行,而且每次只會執行一個工做線程。
再接着是代碼演示,網上大部分的代碼都是比較Service與IntentService的,定義足夠長的休眠時間,演示Service的ANR異常(Application Not Responding 應用程序無響應),而後引出IntentService有多好!
這裏就不演示Service了,網上的都是自定義Service,而後在onStart()方法 中Thread.sleep(20000),而後引起ANR異常,有興趣的能夠本身寫代碼試試,這裏的話只演示下IntentService的用法!
AndroidManifest.xml註冊下Service
<service android:name=".TestService3" android:exported="false">
<intent-filter >
<action android:name="com.test.intentservice"/>
</intent-filter>
</service>
服務代碼:TestService3.java
public class TestService3 extends IntentService { private final String TAG = "hehe";
//必須實現父類的構造方法 public TestService3() { super("TestService3"); } @Override protected void onHandleIntent(Intent intent) { // 若屢次啓動,則以工做隊列的方式在 IntentService 的 onHandleIntent 回調方法中以串行方式執行,執行完自動結束 //Activity --> Intent攜帶識別參數,根據參數不一樣執行不一樣的任務 String action = intent.getExtras().getString("param"); if(action.equals("s1"))
Log.i(TAG,"啓動service1"); else if(action.equals("s2"))
Log.i(TAG,"啓動service2"); else if(action.equals("s3"))
Log.i(TAG,"啓動service3"); //讓服務休眠2秒 try{ Thread.sleep(2000); }catch(InterruptedException e){
e.printStackTrace();
} } //重寫其餘方法,用於查看方法的調用順序 @Override public IBinder onBind(Intent intent) { Log.i(TAG,"onBind"); return super.onBind(intent); } @Override public void onCreate() { Log.i(TAG,"onCreate"); super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG,"onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void setIntentRedelivery(boolean enabled) { super.setIntentRedelivery(enabled); Log.i(TAG,"setIntentRedelivery"); } @Override public void onDestroy() { Log.i(TAG,"onDestroy"); super.onDestroy(); } }
在MainActivity啓動三次服務:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent it1 = new Intent("com.test.intentservice"); Bundle b1 = new Bundle(); b1.putString("param", "s1"); it1.putExtras(b1); Intent it2 = new Intent("com.test.intentservice"); Bundle b2 = new Bundle(); b2.putString("param", "s2"); it2.putExtras(b2); Intent it3 = new Intent("com.test.intentservice"); Bundle b3 = new Bundle(); b3.putString("param", "s3"); it3.putExtras(b3); //接着啓動屢次IntentService,每次啓動,都會新建一個工做線程 //但始終只有一個IntentService實例 startService(it1); startService(it2); startService(it3); } }
IntentService,能夠看作是Service和HandlerThread的結合體,在完成了使命以後會自動中止,適合須要在工做線程處理UI無關任務的場景。
比較少使用,Google 爲方便開發者使用,提升開發效率,封裝了 IntentService 和 HandlerThread (繼承自 Thread,內部封裝了 Looper)。
IntentService內部的HandlerThread 繼承自 Thread,內部封裝了 Looper,在這裏新建線程並啓動,因此啓動 IntentService 不須要新建線程。
IntentService 中使用的 Handler、Looper、MessageQueue 機制把消息發送到線程中去執行的,
因此,屢次啓動 IntentService 不會從新建立新的服務和新的線程,只是把消息加入消息隊列中等待執行,而若是服務中止,會清除消息隊列中的消息,後續的事件得不到執行。
@Override public void onStart(Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); } @Override public void onDestroy() { mServiceLooper.quit(); }
好比監聽是否有wifi網絡,這是個全局settting,須要廣播。
public class MyBRReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"網絡狀態發生改變~",Toast.LENGTH_SHORT).show(); } }
動態註冊:
public class MainActivity extends AppCompatActivity { MyBRReceiver myReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
myReceiver = new MyBRReceiver(); IntentFilter itFilter = new IntentFilter(); itFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); registerReceiver(myReceiver, itFilter); // <---- 動態註冊 } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(myReceiver); } }
在AndroidManifest.xml中對該BroadcastReceiver進行註冊,添加開機廣播的intent-filter!
<receiver android:name=".BootCompleteReceiver"> <intent-filter> <action android:name = "android.intent.cation.BOOT_COMPLETED"> </intent-filter> </receiver> <!-- 權限 --> <uses-permission android:name = "android.permission.RECEIVE_BOOT_COMPLETED"/>
自定義一個BroadcastReceiver,重寫onReceive完成事務處理。
public class BootCompleteReceiver extends BroadcastReceiver { private final String ACTION_BOOT = "android.intent.action.BOOT_COMPLETED";
@Override public void onReceive(Context context, Intent intent) { if (ACTION_BOOT.equals(intent.getAction())) Toast.makeText(context, "開機完畢~", Toast.LENGTH_LONG).show(); } }
註冊完了接收器,再談使用。
AndroidManifest.xml中註冊下,寫上Intent-filter:
<receiver android:name = ".MyBroadcastReceiver"> <intent-filter> <action android:name = "com.example.broadcasttest.MY_BROADCAST"/> </intent-filter> </receiver>
MyBroadcastReceiver.java
public class MyBroadcastReceiver extends BroadcastReceiver { private final String ACTION_BOOT = "com.example.broadcasttest.MY_BROADCAST";
@Override public void onReceive(Context context, Intent intent) { // <---- if(ACTION_BOOT.equals(intent.getAction())) Toast.makeText(context, "收到告白啦~",Toast.LENGTH_SHORT).show(); } }
MainActivity.java:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
Button btn_send = (Button) findViewById(R.id.btn_send); btn_send.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendBroadcast(new Intent("com.example.broadcasttest.MY_BROADCAST")); // ----> } }); } }
Goto:Android - sendOrderedBroadcast
不能經過靜態註冊方式來接受,相比起系統全局廣播更加高效。
像微信同樣,正在運行的微信,若是咱們用別的手機再次登錄本身的帳號,前面這個是會提醒帳戶 在別的終端登陸這樣,而後把咱們打開的全部Activity都關掉,而後回到登錄頁面這樣~
Step 1:準備一個關閉全部Activity的ActivityCollector ,這裏以前用前面Activity提供的那個!
ActivityCollector.java
public class ActivityCollector { private static List<Activity> activities = new ArrayList<Activity>(); 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(); } } } }
Step 2:先寫要給簡單的BaseActivity,用來繼承,接着寫下登錄界面!
public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityCollector.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); ActivityCollector.removeActivity(this); } }
LoginActivity.java:
public class LoginActivity extends BaseActivity implements View.OnClickListener{ private SharedPreferences pref; private SharedPreferences.Editor editor; private EditText edit_user; private EditText edit_pawd; private Button btn_login; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); pref = PreferenceManager.getDefaultSharedPreferences(this); // ----> 存儲方式 bindViews(); //--> } private void bindViews() { edit_user = (EditText) findViewById(R.id.edit_user); edit_pawd = (EditText) findViewById(R.id.edit_pawd); btn_login = (Button) findViewById(R.id.btn_login); btn_login.setOnClickListener(this); }
@Override protected void onStart() { super.onStart(); if(!pref.getString("user","").equals("")){ edit_user.setText(pref.getString("user","")); edit_pawd.setText(pref.getString("pawd","")); } } @Override public void onClick(View v) { String user = edit_user.getText().toString(); String pawd = edit_pawd.getText().toString();
if(user.equals("123") && pawd.equals("123")) { editor = pref.edit(); editor.putString("user", user); editor.putString("pawd", pawd); editor.commit();
Intent intent = new Intent(LoginActivity.this, MainActivity.class); // LoginActivity --> MainActivity startActivity(intent);
Toast.makeText(LoginActivity.this,"喲,居然蒙對了~",Toast.LENGTH_SHORT).show(); finish(); } else { Toast.makeText(LoginActivity.this,"這麼簡單都輸出,腦子呢?",Toast.LENGTH_SHORT).show(); } } }
Step 3:自定義一個BroadcastReceiver,在onReceive裏完成彈出對話框操做,以及啓動登錄頁面: MyBcReceiver.java
public class MyBcReceiver extends BroadcastReceiver {
@Override public void onReceive(final Context context, Intent intent) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context); dialogBuilder.setTitle("警告:"); dialogBuilder.setMessage("您的帳號在別處登陸,請從新登錄~"); dialogBuilder.setCancelable(false); dialogBuilder.setPositiveButton("肯定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCollector.finishAll(); Intent intent = new Intent(context, LoginActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } });
AlertDialog alertDialog = dialogBuilder.create(); alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); alertDialog.show(); } }
別忘了AndroidManifest.xml中加上系統對話框權限:
< uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Step 4:在MainActivity中,實例化localBroadcastManager,拿他完成相關操做,另外銷燬時 注意unregisterReceiver!
MainActivity.java
public class MainActivity extends BaseActivity { private MyBcReceiver localReceiver; private LocalBroadcastManager localBroadcastManager; private IntentFilter intentFilter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
localBroadcastManager = LocalBroadcastManager.getInstance(this); localReceiver = new MyBcReceiver(); intentFilter = new IntentFilter(); intentFilter.addAction("com.jay.mybcreceiver.LOGIN_OTHER"); localBroadcastManager.registerReceiver(localReceiver, intentFilter); // ----> 初始化廣播接收者,設置過濾器 Button btn_send = (Button) findViewById(R.id.btn_send); btn_send.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent("com.jay.mybcreceiver.LOGIN_OTHER"); localBroadcastManager.sendBroadcast(intent); } }); } @Override protected void onDestroy() { super.onDestroy(); localBroadcastManager.unregisterReceiver(localReceiver); } }
經常使用的系統廣播總結:
Intent.ACTION_AIRPLANE_MODE_CHANGED; //關閉或打開飛行模式時的廣播 <strong>Intent.ACTION_BATTERY_CHANGED; //充電狀態,或者電池的電量發生變化 //電池的充電狀態、電荷級別改變,不能經過組建聲明接收這個廣播,只有經過Context.registerReceiver()註冊 <strong>Intent.ACTION_BATTERY_LOW; //表示電池電量低 <strong>Intent.ACTION_BATTERY_OKAY; //表示電池電量充足,即從電池電量低變化到飽滿時會發出廣播 Intent.ACTION_BOOT_COMPLETED; //在系統啓動完成後,這個動做被廣播一次(只有一次)。 Intent.ACTION_CAMERA_BUTTON; //按下照相時的拍照按鍵(硬件按鍵)時發出的廣播 Intent.ACTION_CLOSE_SYSTEM_DIALOGS; //當屏幕超時進行鎖屏時,當用戶按下電源按鈕,長按或短按(無論有沒跳出話框),進行鎖屏時,android系統都會廣播此Action消息 Intent.ACTION_CONFIGURATION_CHANGED; //設備當前設置被改變時發出的廣播(包括的改變:界面語言,設備方向,等,請參考Configuration.java) Intent.ACTION_DATE_CHANGED; //設備日期發生改變時會發出此廣播 Intent.ACTION_DEVICE_STORAGE_LOW; //設備內存不足時發出的廣播,此廣播只能由系統使用,其它APP不可用? Intent.ACTION_DEVICE_STORAGE_OK; //設備內存從不足到充足時發出的廣播,此廣播只能由系統使用,其它APP不可用? Intent.ACTION_DOCK_EVENT; // //發出此廣播的地方frameworks\base\services\java\com\android\server\DockObserver.java Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE; ////移動APP完成以後,發出的廣播(移動是指:APP2SD) Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; //正在移動APP時,發出的廣播(移動是指:APP2SD) Intent.ACTION_GTALK_SERVICE_CONNECTED; //Gtalk已創建鏈接時發出的廣播 Intent.ACTION_GTALK_SERVICE_DISCONNECTED; //Gtalk已斷開鏈接時發出的廣播 Intent.ACTION_HEADSET_PLUG; //在耳機口上插入耳機時發出的廣播 Intent.ACTION_INPUT_METHOD_CHANGED; //改變輸入法時發出的廣播 Intent.ACTION_LOCALE_CHANGED; //設備當前區域設置已更改時發出的廣播 Intent.ACTION_MANAGE_PACKAGE_STORAGE; // Intent.ACTION_MEDIA_BAD_REMOVAL; //未正確移除SD卡(正確移除SD卡的方法:設置--SD卡和設備內存--卸載SD卡),但已把SD卡取出來時發出的廣播 //廣播:擴展介質(擴展卡)已經從 SD 卡插槽拔出,可是掛載點 (mount point) 還沒解除 (unmount) Intent.ACTION_MEDIA_BUTTON; //按下"Media Button" 按鍵時發出的廣播,假若有"Media Button" 按鍵的話(硬件按鍵) Intent.ACTION_MEDIA_CHECKING; //插入外部儲存裝置,好比SD卡時,系統會檢驗SD卡,此時發出的廣播? Intent.ACTION_MEDIA_EJECT; //已拔掉外部大容量儲存設備發出的廣播(好比SD卡,或移動硬盤),無論有沒有正確卸載都會發出此廣播? //廣播:用戶想要移除擴展介質(拔掉擴展卡)。 Intent.ACTION_MEDIA_MOUNTED; //插入SD卡而且已正確安裝(識別)時發出的廣播 //廣播:擴展介質被插入,並且已經被掛載。 Intent.ACTION_MEDIA_NOFS; // Intent.ACTION_MEDIA_REMOVED; //外部儲存設備已被移除,無論有沒正確卸載,都會發出此廣播? // 廣播:擴展介質被移除。 Intent.ACTION_MEDIA_SCANNER_FINISHED; //廣播:已經掃描完介質的一個目錄 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE; // Intent.ACTION_MEDIA_SCANNER_STARTED; //廣播:開始掃描介質的一個目錄 Intent.ACTION_MEDIA_SHARED; // 廣播:擴展介質的掛載被解除 (unmount),由於它已經做爲 USB 大容量存儲被共享。 Intent.ACTION_MEDIA_UNMOUNTABLE; // Intent.ACTION_MEDIA_UNMOUNTED // 廣播:擴展介質存在,可是尚未被掛載 (mount)。 Intent.ACTION_NEW_OUTGOING_CALL; Intent.ACTION_PACKAGE_ADDED; //成功的安裝APK以後 //廣播:設備上新安裝了一個應用程序包。 //一個新應用包已經安裝在設備上,數據包括包名(最新安裝的包程序不能接收到這個廣播) Intent.ACTION_PACKAGE_CHANGED; //一個已存在的應用程序包已經改變,包括包名 Intent.ACTION_PACKAGE_DATA_CLEARED; //清除一個應用程序的數據時發出的廣播(在設置--應用管理--選中某個應用,以後點清除數據時?) //用戶已經清除一個包的數據,包括包名(清除包程序不能接收到這個廣播) Intent.ACTION_PACKAGE_INSTALL; //觸發一個下載而且完成安裝時發出的廣播,好比在電子市場裏下載應用? // Intent.ACTION_PACKAGE_REMOVED; //成功的刪除某個APK以後發出的廣播 //一個已存在的應用程序包已經從設備上移除,包括包名(正在被安裝的包程序不能接收到這個廣播) Intent.ACTION_PACKAGE_REPLACED; //替換一個現有的安裝包時發出的廣播(無論如今安裝的APP比以前的新仍是舊,都會發出此廣播?) Intent.ACTION_PACKAGE_RESTARTED; //用戶從新開始一個包,包的全部進程將被殺死,全部與其聯繫的運行時間狀態應該被移除,包括包名(從新開始包程序不能接收到這個廣播) Intent.ACTION_POWER_CONNECTED; //插上外部電源時發出的廣播 Intent.ACTION_POWER_DISCONNECTED; //已斷開外部電源鏈接時發出的廣播 Intent.ACTION_PROVIDER_CHANGED; // Intent.ACTION_REBOOT; //重啓設備時的廣播 Intent.ACTION_SCREEN_OFF; //屏幕被關閉以後的廣播 Intent.ACTION_SCREEN_ON; //屏幕被打開以後的廣播 Intent.ACTION_SHUTDOWN; //關閉系統時發出的廣播 Intent.ACTION_TIMEZONE_CHANGED; //時區發生改變時發出的廣播 Intent.ACTION_TIME_CHANGED; //時間被設置時發出的廣播 Intent.ACTION_TIME_TICK; //廣播:當前時間已經變化(正常的時間流逝)。 //當前時間改變,每分鐘都發送,不能經過組件聲明來接收,只有經過Context.registerReceiver()方法來註冊 Intent.ACTION_UID_REMOVED; //一個用戶ID已經從系統中移除發出的廣播 // Intent.ACTION_UMS_CONNECTED; //設備已進入USB大容量儲存狀態時發出的廣播? Intent.ACTION_UMS_DISCONNECTED; //設備已從USB大容量儲存狀態轉爲正常狀態時發出的廣播? Intent.ACTION_USER_PRESENT; // Intent.ACTION_WALLPAPER_CHANGED; //設備牆紙已改變時發出的廣播
經過 ContentResolver來讀取其餘應用的信息,最經常使用的莫過於讀取聯繫人, 多媒體信息等!
1)簡單的讀取收件箱信息:
2)簡單的往收件箱裏插入一條信息
3)簡單的讀取手機聯繫人
4)查詢指定電話的聯繫人信息
5)添加一個新的聯繫人
自定義ContentProvider
經過ContentObserver監聽ContentProvider的數據變化。
Ref:內容提供者ContentProvider和內容解析者ContentResolver
內容提供者ContentProvider
使用ContentProvider對外共享數據的好處是統一了數據的訪問方式。
內容解析者ContentResolver
使用ContentResolver調用ContentProvider提供的接口,操做數據。例如:
當外部應用須要對ContentProvider中的數據進行添加、刪除、修改和查詢操做時,可使用ContentResolver 類來完成,要獲取ContentResolver 對象。
【具體用到時再細究】