android学习笔记----Broadcast Receiver接收系统广播流程(ip拨号器、sd卡状态、短信、应用安装卸载)

android内置了很多系统级别的广播,比如手机开机完成后会发出一条广播,电池电量发生变化会发出一条广播,时间或时区发生变化也会发出一条广播等等。要想接收到这些广播,就需要使用广播接收器,下面来基本了解一下过程。

目录

IP拨号器:

SD卡状态的监听:

短信监听器:

应用的卸载安装:


 

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方法可以模仿系统源码来写)

https://www.androidos.net.cn/android/8.0.0_r4/xref/packages/apps/BasicSmsReceiver/AndroidManifest.xml

找到这个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============================

posted @ 2018-08-18 15:02  绿叶萌飞  阅读(336)  评论(0编辑  收藏  举报