应用组件是 Android 应用的基本构建基块。
共有四种不同的应用组件类型。每种类型都服务于不同的目的,并且具有定义组件的创建和销毁方式的不同生命周期。
Activity、服务和广播接收器 — 通过名为 Intent 的异步消息进行启动。Intent 会在运行时将各个组件相互绑定(您可以将 Intent 视为从其他组件请求操作的信使),无论组件属于您的应用还是其他应用。
Intent是连接应用程序的三个核心组件——Activity、Service和BroadcastReceiver的桥梁。
Intent负责对应用中操作的动作、动作涉及数据及附加数据进行描述。
一个Intent对象其实就是信息的捆绑。
Intent声明启动的组件有两种方式:
显式Intent:通过组件名指定启动的目标组件,比如startActivity(new Intent(A.this,B.class));
每次启动的组件只有一个;
隐式Intent:不指定组件名,而指定Intent的Action,Data,或Category,当我们启动组件时, 会去匹配AndroidManifest.xml相关组件的Intent-filter
,逐一匹配出满足属性的组件,当不止一个满足时, 会弹出一个让我们选择启动哪个的对话框。
Intent对象由组件(Componen)名称、操作(Action)、操作类别(Category)、数据(Data)、数据类型(Type)、附加信息(Extras)及标志(Flags)七部分组成。
组件(Component)名称
要处理该Intent的组件名称。通过setComponent()
来设置组件名和getComponent()
读取组件名。
ComponentName cn = new ComponentName(OneActivity.this, TwoActivity.class);
Intent it = new Intent();
it.setComponent(cn);
操作(Action)
一个字符串,用于命名要采取的行动。设置该Intent会触发的操作类型,可以通过setAction()
方法进行设置,在Android系统之中已经为用户准备好了一些表示Action操作的常量,例如:ACTION_CALL、ACTION_MAIN等。
操作类别(Category)
对执行操作的类别进行描述,可以通过addCategory()
方法设置多个类别,removeCategory()
删除上次添加的类别,getCategories()
获取当前对象所包含的全部类别。
数据(Data)
执行动作要操作的数据,用指向数据的一个URI来表示。描述Intent所操作数据的URI及类型,可以通过setData()
进行设置,不同的操作对应着不同的Data。
数据类型(Type)
指定要传送数据的MIME类型,可以直接通过setType()
方法进行设置。
附加信息(Extras)
其他所有附加信息的集合,传递的是一组键值对,可以使用putExtra()
方法进行设置,主要的功能是传递数据(Uri)所需要的一些额外的操作信息。
标志(Flags)
用于指示Android系统如何加载并运行一个操作,可以通过addFlags()
方法进行增加。
//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");
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.打开浏览器:
// 打开百度主页
Uri uri = Uri.parse("http://www.baidu.com");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
//===============================================================
//5.发送电子邮件:(阉割了Google服务的没戏!!!!)
// 给[email protected]发邮件
Uri uri = Uri.parse("mailto:[email protected]");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
startActivity(intent);
// 给[email protected]发邮件发送内容为“Hello”的邮件
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, "[email protected]");
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 = {"[email protected]", "[email protected]"}; // 收件人
String[] ccs = {"[email protected]", "[email protected]"}; // 抄送
String[] bccs = {"[email protected]", "[email protected]"}; // 密送
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);
//===============================================================
//18.调用系统编辑添加联系人(高版本SDK有效):
Intent it = newIntent(Intent.ACTION_INSERT_OR_EDIT);
it.setType("vnd.android.cursor.item/contact");
//it.setType(Contacts.CONTENT_ITEM_TYPE);
it.putExtra("name","myName");
it.putExtra(android.provider.Contacts.Intents.Insert.COMPANY, "organization");
it.putExtra(android.provider.Contacts.Intents.Insert.EMAIL,"email");
it.putExtra(android.provider.Contacts.Intents.Insert.PHONE,"homePhone");
it.putExtra(android.provider.Contacts.Intents.Insert.SECONDARY_PHONE,"mobilePhone");
it.putExtra( android.provider.Contacts.Intents.Insert.TERTIARY_PHONE,"workPhone");
it.putExtra(android.provider.Contacts.Intents.Insert.JOB_TITLE,"title");
startActivity(it);
//===============================================================
//19.调用系统编辑添加联系人(全有效):
Intent intent = newIntent(Intent.ACTION_INSERT_OR_EDIT);
intent.setType(People.CONTENT_ITEM_TYPE);
intent.putExtra(Contacts.Intents.Insert.NAME, "My Name");
intent.putExtra(Contacts.Intents.Insert.PHONE, "+1234567890");
intent.putExtra(Contacts.Intents.Insert.PHONE_TYPE,Contacts.PhonesColumns.TYPE_MOBILE);
intent.putExtra(Contacts.Intents.Insert.EMAIL, "[email protected]");
intent.putExtra(Contacts.Intents.Insert.EMAIL_TYPE, Contacts.ContactMethodsColumns.TYPE_WORK);
startActivity(intent);
//===============================================================
//20.打开另一程序
Intent i = new Intent();
ComponentName cn = new ComponentName("com.example.jay.test",
"com.example.jay.test.MainActivity");
i.setComponent(cn);
i.setAction("android.intent.action.MAIN");
startActivityForResult(i, RESULT_OK);
//===============================================================
//21.打开录音机
Intent mi = new Intent(Media.RECORD_SOUND_ACTION);
startActivity(mi);
//===============================================================
//22.从google搜索内容
Intent intent = new Intent();
intent.setAction(Intent.ACTION_WEB_SEARCH);
intent.putExtra(SearchManager.QUERY,"searchString")
startActivity(intent);
Activity程序支持的Intent操作方法:
如果现在Receive需要在回传给Send数据的话,则就不能使用startActivity()
方法,只能通过
startActivityForResult()
方法完成了,但是如果要想接收回传数据的话,则需要Activity常量的支持:
public static final int RESULT_OK
public static final int RESULT_CANCELED
public static final int RESULT_FIRST_USER
Intent传递简单数据:
如果你想某个数据可以在任何地方都能获取到,你就可以考虑使用 Application全局对象了!
Android系统在每个程序运行的时候创建一个Application对象,而且只会创建一个,所以Application 是单例(singleton)模式的一个类,而且Application对象的生命周期是整个程序中最长的,他的生命周期等于这个程序的生命周期。如果想存储一些静态的值(固定不改变的,也可以变),如果你想使用 Application就需要自定义类实现Application类,并且告诉系统实例化的是我们自定义的Application 而非系统默认的,而这一步,就是在AndroidManifest.xml中为我们的application标签添加:name属性!
自定义Application类:
class MyApp extends Application {
private String myState;
private static MyApp instance;
public static MyApp getInstance(){
return instance;
}
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
@Override
public void onCreate(){
onCreate();
instance = this;
}
}
AndroidManifest.xml中声明:
在需要的地方调用:
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
// 调用 MyApp.getInstance()来获得Application的全局对象
MyApp appState = MyApp.getInstance();
String state = appState.getState();
...
}
}
Activity用于显示用户界面,用户通过Activity交互完成相关操作。一个App允许有多个Activity。
应用程序组件有其生命周期:由Android初始化它们,以相应Intent响应意图,直到结束,实例被销毁。
Activity被一个Activity栈管理。堆栈中保存对象的实例,在一个任务中可能存在多个同一Activity的实例。
生命周期中的五种状态:启动,运行,暂停,停止,销毁。
Activity的生命周期状态转变:
onCreate(Bundle)
:
onSaveInstanceState()
方法保存的状态信息。onStart()
或onRestart()
方法。onStart()
:当Activity对用户即将可见时调用。
onResume()
:用户可以开始与活动进行交互时会调用该方法。
onPause()
:活动将进入后台时会运行该方法。
onStop()
:在一段时间内不需要某个活动时,调用该方法。
onRestart()
:将已处于停止状态的活动重新显示给用户。
onDestroy()
:销毁活动前调用该方法。如果内存不足,系统会终止进程,可能不需要调用该方法。
onSaveInstanceState(Bundle)
:调用该方法让活动可以保存每个实例的状态。
onRestoreInstanceState(Bundle)
:使用onSaveInstanceState()
方法保存的状态来重新初始化某个活动时调用该方法。
Activity生命周期中函数的调用过程
生命周期函数调用举例
有两个界面Activity A和Activity B。
1、先启动第一个界面Activity A,方法回调的次序是:
2、Activity A不关闭,跳转第二个Activity B,方法回调的次序是:
3、在点击back回到第一个界面,这时方法回调的次序是:
4、在点击Exit退出应用时,方法回调的次序是:
onSaveInstanceState(Bundle)
会在下述情形中被调用:
重要原则:当系统"未经你许可"时销毁了你的activity,则onSaveInstanceState
会被系统调用, 这是系统的责任,因为它必须要提供一个机会让你保存你的数据(你可以保存也可以不保存)。
一个Activity被激活并运行,即为建立了一个Activity的实例。
Activity实例与用户交互,产生界面状态信息。如用户所选取的值,光标的位置等。
当Activity实例进入“暂停”或“停止”状态时,需要保存这些临时的状态信息。
保存状态信息的方法:SharedPreferences对象和Bundle对象。
SharedPreferences对象
每个Activity都有一个无名的SharedPreferences对象。其SharedPreferences对象的访问权限是私有的。
SharedPreferences提供一种基于name/value形式的键值二元组的存储方式。
SharedPreferences支持的数据类型有:String、Long、Float、Integer、Boolean。
方法:
getPreferences()
:获取SharedPreferences对象。put...()
方法保存键-值对。例如保存字符串型的使用putString()
方法。使用SharedPreferences对象的一般步骤:
SharedPreferences
对象。SharedPreferences.Editor
对象。put...()
方法保存键-值对。commit()
方法进行提交。例,Activity的SharedPreferences对象读写代码:
Protected void saveActivityPreferences(){
SharedPreferences activityPref=getPreferences(Activity.MODE_PRIVATE);
//获取Activity的匿名SharedPreferrences对象
Editor editor=activityPref.edit();
TextView textView=(TextView)findViewById(R.id.textView);
//获取TextView控件对象
editor.putString(“TextValue”,textView.getText().toString());
//存储TextView控件信息
editor.commit();
}
Bundle对象
在Activity生命周期的方法中,使用Bundle来完成相关信息的保存和读写。
生命周期中几种状态调用的方法,其输入参数是Bundle类型的有:onCreate()
、onSaveInstanceState()
、onRestoreInstanceState()
。
在使用Bundle传递数据时,要注意,Bundle的大小是有限制的, Bundle内容大小应小于 0.5MB,如果大于这个值,是会报TransactionTooLargeException异常的!
Bundle与SharedPreferences的区别:
Service是后台运行的,没有用户交互界面的“服务”。如:播放音乐;检测SD卡上文件的变化;后台数据计算,如记录用户的地理信息位置的改变;发出Notification。
Service一般由Activity启动,但不依赖于Activity 。也可以由其他的Service或者Broadcast Receiver启动。
如果service正在调用onCreate()
、onStart()
或onDestory()
方法,那么用于当前service的进程则变为前台进程以避免被killed。
如果service已经被启动,拥有它的进程仅比可见的进程低,而比不可见的进程重要,这就意味着service一般不会被killed.
如果客户端已经连接到service ,那么拥有它的进程则拥有最高的优先级,可以认为该service是可见的。
如果service可以使用startForeground(int, Notification)
方法来将service设置为前台状态,那么系统就认为该service是对用户可见的,并不会在内存不足时killed。
TestService2.java:
public class TestService2 extends Service {
private final String TAG = "TestService2";
private int count;
private boolean quit;
//定义onBinder方法所返回的对象
private MyBinder binder = new MyBinder();
public class MyBinder extends Binder
{
public int getCount()
{
return count;
}
}
//必须实现的方法,绑定改Service时回调该方法
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind方法被调用!");
return binder;
}
//Service被创建时回调
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate方法被调用!");
//创建一个线程动态地修改count的值
new Thread()
{
public void 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);
}
}
在AndroidManifest.xml中对Service组件进行注册:
MainActivity.java:
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() {
//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) {
//绑定service
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
btncancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//解除service绑定
unbindService(conn);
}
});
btnstatus.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "Service的count的值为:"
+ binder.getCount(), Toast.LENGTH_SHORT).show();
}
});
}
}
由于
所以如果把耗时线程放到Service中的onStart()方法中,很容易会引起ANR异常(Application Not Responding)。
IntentService是继承与Service并处理异步请求的一个类,在IntentService中有 一个工作线程来处理耗时操作,请求的Intent记录会加入队列
工作流程:
startService(Intent)
来启动IntentService;TestService3.java
public class TestService3 extends IntentService {
private final String TAG = "hehe";
//必须实现父类的构造方法
public TestService3()
{
super("TestService3");
}
//必须重写的核心方法
@Override
protected void onHandleIntent(Intent intent) {
//Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务
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();
}
}
AndroidManifest.xml注册下Service
在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);
}
}
BroadcastReceiver是对广播消息进行过滤并响应的控件。
接收广播服务的过程
registerReceiver()
方法来注册。代码如:MyReceiver receiver=new MyReceiver(); //创建相关对象
IntentFilter filter=new IntentFilter();
filter.addAction(DATE_CHANGED);
registerReceiver(receiver, filter); //动态注册BroadcastReceiver
两种注册方式比较:
自定义一个BroadcastReceiver,在onReceive()
方法中完成广播要处理的事务,比如使用Toast提示"网络状态发生改变"。
public class MyBRReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"网络状态发生改变~",Toast.LENGTH_SHORT).show();
}
}
MainActivity.java中动态注册广播:
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);
}
}
自定义一个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中对该BroadcastReceiver进行注册,添加开机广播的intent-filter!别忘了加上android.permission.RECEIVE_BOOT_COMPLETED的权限!
重启下手机会发现过了一会儿,就会弹出开机完毕这个Toast。
不要在广播里添加过多逻辑或者进行任何耗时操作,因为在广播中是不允许开辟线程的, 当onReceiver( )方法运行较长时间(超过10秒)还没有结束的话,那么程序会报错(ANR), 广播更多的时候扮演的是一个打开其他组件的角色,比如启动Service,Notification提示, Activity等!
标准广播:用sendBroadcast和sendStickyBroadcast发送广播
有序广播:用sendOrderBroadcast发送
sendStickyBroadcast 与其它发送方式的不同之处:Intent在发送之后一直存在,并且在以后调用registerReceive注册相匹配的Receive时会把这个Intent直接返回给新注册的Receive。
可以调用abortBroadcast()
截断广播的继续传递。
自定义广播子类来接收广播
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();
}
}
在AndroidManifest.xml中注册下,写上Intent-filter:
把上面这个程序项目运行下,然后关掉,接下来我们新建一个项目, 在这个项目里完成广播发送~新建Demo2,布局就一个简单按钮,然后在MainActivity中完成广播发送:
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"));
}
});
}
}
前面写的广播都是全局广播!这意味着我们APP发出的广播,其他APP都会接收到, 或者其他APP发送的广播,我们的APP也同样会接收到,这样容易引起一些安全性的问题!而 Android中给我们提供了本地广播的机制,使用该机制发出的广播只会在APP内部传播,而且 广播接收者也只能收到本应用发出的广播!
LocalBroadcastManager.getInstance()
获得实例;LocalBroadcastManager.registerReceiver()
注册广播;LocalBroadcastManager.sendBroadcast()
发送广播;LocalBroadcastManager.unregisterReceiver()
取消广播。FLAG_ACTIVITY_NEW_TASK
的标记,不然会报错,因为需要一个栈来存放新打开的Activity;TYPE_SYSTEM_ALERT
,不然是无法弹出的。像微信一样,正在运行的微信,如果我们用别的手机再次登陆自己的账号,前面这个是会提醒账户"在别的终端登录",然后把我们打开的所有Activity都关掉,然后回到登陆页面。
准备一个关闭所有Activity的ActivityCollector
public class ActivityCollector {
private static List activities = new ArrayList();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
public static void finishAll() {
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}
先写要给简单的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);
startActivity(intent);
Toast.makeText(LoginActivity.this,"哟,竟然蒙对了~",Toast.LENGTH_SHORT).show();
finish();
}else{
Toast.makeText(LoginActivity.this,"这么简单都输出,脑子呢?",Toast.LENGTH_SHORT).show();
}
}
}
自定义一个BroadcastReceiver,在onReceive里完成弹出对话框操作,以及启动登陆页面。
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" />
在MainActivity中,实例化localBroadcastManager,拿他完成相关操作,另外销毁时 注意unregisterReceiver!
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);
}
}
ContentProvider是实现两个程序间进行数据交换的组件。
Android程序中的数据(如:SharedPreferences、文件数据和数据库数据等)都是私有的。
当我们想允许自己的应用的数据允许别的应用进行读取操作,可以让我们的APP实现ContentProvider类,同时注册一个Uri,然后其他应用只要使用ContentResolver根据Uri就可以操作我们的APP中的数据了。
对ContentProvider中的数据进行操作,都需要在AndroidManifest.xml文件中添加相应的权限。
例如,对手机的通讯录进行查询和修改操作,则在AndroidManifest.xml文件的
标签内需要添加下列权限设置:
…
…
ContentProvider类为程序提供一组标准的抽象接口,程序通过该接口暴露自己的数据。
当外部应用程序需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 接口来完成。
ContentResolver提供的接口与ContentProvider中需要实现的接口对应。
获取ContentResolver对象的方法:getContentResolver()
例:ContenteResolver cr=getContentResolver();
在ContentProvider和ContentResolver中用到的Uri形式:
content://contacts/people/
—— 指定全部的联系人数据content://contacts/people/2
—— 指定ID为2的联系人数据Uri类常用的操作方法
Uri的辅助操作类:ContentUris
由于所有的数据都要通过Uri进行传递,以增加操作为例,当用户执行完增加数据操作后往往需要将增加后的数据ID通过Uri进行返回,当接收到这个Uri的时候就需要从里面取出增加的ID,为了方便用户这种取出数据的操作,在Android中又提供了一个android.content.ContentUris的辅助工具类,帮助用户完成Uri的若干操作;
ContentUris类常用的操作方法
Uri的辅助操作类:UriMatcher类
由于在使用ContentProvider类操作的时候,某一个方法都可能要传递多种Uri,例如:以query()
方法为例,有可能表示查询全部,有可能表示按ID查询,所以必须对这些传递的Uri
进行判断后才可以决定最终的操作形式,为了方便的用户的判断,专门提供了android.content.UriMatcher类,进行Uri的匹配;
UriMatcher类常用的操作方法: