Android学习之旅Android开发经验谈Android技术知识

总结一波 Android 四大组件

2018-12-25  本文已影响0人  TengFeiGo

Activity

这里我主要重点记录 Activity 正常以及异常情况下的生命周期、开发过程中的遇到的难点以及启动模式等,如果你想详细学习关于 Activity 的知识点请去查阅官方文档 Activity | Android Developer

Activity 的生命周期
activity_lifecycle.png
  1. onCreate() : 首次创建 Activity 时回调,可以在此方法中执行绑定视图、初始化数据等操作,系统向此方法传递一个 Bundle 对象,其中包含 Activity 的上一状态,不过前提是捕获了该状态( Bundle 不为 null), onCreate 执行完之后紧接着执行 onStart 。

  2. onRestart() : 在 Activity 已经停止并且即将重新启动的时候执行此方法,onRestart 执行完之后紧接着执行 onStart 方法。

  3. onStart() : 在 Activity 即将可见之前调用,如果在执行 onStart 时 Activity 转入前台状态则紧接着执行 onResume ,如果 Activity 转入隐藏状态则紧接着执行 onStop 方法。

  4. onResume() : 在 Activity 即将与用户处于交互状态的时候执行,此时 Activity 可以接受用户的输入,onResume 后紧接着执行 onPause 。

  5. onPause() : 当 Activity 即将不可见或者处于暂停状态时执行此方法,可以在此方法中执行轻量级的保存数据和释放资源的操作,当从当前 Activity 跳转到另一个 Activity 时首先只有在当前 Activity 的 onPause 方法执行完毕了才能继续执行下一个 Activity ,如果执行 onPause 方法时当前 Activity 返回了前台则紧接着执行 onResume ,如果 Activity 转入用户不可见状态则紧接执行 onStop 方法。

  6. onStop() : 当 Activity 处于不可见状态时会被回调此方法,如果 Activity 被销毁或者当另一个 Activity 覆盖到现在的 Activity 上时会回调 onStop ,如果执行 onStop 时 Activity 恢复与用户交互的状态则紧接执行 onRestart 方法,如果 Activity 被销毁则紧接执行 onDestroy 方法,在 onStop 方法中可以执行较重量级的释放资源操作。

  7. onDestroy() : 在 Activity 被销毁时调用,这是 Activity 生命周期最后被执行的方法,当手动调用 finish() 方法或者因为内存不足导致 Activity 被系统回收都会触发此方法,在此方法中可以执行完全的释放资源的操作。

