[Android] 02 - Four Basic Components and Intent

四大組件: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"/>

 

三個比較重要的文件:

      • MainActivity.java,
      • activity_main佈局文件,
      • Android配置文件。

 

 

 

 

Activity生命週期

 

學習資源:

代碼下載: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;
package and import
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的能力

  1. 當咱們只弄一個參數的時候,咱們知道Bundle 幫咱們保存當咱們切換屏幕啊,異常終止等shut down的行爲
  2. 可是5.0後一些特殊行爲powered off 或者其餘行爲。他會幫咱們保存在outPersistentState中,一種更加堅固的保存數據方式
@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"設置除外)。
View Code

 

*** 不由止的話

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的管理:

    • 修改AndroidManifest.xml中 < activity >的相關屬性值【----> 詳細描述】
      • taskAffinity
      • launchMode
      • allowTaskReparenting
      • clearTaskOnLaunch
      • alwaysRetainTaskState
      • finishOnTaskLaunch
    • 在代碼中經過傳遞特殊標識的Intent給startActivity( )
      • FLAG_ACTIVITY_NEW_TASK
      • FLAG_ACTIVITY_CLEAR_TOP
      • FLAG_ACTIVITY_SINGLE_TOP

 

相關屬性值 - taskAffinity

1)當啓動 activity的Intent對象包含FLAG_ACTIVITY_NEW_TASK標記:

當傳遞給startActivity()的Intent對象包含 FLAG_ACTIVITY_NEW_TASK標記時,系統會爲須要啓動的Activity尋找與當前Activity不一樣Task。

    • 若是要啓動的 Activity的Affinity屬性與當前全部的Task的Affinity屬性都不相同,系統會新建一個帶那個Affinity屬性的Task,並將要啓動的Activity壓到新建的Task棧中;
    • 不然將Activity壓入那個Affinity屬性相同的棧中。

2)allowTaskReparenting屬性設置爲true

那麼它能夠從一個Task(Task1)移到另一個有相同Affinity的Task(Task2)中(Task2帶到前臺時)。

若是一個.apk文件從用戶角度來看包含了多個"應用程序",你可能須要對那些 Activity賦不一樣的Affinity值。

 

相關屬性值 - launchMode

四個可選值,啓動模式咱們研究的核心,他們分別是:standard (default),singleTopsingleTasksingleInstance----> (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了。

 

 

 

Service

 

1) StartService(),啓動Service
2) BindService(),啓動Service
3) 啓動Service後,綁定Service

 

1) StartService啓動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);  
                  
            }  
        });  
    }  
}

 

2) BindService啓動Service

① 當首次使用bindService綁定一個Service時,系統會實例化一個Service實例,並調用其onCreate()和onBind()方法,

     而後,調用者就能夠經過IBinderService進行交互了,

     此後,若是再次使用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(); } }); } }

 

3) StartService啓動Service後bindService綁定【有點亂,沒搞明白】

若是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也不會被銷燬!

 

 

  • Android中的AIDL跨進程通訊 - binder

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使用Binder機制來實現進程間的通訊?

還在於可靠性、傳輸性、安全性。android創建了一套新的進程間通訊方式。

 

 

 

  • IntentService 

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,用完即走

參考:IntentService 示例與詳解

 

IntentService,能夠看作是Service和HandlerThread的結合體,在完成了使命以後會自動中止,適合須要在工做線程處理UI無關任務的場景。

    • 當任務執行完後,自動中止。
    • 若是啓動屢次,那麼每個耗時操做會以工做隊列的方式在 IntentService 的 onHandleIntent 回調方法中以串行方式執行,執行完自動結束。

比較少使用,Google 爲方便開發者使用,提升開發效率,封裝了 IntentService 和 HandlerThread (繼承自 Thread,內部封裝了 Looper)。

 

啓動 IntentService 爲何不須要新建線程?

IntentService內部的HandlerThread 繼承自 Thread,內部封裝了 Looper,在這裏新建線程並啓動,因此啓動 IntentService 不須要新建線程。

爲何屢次啓動 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();
}

 

 

 

BroadcastReceiver

 

 

動態註冊實例

好比監聽是否有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;
//設備牆紙已改變時發出的廣播
View Code

 

 

 

ContentProvider

 

經過 ContentResolver來讀取其餘應用的信息,最經常使用的莫過於讀取聯繫人, 多媒體信息等!

1)簡單的讀取收件箱信息:

2)簡單的往收件箱裏插入一條信息

3)簡單的讀取手機聯繫人

4)查詢指定電話的聯繫人信息

5)添加一個新的聯繫人

自定義ContentProvider

經過ContentObserver監聽ContentProvider的數據變化。

 

Ref:內容提供者ContentProvider和內容解析者ContentResolver

內容提供者ContentProvider

使用ContentProvider對外共享數據的好處是統一了數據的訪問方式

內容解析者ContentResolver

使用ContentResolver調用ContentProvider提供的接口,操做數據。例如:

當外部應用須要對ContentProvider中的數據進行添加、刪除、修改和查詢操做時,可使用ContentResolver 類來完成,要獲取ContentResolver 對象。

 

【具體用到時再細究】

相關文章
相關標籤/搜索