[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 对象。

 

【具体用到时再细究】

相关文章
相关标签/搜索