分情况讨论 Activity 的生命周期执行流程
  1. 当一个 Activity 启动并与用户处于交互状态时 :
    会首先执行 Activity 的 onCreate 方法,紧接着执行 onStart 和 onResume 方法。

  2. 当从一个 Activity(FirstActivity) 跳转到另一个 Activity(SecondActivity) 时 :
    首先执行 FirstActivity 的 onPause 方法,紧接着执行 SecondActivity 的 onCreate onStart onResume 方法,当 SecondActivity 完全覆盖 FirstActivity 时执行 FirstActivity 的 onStop 方法。

  3. 从 SecondActivity 按 back 键返回到 FirstActivity 时 :
    首先执行 SecondActivity 的 onPause 方法,接着执行 FirstActivity 的 onRestart onStart onResume,这时 FirstActivity 与用户处于交互状态,SecondActivity 执行 onStop onDestroy 方法。

  4. Activity 处于 onResume 状态,此时按 Home 键返回桌面 :
    依次执行 onPause onStop 方法,此时 Activity 处于后台状态,注意并没有执行 onDestroy 方法,此时如果系统内存资源紧张会优先“杀死” Activity 。

  5. Activity 处于 onResume 状态,按锁屏键 :
    依次执行 onPause onStop 方法,此时 Activity 处于后台状态,与第四条的效果一样,此刻如果解锁会依次执行 onRestart onStart onResume 方法。

  6. Activity 处于 onResume 状态,弹出一个 Dialog(不是 Activity 类型的 Dialog ):
    注意此时此刻不会执行当前 Activity 的任何生命周期方法,因为 Dialog 依托于当前的 Activity ,用户与 Activity 依然处于交互状态。

  7. Activity 处于 onResume 状态,弹出一个 Activity 类型的 Dialog :
    执行当前 Activity 的 onPause 方法,接着执行 DialogActivity 的 onCreate onStart onResume 方法。

  8. 接着第七步,当从 DialogActivity 按 back 键回退到上一个 Activity 时:
    首先回调 DialogActivity 的 onPause 方法,紧接着执行 Activity 的 onResume 方法, DialogActivity 继续执行 onStop onDestroy 方法,注意 Activity 并没有执行 onStart 方法。

  9. FirstActivity 处于 onResume 状态,跳转到半透明风格的 SecondActivity :
    首先回调 FirstActivity 的 onPause 方法,接着执行 SecondActivity 的 onCreate onStart onResume 方法,按 back 键回退到 FirstActivity 首先执行 SecondActivity 的 onPause 方法,接着执行 FirstActivity 的 onResume 方法,最后执行 SecondActivity 的 onStop onDestroy,注意这里的半透明风格指的是开发者给 Activity 设置的 theme ,哪怕你视觉上看到 SecondActivity 完全覆盖住了 FirstActivity ,在按 back 键时也不会回调 FirstActivity 的 onStart 方法。

  10. 在 Activity 的 onCreate 中执行 finish() 方法时:
    首先会回调 onCreate 方法,紧接着回调 onDestroy 方法,不走其余的生命周期方法。

  11. 当系统配置发生改变时:
    当系统配置发生改变比如说手机屏幕发生旋转时,当前 Activity 首先会被销毁并回调 onSaveInstanceState 方法来保存数据,onSaveInstanceState 方法在 onStop 之前调用,但不保证在 onPause 之前还是之后执行,当 Activity 重新被创建后会按一般的流程 onCreate onStart onResume 来重走 Activity 的生命周期方法,onRestoreInstanceState 在 onStart 之后执行,在 onSaveInstanceState 中保存的数据会以参数 Bundle 的形式传递给 onCreate 和 onRestoreInstanceState ,onRestoreInstanceState 一旦被调用它的参数 Bundle savedInstanceState 一定会有值的,在 onCreate 中不保证其参数 Bundle savedInstanceState 是有值的,所以在 onCreate 中获取上一个被销毁的 Activity 的保存的数据时一定要进行判空的操作,注意系统只有在 Activity 异常终止的时候才会调用 onSaveInstanceState 与 onRestoreInstanceState 来存储和恢复数据,其它情况不会触发,但是按 Home 键或者启动新 Activity 依然会单独触发 onSaveInstanceState 。
    给你的 Activity 配置 android:configChanges="orientation|screenSize" 可以确保在屏幕旋转时不走生命周期方法而是会调用 onConfigurationChanged 方法。

Activity 的启动模式

给 Activity 配置启动模式,只需要在 AndroidManifest 文件中的 Activity 节点下设置 launchMode 属性,除此之外还可以通过给要启动的 Activity 的 Intent 设置 Flags 来指定要启动的 Activity 的启动模式。

  1. standard :默认的启动模式,在该模式下每次启动 Activity 都会创建一个 Activity 实例,多次启动同一个 Activity 会创建多个该 Activity 实例并将其放到任务栈中,如果启动一个 standard 模式的 Activity,那么该 Activity 会被放置到启动它的那个 Activity 所在的任务栈中。

  2. singleTop :栈顶复用模式,在任务栈中如果该启动模式的 Activity 位于栈顶位置,那么多次启动该 Activity 不会创建该 Activity 的实例更不会调用 onCreate onStart 方法而是会回调 onNewIntent 方法,并在其参数 Intent intent 中携带传递过来的数据,如果在任务栈中存在一个 singleTop 的 Activity ,但是不在栈顶位置,那么启动该 Activity 时会重新创建该 Activity 的实例并入栈。

  3. singleTask :栈内复用模式,如果任务栈中存在该启动模式的 Activity ,那么多次启动该 Activity 并不会多次创建该 Activity 的实例而是会回调其 onNewIntent 方法,如果启动了 singleTask 模式的 Activity 那么首先会寻找是否存在待启动 Activity 所需的任务栈,如果不存在那么会新建该 Activity 所需的任务栈并将其 Activity 实例添加进该任务栈中,如果该启动模式的 Activity 不在栈顶那么会将它上面的所有 Activity 出栈,并将其拉到栈顶位置。

  4. singleInstance :单实例模式,拥有 singleTask 的全部特性,启动此模式下的 Activity 会运行在一个单独的任务栈中,也就就是说启动了一个 singleInstance 模式的 Activity 系统会为它分配一个独立的任务栈让它运行在里面。

