android学习笔记----Broadcast Receiver接收系统广播流程(ip拨号器、sd卡状态、短信、应用安装卸载)
android内置了很多系统级别的广播,比如手机开机完成后会发出一条广播,电池电量发生变化会发出一条广播,时间或时区发生变化也会发出一条广播等等。要想接收到这些广播,就需要使用广播接收器,下面来基本了解一下过程。
目录
IP拨号器:
想实现的界面是ip拨号器,比如打长途电话时需要加上前缀打电话就会便宜一些,那么把前缀保存一下,然后打长途时,只要前面有0开头就会自动加上这个前缀,比如保存前缀12345,那么拨打电话0134679,拨打出去就是123450134679。这个没有实际用处,供学习使用。
1.定义广播接收者,相当于买了一个收音机
public class OutGoingCallReceiver extends BroadcastReceiver
2.在清单文件里面配置,相当于安装了一块电池
<receiver android:name=".OutGoingCallReceiver" >
3.具体调到一个合适的频道
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
4.记得加上权限
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ip_dialer">
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<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">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".OutGoingCallReceiver" >
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
</application>
</manifest>
MainActivity.java
import android.Manifest;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private EditText et_number;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.PROCESS_OUTGOING_CALLS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.PROCESS_OUTGOING_CALLS}, 1);
}
et_number = (EditText) findViewById(R.id.et_ipnumber);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0) {
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.PROCESS_OUTGOING_CALLS)) {
showPermissionDialog(permissions);
} else {
Toast.makeText(this, "您已拒绝权限,请在设置手动打开权限", Toast.LENGTH_SHORT).show();
}
return;
}
}
}
break;
}
}
private void showPermissionDialog(final String[] permissions) {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle("提示!");
dialog.setMessage("这个权限关系到发送短信,如拒绝需要在设置手动打开!");
dialog.setCancelable(false);
dialog.setPositiveButton("授权", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(MainActivity.this, permissions, 1);
}
});
dialog.setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.show();
}
// 点击按钮,保存用户输入的ip号码
public void onclick(View view) {
String ipnumber = et_number.getText().toString().trim();
// 把当前ipnumber存起来,存到sp里
SharedPreferences sp = getSharedPreferences("config", MODE_PRIVATE);
// 获取sp的编辑器
sp.edit().putString("ipnumber", ipnumber).commit();
// 保存成功
Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show();
}
}
OutGoingCallReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
public class OutGoingCallReceiver extends BroadcastReceiver {
private static final String TAG = "OutGoingCallReceiver";
// 当进行外拨电话的时候调用
@Override
public void onReceive(Context context, Intent intent) {
// 获取用户输入的ip号码,ip号码在config.xml
SharedPreferences sp = context.getSharedPreferences("config", Context.MODE_PRIVATE);
String ipnumber = sp.getString("ipnumber", "");
// 获取当前拨打的电话号码
String currentNumber = getResultData();
Log.d(TAG, "currentNumber:" + currentNumber);
// 判断当前拨打的号码是否是长途
if (currentNumber.startsWith("0")) { // 长途以0开头
// 修改拨打电话号码
setResultData(ipnumber + currentNumber);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/et_ipnumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入ip号码"/>
<Button
android:onClick="onclick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="保存"/>
</LinearLayout>
运行示例:
SD卡状态的监听:
和上面类似
定义广播接收者:
添加一个SdcardStateReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class SdcardStateReceiver extends BroadcastReceiver {
private static final String TAG = "SdcardStateReceiver";
// 当sd卡状态发生改变的时候执行
@Override
public void onReceive(Context context, Intent intent) {
// 获取到当前广播的事件类型
String action = intent.getAction();
if ("android.intent.action.MEDIA_MOUNTED".equals(action)) {
Log.d(TAG, "======sd卡挂载了");
} else if ("android.intent.action.MEDIA_UNMOUNTED".equals(action)) {
Log.d(TAG, "======sd卡卸载了");
}
}
}
在清单文件里配置一下:
<receiver android:name=".SdcardStateReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED" />
<action android:name="android.intent.action.MEDIA_UNMOUNTED" />
<!--小细节,这里需要配置一个data,约束类型是file,因为sd卡里存的数据类型都是file-->
<data android:scheme="file" />
</intent-filter>
</receiver>
运行示例:
短信监听器:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.smsstatusmonitor">
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<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">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".SmsListenerReceiver">
<intent-filter>
<!--这个action被隐藏了-->
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
</application>
</manifest>
MainActivity.java
import android.Manifest;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECEIVE_SMS}, 1);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0) {
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.RECEIVE_SMS)) {
showPermissionDialog(permissions);
} else {
Toast.makeText(this, "您已拒绝权限,请在设置手动打开权限", Toast.LENGTH_SHORT).show();
}
return;
}
}
}
break;
}
}
private void showPermissionDialog(final String[] permissions) {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle("提示!");
dialog.setMessage("这个权限关系到发送短信,如拒绝需要在设置手动打开!");
dialog.setCancelable(false);
dialog.setPositiveButton("授权", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(MainActivity.this, permissions, 1);
}
});
dialog.setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
dialog.show();
}
}
SmsListenerReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;
public class SmsListenerReceiver extends BroadcastReceiver {
private static final String TAG = "SmsListenerReceiver";
// 当短信到来的时候执行
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "==============onReceive: ");
// 获取发送者的号码和发送内容
Bundle extras = intent.getExtras();
if (extras == null) return;
Object[] objects = (Object[]) extras.get("pdus");
String format = intent.getStringExtra("format");// 为什么"format"后面再说明
// 获取smsmessage实例
SmsMessage message = null;
for (int i = 0; i < objects.length; i++) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
message = SmsMessage.createFromPdu((byte[]) objects[i], format);
} else {
message = SmsMessage.createFromPdu((byte[]) objects[i]);
}
// 获取短信的发送地址和内容
String fromAddress = message.getOriginatingAddress();
String messageBody = message.getMessageBody().toString();
Log.d(TAG, "=========body:" + messageBody + "-----" + fromAddress);
}
}
}
运行结果示例:
能够将接收到的短信的号码和内容读取出来
刚刚代码里面出现了String format = intent.getStringExtra("format");为什么是"format"呢
现在我们看到public static SmsMessage createFromPdu(byte[] pdu, String format)方法
看到这个format是android.provider.Telephony.Sms.Intents这个类的代码,点进去查看
ctrl+F查找,看到Intent类里面有这么一段话,源码写的就是"format",可以取出这个键,那么我们仿照执行就不会错了。
多说一点题外话=============
可以多查阅android系统源码(这个onReceive方法可以模仿系统源码来写)
找到这个SmsMessageReceiver类,这里有onReceive()方法,仿照写就行了
应用的卸载安装:
和上面都是大同小异
1.定义广播接收者
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class AppStateReceiver extends BroadcastReceiver {
private static final String TAG = "AppStateReceiver";
// 当有新的应用被安装或者应用被卸载时调用
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前广播事件类型
String action = intent.getAction();
if ("android.intent.action.PACKAGE_INSTALL".equals(action)) {
Log.d(TAG, "===============应用被安装了111111111111");
} else if ("android.intent.action.PACKAGE_ADDED".equals(action)) {
Log.d(TAG, "===============应用被安装了222222222222");
} else if ("android.intent.action.PACKAGE_REMOVED".equals(action)) {
Log.d(TAG, "===============应用被卸载了" + intent.getData());
}
}
}
批注:
这里的intent.getData()可以获取被卸载的应用包名,根据需要可加可不加
2.在清单文件里面配置
在application标签里面
<receiver android:name=".AppStateReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_INSTALL" />
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<!--想让action事件生效,还需要配置一个data-->
<data android:scheme="package" />
</intent-filter>
</receiver>
当其他应用被卸载了,日志打印结果如下:
当其他应用被安装时,日志打印结果如下:
其实android.intent.action.PACKAGE_INSTALL是保留的广播事件类型
而android.intent.action.PACKAGE_ADDED才是安装应用后发起的广播类型。
=============================Talk is cheap, show me the code============================