安卓控件——广播
其实在我们的安卓系统中有好多的地方都应用到了广播的部分:比如当我们手机电量很低的时候,手机就会发一个广播,告知用户,作出相应的操作。
1.项目布局:
7个button,俩text;
2.布局文件:
activity_main:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:outlineAmbientShadowColor="@android:color/holo_blue_bright">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/button2"
android:layout_alignLeft="@+id/button2"
android:text="Handler方式定时发送广播" />
<Button
android:id="@+id/button5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/textView1"
android:layout_centerHorizontal="true"
android:layout_marginTop="176dp"
android:text="取消发送广播" />
<Button
android:id="@+id/button7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/button5"
android:layout_alignLeft="@+id/button5"
android:layout_marginBottom="46dp"
android:text="AlarmManager方式定时发送广播 5秒后发送广播,只发送一次" />
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/button5"
android:layout_below="@+id/button5"
android:text="Handler方式定时服务" />
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/button3"
android:layout_below="@+id/button3"
android:text="AlarmManager方式定时服务" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/button7"
android:layout_alignLeft="@+id/button7"
android:text="AlarmManager方式定时发送广播 每隔5秒重复发广播" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/button1"
android:layout_alignParentTop="true"
android:layout_marginTop="41dp"
android:text="敌军还有5秒到达战场,碾碎他们" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_alignParentTop="true"
android:text="敌军还有5秒到达战场,碾碎他们" />
<Button
android:id="@+id/button6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/button4"
android:layout_below="@+id/button4"
android:text="取消定时服务" />
</RelativeLayout>
3.服务类:
TimerService.java:
package com.example.broadcast;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class TimerService extends Service {
private String TAG =TimerService.class.getSimpleName();
String message;
int count=1;
public TimerService() {
}
@Override
public void onCreate(){
super.onCreate();
sentMassage();
}
public void onDestroy(){
super.onDestroy();
count=1;
Log.i(TAG,"进程销毁咯!!");
}
@SuppressLint("WrongConstant")
private void sentMassage() {
Toast.makeText(getApplicationContext(),"创建一个后台任务",0).show();
stopSelf();
}
public String getMessage(){
return message;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
4.服务工具类:
4.1 ServiceUtil.java:
package com.example.broadcast;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import java.util.List;
public class ServiceUtil {
private final static String ServiceName="com.example.broadcast.TimerService";
@SuppressLint("LongLogTag")
public static boolean isServiceRunning(Context context, String className){
boolean isRunning=false;
ActivityManager activityManager =(ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo>serviceInfos=activityManager.getRunningServices(50);
if (null==serviceInfos||serviceInfos.size()<1){
return false;
}
for (int i=0;i<serviceInfos.size();i++){
if (serviceInfos.get(i).service.getClassName().contains(className)){
isRunning=true;
break;
}
}
Log.i("ServiceUtil-AlarmManager", className + " isRunning = " + isRunning);
return isRunning;
};
@SuppressLint("LongLogTag")
public static void startAMService(Context context){
Log.i("ServiceUtil-AlarmManager", "invokeTimerPOIService wac called.." );
PendingIntent alarmSender =null;
Intent startIntent=new Intent(context,TimerService.class);
startIntent.setAction(ServiceName);
try{
alarmSender=PendingIntent.getService(context,0,startIntent,0);
}catch (Exception e){
Log.i("ServiceUtil-AlarmManager", "failed to start " + e.toString());
}
@SuppressLint("ServiceCast") AlarmManager am =(AlarmManager) context.getSystemService(Activity.ACTIVITY_SERVICE);
am.setInexactRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),5*1000,alarmSender);
}
@SuppressLint("LongLogTag")
public static void cancleAMServicer(Context context){
Log.i("ServiceUtil-AlarmManager", "cancleAlarmManager to start ");
Intent intent =new Intent(context,TimerService.class);
intent.setAction(ServiceName);
PendingIntent pendingIntent=PendingIntent.getService(context,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm=(AlarmManager)context.getSystemService(Activity.ALARM_SERVICE);
alarm.cancel(pendingIntent);
}
public static void startHandlerService(Context cxt){
Intent intent=new Intent(cxt,TimerService.class);
cxt.startService(intent);
}
public static void stopHandlerService(Context cxt){
Intent intent=new Intent(cxt,TimerService.class);
cxt.stopService(intent);
}
}
4.2 实现广播和接收机制的五个步骤:
- 创建Intent对象,设置其属性action,这个属性是接收广播数据的标志标识,只有注册了相同的action属性的广播接收器才可以接收到相同的数据。
- 编写广播信息内容,将需要播发的内容封装到Intent里面,通过Activity或者是Service继承父类的Context的setContext()方式将广播发送出去;
- 编写继承BroadcastRecevier的子类作为广播接收器,该对象是接收广播信息并对信息进行处理的组件,在子类中重写Onrecieve()方法。
- 配置文件中注册广播接收类
- 销毁广播接收器:Android系统在执行onReceive()方法的时候会启动一个程序计时器,到一定的时间,该进程就会被销毁(所以广播机制不适合传递大数据量的信息)
5.主类:
5.1 MainActivity.java:
package com.example.broadcast;
import androidx.appcompat.app.AppCompatActivity;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private final String MESSAGE = "message";
private Context mContext;
private TextView tv, tv2;
private Button bt1, bt2, bt3, bt4, bt5, bt6,bt7;
private final int Time = 5 * 1000;
private boolean isHanderType = false;
private static final String ACTION_NAME = "android.intent.action.alarm.timer";
private static final String ACTION_NAME2 = "android.intent.action.handler.timer";
private int countHandler = 1;
private int countAlarm = 0;
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
handler.postDelayed(runnable, Time);
Intent mIntent = new Intent(ACTION_NAME2);
mIntent.putExtra(MESSAGE, "这是第" + countHandler + "次"
+ "Handler发送的广播, 接下来是第" + countHandler + "次");
sendBroadcast(mIntent);
}
};
Runnable runnable2 = new Runnable() {
@Override
public void run() {
Intent intent = new Intent(mContext, TimerService.class);
startService(intent);
handler.postDelayed(runnable2, Time);
}
};
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String message = intent.getStringExtra(MESSAGE);
if (action.equals(ACTION_NAME)) {
tv.setText("第" + countAlarm + "发送"
+ "AlarmManager的Alarm广播, 接下来发送第" + countAlarm
+ "次广播");
tv2.setText("与handler广播不同,我们的alarm广播是首先执行一次后,就得等待5s后在执行,并且发送的时候只执行一次");
countAlarm++;
} else if (action.equals(ACTION_NAME2)) {
if (!TextUtils.isEmpty(message)) {
tv2.setText(message);
}
countHandler++;
}
}
};
@Override
protected void onResume() {
super.onResume();
registerBoradcastReceiver();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = MainActivity.this;
tv = (TextView) findViewById(R.id.textView1);
tv2 = (TextView) findViewById(R.id.textView2);
bt1 = (Button) findViewById(R.id.button1);
bt2 = (Button) findViewById(R.id.button2);
bt3 = (Button) findViewById(R.id.button3);
bt4 = (Button) findViewById(R.id.button4);
bt5 = (Button) findViewById(R.id.button5);
bt6 = (Button) findViewById(R.id.button6);
bt7 = (Button) findViewById(R.id.button7);
bt1.setOnClickListener(onClickListener);
bt2.setOnClickListener(onClickListener);
bt3.setOnClickListener(onClickListener);
bt4.setOnClickListener(onClickListener);
bt5.setOnClickListener(onClickListener);
bt6.setOnClickListener(onClickListener);
bt7.setOnClickListener(onClickListener);
}
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
sendTimerBoaadCastReceiver(true,-1);
bt1.setEnabled(false);
break;
case R.id.button2:
sendTimerBoaadCastReceiver(false,2);
bt2.setEnabled(false);
break;
case R.id.button3:
sendTimerService(true);
bt3.setEnabled(false);
break;
case R.id.button4:
sendTimerService(false);
bt4.setEnabled(false);
break;
case R.id.button5:
cancelAlLBR();
break;
case R.id.button6:
cancelAlLService();
break;
case R.id.button7:
sendTimerBoaadCastReceiver(false,1);
bt7.setEnabled(false);
break;
}
}
};
private void registerBoradcastReceiver() {
IntentFilter myIntentFilter = new IntentFilter();
myIntentFilter.addAction(ACTION_NAME);
myIntentFilter.addAction(ACTION_NAME2);
registerReceiver(mBroadcastReceiver, myIntentFilter);
}
@SuppressLint("WrongConstant")
private void sendTimerBoaadCastReceiver(boolean isHandler, int state) {
if (isHandler) {
handler.postDelayed(runnable, Time);
} else {
Intent mIntent = new Intent(ACTION_NAME);
Toast.makeText(MainActivity.this, "发送广播,开始进攻", 0).show();
sendBroadcast(mIntent);
PendingIntent pendIntent = PendingIntent.getBroadcast(mContext, 0,
mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
switch (state) {
case 1:
long triggerAtTime = SystemClock.elapsedRealtime() + Time;
manager.set(AlarmManager.ELAPSED_REALTIME, triggerAtTime,
pendIntent);
break;
case 2:
manager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), Time, pendIntent);
break;
case 3:
break;
case 4:
break;
}
}
}
private void cancelHandlerBR() {
handler.removeCallbacks(runnable);
countHandler = 1;
tv2.setText("敌军还有5s到达,击垮他们");
}
private void cancelAlarmManagerBR() {
Intent mIntent = new Intent(ACTION_NAME);
PendingIntent pendIntent = PendingIntent.getBroadcast(mContext, 0,
mIntent, 0);
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
manager.cancel(pendIntent);
countAlarm = 0;
tv.setText("敌军还有5s到达,击垮他们");
}
private void cancelAlLBR() {
cancelHandlerBR();
cancelAlarmManagerBR();
bt1.setEnabled(true);
bt2.setEnabled(true);
bt7.setEnabled(true);
}
private void cancelAlLService() {
handler.removeCallbacks(runnable2);
ServiceUtil.cancleAMServicer(mContext);
ServiceUtil.stopHandlerService(mContext);
bt3.setEnabled(true);
bt4.setEnabled(true);
bt6.setEnabled(true);
tv2.setText("敌军还有5s到达,击垮他们");
tv.setText("敌军还有5s到达,击垮他们");
}
private void sendTimerService(boolean isHandler) {
if (isHandler) {
handler.postDelayed(runnable2, Time);
} else {
ServiceUtil.startAMService(mContext);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
cancelAlLBR();
cancelAlLService();
unregisterReceiver(mBroadcastReceiver);
}
@Override
protected void onPause() {
super.onPause();
}
}
5.2 对比
注意handler和alarmManager有一些不同, 首先我们给两种方式定义一个计数器:
- handler广播发送次数计数器:
private int countHandler = 1;
- alarm广播发送次数计数器
private int countAlarm = 0;
注意:与handler广播不同,我们的alarm广播是首先执行一次后,就得等待5s后在执行,并且发送的时候只执行一次
5.3 按钮触发
注意我們也需要设置在正在执行广播的时候,我们的按钮出处于的状态(能否被点击)
- 取消广播的时候:
相应的三个广播按钮就是可以用了。
private void cancelAlLBR() {
cancelHandlerBR();
cancelAlarmManagerBR();
bt1.setEnabled(true);
bt2.setEnabled(true);
bt7.setEnabled(true);
}
- 取消所有的服务的时候:
这个时候相应的三个启动按钮还是可以用的
private void cancelAlLService() {
handler.removeCallbacks(runnable2);
ServiceUtil.cancleAMServicer(mContext);
ServiceUtil.stopHandlerService(mContext);
bt3.setEnabled(true);
bt4.setEnabled(true);
bt6.setEnabled(true);
tv2.setText("敌军还有5s到达,击垮他们");
tv.setText("敌军还有5s到达,击垮他们");
}
6. 项目文件配置:
AndroidManifest.xml
这个地方请注意,如果是你在新建服务的时候选择的是新建服务,那么就会自动在这个配置文件里面自动注册,但是如果你直接新建的java文件,也没关系,但是你需要自己在这个地方进行相关的配置。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcast">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".TimerService"
android:enabled="true"
android:exported="true"></service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
7. 成果展示:
8.提取几个重要片段:
看完我们整个代码流程,梳理一下,我们在服务工具类文件里设置action属性,再在项目启动主文件里设置注册广播以及处理onClick按钮点击事件的处理,启动后台服务,将intent传值给后台服务,TimerService服务类就启动服务,发送广播,根据广播信息更改文本。在到服务工具类启动后台任务,换新闹钟服务
8.1 ServiceUtil.java:设置action属性
Intent startIntent=new Intent(context,TimerService.class);
startIntent.setAction(ServiceName);
8.3 MainActivity.java:注册广播
registerReceiver(mBroadcastReceiver, myIntentFilter);
8.4 MainActivity.java:按钮点击事件的处理
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
sendTimerBoaadCastReceiver(true,-1);
bt1.setEnabled(false);
break;
case R.id.button2:
sendTimerBoaadCastReceiver(false,2);
bt2.setEnabled(false);
break;
case R.id.button3:
sendTimerService(true);
bt3.setEnabled(false);
break;
case R.id.button4:
sendTimerService(false);
bt4.setEnabled(false);
break;
case R.id.button5:
cancelAlLBR();
break;
case R.id.button6:
cancelAlLService();
break;
case R.id.button7:
sendTimerBoaadCastReceiver(false,1);
bt7.setEnabled(false);
break;
}
}
};
我觉得这个部分还是不简单地,大家有什么问题希望及时留言嗷,私信都可以!
|