2.2 动态注册
AndroidMonifest 一定添加
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
Activity
registerReceiver(new MyReceiver(),new IntentFilter("xxx"));
sendBroadcast(new Intent("xxx"));
xml中注册的广播优先级高于动态注册
3. 内部实现机制
- 自定义广播接受者 BrodcastReceiver,并复习 onRecvice()方法
- 通过 Binder 机制 AMS (Activity Manager Service)进行注册
- 广播发送者通过 Binder 机制向AMS发送广播
- AMS查找符合相应条件(IntentFilter/Permission等) 的BriadcastReaceiver,将广播发送到 BrodcastReceiver(一般情况下是Activity)相应的消息循环队列之中。
- 消息循环 执行拿到此广播后,回调 BrodcastReceiver 中的 onReceiver() ,完成广播发送
4. 本地广播
4.1 LocalBrodcastManager详解
- 使用它发送的广播将只在自身app传播,因此不必担心泄漏隐私数据
- 其他APP 无法对你的app发送该广播,因为你的app 根本就不可能接收到非自身应用发送的该广播,因此不必担心有安全漏洞可以利用。
- 比系统的全局广播更加高效。
Demo
public class Main4Activity extends AppCompatActivity { private LocalBroadcastManager localBroadcastManager; private IntentFilter intentFilter; private Receiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main4); //过滤器 intentFilter=new IntentFilter(); //添加意图 intentFilter.addAction("xxx"); receiver=new Receiver(); //获取实例 localBroadcastManager=LocalBroadcastManager.getInstance(this); //注册本地广播监听器 localBroadcastManager.registerReceiver(receiver,intentFilter); findViewById(R.id.btn_send_Nor).setOnClickListener(v -> { Intent intent=new Intent("xxx"); localBroadcastManager.sendBroadcast(intent); }); } class Receiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "收到消息", Toast.LENGTH_SHORT).show(); } } }
4.2 LocalBroadcastManager
- LocalBroadcastManager 高效的原因主要是因为它内部是通过Handler 实现的,它的 senBroadcast() 方法含义并非和我们平常所用的一样,它的 sendBroadcast() 方法其实是通过 Handler 发送了一个 Message 实现的。
- 既然它内部是通过Handler实现广播发送,那么相比系统广播通过Binder 实现那肯定是更高效了。同时 别的应用无法向我们的应用发送广播,而我们应用内发送的广播也不会离开我们的应用。
- LocalBroadcastManager 内部协作主要是靠两个 Map :mReceier 和 mActions ,当然还有一个 List 集合 mPendingBroadcast,这个主要就是存储待接收的广播对象。
5. 静态注册于动态注册的区别
- 静态广播:
注册完成就一直在运行
直接把广播接受者写在AndrodMofit,即使Activity被销毁,还是可以收到广播。
- 动态注册:必须在代码中执行 受activity的生命周期影响
- 当广播为有序广播时:
- 同优先级的广播接收器,静态注册优先级高于动态注册
- 同优先级的同类广播接收器,静态广播:先扫描的优先于后扫描的。动态广播:先注册得优先于后注册的。
- 当广播为标准广播时:
1.无视优先级,动态广播优先于静态广播接收器
2.同优先级的同类广播接收器,静态广播:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
6. 需要注意的地方
当如果要进行的操作需要花费比较长的时间,则不适合放在BroadcastReceiver中进行处理。
引用网上找到的一段解释:
在 Android 中,程序的响应( Responsive )被活动管理器( Activity Manager )和窗口管理器( Window Manager )这两个系统服务所监视。当 BroadcastReceiver 在 10 秒内没有执行完毕,Android 会认为该程序无响应。所以在 BroadcastReceiver 里不能做一些比较耗时的操作,否侧会弹出ANR ( Application No Response )的对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent 给 Service ,由 Service 来完成。而不是使用子线程的方法来解决,因为 BroadcastReceiver 的生命周期很短(在 onReceive() 执行后 BroadcastReceiver 的实例就会被销毁),子线程可能还没有结束BroadcastReceiver 就先结束了。如果 BroadcastReceiver 结束了,它的宿主进程还在运行,那么子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。
ContentProvider 内容提供者
Android四大组件之一,它主要作用就是将程序的内部数据和外部进行共享,微数据提供外部访问接口,被访问的数据主要以数据库的形式存在,而且还可以选择共享那一部分的数据。这样一来,对于程序当中的隐私数据可以不共享,从而更加安全。contentprovider是一种跨程序共享数据的重要组件。
为什么Android要提供 ContentProvider ,而不是直接让我们进行操作,这样不是更复杂吗?
因为我们一一部手机里面可不只有一个app提供内容,它可能安装了很多含有提供商的应用,比如联系人,日历等。有如此多的提供者,如果你开发一块应用要使用其中多个,你不得了解每个 ContentProvider 的不同实现吗,这样来看,岂不是工作量特别大。所以Android为我们提供了 ContentProvider 来同意管理与不同的 ContentProvider 间的操作。
只需它是靠什么来制定不同的访问规则,请看下面。
// 设置URI Uri uri = Uri.parse("content://com.carson.provider/User/1") // 上述URI指向的资源是:名为 `com.carson.provider`的`ContentProvider` 中表名 为`User` 中的 `id`为1的数据 // 特别注意:URI模式存在匹配通配符* & # // *:匹配任意长度的任何有效字符的字符串 // 以下的URI 表示 匹配provider的任何内容 content://com.example.app.provider/* // #:匹配任意长度的数字字符的字符串 // 以下的URI 表示 匹配provider中的table表的所有行 content://com.example.app.provider/table/#
2. 使用方法:
新建一个类继承ContentProvider的方式,并重写它的6个抽象方法。
1.onCreaete()
初始化内容提供器,通常会在这里完成,对数据库的创建和升级数据库,返回true,和false,
2.query()
从内容提供器中查询数据,使用uri参数确定来查询那个那张表,progjction参数用于确定查询那些列,selection和selectionAargs参数用于约束查询哪些行,查询的结果存放在Cursor对象中。
3.insert()
想内容提供器中添加一条数据,使用uri参数来确定要添加到的表,待添加的数据保存在values参数中,添加完成后,返回一个用于表示这条新记录的uri.
4.update()
更新内容提供器中已有的数据,使用URI参数来确定更新那一张表中的数据,新数据保存在values参数中,,selection和selectionArgs参数用于约束更新那些行,受影响的的行数将做为返回值返回。
5.delete()
从内容提供器中删除数据2,使用uri参数来确定删除哪一样表中的数据,selection和selectionArgs参数用于约束删除那些行,被删除的行数将作为返回值返回。
6.getType()
根据返回的内容URI来返回相应的MIME类型。
而他们每一个方法都带有一个uri参数,这个参数正是调用ConterntResolver的增删改查方法时传递过来的。