任务相关性(taskAffinity)

taskAffinity 是在 AndroidManifest 文件 Activity 节点下定义的属性,用来标记 Activity 所需的任务栈名,也就是说该 Activity 需要运行在与 taskAffinity 所定义的相匹配的任务栈中, taskAffinity 只能与 singleTask 启动模式下的 Activity 一起配合使用。

Activity 常用的 Flags
  1. FLAG_ACTIVITY_NEW_TASK : 用与指定待启动的 Activity 的启动模式为 singleTask

  2. FLAG_ACTIVITY_SINGLE_TOP : 用与指定待启动的 Activity 的启动模式为 singleTop

  3. FLAG_ACTIVITY_CLEAR_TOP : 在同一个任务栈中所有位于待启动的 Activity 之上的 Activity 都要出栈,如果待启动的 Activity 的启动模式是 singleTask 并且在任务栈中已经存在了该 Activity 那么会调用它的 onNewIntent 方法,如果待启动的 Activity 的启动模式是 standard 那么它和它之上的 Activity 都要出栈,并且系统会创建一个新的 Activity 并放入到任务栈中

  4. FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS : 如果不希望用户通过历史列表回到某个 Activity 里那么使用这个标记,设置这个标记等同在 AndroidManifest 文件中 Activity 节点下定义 android:excludeFromRecents="true"

Service

Service 的生命周期
service_lifecycle.png

Service 是一个可以长时间在后台运行而不需要提供用户界面的组件,它可以由其它的应用组件启动,并且在用户切换到其它的应用后 Service 依然可以在后台运行,除此之外组件还可以与 Service 绑定,通过绑定 Service 来与服务进行交互调用 Service 里定义的方法,执行后台下载、播放音乐、进程间通信等操作。

启动服务的方式有两种 startService(启动服务) 和 bindService(绑定服务),不同的启动方式对应的生命周期方法也有所不同,注意在 Android 5.0 之后根据官网介绍:为了确保应用的安全性,请始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。 启动哪个服务存在一定的不确定性,而如果对这种不确定性的考量非常有必要,则可为服务提供 Intent 过滤器并从 Intent 中排除相应的组件名称,但随后必须使用 setPackage() 方法设置 Intent 的软件包,这样可以充分消除目标服务的不确定性。此外,还可以通过添加 android:exported 属性并将其设置为 "false",确保服务仅适用于您的应用。这可以有效阻止其他应用启动您的服务,即便在使用显式 Intent 时也如此。

  1. 启动服务
    onCreate -> onStartCommand -> onDestroy
    通过调用 startService 方法来启动一个服务,onCreate 会在第一次启动服务的时候被调用,多次调用 startService 方法会多次回调 onStartCommand ,通过 stopService 来关闭服务,startService 方式启动的服务会长时间运行在后台,哪怕用户切换了应用它依然会继续运行。

  2. 绑定服务
    onCreate -> onBind -> onUnbind -> onDestroy
    通过调用 bindService 方法来绑定一个服务,调用 unbindService 来解除绑定,绑定服务时并不会调用 onStartCommand 方法,多次 bindService 只会回调一次 onBind 方法,通过这种方式启动服务,如果客户端挂了服务端一样也会挂,但是
    bindService 可以方便开发者调用 Service 中的方法。

public class MyService extends Service {

