Android学习笔记-四大组件-基础知识2

2019-01-19  本文已影响0人  予久i

BroadcastReceiver

Android应用可以从Android系统和其他Android应用发送或接收广播消息,类似于 发布 - 订阅 设计模式。当感兴趣的事件发生时,发送这些广播。例如,Android系统在发生各种系统事件时发送广播,例如系统启动或设备开始充电时。例如,应用程序还可以发送自定义广播,以通知其他应用程序他们可能感兴趣的内容(例如,已下载了一些新数据)。

什么是广播

由上述摘自官方文档的描述我们可以知道,广播可以用来在:APP内部、不同APP之间、APP与Android系统之间传递消息。

广播应用场景

通过上述理解我们可以总结一下广播的常用的应用场景

广播使用

广播角色

使用广播来进行消息传递,如果需要传递一个消息,那么最少需要一个消息的发送者,一个消息的接收者。广播进行消息的传递也是使用了广播发送广播接收两个角色

广播接收

为了方便理解先说广播接收,即如何接收其他广播发送来的消息。广播的接收使用BroadcastReceiver类,要实现一个广播接收者需要两个步骤
1、继承BroadcastReceiver并重写onReceive()方法

public class MyBroadcastReceiver extends BroadcastReceiver {
   /**
    * 重写该方法,当接收到注册的相应广播后会执行该方法
    * @param context
    * @param intent
    */
   @Override
   public void onReceive(Context context, Intent intent) {
       Log.e("接收到一个广播");
   }
}

广播接收者示例代码
2、注册广播接收者
广播接收的注册分为两种方式。

<receiver android:name="com.example.MyBroadcastReceiver">
    <intent-filter>
        <!-- 接收的广播类型 -->
        <action android:name="MY_STATIC_BROADCAST" />
        <action android:name="Intent.ACTION_SCREEN_OFF" />
    </intent-filter>
</receiver>1

注意:Android8.0禁止了大多数静态注册
动态注册
不常驻广播,跟随组件生命周期变化,在特定时刻监听广播可以使用动态注册。
使用:在代码中调用Context.registerReceiver()方法进行注册

//动态注册广播
myBroadcastReceiver=new MyBroadcastReceiver(); //实例化广播接收者和IntentFilter
IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction(RECEIVER_ACTION);    //设置接收广播类型
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);   //可以接收多个广播类型
registerReceiver(mDynamicRegisterReceiver,intentFilter);    //注册广播

注意:动态广播最好在onResume()中注册onPause()中注销,因为onPause()方法在app死亡之前一定会被执行。其他则不一定。动态广播注册后必须注销,否则将导致内存泄漏。不允许重复注册或者重复注销。
动态注册代码示例

广播发送

广播发送根据所发送的消息类型以及发送方式分为了以下几种广播类型

//发送动态广播
Intent intent=new Intent();
intent.setAction(RECEIVER_ACTION);
sendBroadcast(intent);

注意:如果发送广播需要某一权限接收广播则也需要相应权限。

//注册本地广播
mDynamicRegisterReceiver=new DynamicRegisterReceiver();              
mLocalBroadcastManager=LocalBroadcastManager.getInstance(BroadcastReceiverActivity.this);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(RECEIVER_ACTION);              
mLocalBroadcastManager.registerReceiver(mDynamicRegisterReceiver,intentFilter);
//发送本地广播
Intent intent=new Intent();
intent.setAction(RECEIVER_ACTION);
mLocalBroadcastManager.sendBroadcast(intent);

广播原理

广播接收者通过Binder机制在AMS注册,
广播发送者通过Binder机制向AMS发送广播,
AMS根据广播发送者要求,在已注册列表中,依据IntentFilter/Permission寻找合适的广播接收者
AMS将广播发送到合适的广播接收者相应的消息循环队列中
广播接受者通过消息队列拿到广播,并回调onReceive()方法。
广播接收和注册异步执行。

注意事项

上文代码


ContentProvider

内容提供程序管理对结构化数据集的访问。它们封装数据,并提供用于定义数据安全性的机制。 内容提供程序是连接一个进程中的数据与另一个进程中运行的代码的标准界面。

什么是ContentProvider

ContentProvider内容提供者,在Android四大组件的日常使用中出现次数相对较少,但是重要性一点也没有减少,由官方解释我们可以知道它是用来统一管理数据进行数据共享并且可以进行跨进程通信

ContentProvider的使用

上面说道ContentProvider提供统一的数据管理和数据共享、进行跨进程通信并举了相关例子,接下来详细说明一下。

接下来说一下具体使用

URI、UriMatcher、MIME

主题名 授权信息 表名 记录
content com.example.provider User 1

如:content://com.example.provider/User/1
uri中可以使用通配符*#
*:任意长度的有效字符串
#:任意长度的数字字符串

//初始化
mUriMatcher=new UriMatcher(UriMatcher.NO_MATCH);
//在ContentProvider中注册uri
mUriMatcher.addURI(AUTHORITY,"user",user_code);

2、UriMatcher.match(uri)
根据uri返回匹配该uri的自定义代码

