Android四大组件之Activity全面学习
来源:互联网 发布:python哪个gui最简单 编辑:程序博客网 时间:2024/06/14 01:19
广播机制简介
Android 中的每个 app 都可以对自己感兴趣的广播进行注册。
这些广播可以是来自系统的,也可以是来自其他 app 的。
发送广播是通过 Intent 的,而接收广播需要通过Broadcast Receiver(广播接收器)。
广播的类型
Android 中的广播主要分两类:标准广播和有序广播。
标准广播
它是一种完全异步执行的广播,在它发出去以后,所有的接收器差不多同时接收,没有先后顺序。
这种广播效率高,同时无法被截断。
有序广播
它是一种同步执行的广播,在它发出去以后,同一时刻只有一个接收器可以收到这条广播。
当接收器中的逻辑执行完毕后,广播才会继续传递。
接收系统广播
Android 内置了很多系统级别的广播。
创建广播接收器
要想接收到系统内置的这些广播,先要创建一个广播接收器。
创建一个类,继承自BroadcastReceiver,并重写父类方法onReceive()就行了。
动态注册和静态注册
创建完接收器还不够,还要注册接收器。
注册接收器的方式一般有两种,在代码中注册和在AndroidManifest.xml中注册。
前者为动态注册,后者为静态注册。
动态注册必须等到 app 启动之后才能接收广播,而静态注册可以实现未启动应用也可以接收广播。
动态接收网络状态广播
我们写一个例子,创建一个接收器,注册它,然后就可以监听系统的网络状态了。
这需要访问网络的权限,所以要在AndroidManifest.xml中添加:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
实现代码:
public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver = new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver,intentFilter); } @Override protected void onDestroy(){ super.onDestroy(); unregisterReceiver(networkChangeReceiver); } class NetworkChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo(); if (networkInfo != null && networkInfo.isAvailable()){ Toast.makeText(context,"network is available", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(context,"network is unavailable",Toast.LENGTH_SHORT).show(); } } }}
静态接收开机启动广播
写完动态注册,再写一个静态注册的例子,之前已经说过静态注册的好处是,不用启动app也可以实现监听广播。
要想实现接收广播,首先要有个广播接收器。
- 在包名上右键/new/other/Broadcast Receiver,创建一个广播接收器,创建界面上有个exported属性,表示是否允许接收app以外的广播。
实际上这时候去看AndroidManifest.xml,可以看到AS已经自动注册了这个接收器,这就是静态注册。
<receiver android:name=".MyReceiver" android:enabled="true" android:exported="true"></receiver>
- 我们给这个接收器添加一个接收对象。
<receiver android:name=".MyReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
- 监听开机启动广播需要权限。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- 再看接收器内部,当手机开机的时候会弹出这个Toast。
@Override public void onReceive(Context context, Intent intent) { // TODO: This method is called when the BroadcastReceiver is receiving // an Intent broadcast. Toast.makeText(context,"启动成功",Toast.LENGTH_SHORT).show(); }
注意,不要在onReceive方法中添加过多的逻辑或者耗时操作,因为接收器中不允许创建线程,如果onReceive运行了较长时间还不结束,就会报错。
发送自定义广播
之前介绍过广播分两种,一种是标准广播,也就是多个接收器可以同时接收到。
还有一种是有序广播,同一时刻只能有一个接收器接收到。
下面分别来介绍。
发送标准广播
- 编写触发广播代码
Intent intent = new Intent("arch.qtrun.com.myapplication.MY_BROADCAST"); sendBroadcast(intent);
“arch.qtrun.com.myapplication.MY_BROADCAST”是在静态注册中声明的广播标记。
- 创建接收器
创建接收器的方法和上面接收开机广播的接收器是一样的。
创建Broadcast Receiver/在静态注册中添加广播标记。
<receiver android:name=".MyReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="arch.qtrun.com.myapplication.MY_BROADCAST" /> </intent-filter> </receiver>
发送有序广播
有序广播触发函数如下:
Intent intent = new Intent("arch.qtrun.com.myapplication.MY_BROADCAST"); // 第二个参数是权限相关的字符串 sendOrderedBroadcast(intent,null);
有序广播的接收器的创建跟标准广播没有什么不同,但是它可以设置优先级,优先级高的会先接收到广播。
<receiver android:name=".MyReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"> <action android:name="arch.qtrun.com.myapplication.MY_BROADCAST" /> </intent-filter> </receiver>
因为有序广播同一时刻只能有一个接收器接收它,整个接收的顺序是直线式的传递的,每一个接收者还可以设置广播是否继续传递,也就是拦截广播的功能。
public void onReceive(Context context, Intent intent) { // TODO: This method is called when the BroadcastReceiver is receiving // an Intent broadcast. Toast.makeText(context,"receive",Toast.LENGTH_SHORT).show(); // 广播不会继续传递 abortBroadcast(); }
使用本地广播
前面的广播,无论是系统广播,还是自定义的标准广播、有序广播,都是全局的,也就是整个系统任意的app都可以接收的到。
还有一种广播,只能在本app内使用。
本地广播使用LocalBroadcastManager来对广播进行管理,并提供了发送广播和注册广播接收器的方法。
需要注意的是本地广播是不能使用静态方式注册的。
public class MainActivity extends AppCompatActivity { private IntentFilter intentFilter; private LocalBroadcastManager localBroadcastManager; private LocalReceiver localReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); localBroadcastManager = LocalBroadcastManager.getInstance(this); // 注册广播 intentFilter = new IntentFilter(); intentFilter.addAction("arch.qtrun.com.myapplication.LOCAL_BROADCAST"); // 创建接收器 localReceiver = new LocalReceiver(); // 注册接收器 localBroadcastManager.registerReceiver(localReceiver,intentFilter); Intent intent = new Intent("arch.qtrun.com.myapplication.LOCAL_BROADCAST"); localBroadcastManager.sendBroadcast(intent); } @Override protected void onDestroy(){ super.onDestroy(); localBroadcastManager.unregisterReceiver(localReceiver); }}class LocalReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,"local receive",Toast.LENGTH_SHORT).show(); }}
广播的最佳实践–实现强制下线功能
这一节主要实现一个简单的app,它有一个登陆界面,登陆成功以后进入主界面,在主界面里面有个按钮,点击会发送强制退出的广播。
app收到这个广播后会弹出对话框,点击对话框会重新进入到登陆界面。
这个app第一个启动的界面是登陆界面,所以我们要创建一个activity作为登陆界面。
这个activity在创建的时候要同时创建一个匹配的布局文件。
布局文件是这样的:
// activity_login.xml<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="60dp"> <TextView android:layout_width="90dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="18sp" android:text="Account:"/> <EditText android:id="@+id/account" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="60dp"> <TextView android:layout_width="90dp" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textSize="18sp" android:text="Password:"/> <EditText android:id="@+id/password" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" android:inputType="textPassword"/> </LinearLayout> <Button android:id="@+id/login" android:layout_width="match_parent" android:layout_height="60dp" android:text="Login"/></LinearLayout>
下面是登陆activity的主逻辑,它主要做的就是登陆成功以后进入到MainActivity:
//LoginActivitypublic class LoginActivity extends BaseActivity { private EditText accountEdit; private EditText passwordEdit; private Button login; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); // 初始化登陆界面 accountEdit = (EditText)findViewById(R.id.account); passwordEdit = (EditText)findViewById(R.id.password); login = (Button)findViewById(R.id.login); login.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view) { String account = accountEdit.getText().toString(); String password = passwordEdit.getText().toString(); if (account.equals("admin") && password.equals("123456")){ // 如果用户名和密码都输入正确就进入main主界面 Intent intent = new Intent(LoginActivity.this,MainActivity.class); startActivity(intent); finish(); }else { Toast.makeText(LoginActivity.this,"account or password is invalid",Toast.LENGTH_SHORT).show(); } } }); }}
因为app的第一个activity是登陆,所以在AndroidManifest.xml中要修改一下启动activity
// AndroidManifest.xml <activity android:name=".MainActivity"></activity> <activity android:name=".LoginActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
登陆成功以后,会进入主界面,主界面主要有一个按钮,点击按钮会发送一条广播。
// MainActivitypublic class MainActivity extends BaseActivity { private EditText accountEdit; private EditText passwordEidt; private Button login; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 创建强制退出按钮 Button forceOffline = (Button)findViewById(R.id.force_offline); forceOffline.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view) { // 点击强制退出按钮,发送广播 Intent intent = new Intent("arch.qtrun.com.myapplication.FORCE_OFFLINE"); sendBroadcast(intent); } }); } @Override protected void onDestroy(){ super.onDestroy(); }}
因为要收集所有活动,用于在收到强制退出广播的时候一次清除掉它们。
所以需要将LoginActivity和MainActivity继承自BaseActivity,这样在BaseActivity的onCreate()方法中就方便将这些子类放到容器了,到时候只要有这个容器,就可以轻易的操作所有的activity。
BaseActivity稍微复杂一些,里面主要是注册广播接收者以及将activity放入容器中的一些代码。
// BaseActivitypublic class BaseActivity extends AppCompatActivity { private ForceOfflineReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 将本活动放到活动收集器里面,用于强制退出时清空所有运行的活动 ActivityCollector.addActivity(this); } @Override // app生命周期:app暂停状态,app准备进入后台时会调用这个方法 protected void onPause(){ super.onPause(); // app在后台时不使用广播接收者 if (receiver != null){ unregisterReceiver(receiver); receiver = null; } } @Override // app生命周期:app恢复状态,app准备从后台进入前台时调用这个方法 protected void onResume(){ super.onResume(); // app恢复以后,重新注册接收者 receiver = new ForceOfflineReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("arch.qtrun.com.myapplication.FORCE_OFFLINE"); registerReceiver(receiver,intentFilter); } @Override protected void onDestroy(){ super.onDestroy(); // app销毁时,清空堆上的数组 ActivityCollector.removeActivity(this); } // 创建广播接收者的类 class ForceOfflineReceiver extends BroadcastReceiver{ @Override // 接受到广播会做如下操作 public void onReceive(final Context context, Intent intent) { // 接收到强制退出广播时,弹出对话框 AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle("Warning"); builder.setMessage("You are forced to be offline.Please try to login again"); builder.setCancelable(false); builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override // 点击对话框 public void onClick(DialogInterface dialogInterface, int i) { // 结束所有正在运行的活动 ActivityCollector.finishAll(); // 重新进入login界面 Intent intent = new Intent(context,LoginActivity.class); context.startActivity(intent); } }); // 显示对话框 builder.show(); } }}
最后别忘了用于将activity放入容器的类,很简单的几个方法。
// ActivityCollectorpublic class ActivityCollector { // 收集活动的容器 public static List<Activity> 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(); } } }}
参考
《第一行代码 Android 第2版》.郭霖.人民邮电出版社.2016.12
- Android四大组件之Activity全面学习
- Android四大组件之Service全面学习
- Android学习笔记--四大组件之Activity
- Android 四大组件学习之Activity生命周期
- Android四大组件学习之Activity
- Android 四大组件学习之Activity一
- Android 四大组件学习之Activity二
- Android 四大组件学习之Activity三
- Android 四大组件学习之Activity四
- Android 四大组件学习之Activity五
- Android 四大组件学习之Activity七
- Android 四大组件学习之Activity六
- Android学习笔记四大组件之Activity
- Android学习之路--四大组件--Activity
- Android四大组件学习之Activity
- 【android学习】四大组件-Activity
- 【android学习】四大组件-Activity
- Android四大组件之Activity
- 移动互联网下半场争夺战:逃不过娱乐大网,得年轻中产得天下
- 安全认证中心
- 操作系统中heap 和 stack的区别
- 什么是linux
- Vue.js入门第一篇
- Android四大组件之Activity全面学习
- 写给程序员的第二封信
- 指针+1
- Java代码性能优化技巧
- caffe代码阅读5:Layer的实现细节
- Python学习笔记(11)- 函数(1)
- 高性能Mysql:服务器性能剖析
- javascript 高级——几种对象创建方法
- java中char类型的默认值是 '\u0000' 吗?是空字符还是空格?