一、Android四大组件学习笔记,看完这篇就够了

96
麦典威
0.3 2019.03.09 09:38* 字数 5352

本文参考以下文章和书籍,总结梳理、实操后整理为这篇学习笔记:
https://www.jianshu.com/p/51aaa65d5d25
http://www.cnblogs.com/huihuizhang/p/7623760.html
https://www.jianshu.com/p/43f36e5ba122
https://blog.csdn.net/codeyanbao/article/details/81813501
郭霖大神的《Android 第一行代码(第二版)》
任玉刚主席的《Android 开发艺术探索》

1、Activity

1.1 Activity生命周期

activity_life.png
1.1.1 对上图声明周期的基本描述:
  • 启动activity:系统先调用onCreate(),然后调用onStart(),最后调用onResume()方法,activity进入运行状态。
  • activity被其他activity覆盖其上(DialogActivity)或者锁屏:系统会调用onPause()方法,暂停当前activity的执行。
  • 当前activity由被覆盖状态回到前台或者解锁屏:系统会调用onResume()方法,再次进入运行状态。
  • 当前Activity转到新的Activity界面或按Home键回到主屏,自身退居后台:系统会先调用onPause方法,然后调用onStop方法,进入停滞状态。
  • 用户后退回到此Activity:系统会先调用onRestart方法,然后调用onStart方法,最后调用onResume方法,再次进入运行状态。
  • 当前Activity处于被覆盖状态或者后台不可见状态,即第2步和第4步,系统内存不足,杀死当前Activity,而后用户退回当前Activity:再次调用onCreate方法、onStart方法、onResume方法,进入运行状态。
  • 用户退出当前Activity:系统先调用onPause方法,然后调用onStop方法,最后调用onDestory方法,结束当前Activity。
1.1.2 特别注意:
  • onRestart():表示activity正在重新启动 ,一般情况下,当前activity从不可见重新变成可见状态时,onRestart()就会被调用,这种情形一般是用户行为所导致的,比如用户按HOME键切换到桌面然后重新打开APP或者按back键。
  • onStart():activity可见了,但是还没有出现在前台,还无法和用户交互。
  • onPause():表示activity正在停止,此时可以做一些存储数据,停止动画等工作,注意不能太耗时,因为这会影响到新activity的显示,onPause必须先执行完,新的activity的onResume才会执行
  • 从activity是否可见来说,onstart()和onStop()是配对的,从activity是否在前台来说,onResume()和onPause()是配对的。
  • 旧activity先onPause,然后新activity再启动
  • 当activity中弹出dialog对话框的时候,activity不会回调onPause。然而当activity启动dialog风格的activity的时候,此activity会回调onPause函数。
1.1.3 异常情况下的生命周期

这种情况很常见,比如当系统资源配置发生改变以及系统内存不足时,activity就可能被杀死。

1.1.3.1 情况1:资源相关的系统配置发生改变导致activity被杀死并重新创建

比如说当前activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了改变,在默认情况下,activity就会被销毁并且重新创建,当然我们也可以组织系统重新创建我们的activity。
系统配置发生改变以后,activity会销毁,其onPause,onStop,onDestory均会被调用,由于activity是在异常情况下终止的,系统会调用onSaveInstance来保存当前activity状态,这个方法的调用时机是在onStop之前。与onPause没有既定的时序关系,当activity重新创建后,系统会调用onRestoreInstanceState,并且把activity销毁时onSaveInstanceState方法保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。onRestoreInstanceState()在onStart()方法后回调。
同时,在onSaveInstanceState和onRestoreInstanceState方法中,系统自动为我们做了一些恢复工作,如:文本框(EditeText)中用户输入的数据,ListView滚动的位置等,这些view相关的状态系统都能够默认为我们恢复。可以查看view源码,和activity一样,每个view都有onSaveInstanceState方法和onRestoreInstanceState方法。

打印日志如下:

