Android 的四大组件

目录

1.Activity

2.Service与intentService

3.LocalBroadcastManager的BroadcastReceiver

4.ContentProvider


Activity

        Activity是一个Android的应用组件,它提供屏幕进行交互。每个Activity都会获得一个用于绘制其用户界面的窗口,窗口可以充满哦屏幕也可以小于屏幕并浮动在其他窗口之上。

        一个应用通常是由多个彼此松散联系的Activity组成,一般会指定应用中的某个Activity为主活动,也就是说首次启动应用时给用户呈现的Activity。

将Activity设为主活动的方法,如下面代码所示需要在AndroidManifest文件中添加以下内容

<application>
     ....
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

</activity>
        ....
</application>

在 android 中,Activity 拥有四种基本状态:

1.Active/Runing 一个新 Activity 启动入栈后,它在屏幕最前端,处于栈的最顶端,此时它处于可见并可和用户交互的激活状态。

2.Paused 当 Activity 被另一个透明或者 Dialog 样式的 Activity 覆盖时的状态。此时它依然与窗口管理器保持连接,系统继续维护其内部状态,所以它仍然可见,但它已经失去了焦点故不可与用户交互。

3.Stoped 当 Activity 被另外一个 Activity 覆盖、失去焦点并不可见时处于 Stop ed 状态。

4.Killed Activity 被系统杀死回收或者没有被启动时处于 Killed 状态。

        如果一个活动在处于停止或者暂停的状态下,系统内存缺乏时会将其结束(finish)或者杀死(kill)。这种非正常情况下,系统在杀死或者结束之前会调用onSaveInstance()方法来保存信息,同时,当Activity被移动到前台时,重新启动该Activity并调用onRestoreInstance()方法加载保留的信息,以保持原有的状态。

        当一个 Activity 实例被创建、销毁或者启动另外一个 Activity 时,它在这四种状态之间进行转换,这种转换的发生依赖于用户程序的动作。下图说明了 Activity 在不同状态间转换的时机和条件


Service与intentService

Service 简介

  • Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。
  • 例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
  • 【1】Service是Android中实现程序后台运行的解决方案,它非常适用于去执行那些不需要和用户交互而且还要求长期运行的任务。
  • 【2】Service默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在UI线程中,因此,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作
  • 【3】Service的运行不依赖于任何用户界面,即使程序被切换到后台或者用户打开另一个应用程序,Service仍然能够保持正常运行,这也正是Service的使用场景。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行

 Service基本上分为两种形式:

【1】启动状态
当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响,除非手动调用才能停止服务, 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。
 
【2】绑定状态
当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。


IntentService

  • 一个封装了HandlerThread和Handler的异步框架。
  • 是一种特殊Service,继承自Service,是抽象类,必须创建子类才可以使用。
  • 可用于执行后台耗时的任务,任务执行后会自动停止
  • 具有高优先级(服务的原因),优先级比单纯的线程高很多,适合高优先级的后台任务,且不容易被系统杀死。
  • 启动方式和Service一样。
  • 可以多次启动,每个耗时操作都会以工作队列的方式在IntentService的onHandleIntent回调方法中执行。
  • 串行执行。

注意:

  1. 如果只有一个任务,是可以进行耗时操作的。
  2. 如果有很多任务,由于内部的HandlerThread是串行执行任务,会导致耗时操作阻塞了后续任务的执行

Service和IntentService的区别

  • IntentService继承自Service
  • IntentService任务执行完后会自动停止
  • IntentService和Service优先级一致,比Thread高。
  • Service处于主线程不能直接进行耗时操作; IntentService内部有HandlerThread,可以进行耗时操作。

LocalBroadcastManager的BroadcastReceiver

BroadcastReceiver安全问题
BroadcastReceiver设计的初衷是从全局考虑可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言BroadcastReceiver是存在安全性问题的(恶意程序脚本不断的去发送你所接收的广播)。为了解决这个问题LocalBroadcastManager就应运而生了。

LocalBroadcastManager
LocalBroadcastManager是Android Support包提供了一个工具,用于在同一个应用内的不同组件间发送Broadcast。LocalBroadcastManager也称为局部通知管理器,这种通知的好处是安全性高,效率也高,适合局部通信,可以用来代替Handler更新UI