用于指定某个扩展名的文件用某种应用程序打开。
类型+子类型组成
1、必须以vnd开头
2、内容uri以路径结尾即表名结尾后接android.cursor.dir/,如果以id结尾即数据库表id结尾则后接android.cursor.item/
3、最后接上vnd.<authority>.<path>,例:
content://com.chenlei.content_provider.user
对应MIME类型为:vnd.android.cursor.dir/vnd.com.chenlei.content_provider.user
content://com.chenlei.content_provider.user/1
对应MIME类型为:vnd.android.cursor.item/vnd.com.chenlei.content_provider.user

ContentProvider

ContentProvider即是内容提供器,它可以用来统一管理和组织应用数据,向其他应用程序提供接口 便于其他应用程序对本应用允许的数据进行操作,以进行跨进程通讯。
简单来说就是ContentProvider向其他APP提供,自己APP内数据,的访问接口
主要方法由以下几个

    /**
     * 初始化ContentProvider的使用使用
     * 通常完成数据库的创建和升级操作
     * @return true初始化成功,false初始化失败
     */
    @Override
    public boolean onCreate() {
        mContext=getContext();
        mDBHelper=new DBHelper(getContext());
        sqLiteDatabase=mDBHelper.getWritableDatabase();
        //初始化数据库表
        sqLiteDatabase.execSQL("delete from user");
        sqLiteDatabase.execSQL("insert into user values(1,'Carson');");
        sqLiteDatabase.execSQL("insert into user values(2,'Kobe');");
        sqLiteDatabase.execSQL("delete from job");
        sqLiteDatabase.execSQL("insert into job values(1,'Android');");
        sqLiteDatabase.execSQL("insert into job values(2,'iOS');");

        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
        String tableName = getTableName(uri);
        sqLiteDatabase=mDBHelper.getReadableDatabase();
        Cursor cursor=null;

        cursor=sqLiteDatabase.query(tableName,strings,s,strings1,null,null,s1,null);

        return cursor;
    }

    /**
     * 根据传入的内容来返回相应的MIME类型
     * @param uri
     * @return
     */
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        String mime = null;
        switch (mUriMatcher.match(uri)){
            case user_code:
                mime="vnd.android.cursor.dir/vnd.com.chenlei.content_provider.user";
                break;
            case job_code:
                mime="vnd.android.cursor.dir/vnd.com.chenlei.content_provider.job";
                break;
        }
        return mime;
    }

    /**
     * 插入数据
     * @param uri 数据的资源路径
     * @param contentValues 要插入的数据内容
     * @return 返回一个用于记录新纪录的uri
     */
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        String tableName=getTableName(uri);
        //插入数据
        sqLiteDatabase.insert(tableName,null,contentValues);

        mContext.getContentResolver().notifyChange(uri,null);

        return uri;
    }

示例代码

AndoridManifest.xml中注册

当代码完成后还需要在AndoridManifest.xml中注册才能被访问使用。

<!--声明外界进程可访问该Provider的权限(读 & 写)-->
<!--android:permission="com.chenlei.PROVIDER"-->
<!--权限可细分为读 & 写的权限-->
<!--外界需要声明同样的读 & 写的权限才可进行相应操作,否则会报错-->
<!--android:readPermisson = "com.chenlei.Read"-->
<!--android:writePermisson = "com.chenlei.Write"-->
<!--设置此provider是否可以被其他进程使用-->
<!--android:exported="true"-->
<provider
    android:authorities="com.chenlei.content_provider" 
    android:name="com.example.androidprimarycodedemo.four_components.about_content_provider.CreateLocalContentProvider"
    android:permission="com.chenlei.PROVIDER"
    android:exported="true"
/>

ContentResolver

由于如果应用需要对多个ContentProvider进行操作,需要了解各个不同ContentProvider的实现等再进行操作。所以API中提供了ContentResolver类统一管理对ContentProvider的操作。
简单来说就是通过ContentResolver,来对其他APP内的数据,进行操作
ContentResolver使用示例。

    //user表的资源路径
    private Uri uriUser=Uri.parse("content://com.chenlei.content_provider/user");
    /**
     * ContentResolver统一管理ContentProvider间的操作
     * 由于如果需要使用多个ContentProvider进行操作,需要了解各个不同ContentProvider的实现等再进行操作
     * 使用ContentResolver同一管理方便操作和使用。
     */
    private ContentResolver contentResolver=null;
    /**
     * 向ContentProvider中插入数据
     */
    private void insertData(){
        contentResolver=getContentResolver();
        //插入表中的数据
        ContentValues contentValues=new ContentValues();
        contentValues.put("_id", 3);
        contentValues.put("name", "Iverson");

        contentResolver.insert(uriUser,contentValues);
        Logger.e("插入完成");
    }

    /**
     * 从ContentProvider中查询数据
     */
    private void queryData(){
        contentResolver=getContentResolver();
        Cursor cursor=contentResolver.query(uriUser,new String[]{"_id","name"}, null, null, null);
        while (cursor.moveToNext()){
            Logger.e(cursor.getInt(0) +","+ cursor.getString(1));
        }
        cursor.close();
    }

示例代码

    <!--应用跨进程通讯的权限-->
    <uses-permission android:name="com.chenlei.PROVIDER"/>

上文代码

ContentProvider部分知识点参考自https://blog.csdn.net/carson_ho/article/details/76101093

上一篇下一篇

猜你喜欢

热点阅读