04-11 09:44:57.350 11757-11757/cn.hotwoo.play:remote I/MainActivity: onCreate
04-11 09:44:57.354 11757-11757/cn.hotwoo.play:remote I/MainActivity: onStart
04-11 09:44:57.356 11757-11757/cn.hotwoo.play:remote I/MainActivity: onResume
04-11 09:44:57.425 11757-11757/cn.hotwoo.play:remote I/MainActivity: onCreateOptionsMenu
04-11 09:44:59.149 11757-11757/cn.hotwoo.play:remote I/MainActivity: onPause
04-11 09:44:59.151 11757-11757/cn.hotwoo.play:remote I/MainActivity: onSaveInstanceState
04-11 09:44:59.151 11757-11757/cn.hotwoo.play:remote I/MainActivity: onStop
04-11 09:44:59.151 11757-11757/cn.hotwoo.play:remote I/MainActivity: onDestroy
04-11 09:44:59.234 11757-11757/cn.hotwoo.play:remote I/MainActivity: onCreate
04-11 09:44:59.235 11757-11757/cn.hotwoo.play:remote I/MainActivity: onStart
04-11 09:44:59.236 11757-11757/cn.hotwoo.play:remote I/MainActivity: onRestoreInstanceState
04-11 09:44:59.237 11757-11757/cn.hotwoo.play:remote I/MainActivity: onResume
04-11 09:44:59.270 11757-11757/cn.hotwoo.play:remote I/MainActivity: onCreateOptionsMenu
04-11 10:02:32.320 11757-11757/cn.hotwoo.play:remote I/MainActivity: onPause
04-11 10:02:32.516 11757-11757/cn.hotwoo.play:remote I/MainActivity: onStop
04-11 10:02:32.516 11757-11757/cn.hotwoo.play:remote I/MainActivity: onDestroy

防止重新创建activity的方法:
activity指定configChange属性来不让系统重新创建activity
android : configChanges = "orientation"

1.1.3.2 情况2:资源内存不足导致低优先级的activity被杀死

这里的情况和前面的情况1数据存储和恢复是完全一致的,activity按照优先级从高到低可以分为如下三种:
(1)前台activity——正在和用户交互的activity,优先级最高
(2)可见但非前台activity——比如activity中弹出了一个对话框,导致activity可见但是位于后台无法和用户直接交互。
(3)后台activity——已经被暂停的activity,比如执行了onStop,优先级最低。

1.2 Activity与Fragment生命周期关系

fragment_life.jpg

创建过程的日志:


fragment_life2.png

销毁过程的日志:


fragment_life3.png

1.3 Activity与menu创建先后顺序

在activity创建完回调onResume后创建menu,回调onCreateOptionsMenu

04-05 00:35:03.452 2292-2292/cn.hotwoo.play:remote I/MainActivity: onCreate
04-05 00:35:03.453 2292-2292/cn.hotwoo.play:remote I/MainActivity: onStart
04-05 00:35:03.454 2292-2292/cn.hotwoo.play:remote I/MainActivity: onResume
04-05 00:35:03.482 2292-2292/cn.hotwoo.play:remote I/MainActivity: onCreateOptionsMenu

2、Service

2.1 本地服务

调用者和service在同一个进程里,运行在主进程的main线程中。所以不能进行耗时操作,可以采用在service里面创建一个Thread来执行任务。service影响的是进程的生命周期。任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例。


service_life.png
2.1.1 第一种启动方式:通过start方式开启服务
2.1.1.1 使用步骤:
  • 定义一个类继承service
  • manifest.xml文件中配置service
  • 使用context的startService(Intent)方法启动service
  • 不再使用时,调用stopService(Intent)方法停止服务
2.1.1.2 生命周期:

onCreate() -- > onStartCommand() -- > onDestory()

  • 如果服务已经开启,调用context.startService()方法重复启动Service,不会重复回调onCreate()方法,但会重复调用onStartCommand()方法,并且系统只会创建Service的一个实例
  • 停止服务需要调用context.stopService()方法,因为只有一个Service实例,所以只需调用一次stopService就能关闭服务,服务停止的时候回调onDestory被销毁。
2.1.1.3 特点:

一旦服务开启就跟调用者(开启者)没有任何关系了。开启者退出了,开启者挂了,服务还在后台长期的运行,开启者不能调用服务里面的方法。

2.1.2 第二种启动方式:采用bind的方式开启服务
2.1.2.1 使用步骤:
  • 定义一个类继承Service
  • 在manifest.xml文件中注册service
  • 使用context的bindService(Intent,ServiceConnection,int)方法启动service
  • 不再使用时,调用unbindService(ServiceConnection)方法停止该服务
2.1.2.2 生命周期:

onCreate() -- > onBind() --> onUnbind() -- > onDestory()

2.1.2.3 特点:

绑定服务不会调用onStart()或者onStartCommand()方法。
bind的方式开启服务、绑定服务,调用者挂了,服务也会跟着挂掉。绑定者可以调用服务里面的方法。

2.1.2.4 示例:

定义一个类继承service

public class MyService extends Service {

    private static final String TAG = "MyService";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate");
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        Log.e(TAG, "onStart");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    //绑定服务时调用这个方法,返回一个IBinder对象
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "onBind");
        return new MyBinder();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e(TAG, "onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy");
    }

    public interface MyIBinder {
        void invokeMethodInMyService();
    }

    public class MyBinder extends Binder implements MyIBinder {

        public void stopService(ServiceConnection serviceConnection) {
            unbindService(serviceConnection);
        }

        @Override
        public void invokeMethodInMyService() {
            for (int i = 0; i < 8; i++) {
                Log.e(TAG, "service is opening " + i);
            }
        }
    }
}

在manifest.xml文件中注册service

<service
            android:name=".MyService"
            android:exported="true">
            <intent-filter>
                <action android:name="com.itlaowang.servicedemo.MyService" />
                <category android:name="android.intent.category.default" />
            </intent-filter>
        </service>

绑定自定义的service

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private Button btnStartService, btnBindService, btnUnbindService;
    private MyService.MyBinder myBinder;
    private ServiceConnection serviceConnection;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnBindService = findViewById(R.id.btn_bind_service);
        btnStartService = findViewById(R.id.btn_start_service);
        btnUnbindService = findViewById(R.id.btn_unbind_service);

        btnBindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 如果不判空,允许创建多个 MyServiceConnection 实例,但是通过 MyServiceConnection 得到的 myBinder 却只有一个实例
                // 通过打印日志(打印MyServiceConnection 和 myBinder 实例的哈希值)可以验证上述结论
                // 注意:如果创建了多个 MyServiceConnection 实例,将无法正常 unbindService
                // 因此必须只能创建一个 MyServiceConnection 实例
                if (serviceConnection == null) {
                    serviceConnection = new MyServiceConnection();
                }
                Log.e(TAG, "onServiceConnected——>serviceConnection=" + serviceConnection.toString());
                bindService(new Intent(MainActivity.this, MyService.class), serviceConnection, Context.BIND_AUTO_CREATE);
            }
        });
        btnStartService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (myBinder != null) {
                    myBinder.invokeMethodInMyService();
                }
            }
        });
        btnUnbindService.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (serviceConnection != null) {
                    unbindService(serviceConnection);
                    myBinder = null; // 如果不赋值为空,点击 btnStartService 仍能执行 invokeMethodInMyService 方法
                }
            }
        });
    }

    class MyServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {//这里的第二个参数IBinder就是Service中的onBind方法返回的
            Log.e(TAG, "onServiceConnected");
            myBinder = (MyService.MyBinder) service;
            Log.e(TAG, "onServiceConnected——>myBinder=" + myBinder.toString());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // onServiceDisconnected() 在连接正常关闭的情况下是不会被调用的
            // 该方法只在Service 被破坏了或者被杀死的时候调用。 例如, 系统资源不足, 要关闭一些Services, 刚好连接绑定的 Service 是被关闭者之一,  这个时候onServiceDisconnected() 就会被调用
            Log.e(TAG, "onServiceDisconnected");
            myBinder = null;
        }
    }

}