好处:
1、因广播数据在本应用范围内传播,你不用担心隐私数据泄露的问题。
2、不用担心别的应用伪造广播,造成安全隐患。
3、相比在系统内发送全局广播,它更高效。

LocalBroadcastManager的用法

LocalBroadcastManager对象的创建
 

LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;

注册广播接收器
 

LocalBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );

发送广播
 

LocalBroadcastManager.sendBroadcast( intent ) ;

取消注册广播接收器
 

LocalBroadcastManager.unregisterReceiver( broadcastReceiver );

LocalBroadcastManager部分源码解析

private static LocalBroadcastManager mInstance;

public static LocalBroadcastManager getInstance(Context context) {
    synchronized (mLock) {
        if (mInstance == null) {
            mInstance = new LocalBroadcastManager(context.getApplicationContext());
        }
        return mInstance;
    }
}

private LocalBroadcastManager(Context context) {
    mAppContext = context;
    mHandler = new Handler(context.getMainLooper()) {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_EXEC_PENDING_BROADCASTS:
                    executePendingBroadcasts();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
} 

从这个部分源码可以看出两点:

1、在获取LocalBroadcastManager对象实例的时候,这里用了单例模式。并且把外部传进来的Context 转化成了ApplicationContext,有效的避免了当前Context的内存泄漏的问题。这一点我们在设计单例模式框架的时候是值得学习的,看源码可以学习到很多东西。

2、在LocalBroadcastManager构造函数中创建了一个Handler.可见 LocalBroadcastManager 的本质上是通过Handler机制发送和接收消息的。

在创建Handler的时候,用了 context.getMainLooper() , 说明这个Handler是在Android 主线程中创建的,广播接收器的接收消息的时候会在Android 主线程,所以我们决不能在广播接收器里面做耗时操作,以免阻塞UI。

package com.app;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private LocalBroadcastManager localBroadcastManager ;
    private MyBroadcastReceiver broadcastReceiver ;
    private IntentFilter intentFilter ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //注册广播接收器
        localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;
        broadcastReceiver = new MyBroadcastReceiver() ;
        intentFilter = new IntentFilter( "myaction") ;
        localBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );

        //在主线程发送广播
        Intent intent = new Intent( "myaction" ) ;
        intent.putExtra( "data" , "主线程发过来的消息" ) ;
        localBroadcastManager.sendBroadcast( intent ) ;

        new Thread(new Runnable() {
            @Override
            public void run() {
                //在子线程发送广播
                Intent intent = new Intent( "myaction" ) ;
                intent.putExtra( "data" , "子线程发过来的消息" ) ;
                localBroadcastManager.sendBroadcast( intent ) ;
            }
        }).start(); ;

    }

    private class MyBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction() ;
            if ( "myaction".equals( action )){
                Log.d( "tttt 消息:" + intent.getStringExtra( "data" )  , "线程: " + Thread.currentThread().getName() ) ;
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        //取消注册广播,防止内存泄漏
        localBroadcastManager.unregisterReceiver( broadcastReceiver );
    }
}

运行结果:

D/tttt 消息:主线程发过来的消息: 线程: main
D/tttt 消息:子线程发过来的消息: 线程: main

可以看出,广播接收器的onReceive方法运行在主线程。


注意事项
虽然LocalBroadcastManager也通过BroadcastReceiver 来接收消息,但是他们两个之间还是有很多区别的。

1、LocalBroadcastManager注册广播只能通过代码注册的方式。传统的广播可以通过代码和xml两种方式注册。
2、LocalBroadcastManager注册广播后,一定要记得取消监听。这一步可以有效的解决内存泄漏的问题。
3、LocalBroadcastManager注册的广播,您在发送广播的时候务必使用LocalBroadcastManager.sendBroadcast(intent); 否则您接收不到广播。传统的发送广播的方法:context.sendBroadcast( intent );


ContentProvider

Ancroid四大组件之一,即内容提供者。

作用:


进程间 进行数据交互 & 共享,即跨进程通信


原理:

  • ContentProvider的底层是采用 Android中的Binder机制
  • 关于Binder:定义

 想要具体了解Binder机制,可以去看这篇文章