    private Binder binder = new MyBinder();

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    class MyBinder extends Binder {

        int sum(int a, int b) {
            return a + b;
        }

    }
}
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private MyService.MyBinder myBinder;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.bt).setOnClickListener(this);
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt:
                myBinder.sum(1,2);
                break;
            default:
                break;
        }
    }
}

至于如何绑定远程服务以及如何使用 AIDL 可以参考我以前的文章 使用 AIDL 实现进程间通信01[艺术探索学习笔记]

startService 和 bindService 混合使用

肯定会有朋友对 startService bindService 同时启动的话 Service 的生命周期该怎么走以及如何关闭有着疑惑,下面让我们具体的操作一番。

  1. 首先 startService
    执行 onCreate -> onStartCommand
  2. 接着绑定服务
    执行 onBind
  3. 执行 stopService
    并没有执行 onDestroy ,Service 依然继续运行着
  4. 执行 unbindService
    先后回调了 onUnbind -> onDestroy

将第 1 步与第 2 步互换一下发现除了启动时先执行了 onBind ,其余并没有什么区别,第 1、2 步不变,先执行第 3 步,在执行第 4 步:onUnbind -> onDestroy , 通过以上的操作我们发现同时执行 startService 和 bindService , 只有在同时执行了 stopService 和 unbindService 之后才能彻底的销毁服务,原因在于:

  1. 若被停止的服务依然有ServiceConnection 与其绑定,则服务不能销毁,直至我们把所有ServiceConnection 解绑
  2. 当所有ServiceConnection 解绑后,系统会自动销毁服务。注意括号里面的内容:不包括同时用startService()启动的情况。此时,我们不得不再调用一次stopService来销毁它
IntentService

IntentService 是 Service 的子类,Service 里的方法运行在主线程中所以不建议在 Service 里执行太过耗时的方法, IntentService 内部维护了一个工作线程来处理耗时操作,下面让我们从源码角度来分析 IntentService 是如何工作的。

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

IntentService 是一个抽象类,所以在使用它的时候必须自定义一个类来继承它,至于如何启动它则与常规的方式一样,我们首先来看它的 onCreate 方法,在它的 onCreate 方法中首先创建了 HandlerThread 对象并启动该线程,你可以把 HandlerThread 理解为可以使用 Handler 的 Thread ,获取 HandlerThread 内部维护的 Looper 对象并将它传递给 IntentService 内部维护的 Handler 对象 ServiceHandler ,在 onStartCommand 方法中将参数 Intent 和 startId 传递给 onStart 方法,在 onStart 方法内部将 Intent 和 startId 封装进 Message 里,通过 mServiceHandler.sendMessage(msg); 将消息发送到消息队列中,这也就是为什么 IntentService 能依次处理传递过来的 Intent 对象,其实也就是借助了 Handler 机制,通过 Handler 来将消息发送到 ServiceHandler 的 handleMessage 中处理,由于 ServiceHandler 使用的是 HandlerThread 维护的 Looper 对象,所以本质上在 onStart 中发送消息时其实就是将在主线程中的“消息”传递给 HandlerThread,而 ServiceHandler 其实就是在 HandlerThread 中处理消息的,这也就解释了为什么在 onHandleIntent 中可以执行耗时操作的原因,紧接着执行 stopSelf 来关闭当前的 Service 。

BroadcastReceiver(广播接收器)

广播是在 Android 组件之间进行消息传递的一种机制并且可以实现跨进程传递消息,而广播接收器主要用来接收和过滤广播消息的,在 Android 中广播可以分为两种:

  1. 标准广播
    标准广播是一种异步执行的广播,在广播发出去后所有的广播接收器几乎可以在同一时间接收到该广播,并且该广播不会被截断,所以标准广播的效率更高点。

  2. 有序广播
    有序广播是一种同步执行的广播,在广播发出去后同一时刻只有一个广播接收器可以接收到广播,优先级别高的广播接收器先接收到广播然后可以传递给级别较低的广播接收器,并且该广播可以被截断,广播被前面的接收器截断后,后面的广播接收器就接收不到该广播了,可以在 intent-filter 中定义 priority 属性来标记此广播的优先级。