activity_main.xml 代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btn_bind_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:padding="3dp"
        android:text="bind_service" />

    <Button
        android:id="@+id/btn_start_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:padding="3dp"
        android:text="start_service" />

    <Button
        android:id="@+id/btn_unbind_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="3dp"
        android:padding="3dp"
        android:text="unbind_service" />

</LinearLayout>

点击 btnBindService 输出日志

2019-03-05 15:59:59.950 19663-19663/com.itlaowang.servicedemo E/MainActivity: onServiceConnected——>serviceConnection=com.itlaowang.servicedemo.MainActivity$MyServiceConnection@33f7462
2019-03-05 15:59:59.965 19663-19663/com.itlaowang.servicedemo E/MyService: onCreate
2019-03-05 15:59:59.965 19663-19663/com.itlaowang.servicedemo E/MyService: onBind
2019-03-05 15:59:59.971 19663-19663/com.itlaowang.servicedemo E/MainActivity: onServiceConnected
2019-03-05 15:59:59.971 19663-19663/com.itlaowang.servicedemo E/MainActivity: onServiceConnected——>myBinder=com.itlaowang.servicedemo.MyService$MyBinder@ee9c6b0

点击 btnStartService 输出日志

2019-03-05 16:00:37.278 19663-19663/com.itlaowang.servicedemo E/MyService: service is opening 0
2019-03-05 16:00:37.278 19663-19663/com.itlaowang.servicedemo E/MyService: service is opening 1
2019-03-05 16:00:37.279 19663-19663/com.itlaowang.servicedemo E/MyService: service is opening 2
2019-03-05 16:00:37.279 19663-19663/com.itlaowang.servicedemo E/MyService: service is opening 3
2019-03-05 16:00:37.279 19663-19663/com.itlaowang.servicedemo E/MyService: service is opening 4
2019-03-05 16:00:37.279 19663-19663/com.itlaowang.servicedemo E/MyService: service is opening 5
2019-03-05 16:00:37.279 19663-19663/com.itlaowang.servicedemo E/MyService: service is opening 6
2019-03-05 16:00:37.279 19663-19663/com.itlaowang.servicedemo E/MyService: service is opening 7

点击 btnUnbindService 或物力返回键 输出日志

2019-03-05 16:01:03.437 19663-19663/com.itlaowang.servicedemo E/MyService: onUnbind
2019-03-05 16:01:03.438 19663-19663/com.itlaowang.servicedemo E/MyService: onDestroy

2.2 远程服务

远程服务是位于另一个项目中的一个对外开放的服务,区别于本地服务。即调用者和service不在同一个进程中,service在单独的进程中的main线程,是一种垮进程通信方式。

通俗的理解就是:
远程服务,调用者和服务在不同的工程代码里面。
本地服务,调用者和服务在同一个工程代码里面。

2.2.1 绑定远程服务的步骤:
  • 在服务的内部创建一个内部类,提供一个方法,可以间接调用服务的方法
  • 把暴露的接口文件的扩展名改为.aidl文件 去掉访问修饰符
  • 实现服务的onbind方法,继承Bander和实现aidl定义的接口,提供给外界可调用的方法
  • 在activity 中绑定服务。bindService()
  • 在服务成功绑定的时候会回调 onServiceConnected方法 传递一个 IBinder对象
  • aidl定义的接口.Stub.asInterface(binder) 调用接口里面的方法

.aidl:android interface definition language 安卓接口定义语言。aidl文件都是公有的,没有访问权限修饰符。

2.2.2 示例

我们要测试远程服务,就必须有两个不同的程序来进行交互。

2.2.2.1 项目1:提供远程服务的程序

定义一个Service

public class MyService extends Service {

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        //返回MyBind对象
        return new MyBinder();
    }

    private void methodInMyService() {
        Toast.makeText(getApplicationContext(), "服务里的方法执行了。。。",
                Toast.LENGTH_SHORT).show();
    }

    /**
     * 直接继承IMyBinder.Stub
     */
     private class MyBinder extends IMyBinder.Stub {
        @Override
        public void invokeMethodInMyService() throws RemoteException {
            methodInMyService();
        }
    }
}