Android跨进程通信:图文详解 Binder机制 原理_Carson带你学Android的博客-CSDN博客

具体使用:


统一资源标识符(URI) 

  • 外界进程通过 URI 找到对应的ContentProvider & 其中的数据,再进行数据操作
  • URI分为 系统预置 & 自定义,分别对应系统内置的数据(如通讯录、日程表等等)和自定义数据库:

// 设置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/# 


MIME数据类型

  • 作用:指定某个扩展名的文件用某种应用程序来打开
    如指定.html文件采用text应用程序打开、指定.pdf文件采用flash应用程序打开

ContentProvider类

ContentProvider支持表格(主要),文件的形式来组织数据

具体使用:

  • 进程间共享数据的本质是:添加、删除、获取 & 修改(更新)数据
  • <-- 4个核心方法 -->
      public Uri insert(Uri uri, ContentValues values) 
      // 外部进程向 ContentProvider 中添加数据
    
      public int delete(Uri uri, String selection, String[] selectionArgs) 
      // 外部进程 删除 ContentProvider 中的数据
    
      public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
      // 外部进程更新 ContentProvider 中的数据
    
      public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  String sortOrder)  
      // 外部应用 获取 ContentProvider 中的数据
    
    // 注:
      // 1. 上述4个方法由外部进程回调,并运行在ContentProvider进程的Binder线程池中(不是主线程)
     // 2. 存在多线程并发访问,需要实现线程同步
       // a. 若ContentProvider的数据存储方式是使用SQLite & 一个,则不需要,因为SQLite内部实现好了线程同步,若是多个SQLite则需要,因为SQL对象之间无法进行线程同步
      // b. 若ContentProvider的数据存储方式是内存,则需要自己实现线程同步
    
    <-- 2个其他方法 -->
    public boolean onCreate() 
    // ContentProvider创建后 或 打开系统后其它进程第一次访问该ContentProvider时 由系统进行调用
    // 注:运行在ContentProvider进程的主线程,故不能做耗时操作
    
    public String getType(Uri uri)
    // 得到数据类型,即返回当前 Url 所代表数据的MIME类型
    
  • Android为常见的数据(如通讯录、日程表等)提供了内置了默认的ContentProvider
  • 但也可根据需求自定义ContentProvider,但上述6个方法必须重写
  • ContentProvider类并不会直接与外部进程交互,而是通过ContentResolver 类

ContentResolver类

统一管理不同 ContentProvider间的操作

  • 即通过 URI 即可操作 不同的ContentProvider 中的数据
  • 外部进程通过 ContentResolver类 从而与ContentProvider类进行交互

为什么要使用通过ContentResolver类从而与ContentProvider类进行交互,而不直接访问ContentProvider类?

答:

  • 一般来说,一款应用要使用多个ContentProvider,若需要了解每个ContentProvider的不同实现从而再完成数据交互,操作成本高 & 难度大
  • 所以再ContentProvider类上加多了一个 ContentResolver类对所有的ContentProvider进行统一管理。
     

ContentResolver 类提供了与ContentProvider类相同名字 & 作用的4个方法

// 外部进程向 ContentProvider 中添加数据
public Uri insert(Uri uri, ContentValues values)  

// 外部进程 删除 ContentProvider 中的数据
public int delete(Uri uri, String selection, String[] selectionArgs)

// 外部进程更新 ContentProvider 中的数据
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)  

// 外部应用 获取 ContentProvider 中的数据
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

Android 提供了3个用于辅助ContentProvide的工具类:

  • ContentUris(操作URI)
  • UriMatcher(1.在ContentProvider中注册URI    2.根据URI去匹配ContentProvider中对应的数据库。)
  • ContentObserver(观察URI引起ContentProvider的数据变化&通知外界(即访问该数据访问者))

更详细可以看这篇文章:

Carson带你学Android:关于ContentProvider的知识都在这里了!_Carson带你学Android的博客-CSDN博客_contentprovider


 以上就是Android的四大组件,这只是一个大概文档,详细可以关注这个博主

@Carson带你学Android

制作不易,谢谢观看!

猜你喜欢

转载自blog.csdn.net/qq_62277763/article/details/127989155