动态注册监听网络状态变化广播
  1. 自定义 MyBroadcastReceiver 继承 BroadcastReceiver,需求是监听网络变化
public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        final ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        cm.requestNetwork(new NetworkRequest.Builder().build(), new ConnectivityManager.NetworkCallback() {
            @Override
            public void onLost(Network network) {
                super.onLost(network);
                ///网络不可用的情况下的方法
            }

            @Override
            public void onAvailable(Network network) {
                super.onAvailable(network);
                ///网络可用的情况下的方法
            }
        });
    }

}
  1. 在 Activity 中动态注册广播
public class MainActivity extends AppCompatActivity {

    private IntentFilter intentFilter;
    private MyBroadcastReceiver myBroadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intentFilter = new IntentFilter();
        // 当网络状态发生变化时,系统会发出一个 action 为 android.net.conn.CONNECTIVITY_CHANGE 的广播
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        myBroadcastReceiver = new MyBroadcastReceiver();
        registerReceiver(myBroadcastReceiver,intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(myBroadcastReceiver);
    }
}
  1. 在清单文件中注册广播接收器
<receiver
      android:name=".MyBroadcastReceiver"
      android:enabled="true"
      android:exported="true" />
  1. 在使用动态注册的时候系统内的任何应用均可监听并触发我们的 Receiver,因此你可以将 android:exported 便签设置为 false 标记它只可以在应用内部使用,或者使用自定义权限:
String permission = "com.example.tengfei.broadcastreveiver.MyBroadcastReceiver";
intentFilter = new IntentFilter();
// 当网络状态发生变化时,系统会发出一个 action 为 android.net.conn.CONNECTIVITY_CHANGE 的广播
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
myBroadcastReceiver = new MyBroadcastReceiver();
registerReceiver(myBroadcastReceiver,intentFilter,permission,null);

Intent intent = new Intent();
intent.putExtra("key","value");
intent.setAction("android.net.conn.
Intent intent = new Intent();
intent.putExtra("key","value");
intent.setAction("android.net.conn.CONNECTIVITY_CHANGE");
sendBroadcast(intent,permission);
关于静态注册

静态注册主要是在项目的清单文件中注册,比如说上面的需求在静态注册时只需要定义 <intent-filter/> 来过滤接收到的广播,只有符合 action 里定义的字符串才可以匹配成功。

      <receiver
         android:name=".MyBroadcastReceiver"
         android:enabled="true"
         android:exported="true">
         <intent-filter>
             <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
         </intent-filter>
     </receiver>

注意在 Android 8.0 中我们无法为绝大多数的隐式广播静态注册,但所有的广播都可以动态注册,并且需要签名权限的广播不受此限制所限,因此建议你的项目中尽快将静态注册的广播替换成动态注册,不过依然还有很多广播不受限制,依然可以静态注册,可以查阅 Implicit Broadcast Exceptions 来了解具体的详情。

如何发送广播
  1. 发送标准广播
 Intent intent = new Intent();
 intent.putExtra("key","value");
 intent.setAction("android.net.conn.CONNECTIVITY_CHANGE");
 sendBroadcast(intent);
  1. 发送有序广播
Intent intent = new Intent();
intent.putExtra("key","value");
intent.setAction("android.net.conn.CONNECTIVITY_CHANGE");
sendOrderedBroadcast(intent,null);
  1. 发送本地广播
    本地广播只在本应用内部有效,相比较其它的广播效率和安全性更高。
Intent intent = new Intent();
intent.putExtra("key","value");
intent.setAction("android.net.conn.CONNECTIVITY_CHANGE");

LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(myBroadcastReceiver,intentFilter);
localBroadcastManager.sendBroadcast(intent);

ContentProvider(内容提供者)

ContentProvider 是 Android 提供的专门用于应用间数据共享的方式,同样它也是 IPC 的一种方式,ContentProvider 主要是以表格的形式组织数据,除此之外 ContentProvider 还支持文件数据,图片视频等。
和其它三大组件一样,在定义 ContentProvider 时同样需要在清单文件中注册:

<provider
    android:name=".BookProvider"
    android:authorities="com.example.tengfei.contentproviderdemo.BookProvider"
    android:permission="com.example.tengfei.permission.BookProvider"
    android:process=":provider" />

authorities 是“主机名”,其它的应用需要通过 authorities 属性的值来找到被它标记的 ContentProvider 。
自定义属于自己应用的 ContentProvider 时需要继承 ContentProvider 并实现以下的方法:

  1. onCreate
    在 ContentProvider 被创建时回调,一般在此方法里执行初始化的操作。
  2. getType
    用来返回一个 Uri 请求所对应的 MIME(媒体) 类型,如果在应用中不关心这个选项直接返回 null 即可。
  3. 实现“增删改查”的四大方法:insert 、delete 、update 、query
    运行在 ContentProvider 所在进程的 binder 线程池中,在另一个应用或者另一个进程里通过 getContentResolver() 来调用相关方法,在 ContentProvider 中通过 UriMatcher 将 uri 与 code 关联起来具体做法如下:
private static final String AUTHORITIES = "com.example.tengfei.contentproviderdemo.BookProvider";

/**
* 如果需要让外界知道访问的是哪一个表,那么需要为它们定义 Uri 和 UriCode ,通过 UriMatcher 来将两者关联起来,
* 当外界通过 Uri 来访问 ContentProvider 的时候就可以获取对应的 UriCode,通过 UriCode 可以知道访问的是哪一个表。
*/

private static final int BOOK_CONTENT_CODE = 0x000;
private static final int USER_CONTENT_CODE = 0x001;

private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);

