BroadcastReceiver详解
使用方式
在Manifest.xml文件中注册
<receiver
android:name=".test.broadcast.MyTestReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
<action android:name="android.intent.action.SCREEN_ON" />
<action android:name="android.intent.action.SCREEN_OFF" />
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
<action android:name="android.intent.action.CONFIGURATION_CHANGED" />
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
在Manifesh.xml文件中注册自定义的BroadcastReceiver,当意图过滤器中的动作发生时,会回调BroadcastReceiver中的onRecevie方法.
public class MyTestReceiver extends BroadcastReceiver {
private static final String TAG = "MyTestReceiver";
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
String action = intent.getAction();
Log.d(TAG, "onReceive: receive action is " + action);
}
}
}
代码中动态注册
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent == null ? "" : intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equalsIgnoreCase(action)) {
Log.i(TAG, "onReceive: 屏幕关闭");
}
context.unregisterReceiver(this);
}
}, intentFilter);
在Activity上下文中调用registerReceiver方法,动态注册一个广播接收者,同时指定了意图过滤器。
这里要注意注册的BroadcastReceiver最好是静态类,防止内存泄漏。否则要及时取消注册。
原理
注册通知
静态注册
通过PackageManagerService安装应用时,会解析安装包中的Manifest.xml文件,存储解析到Receiver标签对应的广播接收器。
通过Unix Socket接收需要安装的Apk文件
//android/os/FileBridge.java
public class FileBridge extends Thread {
private static final int MSG_LENGTH = 8;
private final FileDescriptor mServer = new FileDescriptor();
private final FileDescriptor mClient = new FileDescriptor();
private FileDescriptor mTarget;
//构造函数
//创建Unix Domain Socket的客户端和服务端,然后会将代表客户端的文件描述符发送给客户端进程
public FileBridge() {
try {
Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
} catch (ErrnoException e) {
throw new RuntimeException("Failed to create bridge");
}
}
//mTarget文件描述符用来存储客户端进程发送过来的Apk文件数据
public void setTargetFile(FileDescriptor target) {
mTarget = target;
}
public FileDescriptor getClientSocket() {
return mClient;
}
//这是一个线程,不停监听Socket的Server端
@Override
public void run() {
final byte[] temp = new byte[8192];//读缓冲区
//读取Socket的Server端,如果没有数据会阻塞
//每次先读取8字节,前4个字节代表数据类型,后4四个字节代表数据长度
while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
//按照大端序读取前4字节
final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
//数据写入,将apk文件数据写入到mTraget代表的文件描述中
if (cmd == CMD_WRITE) {
int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
while (len > 0) {
int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
if (n == -1) {
throw new IOException(
"Unexpected EOF; still expected " + len + " bytes");
}
IoBridge.write(mTarget, temp, 0, n);
len -= n;
}
//数据同步,并回显给客户端确认
} else if (cmd == CMD_FSYNC) {
Os.fsync(mTarget);
IoBridge.write(mServer, temp, 0, MSG_LENGTH);
//关闭消息通道,并回显给客户端确认
} else if (cmd == CMD_CLOSE) {
Os.fsync(mTarget);
Os.close(mTarget);
mClosed = true;
IoBridge.write(mServer, temp, 0, MSG_LENGTH);
break;
}
}
}
}
验证APK合法性
- 包名、版本号
- 签名(V2+V1)sudo
- 其他应用验证package-verifier
- 应用申请的权限,需要用户确认
- 拷贝安装包到data/data/{包名}/目录下,并且复制Native库到lib目录下
解析Apk文件
- 添加apk文件路径到AssetManager管理中