创建IMyBinder.aidl文件,工具会自动生成一个IMyBinder的接口

interface IMyBinder {
     void invokeMethodInMyService();
}

在Manifes.xml件中进行配置

<service android:name=".MyService">
      <intent-filter>
            <action android:name="com.example.service" />
      </intent-filter>
</service>
2.2.2.1 项目2:调用远程服务的程序

在此程序中,创建一个和远程服务程序的MyBinder.aidl一样的文件(包括包名)。

public class MainActivity extends Activity {

    private MyConn conn;
    private Intent intent;
    private IMyBinder myBinder;

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

    //开启服务按钮的点击事件
    public void start(View view) {
        intent = new Intent(this, MyService.class);
        conn = new MyConn();
        //绑定服务,
        // 第一个参数是intent对象,表面开启的服务。
        // 第二个参数是绑定服务的监听器
        // 第三个参数一般为BIND_AUTO_CREATE常量,表示自动创建bind
        bindService(intent, conn, BIND_AUTO_CREATE);
    }

    //调用服务方法按钮的点击事件
    public void invoke(View view) {
        try {
            myBinder.invokeMethodInMyService();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private class MyConn implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //通过Stub得到接口类型
            myBinder = IMyBinder.Stub.asInterface(iBinder);
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
        }
    }
}

2.3 IntentService

IntentService是Service的子类,比普通的Service增加了额外的功能。

2.3.1 先看看Service本身存在两个问题:
  • Service不会专门启动一条单独的进程,Service与它所在应用位于同一个进程中;
  • Service也不是专门一条新线程,因此不应该在Service中直接处理耗时的任务;

IntentService 能很好的解决这两个问题

2.3.2 IntentService特征
  • 会创建独立的worker线程来处理所有的Intent请求;
  • 会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题;
  • 所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;
  • 为Service的onBind()提供默认实现,返回null;
  • 为Service的onStartCommand提供默认实现,将请求Intent添加到队列中;
2.3.3 示例

定义一个类继承IntentService

public class MyIntentService extends IntentService {

    private static final String TAG = "MyIntentService";

    public MyIntentService() {
        super("MyIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.e(TAG, "线程名称2:" + Thread.currentThread().getName());//线程名称2:IntentService[MyIntentService]
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG,"onDestroy");
        Log.e(TAG, "线程名称3:" + Thread.currentThread().getName());//线程名称3:main
    }
}

在manifest.xml文件中注册service

<service android:name=".chapter10.MyIntentService" />

IntentService的启动类

public class IntentServiceAct extends AppCompatActivity {

    private static final String TAG = "IntentServiceAct";
    private Context context;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        context = this;
        setContentView(R.layout.activity_intentservice);

        Button btnStart = findViewById(R.id.btn_start);
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "线程名称1:" + Thread.currentThread().getName());//线程名称1:main
                Intent startIntent = new Intent(context, MyIntentService.class);
                startService(startIntent);
            }
        });

        Button btnStop = findViewById(R.id.btn_stop);
        btnStop.setVisibility(View.GONE);
    }
}

activity_intentservice.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">

    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开启IntentService"
        android:textAllCaps="false" />

</LinearLayout>

点击"开启IntentService"按钮输出日志

2019-03-09 09:23:08.164 10232-10232/com.study.tao1024.firstcode E/IntentServiceAct: 线程名称1:main
2019-03-09 09:23:08.178 10232-10283/com.study.tao1024.firstcode E/MyIntentService: 线程名称2:IntentService[MyIntentService]
2019-03-09 09:23:08.180 10232-10232/com.study.tao1024.firstcode E/MyIntentService: onDestroy
2019-03-09 09:23:08.180 10232-10232/com.study.tao1024.firstcode E/MyIntentService: 线程名称3:main

3、BroadcastReceiver