static {
     URI_MATCHER.addURI(AUTHORITIES, "book", BOOK_CONTENT_CODE);
     URI_MATCHER.addURI(AUTHORITIES, "user", USER_CONTENT_CODE);
  }

调用 UriMatcher 的 addURI 方法将 authorities 、path、code 拼装起来,在我们代码中 authorities 指在清单文件中指定的“主机名”,path 指你的自定义数据库表的表名(也就是你要访问的那一个表),code 一个 int 值可以自定义(用来标记你要访问的表),最终拼装的完整 uri 为 content://com.example.tengfei.contentproviderdemo.BookProvider/book ,比如在调用 getContentResolver() 来调用 query 方法时所传入的第一个参数就是上文中定义的 uri ,而自定义内容提供者的 query 方法实际上最终也就是被 getContentResolver() 所调用,通过参数 1 可以获取到 Uri 在根据 Uri 来判断最终请求的是哪一个表,在 query 的返回方法中调用数据库的查询方法来将最终的查询结果返回回去。

Cursor cursor = getContentResolver().query(BOOK_CONTENT_URI, new String[]{"_id", "name"}, null, null, null);
            if (cursor != null) {
                    while (cursor.moveToNext()) {
                        Log.i(TAG, " _id # " + cursor.getInt(0) + " name # " + cursor.getString(1));
                    }
                    cursor.close();
                }
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Log.i(TAG, "query # " + Thread.currentThread().getName());
        String tabName = getTableName(uri);
        if (tabName == null) {
            throw new IllegalArgumentException("UnSupported URI" + uri);
        }
        return mDb.query(tabName, projection, selection, selectionArgs, null, null, sortOrder, null);
    }

这里仅做举例子,实际上在我最喜欢的一本书《Android开发艺术探索》第二章中有对 ContentProvider 的使用的具体分析,大家不妨去看看,毕竟 ContentProvider 在实际开发中遇到的机会并不多,我也趁写博客时现学一下做一下知识积累。

参考资料

  1. Android 四大组件
  2. Android开发艺术探索
  3. Activity | Android Developer 谷歌官方文档
  4. 服务 | Android Developer 谷歌官方文档
  5. startService与bindService混合使用对Service生命周期的影响
  6. Android BroadcastReceiver使用详解
上一篇 下一篇

猜你喜欢

热点阅读