广播被分为两种不同的类型:“普通广播(Normal broadcasts)”和“有序广播(Ordered broadcasts)”。

  • 普通广播是完全异步的,可以在同一时刻(逻辑上)被所有接收者接收到,消息传递的效率比较高,但缺点是:接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播。
  • 有序广播是按照接收者声明的优先级别(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000。也可以调用IntentFilter对象的setPriority()进行设置),被接收者依次接收广播。如:A的级别高于B,B的级别高于C,那么,广播先传给A,再传给B,最后传给C。A得到广播后,可以往广播里存入数据,当广播传给B时,B可以从广播中得到A存入的数据。

3.1 发送广播

Context.sendBroadcast()

发送的是普通广播,所有订阅者都有机会获得并进行处理。

Context.sendOrderedBroadcast()

发送的是有序广播,系统会根据接收者声明的优先级别按顺序逐个执行接收者,前面的接收者有权终止广播(BroadcastReceiver.abortBroadcast()),如果广播被前面的接收者终止,后面的接收者就再也无法获取到广播。对于有序广播,前面的接收者可以将处理结果通过setResultExtras(Bundle)方法存放进结果对象,然后传给下一个接收者,通过代码:Bundle bundle =getResultExtras(true))可以获取上一个接收者存入在结果对象中的数据。
比如系统收到短信,发出的广播属于有序广播。如果想阻止用户收到短信,可以通过设置优先级,让你们自定义的接收者先获取到广播,然后终止广播,这样用户就接收不到短信了。

3.2 生命周期

如果一个广播处理完onReceive 那么系统将认定此对象将不再是一个活动的对象,也就会finished掉它。


BroadcastReceiver生命周期

注意:BroadcastReceiver生命周期很短
如果需要在onReceiver完成一些耗时操作,应该考虑在Service中开启一个新线程处理耗时操作,不应该在BroadcastReceiver中开启一个新的线程,因为BroadcastReceiver生命周期很短,在执行完onReceiver以后就结束,如果开启一个新的线程,可能出现BroadcastRecevier退出以后线程还在,而如果BroadcastReceiver所在的进程结束了,该线程就会被标记为一个空线程,根据Android的内存管理策略,在系统内存紧张的时候,会按照优先级,结束优先级低的线程,而空线程无异是优先级最低的,这样就可能导致BroadcastReceiver启动的子线程不能执行完成。

3.1 静态注册广播

3.1.1 使用步骤
  • 自定义一个类继承BroadcastReceiver
  • 重写onReceive方法
  • 在manifest.xml中注册
3.1.2 示例

自定义MyBroadcastReceiver继承BroadcastReceiver

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i("fuck","intent-action : " + intent.getAction());
        if(intent.getAction().equals("test")){
            Toast.makeText(context,"fuck",Toast.LENGTH_LONG).show();
        }
    }
}

在manifest.xml中注册

//广播接收器
        <receiver android:name=".broadcast.MyBroadcastReceiver">

            <intent-filter>
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
                <action android:name="test"/>//这里自定义一个广播动作
            </intent-filter>

        </receiver>

一定要加上这个权限(坑)

<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>

发送广播

Intent intent = new Intent("test");
sendBroadcast(intent);

3.2 动态注册广播

与静态注册广播不同的是动态注册广播不用在Manifest.xml中注册,而是在类中使用以下代码注册

registerReceiver(new MyBroadcastReceiver(),new IntentFilter("test"));

3.3 静态注册广播和动态注册广播的区别

  • 动态注册广播不是常驻型广播,也就是说广播跟随activity的生命周期。注意: 在activity结束前,移除广播接收器。
  • 静态注册是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。
  • 当广播为有序广播时:
    • 1 优先级高的先接收
    • 2 同优先级的广播接收器,动态优先于静态
    • 3 同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。
  • 当广播为普通广播时:
    • 1 无视优先级,动态广播接收器优先于静态广播接收器
    • 2 同优先级的同类广播接收器,静态:先扫描的优先于后扫描的,动态:先注册的优先于后注册的。

3.4 小结

  • 在Android 中如果要发送一个广播必须使用sendBroadCast 向系统发送对其感兴趣的广播接收器中。
  • 使用广播必须要有一个intent 对象必设置其action动作对象
  • 使用广播必须在配置文件中显式的指明该广播对象
  • 每次接收广播都会重新生成一个接收广播的对象
  • 在BroadCastReceiver中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理
  • 如果在AndroidManifest.xml中注册,当应用程序关闭的时候,也会接收到广播。在应用程序中注册就不产生这种情况了。

3.5 再啰嗦几句

当如果要进行的操作需要花费比较长的时间,则不适合放在BroadcastReceiver中进行处理。
引用网上找到的一段解释:
在 Android 中,程序的响应( Responsive )被活动管理器( Activity Manager )和窗口管理器( Window Manager )这两个系统服务所监视。当 BroadcastReceiver 在 10 秒内没有执行完毕,Android 会认为该程序无响应。所以在 BroadcastReceiver 里不能做一些比较耗时的操作,否侧会弹出ANR ( Application No Response )的对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent 给 Service ,由 Service 来完成。而不是使用子线程的方法来解决,因为 BroadcastReceiver 的生命周期很短(在 onReceive() 执行后 BroadcastReceiver 的实例就会被销毁),子线程可能还没有结束BroadcastReceiver 就先结束了。如果 BroadcastReceiver 结束了,它的宿主进程还在运行,那么子线程还会继续执行。但宿主进程此时很容易在系统需要内存时被优先杀死,因为它属于空进程(没有任何活动组件的进程)。

4、ContentProvider

ContentProvider是android四大组件之一的内容提供器,它主要的作用就是将程序的内部的数据和外部进行共享,为数据提供外部访问接口,被访问的数据主要以数据库的形式存在,而且还可以选择共享哪一部分的数据。这样一来,对于程序当中的隐私数据可以不共享,从而更加安全。contentprovider是android中一种跨程序共享数据的重要组件。

4.1 系统的ContentProvider

系统的ContentProvider有很多,如通话记录,短信,通讯录等等,都需要和第三方的app进行共享数据。既然是使用系统的,那么contentprovider的具体实现就不需要我们担心了,使用内容提供者的步骤如下

  • 获取ContentResolver实例
  • 确定Uri的内容,并解析为具体的Uri实例
  • 通过ContentResolver实例来调用相应的方法,传递相应的参数,但是第一个参数总是Uri,它制定了我们要操作的数据的具体地址

可以通过读取系统通讯录的联系人信息,显示在Listview中来实践这些知识。不要忘记在读取通讯录的时候,在清单文件中要加入相应的读取权限。

4.2 自定义ContentProvider

系统的contentprovider在与我们交互的时候,只接受了一个Uri的参数,然后根据我们的操作返回给我们结果。系统到底是如何根据一个Uri就能够提供给我们准确的结果呢?只有自己亲自实现一个看看了。
和之前提到的一样,想重新自定义自己程序中的四大组件,就必须重新实现一个类,重写这个类中的抽象方法,在清单文件中注册,最后才能够正常使用。
重新实现ContentProvider之后,发现我们重写了6个重要的抽象方法:oncreate、 query、update 、insert 、delete 、gettype 。
大部分的方法在数据库那里已经见过了,他们内部的逻辑可想而知都是对数据的增删改查操作,其中这些方法的第一个参数大多都是Uri实例。其中有两个方法比较特殊:

  • oncreate方法应该是内容提供者创建的时候所执行的一个回调方法,负责数据库的创建和更新操作。这个方法只有我们在程序中获取ContentResolver实例之后准备访问共享数据的时候,才会被执行。
  • gettype方法是获取我们通过参数传递进去的Uri的MIME类型,这个类型是什么,后面会有实例说明。

内容提供者首先要做的一个事情就是将我们传递过来的Uri解析出来,确定其他程序到底想访问哪些数据。Uri的形式一般有两种:

如果是内容提供器的设计者,那么我们肯定知道这个程序的数据库是什么样的,每一张表,或者每一张表中的_id都应该有一个唯一的内容Uri。我们可以将传递进来的Uri和我们存好的Uri进行匹配,匹配到了之后,就说明数据源已经找到,便可以进行相应的增删改查操作。

Android学习路径图