精华内容
参与话题
问答
  • android 四大组件详解

    万次阅读 2017-07-07 15:35:01
    这个文章主要是讲Android开发的四大组件,本文主要分为 一、Activity详解 二、Service详解 三、Broadcast Receiver详解 四、Content Provider详解 外加一个重要组件 intent的详解。 一、Activity详解 ...

    注:本文来自“友盟杯”,仅在此阅读,学习


    这个文章主要是讲Android开发的四大组件,本文主要分为

    一、Activity详解
    二、Service详解
    三、Broadcast Receiver详解
    四、Content Provider详解
    外加一个重要组件 intent的详解。



    一、Activity详解
    Activty的生命周期的也就是它所在进程的生命周期。


     

    一个Activity的启动顺序:

    onCreate()——>onStart()——>onResume()

    当另一个Activity启动时:
    第一个Activity onPause()——>第二个Activity    onCreate()——>onStart()——>onResume() 
    ——>第一个Activity   onStop()

    当返回到第一个Activity时:
    第二个Activity onPause() ——> 第一个Activity onRestart()——>onStart()——>onResume() 
    ——>第二个Activity   onStop()——>onDestroy()

    一个Activity的销毁顺序:
    (情况一)onPause()——><Process Killed> 
    (情况二)onPause()——>onStop()——><Process Killed> 
    (情况三)onPause()——>onStop()——>onDestroy()

      每一个活动( Activity )都处于某一个状态,对于开发者来说,是无法控制其应用程序处于某一个状态的,这些均由系统来完成。

      但是当一个活动的状态发生改变的时候,开发者可以通过调用 onXX() 的方法获取到相关的通知信息。

      在实现 Activity 类的时候,通过覆盖( override )这些方法即可在你需要处理的时候来调用。


             一、 onCreate :当活动第一次启动的时候,触发该方法,可以在此时完成活动的初始化工作。 
    onCreate 方法有一个参数,该参数可以为空( null ),也可以是之前调用 onSaveInstanceState ()方法保存的状态信息。
            二、  onStart :该方法的触发表示所属活动将被展现给用户。
            三、  onResume :当一个活动和用户发生交互的时候,触发该方法。
           四、  onPause :当一个正在前台运行的活动因为其他的活动需要前台运行而转入后台运行的时候,触发该方法。这时候需要将活动的状态持久化,比如正在编辑的数据库记录等。
            五、  onStop :当一个活动不再需要展示给用户的时候,触发该方法。如果内存紧张,系统会直接结束这个活动,而不会触发 onStop 方法。 所以保存状态信息是应该在onPause时做,而不是onStop时做。活动如果没有在前台运行,都将被停止或者Linux管理进程为了给新的活动预留足够的存储空间而随时结束这些活动。因此对于开发者来说,在设计应用程序的时候,必须时刻牢记这一原则。在一些情况下,onPause方法或许是活动触发的最后的方法,因此开发者需要在这个时候保存需要保存的信息。
            六、onRestart :当处于停止状态的活动需要再次展现给用户的时候,触发该方法。
            七、 onDestroy :当活动销毁的时候,触发该方法。和 onStop 方法一样,如果内存紧张,系统会直接结束这个活动而不会触发该方法。
    ·        onSaveInstanceState :系统调用该方法,允许活动保存之前的状态,比如说在一串字符串中的光标所处的位置等。 
    通常情况下,开发者不需要重写覆盖该方法,在默认的实现中,已经提供了自动保存活动所涉及到的用户界面组件的所有状态信息。

      Activity栈

      上面提到开发者是无法控制Activity的状态的,那Activity的状态又是按照何种逻辑来运作的呢?这就要知道 Activity 栈。
      每个Activity的状态是由它在Activity栈(是一个后进先出LIFO,包含所有正在运行Activity的队列)中的位置决定的。
      当一个新的Activity启动时,当前的活动的Activity将会移到Activity栈的顶部。
      如果用户使用后退按钮返回的话,或者前台的Activity结束,活动的Activity就会被移出栈消亡,而在栈上的上一个活动的Activity将会移上来并变为活动状态。如下图所示:
       一个应用程序的优先级是受最高优先级的Activity影响的。当决定某个应用程序是否要终结去释放资源,Android内存管理使用栈来决定基于Activity的应用程序的优先级。
      Activity状态
      一般认为Activity有以下四种状态:
      活动的:当一个Activity在栈顶,它是可视的、有焦点、可接受用户输入的。Android试图尽最大可能保持它活动状态,杀死其它Activity来确保当前活动Activity有足够的资源可使用。当另外一个Activity被激活,这个将会被暂停。
      暂停:在很多情况下,你的Activity可视但是它没有焦点,换句话说它被暂停了。有可能原因是一个透明或者非全屏的Activity被激活。
      当被暂停,一个Activity仍会当成活动状态,只不过是不可以接受用户输入。在极特殊的情况下,Android将会杀死一个暂停的Activity来为活动的Activity提供充足的资源。当一个Activity变为完全隐藏,它将会变成停止。
      停止:当一个Activity不是可视的,它“停止”了。这个Activity将仍然在内存中保存它所有的状态和会员信息。尽管如此,当其它地方需要内存时,它将是最有可能被释放资源的。当一个Activity停止后,一个很重要的步骤是要保存数据和当前UI状态。一旦一个Activity退出或关闭了,它将变为待用状态。
      待用: 在一个Activity被杀死后和被装在前,它是待用状态的。待用Acitivity被移除Activity栈,并且需要在显示和可用之前重新启动它。
      activity的四种加载模式
      在android的多activity开发中,activity之间的跳转可能需要有多种方式,有时是普通的生成一个新实例,有时希望跳转到原来某个activity实例,而不是生成大量的重复的activity。加载模式便是决定以哪种方式启动一个跳转到原来某个Activity实例。
      在android里,有4种activity的启动模式,分别为:
      ·standard: 标准模式,一调用startActivity()方法就会产生一个新的实例。
      ·singleTop: 如果已经有一个实例位于Activity栈的顶部时,就不产生新的实例,而只是调用Activity中的newInstance()方法。如果不位于栈顶,会产生一个新的实例。
      ·singleTask: 会在一个新的task中产生这个实例,以后每次调用都会使用这个,不会去产生新的实例了。
      ·singleInstance: 这个跟singleTask基本上是一样,只有一个区别:在这个模式下的Activity实例所处的task中,只能有这个activity实例,不能有其他的实例。
      这些启动模式可以在功能清单文件AndroidManifest.xml中进行设置,中的launchMode属性。
      相关的代码中也有一些标志可以使用,比如我们想只启用一个实例,则可以使用 Intent.FLAG_ACTIVITY_REORDER_TO_FRONT 标志,这个标志表示:如果这个activity已经启动了,就不产生新的activity,而只是把这个activity实例加到栈顶来就可以了。
    1.   Intent intent = new Intent(ReorderFour.this, ReorderTwo.class);
    2.   intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    3.   startActivity(intent);
    复制代码


      Activity的加载模式受启动Activity的Intent对象中设置的Flag和manifest文件中Activity的元素的特性值交互控制。
      下面是影响加载模式的一些特性
      核心的Intent Flag有:
      FLAG_ACTIVITY_NEW_TASK
      FLAG_ACTIVITY_CLEAR_TOP
      FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
      FLAG_ACTIVITY_SINGLE_TOP

    核心的特性有:
      taskAffinity
      launchMode
      allowTaskReparenting
      clearTaskOnLaunch
      alwaysRetainTaskState
      finishOnTaskLaunch

     四种加载模式的区别
      所属task的区别
      一般情况下,“standard”和”singleTop”的activity的目标task,和收到的Intent的发送者在同一个task内,就相当于谁调用它,它就跟谁在同一个Task中。
      除非Intent包括参数FLAG_ACTIVITY_NEW_TASK。如果提供了FLAG_ACTIVITY_NEW_TASK参数,会启动到别的task里。
      “singleTask”和”singleInstance” 总是把要启动的activity作为一个task的根元素,他们不会被启动到一个其他task里。
      是否允许多个实例
      “standard”和”singleTop”可以被实例化多次,并且是可以存在于不同的task中;这种实例化时一个task可以包括一个activity的多个实例;
      “singleTask”和”singleInstance”则限制只生成一个实例,并且是task的根元素。
      singleTop 要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不创建新的实例。
      是否允许其它activity存在于本task内
      “singleInstance”独占一个task,其它activity不能存在那个task里;
      如果它启动了一个新的activity,不管新的activity的launch mode 如何,新的activity都将会到别的task里运行(如同加了FLAG_ACTIVITY_NEW_TASK参数)。
      而另外三种模式,则可以和其它activity共存。
      是否每次都生成新实例
      “standard”对于每一个启动Intent都会生成一个activity的新实例;
      “singleTop”的activity如果在task的栈顶的话,则不生成新的该activity的实例,直接使用栈顶的实例,否则,生成该activity的实例。
      比如:
      现在task栈元素为A-B-C-D(D在栈顶),这时候给D发一个启动intent,如果D是 “standard”的,则生成D的一个新实例,栈变为A-B-C-D-D。
      如果D是singleTop的话,则不会生产D的新实例,栈状态仍为A-B-C-D
      如果这时候给B发Intent的话,不管B的launchmode是”standard” 还是 “singleTop” ,都会生成B的新实例,栈状态变为A-B-C-D-B。
      “singleInstance”是其所在栈的唯一activity,它会每次都被重用。
      “singleTask” 如果在栈顶,则接受intent,否则,该intent会被丢弃,但是该task仍会回到前台。 当已经存在的activity实例处理新的intent时候,会调用onNewIntent()方法,如果收到intent生成一个activity实例,那么用户可以通过back键回到上一个状态;如果是已经存在的一个activity来处理这个intent的话,用户不能通过按back键返回到这之前的状态。
    -----------------------------------
    二、Service详解

      service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务嘛,总是藏在后头的。

      Service是在一段不定的时间运行在后台,不和用户交互应用组件。每个Service必须在manifest中 通过<service>来声明。可以通过contect.startservice和contect.bindserverice来启动。

      Service和其他的应用组件一样,运行在进程的主线程中。这就是说如果service需要很多耗时或者阻塞的操作,需要在其子线程中实现。

      service的两种模式(startService()/bindService()不是完全分离的):

      本地服务 Local Service 用于应用程序内部。

      它可以启动并运行,直至有人停止了它或它自己停止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它可以调用Service.stopSelf() 或 Service.stopSelfResult()来自己停止。不论调用了多少次startService()方法,你只需要调用一次stopService()来停止服务。

      用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。
      远程服务 Remote Service 用于android系统内部的应用程序之间。

      它可以通过自己定义并暴露出来的接口进行程序操作。客户端建立一个到服务对象的连接,并通过那个连接来调用服务。连接以调用Context.bindService()方法建立,以调用 Context.unbindService()关闭。多个客户端可以绑定至同一个服务。如果服务此时还没有加载,bindService()会先加载它。

      可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。

     生命周期

    使用context.startService() 启动Service是会会经历:

      context.startService() ->onCreate()- >onStart()->Service running

      context.stopService() | ->onDestroy() ->Service stop

      如果Service还没有运行,则android先调用onCreate()然后调用onStart();如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。

      stopService的时候直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。该Service的调用者再启动起来后可以通过stopService关闭Service。

      所以调用startService的生命周期为:onCreate --> onStart(可多次调用) --> onDestroy
      使用使用context.bindService()启动Service会经历:

      context.bindService()->onCreate()->onBind()->Service running

      onUnbind() -> onDestroy() ->Service stop

      onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。

      所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。

      在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。

    而启动service,根据onStartCommand的返回值不同,有两个附加的模式:

      1. START_STICKY 用于显示启动和停止service。

      2. START_NOT_STICKY或START_REDELIVER_INTENT用于有命令需要处理时才运行的模式。

      服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。

      1. 使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。

      如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。

      如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。

      采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。

      2. 使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。

      onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。

      采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。

      看看官方给出的比较流程示意图:

      官方文档告诉我们,一个service可以同时start并且bind,在这样的情况,系统会一直保持service的运行状态如果service已经start了或者BIND_AUTO_CREATE标志被设置。如果没有一个条件满足,那么系统将会调用onDestory方法来终止service.所有的清理工作(终止线程,反注册接收器)都在onDestory中完成。

      拥有service的进程具有较高的优先级

      官方文档告诉我们,Android系统会尽量保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindService)到它。当内存不足时,需要保持,拥有service的进程具有较高的优先级。

      1. 如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被killed。

      2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed.

      3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。

      4. 如果service可以使用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。

      如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。

      本地service

      1.不需和Activity交互的本地服务

    1. public class LocalService extends Service {

    2. private static final String TAG = "LocalService";

    3. @Override
    4. public IBinder onBind(Intent intent) {
    5. Log.i(TAG, "onBind");
    6. return null;
    7. }

    8. @Override
    9. public void onCreate() {
    10. Log.i(TAG, "onCreate");
    11. super.onCreate();
    12. }

    13. @Override
    14. public void onDestroy() {
    15. Log.i(TAG, "onDestroy");
    16. super.onDestroy();
    17. }

    18. @Override
    19. public void onStart(Intent intent, int startId) {
    20. Log.i(TAG, "onStart");
    21. super.onStart(intent, startId);
    22. }
    23. }
    复制代码
      Activity:
    1. public class ServiceActivity extends Activity {

    2. @Override
    3. protected void onCreate(Bundle savedInstanceState) {
    4. super.onCreate(savedInstanceState);
    5. setContentView(R.layout.servicedemo);

    6. ((Button) findViewById(R.id.startLocalService)).setOnClickListener(
    7. new View.OnClickListener(){

    8. @Override
    9. public void onClick(View view) {
    10. // TODO Auto-generated method stub
    11. startService(new Intent("com.demo.SERVICE_DEMO"));

    12. });

    13. ((Button) findViewById(R.id.stopLocalService)).setOnClickListener(
    14. new View.OnClickListener(){

    15. @Override
    16. public void onClick(View view) {
    17. // TODO Auto-generated method stub
    18. stopService(new Intent("com.demo.SERVICE_DEMO"));
    19. }
    20. });
    21. }

    22. }
    复制代码
      在AndroidManifest.xml添加:
    1. <service android:name=".LocalService">
    2. <intent-filter>
    3. <action android:name="com.demo.SERVICE_DEMO" />
    4. <category android:name="android.intent.category.default" />
    5. </intent-filter>
    6. </service>
    复制代码
      否则启动服务时会提示new Intent找不到"com.demo.SERVICE_DEMO"。

      对于这类不需和Activity交互的本地服务,是使用startService/stopService的最好例子。

      运行时可以发现第一次startService时,会调用onCreate和onStart,在没有stopService前,无论点击多少次startService,都只会调用onStart。而stopService时调用onDestroy。再次点击stopService,会发现不会进入service的生命周期的,即不会再调用onCreate,onStart和onDestroy。

      而onBind在startService/stopService中没有调用。

      2.本地服务和Activity交互

      对于这种case,官方的sample(APIDemo\app.LocalService)是最好的例子:

    1. /**
    2. * This is an example of implementing an application service that runs locally
    3. * in the same process as the application. The {@link LocalServiceController}
    4. * and {@link LocalServiceBinding} classes show how to interact with the
    5. * service.
    6. *
    7. * <p>Notice the use of the {@link NotificationManager} when interesting things
    8. * happen in the service. This is generally how background services should
    9. * interact with the user, rather than doing something more disruptive such as
    10. * calling startActivity().
    11. */
    12. public class LocalService extends Service {
    13. private NotificationManager mNM;

    14. /**
    15. * Class for clients to access. Because we know this service always
    16. * runs in the same process as its clients, we don't need to deal with
    17. * IPC.
    18. */
    19. public class LocalBinder extends Binder {
    20. LocalService getService() {
    21. return LocalService.this;
    22. }
    23. }

    24. @Override
    25. public void onCreate() {
    26. mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

    27. // Display a notification about us starting. We put an icon in the status bar.
    28. showNotification();
    29. }

    30. @Override
    31. public int onStartCommand(Intent intent, int flags, int startId) {
    32. Log.i("LocalService", "Received start id " + startId + ": " + intent);
    33. // We want this service to continue running until it is explicitly
    34. // stopped, so return sticky.
    35. return START_STICKY;
    36. }

    37. @Override
    38. public void onDestroy() {
    39. // Cancel the persistent notification.
    40. mNM.cancel(R.string.local_service_started);

    41. // Tell the user we stopped.
    42. Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
    43. }

    44. @Override
    45. public IBinder onBind(Intent intent) {
    46. return mBinder;
    47. }

    48. // This is the object that receives interactions from clients. See
    49. // RemoteService for a more complete example.
    50. private final IBinder mBinder = new LocalBinder();

    51. /**
    52. * Show a notification while this service is running.
    53. */
    54. private void showNotification() {
    55. // In this sample, we'll use the same text for the ticker and the expanded notification
    56. CharSequence text = getText(R.string.local_service_started);

    57. // Set the icon, scrolling text and timestamp
    58. Notification notification = new Notification(R.drawable.stat_sample, text,
    59. System.currentTimeMillis());

    60. // The PendingIntent to launch our activity if the user selects this notification
    61. PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
    62. new Intent(this, LocalServiceController.class), 0);

    63. // Set the info for the views that show in the notification panel.
    64. notification.setLatestEventInfo(this, getText(R.string.local_service_label),
    65. text, contentIntent);

    66. // Send the notification.
    67. // We use a layout id because it is a unique number. We use it later to cancel.
    68. mNM.notify(R.string.local_service_started, notification);
    69. }
    70. }
    复制代码
      这里可以发现onBind需要返回一个IBinder对象。也就是说和上一例子LocalService不同的是,

      1. 添加了一个public内部类继承Binder,并添加getService方法来返回当前的Service对象;

      2. 新建一个IBinder对象--new那个Binder内部类;

      3. onBind方法返还那个IBinder对象。

      Activity:

    1. /**
    2. * <p>Example of binding and unbinding to the {@link LocalService}.
    3. * This demonstrates the implementation of a service which the client will
    4. * bind to, receiving an object through which it can communicate with the service.</p>
    5. */
    6. public class LocalServiceBinding extends Activity {
    7. private boolean mIsBound;
    8. private LocalService mBoundService;

    9. @Override
    10. protected void onCreate(Bundle savedInstanceState) {
    11. super.onCreate(savedInstanceState);

    12. setContentView(R.layout.local_service_binding);

    13. // Watch for button clicks.
    14. Button button = (Button)findViewById(R.id.bind);
    15. button.setOnClickListener(mBindListener);
    16. button = (Button)findViewById(R.id.unbind);
    17. button.setOnClickListener(mUnbindListener);
    18. }

    19. private ServiceConnection mConnection = new ServiceConnection() {
    20. public void onServiceConnected(ComponentName className, IBinder service) {
    21. // This is called when the connection with the service has been
    22. // established, giving us the service object we can use to
    23. // interact with the service. Because we have bound to a explicit
    24. // service that we know is running in our own process, we can
    25. // cast its IBinder to a concrete class and directly access it.
    26. mBoundService = ((LocalService.LocalBinder)service).getService(); 

    27. // Tell the user about this for our demo.
    28. Toast.makeText(LocalServiceBinding.this, R.string.local_service_connected,
    29. Toast.LENGTH_SHORT).show();
    30. }

    31. public void onServiceDisconnected(ComponentName className) { 
    32. // This is called when the connection with the service has been
    33. // unexpectedly disconnected -- that is, its process crashed.
    34. // Because it is running in our same process, we should never
    35. // see this happen.
    36. mBoundService = null;
    37. Toast.makeText(LocalServiceBinding.this, R.string.local_service_disconnected,
    38. Toast.LENGTH_SHORT).show();
    39. }
    40. };

    41. private OnClickListener mBindListener = new OnClickListener() {
    42. public void onClick(View v) {
    43. // Establish a connection with the service. We use an explicit
    44. // class name because we want a specific service implementation that
    45. // we know will be running in our own process (and thus won't be
    46. // supporting component replacement by other applications).
    47. bindService(new Intent(LocalServiceBinding.this, 
    48. LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
    49. mIsBound = true;
    50. }
    51. };

    52. private OnClickListener mUnbindListener = new OnClickListener() {
    53. public void onClick(View v) {
    54. if (mIsBound) {
    55. // Detach our existing connection.
    56. unbindService(mConnection);
    57. mIsBound = false;
    58. }
    59. }
    60. };
    61. }
    复制代码


      明显看出这里面添加了一个名为ServiceConnection类,并实现了onServiceConnected(从IBinder获取Service对象)和onServiceDisconnected(set Service to null)。

      而bindService和unbindService方法都是操作这个ServiceConnection对象的。

      AndroidManifest.xml里添加:

    1.   <service android:name=".app.LocalService" />
    复制代码
      这里没什么特别的,因为service没有需要什么特别的action,所以只是声明service而已,而activity和普通的没差别。

      运行时,发现调用次序是这样的:

      bindService:

      1.LocalService : onCreate

      2.LocalService : onBind

      3.Activity: onServiceConnected

      unbindService: 只是调用onDestroy

      可见,onStart是不会被调用的,而onServiceDisconnected没有调用的原因在上面代码的注释有说明。


    ------------------------
    三、Broadcast Receiver详解


      BroadcastReceiver 用于异步接收广播Intent。主要有两大类,用于接收广播的:

      ·正常广播 Normal broadcasts(用 Context.sendBroadcast()发送)是完全异步的。它们都运行在一个未定义的顺序,通常是在同一时间。这样会更有效,但意味着receiver不能包含所要使用的结果或中止的API。

      ·有序广播 Ordered broadcasts(用 Context.sendOrderedBroadcast()发送)每次被发送到一个receiver。所谓有序,就是每个receiver执行后可以传播到下一个receiver,也可以完全中止传播--不传播给其他receiver。 而receiver运行的顺序可以通过matched intent-filter 里面的android:priority来控制,当priority优先级相同的时候,Receiver以任意的顺序运行。

      要注意的是,即使是Normal broadcasts,系统在某些情况下可能会恢复到一次传播给一个receiver。 特别是receiver可能需要创建一个进程,为了避免系统超载,只能一次运行一个receiver。

      Broadcast Receiver 并没有提供可视化的界面来显示广播信息。可以使用Notification和Notification Manager来实现可视化的信息的界面,显示广播信息的内容,图标及震动信息。

      生命周期

      一个BroadcastReceiver 对象只有在被调用onReceive(Context, Intent)的才有效的,当从该函数返回后,该对象就无效的了,结束生命周期。

      因此从这个特征可以看出,在所调用的onReceive(Context, Intent)函数里,不能有过于耗时的操作,不能使用线程来执行。对于耗时的操作,请start service来完成。因为当得到其他异步操作所返回的结果时,BroadcastReceiver 可能已经无效了。

      发送广播

      事件的广播比较简单,构建Intent对象,可调用sendBroadcast(Intent)方法将广播发出。另外还有sendOrderedBroadcast(),sendStickyBroadcast()等方法,请查阅API Doc。

      1.new Intent with action name

      Intent intent = new Intent(String action);

      或者 只是new Intent, 然后

      intent.setAction(String action);

      2.set data等准备好了后,in activity,

      sendBroadcast(Intent); // 发送广播

      接收广播

      通过定义一个继承BroadcastReceiver类来实现,继承该类后覆盖其onReceiver方法,并在该方法中响应事件。
    1. public class SMSReceiver extends BroadcastReceiver { 

    2.         @Override 
    3.         public void onReceive(Context context, Intent intent) { 
    4.                 // get data from SMS intent 
    5.                 Bundle bundle = intent.getExtras(); 
    6.                 if (bundle != null){ 
    7.                         // get message by "pdus" 
    8.                         Object[] objArray = (Object[]) bundle.get("pdus"); 

    9.                         // rebuild SMS 
    10.                         SmsMessage[] messages = new SmsMessage[objArray.length]; 
    11.                         for (int i=0; i < objArray.length; i++){ 
    12.                                 messages[i] = SmsMessage.createFromPdu((byte[])objArray[i]); 

    13.                                 StringBuilder str = new StringBuilder("from: "); 
    14.                                 str.append(messages[i].getDisplayOriginatingAddress()); 
    15.                                 str.append("\nmessage:\n"); 
    16.                                 str.append(messages[i].getDisplayMessageBody()); 

    17.                                 Toast.makeText(context, str.toString(), Toast.LENGTH_LONG) 
    18.                                                 .show(); 
    19.                         } 
    20.                 } 
    21.         } 
    22. }
    复制代码
      注册Receiver

      注册有两种方式:

      1. 静态方式,在AndroidManifest.xml的application里面定义receiver并设置要接收的action。

    1.   <receiver android:name=".SMSReceiver">

    2.   <intent-filter>

    3.   <action android:name="android.provider.Telephony.SMS_RECEIVED" />

    4.   </intent-filter>

    5.   </receiver>
    复制代码
    2. 动态方式, 在activity里面调用函数来注册,和静态的内容差不多。一个形参是receiver,另一个是IntentFilter,其中里面是要接收的action。
    1. public class HelloDemo extends Activity {    
    2.         private BroadcastReceiver receiver;    

    3.         @Override 
    4.         protected void onStart() { 
    5.                 super.onStart(); 

    6.                 receiver = new CallReceiver(); 
    7.                 registerReceiver(receiver, new IntentFilter("android.intent.action.PHONE_STATE")); 
    8.         } 

    9.         @Override 
    10.         protected void onStop() { 
    11.                 unregisterReceiver(receiver); 
    12.                 super.onStop(); 
    13.         } 
    14. }
    复制代码
      一个receiver可以接收多个action的,即可以有多个intent-filter,需要在onReceive里面对intent.getAction(action name)进行判断。

      个人推荐使用静态注册方式,由系统来管理receiver,而且程序里的所有receiver,可以在xml里面一目了然。而动态注册方式,隐藏在代码中,比较难发现。

      而且动态注册,需要特别注意的是,在退出程序前要记得调用Context.unregisterReceiver()方法。一般在activity的onStart()里面进行注册, onStop()里面进行注销。官方提醒,如果在Activity.onResume()里面注册了,就必须在Activity.onPause()注销。

      Permission权限

      要接收某些action,需要在AndroidManifest.xml里面添加相应的permission。例如接收SMS:

    1. <uses-permission android:name="android.permission.RECEIVE_SMS" />
    复制代码
      下面给出动态注册的接收来电的广播处理的CallReceiver的代码:

      一种方式是直接读取intent.getStringExtra("incoming_number")来获取来电号码:

    1. public class CallReceiver extends BroadcastReceiver { 

    2.         @Override 
    3.         public void onReceive(Context context, Intent intent) { 
    4.                 TelephonyManager teleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 
    5.                  
    6.                 switch(teleManager.getCallState()){ 
    7.                 case TelephonyManager.CALL_STATE_RINGING: //响铃 
    8.                         Toast.makeText(context, "Ringing: " + intent.getStringExtra("incoming_number"), Toast.LENGTH_LONG).show(); 
    9.                         break; 
    10.                 case TelephonyManager.CALL_STATE_OFFHOOK: //接听 
    11.                         Toast.makeText(context, "OffHook: " + intent.getStringExtra("incoming_number"), Toast.LENGTH_LONG).show(); 
    12.                         break; 
    13.                 case TelephonyManager.CALL_STATE_IDLE: //挂断 
    14.                         Toast.makeText(m_context, "Idle: " + incomingNumber, Toast.LENGTH_LONG).show(); 
    15.                         break; 
    16.                 } 
    17.         } 
    18. }
    复制代码
      在运行时,发现除了响铃时可以获取来电号码,接听和挂断都不能成功获取的,显示为null。

      另一种方式是通过PhoneStateListener的onCallStateChanged来监听状态的变化:

    1. public class CallReceiver extends BroadcastReceiver { 

    2.         private Context m_context; 
    3.         @Override 
    4.         public void onReceive(Context context, Intent intent) { 
    5.                 m_context = context; 
    6.                 TelephonyManager teleManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 
    7.                 teleManager.listen(new PhoneStateListener(){ 

    8.                         @Override 
    9.                         public void onCallStateChanged(int state, String incomingNumber) { 
    10.                                 switch(state){ 
    11.                                 case TelephonyManager.CALL_STATE_RINGING: //响铃 
    12.                                         Toast.makeText(m_context, "Ringing: " + incomingNumber, Toast.LENGTH_LONG) 
    13.                                                                 .show(); 
    14.                                         break; 
    15.                                 case TelephonyManager.CALL_STATE_OFFHOOK: //接听 
    16.                                         Toast.makeText(m_context, "OffHook: " + incomingNumber, Toast.LENGTH_LONG) 
    17.                                         .show(); 
    18.                                         break; 
    19.                                 case TelephonyManager.CALL_STATE_IDLE: //挂断 
    20.                                         Toast.makeText(m_context, "Idle: " + incomingNumber, Toast.LENGTH_LONG) 
    21.                                         .show(); 
    22.                                         break; 
    23.                                 } 
    24.                         }}, PhoneStateListener.LISTEN_CALL_STATE);  
    25.         } 
    26. }
    复制代码
      运行时也发现incomingNumber在接听和挂断时获取为blank。

      因为这里监听的是通话的状态变化,所以这个receiver会被调用3次。

      监听通话状态需要加上权限:

    1. <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    复制代码
      ===========

      小结:

      1. 对于sendBroadCast的intent对象,需要设置其action name;

      2. 推荐使用显式指明receiver,在配置文件AndroidManifest.xml指明;

      3. 一个receiver可以接收多个action;

      4. 每次接收广播都会重新生成一个接收广播的对象,再次调用onReceive;

      5. 在BroadCast 中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity 或者 Service 去处理。



    --------------------------------------------
    四、Content Provider详解

      ContentProvider(内容提供者)是Android中的四大组件之一。主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对指定应用中的数据进行操作。ContentProvider分为系统的和自定义的,系统的也就是例如联系人,图片等数据。

      android中对数据操作包含有:

      file, sqlite3, Preferences, ContectResolver与ContentProvider前三种数据操作方式都只是针对本应用内数据,程序不能通过这三种方法去操作别的应用内的数据。

      android中提供ContectResolver与ContentProvider来操作别的应用程序的数据。

      使用方式:

      一个应用实现ContentProvider来提供内容给别的应用来操作,

      一个应用通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以。

      以下这段是Google Doc中对ContentProvider的大致概述:

      内容提供者将一些特定的应用程序数据供给其它应用程序使用。内容提供者继承于ContentProvider 基类,为其它应用程序取用和存储它管理的数据实现了一套标准方法。然而,应用程序并不直接调用这些方法,而是使用一个 ContentResolver 对象,调用它的方法作为替代。ContentResolver可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。

      1.ContentProvider

      Android提供了一些主要数据类型的ContentProvider,比如音频、视频、图片和私人通讯录等。可在android.provider包下面找到一些Android提供的ContentProvider。通过获得这些ContentProvider可以查询它们包含的数据,当然前提是已获得适当的读取权限。

      主要方法:

      public boolean onCreate() 在创建ContentProvider时调用
      public Cursor query(Uri, String[], String, String[], String) 用于查询指定Uri的ContentProvider,返回一个Cursor

      public Uri insert(Uri, ContentValues) 用于添加数据到指定Uri的ContentProvider中

      public int update(Uri, ContentValues, String, String[]) 用于更新指定Uri的ContentProvider中的数据

      public int delete(Uri, String, String[]) 用于从指定Uri的ContentProvider中删除数据

      public String getType(Uri) 用于返回指定的Uri中的数据的MIME类型

      *如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。

      例如:要得到所有person记录的Uri为content://contacts/person,那么返回的MIME类型字符串为"vnd.android.cursor.dir/person"。

      *如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头。

      例如:要得到id为10的person记录的Uri为content://contacts/person/10,那么返回的MIME类型字符串应为"vnd.android.cursor.item/person"。

      2.ContentResolver

      当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用Context提供的getContentResolver()方法。

      ContentResolver cr = getContentResolver();

      ContentResolver提供的方法和ContentProvider提供的方法对应的有以下几个方法。

      public Uri insert(Uri uri, ContentValues values) 用于添加数据到指定Uri的ContentProvider中。

      public int delete(Uri uri, String selection, String[] selectionArgs) 用于从指定Uri的ContentProvider中删除数据。

      public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) 用于更新指定Uri的ContentProvider中的数据。

      public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 用于查询指定Uri的ContentProvider。



      3.Uri

      Uri指定了将要操作的ContentProvider,其实可以把一个Uri看作是一个网址,我们把Uri分为三部分。

      第一部分是"content://"。可以看作是网址中的"http://"。

      第二部分是主机名或authority,用于唯一标识这个ContentProvider,外部应用需要根据这个标识来找到它。可以看作是网址中的主机名,比如"blog.csdn.net"。

      第三部分是路径名,用来表示将要操作的数据。可以看作网址中细分的内容路径。

     
    ---------------------------------
    展开全文
  • Android 四大组件,五大存储,六大布局

    万次阅读 多人点赞 2018-08-15 12:28:24
    android四大组件分别是:Activity, service,content provider,broadcast receiver Activity Service Content Provider Broadcast Receiver Android 五大存储 SharedPreferences 方式 ...

    Android 四大组件

    android四大组件分别是:Activity, service,content provider,broadcast receiver

    一、Activity

    1、概念:

    android 中,Activity 相当于一个页面,可以在Activity中添加Button、CheckBox 等控件,一个android 程序有多个Activity组成。

    2、生命周期:

    这里写图片描述

    3、四中启动模式

    Standard 模式 : standard 模式是android 的默认启动模式,在这种模式下,activity可以有多个实例,每次启动Activity,无论任务栈中是否已经存在这个activity的实例,系统都会创建一个新的activity实例。

    SingleTop 模式: 栈顶模式,当一个singleTop模式的activity 已经位于栈顶时,再去启动它时,不在创建实例,如果不在栈顶,就会创建实例。

    SingleTask 模式 : 单任务模式,如果启动的activity 已经存在于 任务栈中,则会将activity移动到栈顶,并将上面的activity出栈,否则创建新的实例

    SingleInstance 模式 :单实例模式,一个activity 一个栈。

    4、三种跳转方式

    显示启动
    Intrent 内部直接声明要启动的activity所对应的的class

    Intent intent = new Intent(MainActivity.this, SecondActivity.class);
    startActivity(intnet);
    

    隐式启动
    进行三个匹配,一个是activity,一个是category,一个是data,全部或者部分匹配,应用于广播原理

    • 清单文件中 里配置activity属性,activity的名字要和跳转内容一样
    <activity 
    	android:name="com.exanple.android.tst.secondActivity"
    	android:label = @string/title>
    	<intent=filter>
    		<action android:name="com.exanple.android.tst.secondActivity/>
    		<category android:name="android.intent.category.DEFAULT"/>
    	<intent-filter/>
    </activity>
    
    • 在需要跳转的地方
    Intent intent = new Intent("com.example.android.tst.secondActivity");
    startActivity(intnet);
    

    跳转后再返回,能获取返回值

    Intent in = new Intent(MainActivity.this,OtehrActivity.class);
    in.putExtra("a",a);
    startActivityForResult(in,1000);
    

    在OTherActivity中设置返回值

    Intent int = new Intent();
    int.putExtra("c",c);
    setResult(1001,int);
    finish();
    

    在MainActivity中获取返回值

    @Override
    protected void onActivityResult(int requestCode, int resultCode	,Intent data) {
    	super.onActivityResult(requestCode,resultCode,data);
    	if(requestCode == 1000){
    		if(resultCode == 1001){
    			int c = data.getExtra("c",0);
    		}
    	}
    }
    

    Service

    定义一个Server

    项目内Server包 右键 --> New --> Service --> Service 或者直接创建Class类,继承Service并重写IBinder方法

    public class MyService extends Service{
    	
    	public MyService(){
    		
    	}
    
    	@Override
    	public IBinder onBind(Intent intent) {
    		return null;
    	}
    	
    	@Override
    	public void onCreate() {
    		super.onCreate();
    	}
    	
    	@Override
    	public int onStartCommand(Intent intent, int flags, int startId) {
    		return super.onStartCommand(intent, flags, startId);
    	}
    	
    	@Override
    	public void onDestroy() {
    		// TODO Auto-generated method stub
    		super.onDestroy();
    	}
    }
    

    重写Service的 onCreate()、onStartCommand()和onDestory()方法。其中 onCreate() 方法在服务创建的时候调用、onStartCommand() 方法会在每次服务启动的时候调用、onDestory() 方法会在服务销毁的时候调用。
    通常情况下,如果我们希望服务一旦启动就立刻去执行任务,就可以将逻辑卸载onStartCommand() 方法里。
    另外需要注意的是,每个服务都需要在Androidmanifest.xml 中进行注册才能生效:

    <application
    	....>
    	...
    	<service
    		android:name=".MyService"
    		android:enabled="true"
    		android:exported="true">
    	</service>
    </application>
    
    启动和停止服务

    启动服务:

    Intent startIntent = new Intent(this, MyService.class);
    startService(startIntent); //启动服务
    

    停止服务:

    Intent stopIntent = new Intent(this, MyService.class);
    stopService(stopIntent); //停止服务
    
    使用前台服务

    前台服务与普通服务的最大区别在于,它会一直有一个正在运行的图标在系统的状态栏中,下拉状态栏后可以看到更加详细的内容,非常类似于通知的效果。

    public class MyService extends Service{
    	Intent intent = new Intent(this, MainActivity.class);
    	PendingIntent pi = PendingIntent.getActivity(this, 0 , intent, 0);
    	Notification notification  = new NotificationCompat.Builder(this)
    		.setContentTitle(" this is content titile")
    		.setContentText("this is content text")
    		.setWhen(System.currentTimeMillis())
    		.setSmallIcon(R.mipmap.ic_launcher);
    		.setLargeIcon(BitmapFactory.decodeResource(getResource(),
    			R.mipmap.ic_launcher))
    		.setContentIntent(pi)
    		.build();
    	startForeground(1,notification);
    }
    

    构造一个Notification对象后并没有使用NotificationManager 来讲通知显示出来,而是调用了startForeground()方法,该方法会将MyService变成一个前台服务,并在系统状态栏中显示出来。

    使用IntentService

    服务中的代码都默认运行在主线程中,如果直接在服务中执行耗时操作很容易出现ANR(Application not Responding)
    所以这个时候需要用到Android多线程编程技术,我们应该在服务的每个具体的方法里启动一个子线程,然后在这里去处理那些耗时的操作:

    public class MyService extends Service{
    	...
    	@Override
    	public int onStartCommand(Intent intent , int flags, int startId){
    		new Thread(new Runnable(){
    			public void run(){
    				//处理具体的逻辑
    			}
    		}).start();
    		return super.onStartCommand(intent, flags, startId);
    	}
    }
    

    但是,这种服务一旦启动之后,就会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来,所以,如果想要实现让一个服务在执行完毕后自动停止的功能,就可以这样写:

    public class MySerivce extends Servcie{
    	...
    	@Override
    	public int onStartCommand(Intent intent, int flats , int startId){
    		new Thread(new Runnable(){
    			public void run(){
    				//处理具体的逻辑
    				stopSelf();
    			}
    		});
    	}
    }
    

    虽说这样的写法并不复杂,但是总会有一些程序员忘记开启线程或者忘记调用stopSelf() 方法。为了简单创建一个异步、会自动停止的服务。Android专门提供了一个IntentService类

    public class MyIntentService extends IntentService{
    	public MyIntentService(){
    		super("MyIntentService");  //调用父类的有参构造方法
    	}
    	@Override
    	protected void onHandleIntent(Intent intent){	
    		//打印当前的线程ID
    		Log.e("mylog","Thread id is” + Thread.cuttentThread().getId();
    	}
    	@Override
    	public void onDestory(){
    		super.onDestory();
    		Log.e("mylog","on Destory executed");
    	}
    }
    

    首先这里提供一个无参的构造方法,并且必须在其内部调用父类的有参构造方法。然后要在子类中去实现onHandleIntent() 这个抽象方法,在这个方法中可以去处理一些逻辑,而且不用担心ANR,因为这个方法已经是在子线程中运行了。
    IntentService线程的调用:

    Intent intent = new Intent(this, MyIntentService.class);
    startServcie(intent);
    

    如此,线程就会自动启动并执行逻辑,执行完毕后自动关闭。这就是IntentService 的好处,能够自动开启和关闭;

    Content Provider

    对于每一个应用程序来说,如果想要访问内容提供器中共享的数据,就一定要借助ContentResolver 类,可以通过Context中的getContentResolver() 方法获取该类的实例。ContentResolver中提供了一系列的方法用于对数据进行CRUD操作,其中insert() 方法用于添加数据,update() 方法用于更新数据,delete() 方法用于删除数据,query() 方法用于查询数据。
    不同于SQLiteDatabase,ContentResolver 中的增删改查都是接收一个URl参数,这个参数被称为内容URL。内容URL给内容提供器中的数据建立了唯一标识符,它主要由两部分组成:authoritypathauthority 是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式进行命名。path则是用于对同一应用程序中不同的表做区分,通常都会添加到authority后面:

    content://com.example.app.provider/table1
    content://com.example.app.provider/table2
    

    在使用内容URL作为参数的时候,需要将URL转换成URL对象:

    Uri uri = Uri.parse("content://com.example.app.provider/table1")
    

    现在我们就可以使用这个uri对象来查询talbe1表中的数据了:

    Cursor cursor = getContentResolver().query(
    	uri,
    	projection,
    	selection,
    	selectionArgs,
    	sortOrder
    );
    

    对应参数的解释:

    query()方法参数 对应SQL部分 描述
    uri from table_name 指定查询某个应用程序下的某个表
    projection select column1, column2 指定查询的列名
    selection where column=value 指定where约束条件
    selectArgs - 为where中的占位符提供具体的值
    orderBy order by column1, column2 指定查询结果的排序方式

    查询完之后,就可以从游标中取值了:

    if(cursor != null){
    	while(cursor.moveToNext()) {
    		String column1 = cursor.getString(cursor.getColumnIndex("column1"));
    		int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
    	}
    	cursor.close();
    }
    
    增删改查

    添加数据

    ContentValues values = new ContentValues();
    values.put(“column1”, "text");
    values.put("column2", 1);
    getContentResolver().insert(uri, values);
    

    更新数据

    ContentValues valuse = new ContentValues();
    valuse.put("column1", "");
    getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[]{"text", 1});
    

    删除数据

    getContentResolver().delete(uri , "column2 = ?", new String[]{ "1"});
    
    实例.

    读取系统联系人
    读取系统联系人需要声明权限,如果系统是6.0以后的,需要申请运行时权限

    if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) 
    	!= PackageManager.PERMISSION_GRANTED) {
    		ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, 1);
    	}else {
    		readContacts();  //读取联系人
    	}
    private void readContacts(){
    	Cursor cursor = null;
    	try{
    		//查询联系人数据
    		cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
    		if(cursor!=null){
    			while(cursor.moveToNext()){
    				//获取联系人姓名
    				String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
    				//获取联系人电话号码
    				String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
    				list.add(name+"\n"+number);
    			}
    		}
    	}catch(Exception e){
    		e.printStackTrace()
    	}finally{
    		if(cursor != null){
    			cursor.close();
    		}
    	}
    }
    
    @Override
    public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults){
    	switch(requestCode){
    		case 1:
    			if(grantResults.length >0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
    				readContacts();
    			}else {
    				//您拒绝了权限
    			}
    	}
    }
    
    创建自己的内容提供器

    创建自己的内容提供器,需要去继承 ContentProvider 类,ContentProvider 类中有6个抽象方法,我们在使用子类继承它的时候,需要将这6个方法全部重写。

    public class MyProvider extends ContentProvider{
    	@Override
    	public boolean onCreate() {
    		return false;
    	}
    	@Override
    	public Cursor query(Uri uri, String[] projection, Stirng selection, String[] selectionArgs, String sortOrder){
    		return null;
    	}
    	@Overrride
    	public Uri insert(Uri uri , ContentValues values){
    		return null;
    	}
    	@Override
    	public int update(Uri uri, ContentValuse values, String selection, String[] selectionArgs){
    		return 0;
    	}
    	@Override
    	public int delete(Uri uri, String selection, String[] selectionArgs){
    		return 0;
    	}
    	@Override
    	public String getType(Uri uri){
    		return null;
    	}
    }
    

    URI 的主要格式有以下两种

    content://com.example.app.provider/table1
    content://com.example.app.provider/table1/1
    
    * : 表示匹配任意长度的任意字符
    # : 表示匹配任意长度的数字
    
    //一个能够匹配任意表的内容URI格式就可以写成:
    content://com.example.app.provider/*
    //一个能够匹配表中任意一行数据的内容URI格式就可以写成:
    content://com.example.app.provider/table1/#
    

    Broadcast Receiver

    android 广播分为两个角色:广播发送者、广播接收者
    android 广播:
    1),用于不同组件间的通信(含:应用内/不同应用之间)
    2),用于多线程通信
    3),与android系统的通信

    自定义广播接收者
    • 继承BroadcastReceive 基类
    • 必须重写抽象方法onReceive()方法
    1,广播接收器收到相应广播后,会自动调用onReceive() 方法
    2,一般情况下,onReceive方法会会涉及与其他组件之间的交互,如 发送Notiotification,启动server等
    3,默认情况下,广播接收器运行在UI线程,因此,onReceive方法不能执行耗时操作,否则将导致ANR
    
    广播接收器注册

    注册的方式有两种:静态注册、动态注册
    静态注册

    • 注册方式:在AndroidManifest.xml 里通过<receive 标签声明
    • 属性说明
    <receiver
    	android:enable="true"/"false"
    	//此broadcastReceiver 是否接受其他应用发出的广播
    	//默认值时由receiver 中d有无inter-filter决定,如果有,默认true,否则默认false
    	android:exported="true"/"false"
    	android:icon="drawable resource"
    	android:label="string resource"
    	//继承BroadcastReceiver子类的类名
        android:name=".mBroadcastReceiver"
    //具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收;
        android:permission="string"
    //BroadcastReceiver运行所处的进程
    //默认为app的进程,可以指定独立的进程
    //注:Android四大基本组件都可以通过此属性指定自己的独立进程
        android:process="string" >
    
    //用于指定此广播接收器将接收的广播类型
    //本示例中给出的是用于接收网络状态改变时发出的广播
     <intent-filter>
    	<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
     </intent-filter>
     </receiver>
    

    注册示例:

    <receiver 
        //此广播接收者类是mBroadcastReceiver
        android:name=".mBroadcastReceiver" >
        //用于接收网络状态改变时发出的广播
        <intent-filter>
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
    </receiver>
    

    当此APP首次启动时,系统会自动实例化mBroadcastReceiver类,并注册到系统中。

    动态注册

    • 注册方式:在代码中调用Context.registerReceiver() 方法
    • 具体代码如下:
    	// 1. 实例化BroadcastReceiver子类 &  IntentFilter
         mBroadcastReceiver mBroadcastReceiver = new mBroadcastReceiver();
         IntentFilter intentFilter = new IntentFilter();
    
        // 2. 设置接收广播的类型
        intentFilter.addAction(android.net.conn.CONNECTIVITY_CHANGE);
    
        // 3. 动态注册:调用Context的registerReceiver()方法
         registerReceiver(mBroadcastReceiver, intentFilter);
    
    
    //动态注册广播后,需要在相应位置记得销毁广播
    unregisterReceiver(mBroadcastReceiver);
    

    特别注意
    动态广播最好在onResume中注册, onPause注销
    原因:
    1,对于动态广播,有注册必然得有注销,否则会导致内存泄漏
    2,onPause在App死亡前一定会被执行,从而保证app死亡前一定会被注销,从而防止内存泄漏

    两种注册方式的区别

    在这里插入图片描述

    广播的发送

    广播的发送 = 广播发送者 将此广播的意图(intent)通过 sendBroasdcast() 方法发送出去
    广播的类型

    • 普通广播 系统广播 有序广播 粘性广播 App 应用内广播
    特别注意:

    对于不同注册方式的广播接收器回调OnReceive(Context context,Intent intent)中的context返回值是不一样的:

    • 对于静态注册(全局+应用内广播),回调onReceive(context,
      intent)中的context返回值是:ReceiverRestrictedContext;
    • 对于全局广播的动态注册,回调onReceive(context, intent)中的context返回值是:Activity
      Context;
    • 对于应用内广播的动态注册(LocalBroadcastManager方式),回调onReceive(context,
      intent)中的context返回值是:Application Context。
    • 对于应用内广播的动态注册(非LocalBroadcastManager方式),回调onReceive(context,
      intent)中的context返回值是:Activity Context;

    Android 五大存储

    SharedPreferences 方式

    SharedPreferences 是使用键值对的方式进行存储数据的。
    想要使用SharedPreferences 来存储数据,首先主要获取到SharedPreferences 对象。Android提供了三种方法用于获取SharedPreferences对象:
    1,Context类中的getSharedPreferences()方法

    //此方法接收两个参数,一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data/<package name>/shared_prefs/目录下
    //第二个参数用于指定操作模式,目前只有MODE_PRIVATE这种模式,和直接传入0效果相同
    SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
    editor.putString("name", "Tom");
    editor.putInt("age",13);
    editor.putBoolean("married",false);
    editor.apply();
    

    2,Activity类中的getPreferences()方法

    //这个方法和Context中的getSharedPreferences()方法很类似,不过它只接收一个操作模式,因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名
    

    3,PreferencesManager类中的getDefaultSharedPreferences()方法

    //这是一个静态方法,它接收一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件
    

    得到了SharedPreferences对象后, 就可以开始想SharedPreferences文件中存储数据了,主要可以分为三步:
    (1)调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象
    (2)向SharedPreferences.Editor 对象中添加数据,比如添加一个布尔值,可以使用putBoolean() 方法
    (3)调用apply()方法的添加的数据提交,从而完成数据存储操作

    SharedPreferences中读取数据
    SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE	);
    String name = pref.getString("name","");
    int age = pref.getInt("age",0);
    boolean married = pref.getBoolean("married", false);
    

    文件存储方式

    SQList 存储方式

    Android 为了让我们能够更加方便的管理数据库,专门提供了一个SQLiteOpenHelper 帮助类,借助这个类可以非常简单的将数据库进行创建好升级。
    SQLiteOpenHelper 中有两个非常重要的实例方法,getReadableDatabase()getWritableDatabase() 。这两个方法可以创建或者打开一个现有的数据库(如果数据库存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入(如磁盘空间已满),getReadableDatabase方法返回的对象将以只读的方式打开数据库,而getWeitableDatabase则出现异常

    例子(在指定路径下创建数据库文件 .db )

    public class MainActivity extends Activity {
    	public static final String PATH_ONE = "KogBill";
    	public static final String PATH_NAME = "KogBill.db";
    	private SQLiteDatabase db;    //声明SQLiteDatabase ,该对象可以操作数据库
    
    	String path = Environment.getExternalStorageDirectory().getAbsolutePath();
    	String path1 = path + File.separator + PATH_ONE;   //需要创建的路径
    	String path2 = path + File.separator + PATH_ONE + 
    			File.separator + PATH_NAME;  							//需要创建的文件
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState){
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		File f = new File(path1);
    		if( !f.exists()){  //创建数据库文件路径
    			f.mkdirs();
    		}	
    		//实例化MySQLiteHelper ,创建指定目录下数据库文件,并创建表
    		MySQLiteHelper mSQL = new MySQLiteHelper(MainActivity.this, path2);
    		db = mSQL.getWritableDatabase();
    	}
    
    	class MySQLiteHelper extends SQLiteOpenHelper{
    			private static final int DATABASE_VERSION = 1;//数据库版本号
    			private static final String CREATE_TABLE = "create table kog_bill ("
    		            + "_id integer primary key autoincrement,"
    		            + "date text, "
    		            + "breakfast text, "
    		            + "lunch text,"
    		            + "dinner text,"
    		            + "happy text,"
    		            + "other text,"
    		            + "spare text)";
    			
    			//方便创建实例,简化构造方法,方法内调用4参数构造方法
    			//参数 name 可以是 数据库名称,也可以数据库文件路径(即可以指定数据库文件路径)
    			public MySQLiteHelper(Context context, String name) {
    				this(context, name, null, DATABASE_VERSION);
    			}
    			//必须要实现的方法
    			public MySQLiteHelper(Context context, String name, CursorFactory factory, int version) {
    				super(context, name, factory, version);
    			}
    	
    			@Override
    			public void onCreate(SQLiteDatabase db) {
    				// 第一次创建数据库时 才会调用
    				Log.e("mylog", "创建数据库表");
    				db.execSQL(CREATE_TABLE);
    			}
    	
    			@Override
    			public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    			}
    			
    		}
    }
    

    根据上述代码,便获得db对象,通过db(SQLiteDatabase)可进行数据库的操作,如 db.query() db.delete()

    如果我们想在创建一个数据库表,参照上述代码,可以在SQLiteOpenHelper的onCreate方法中加入语句:

    @Override
    public void onCreate(SQLiteDatebase db) {
    	db.execSQL(CREATE_TABLE);
    	db.execSQL(CREATE_BOOK);  //新创建一个数据库表
    }
    

    然后重新运行一下,发现并没有创建成功,因为KogBill.db数据库已经存在,所以MySQLiteHelper 中的onCreate方法都不会执行,解决这个办法的方法很简单,只需要将db文件删除,重新运行,便可成功,但是原来数据库中的数据都会被删除。所以需要用到MySQLiteHelper中的update方法。

    class MySQLiteHelper extends SQLiteOpenHelper{
    	.....
    	@Override
    	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
    		db.execSQL("drop table if exists book");  //如果已经存在就删除,防止重复创建
    		onCreate(db);  // 再次执行onCreate 方法
    	}
    }
    

    但是onUpgrade方法默认是不执行的,如何让onUpgrade方法执行,需要用到MySQLiteHelper 构造参数中的版本号:

    private static final int DATABASE_VERSION = 1;//  将版本号 由 1 改为2 
    

    这里将数据库版本号由1改为2,表示对数据库的升级

    数据库的增删改查

    添加数据

    ContentValues values = new ContentValues();
    	values.put("date", str1.isEmpty()?"0.0":str1);
    	values.put("breakfast", ""+str2);
    	values.put("lunch", ""+str3);
    	values.put("dinner", ""+str4);
    	values.put("happy", ""+str5);
    	values.put("other", ""+str6);
    	values.put("spare", ""+str7);
    	long ii = db.insert("kog_bill", "", values);
    	values.clear();
    	if(ii != -1) {
    		Utils.showToast("保存成功!", MainActivity.this);
    	}else {
    		Utils.showToast("保存失败!", MainActivity.this);
    	}
    

    更新数据

    ContentValues valus = new ContentValues();
    valuse.put("other","12");
    db.update("kogBill", values, "_id=?",new String[]{id});
    

    删除数据

    db.delete("kogBill", "_id=?",new String[]{id});
    

    查询数据

    db.query("kog_bill", new String[]{"_id","date","breakfast","lunch","dinner","happy","other","spare"}
    		, null, null, null, null, "date desc");
    
    使用SQL操作数据库

    虽然Android 已经给我们提供了非常方便的API用于操作数据库,不过总会有些人不习惯去使用这些辅助行的方法,而是更加青睐于直接使用SQL来操作数据库,当然Android也是提供的。
    添加数据

    db.execSQL("insert into kogBill ("date","breakfest","lunch","dinner","happy","other","spare") values (?,?,?,?,?,?,?)", new String[]{"1921-1-1",“123”,“1”,“1”,“11”,“2”,“3”});
    

    更新数据

    db.execSQL("update kogBill set other = ? where _id = ? ", new String[]{"12",id});
    

    删除数据

    db.execSQL("delete from kogBill where _id = ?”, new String[]{id});
    
    使用 LitePal 操作数据库

    假设编译环境为AndroidStudio。
    1,引进包

    dependencies{
    	compile fileTree(dir:'libs', include:['*.jar'])
    	compile 'com.android.support:appcompat-v7:23.2.0'
    	testCompile 'junt:junt:4.12'
    	compile 'org.litepal.android:core:1.3.2'   //引入litepal包
    }
    

    2,配置litepal.xml 文件
    右键app/src/main 目录->New -> Directory ,创建一个assets目录,然后在 assets目录下再新建一个litepal.xml 文件,接着编辑文件中的内容

    <?xml version='1.0' encoding="utf-8"?>
    <litepal>
    	<dbname value = "BookStore"></dbname>
    	<version value="1"></version>
    	<list></list>
    </listepal>
    

    其中,<dbname 标签用来指定数据库名,<version 用来指定数据库版本号,<list 标签用来指定所有映射模型。
    最后还需要在配置以下 LitePalApplication, 修改AndroidManifest.xml 中的代码

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    	package="com.example.litepaltest" >
    	<application
    		android:name="org.litepal.LitePalApplication"  //配置 LitePalApplication
    		android:allowBackup="true"
    		.....
    	</application>
    </manifest>
    

    以上,LitePal的配置工作已经结束了,接下来使用LitePal。
    首先将需要实现 javabean类 对应 数据库表.
    然后将javabean类添加到映射模型列表中,修改litepal.xml 中的代码

    <litepal>
    	<dbname value="kogBill" ></dbname>
    	<version value="1"></version>
    	<list>
    		<mapping class="com.example.litepaltest.book"></mapping>  //javabean类的路径
    	</list>
    

    这样所有工作就已经完成,现在只要进行任意一次数据库的操作,数据库db文件就会自动创建,比如:

    Connector.getDatabase();
    
    操作数据

    如果需要对某个表进行数据操作,需要让其对应的javaBean类继承DataSupport

    public class Book extends DataSupport { //让对应的类继承DataSupport
    	...
    }
    

    接下来,进行添加数据的操作:

    Book book = new Book();
    book.setName("...");
    book.setAuthor("...");
    book.setPages(234);
    book.setPrice(12,21);
    book.setPress("unkow");
    book.save();  //执行sava 就可以插入数据了
    

    执行更新数据:

    Book book = new Book();
    book.setPrice(11.11);
    book.setPress("Anchor");
    book.updateAll("name = ?  and authro = ?","..","...");
    

    删除数据:

    DataSupport.deleteAll(Book.class, "price<?","13");
    

    查询数据:

    //查询所有
    List<Book> books = DataSupport.findAll(Book.class);  
    // 查询第一条
    List<Book> books = DataSupport.findFirst(Book.class);
    //查询最后一条
    List<Book> books = DataSupport.findLast(Book.class);
    //查询那几列的数据
    List<Book> books = DataSupport.select("name","author).find(Book.class);
    //条件查询, 页面大于400
    List<Book> books = DataSupport.where("pages >?","400").find(Book.class);
    //将 price 降序排序
    List<Book> books = DataSupport.order(price desc").find(Book.class);
    //查询前3条
    List<Book> books = DataSupport.limit(3).find(Book.class);
    //从下表1开始,往后查询3条
    List<Book> boods = DataSupport.limit(3).offset(1),find(Book.class)
    

    当然这些方法也可以组合起来使用:

    
    List<Book> books = DataSupport.select("name","author","pages")
    															.where("pages>?”,"400")
    															.order("pages")
    															.limit(10)
    															.offset(10)
    															.find(Book.class);
    

    如果有些特殊查询,使用上述方法无法查询时,可以使用如下语句:

    Cursor c  = DataSupport.findBySQL("select * from Book where pages > ? and price < ?”, 
    		"400","20”);
    

    内容提供器(Conent Provider)方式

    网络存储方式

    Android 六大布局

    LinearLayout 线性布局

    线性布局,如名字所描述的那样,这个布局将它所包含的控件在线性方向上一次排列,方向分为 水平方向和数值方向。
    属性 android:orientation = “vertical” | “horizontal” 竖直或水平,默认水平
    属性 android:layout_gravity = “top” | “center” | “bottom” 内部的布局方式
    属性 android:gravity = “top”|"center”|“bottom” 相对于父容器的对齐方式
    属性 android:layout_weidht 使用比例方式执行控件的大小,在手机屏幕适配方面起到非常重要的作用

    TableLayout 表格布局

    表格布局与HTML中的table td tr标签类似

    <table>
    	<tr><td></td></tr>
    </table>
    
    

    如何确定行与列

    • 如果在TableLayout下添加组件,这个组件会占满整行
    • 如果想把多个组件放在同一行,需要添加TableRow的容器,然后把组件放进去
    • TableRow中的组件个数决定的该行的列数,而列的宽度由列中最宽的单元格决定
    • TableRow嗯layout_width属性默认是fill-parent,修改无效。但是layout_height默认是wrapcontent,可以修改
    • 整个表格的宽度取决于父容器的宽度(占满父容器)

    重要的属性:

    • android:collapaseColumns:设置需要被隐藏的列的序号
    • android:shrinkColumns:设置允许被收缩的列的序号
    • android:stretchCoumns:设置运行被拉伸嗯列的序号
      这三个属性都是从0开始算的
    shrinkColumns= "2" //对应第三行
    shrinkColumns = '"0,2" //设置多个都生效
    shrinkColumns = "" //所有列都生效
    
    • android:layout_column=“2”: 表示跳过第二个,直接显示第三个,从1开始
    • android:layout_span=“4”:表示合并*4个单元格,也就说这个组件占4个单元格

    FrameLayout 帧布局

    FrameLayout布局在不使用layout_gravity属性的情况下,布局中的多项元素会在父容器的左上角重叠,使用layout_gravity 属性,可以设置不同的位置。

    在这里插入图片描述

    重要属性

    • top、bottom、left、right:将对象放在其容器的上、下、左、右的位置
    • center、center_vertical、center_horizontal:讲对象居中、水平居中、竖直居中
      在这里插入图片描述
      注意 :区分 “android:gravity”“android:layout_gravity”
      • android:gravity :是对控件本身而言,控制控件自身的内容在控件中的位置
      • android:layout_gravity:是相对于控件父容器而言,设置该控件在其父容器中的位置

    RelativeLayout 相对布局

    相对布局是用的比较多的一种布局。因为布局套用越多,布局加载越慢,如果使用相对布局,紧紧只需要一次布局。一般使用布局都是相对布局+线性布局使用。
    相对布局主要记住重要的属性

    此图来自:https://www.runoob.com/w3cnote/android-tutorial-relativelayout.html
    此图来自:https://www.runoob.com/w3cnote/android-tutorial-relativelayout.html

    注意marginpadding 的区别
    * margin:代表的是偏移,是相对于父容器而言
    * padding:代表的是填充,是本组件内部而言

    GridLayout 网格布局

    网格布局与TableLayout(表格布局)类似,不过网格布局功能更多,也更好用。

    • 可以设置布局中组件的排列方式
    • 可以设置网格布局有几行几列
    • 可以直接设置组件的位置,位于某行某列
    • 可以直接设置组件占多少行多少列

    使用网格布局,需要先设置排列方式、对齐方式、行数、列数。然后对布局中的子组件进行行列设置。
    在这里插入图片描述
    图片来自:https://blog.csdn.net/wangmx1993328/article/details/82770910

    下例是计算器布局:

    在这里插入图片描述

    <GridLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:columnCount="4"
            android:orientation="horizontal"
            android:rowCount="6">
            <TextView
                android:layout_columnSpan="4"
                android:layout_marginLeft="5dp"
                android:layout_marginRight="5dp"
                android:background="#ffcccccc"
                android:text="0"
                android:textSize="50sp"
                android:layout_gravity="fill" />
            <Button
                android:layout_columnSpan="1"
                android:text="1"
                android:layout_gravity="fill" />
            <Button
                android:text="2"
                android:layout_gravity="fill" />
            <Button
                android:text="3"
                android:layout_gravity="fill" />
            <Button
                android:text="+"
                android:layout_gravity="fill" />
    
            <Button
                android:layout_columnSpan="1"
                android:text="4"
                android:layout_gravity="fill" />
            <Button
                android:text="5"
                android:layout_gravity="fill" />
            <Button
                android:text="6"
                android:layout_gravity="fill" />
            <Button
                android:text="-"
                android:layout_gravity="fill" />
            <Button
                android:layout_columnSpan="1"
                android:text="7"
                android:layout_gravity="fill" />
            <Button
                android:text="8"
                android:layout_gravity="fill" />
            <Button
                android:text="9"
                android:layout_gravity="fill" />
            <Button
                android:text="*"
                android:layout_gravity="fill" />
            <Button
                android:layout_columnSpan="1"
                android:text=""
                android:layout_gravity="fill" />
            <Button
                android:text="0"
                android:layout_gravity="fill" />
            <Button
                android:text="="
                android:layout_gravity="fill" />
            <Button
                android:text="/"
                android:layout_gravity="fill" />
        </GridLayout>
    

    AbsoluteLayout 绝对布局

    绝对布局放在最后,是因为绝对布局基本上是不使用的。一般布局需要适配不同机型,如果使用绝对布局,在不同尺寸的手机上显示会变形。

    重要属性

    • android:layout_x:设置组件的x坐标
    • android:layout_y:设置组件的y坐标

    在这里插入图片描述

    <AbsoluteLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <Button
                android:id="@+id/button1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_x="50dp"
                android:layout_y="100dp"
                android:text="=" />
    
            <Button
                android:id="@+id/button2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_x="150dp"
                android:layout_y="100dp"
                android:text="=" />
    
            <Button
                android:id="@+id/button3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_x="100dp"
                android:layout_y="120dp"
                android:text="." />
    
        </AbsoluteLayout>
    
    展开全文
  • 安卓 四大组件

    万次阅读 2018-06-29 11:16:46
    Android四大组件分别为activity、service、content provider、broadcast receive 一、Activity Activity生命周期的方法是成对出现的 onCreate() &amp; onDestory() onStart() &amp; onStop() ...

    Android开发的四大组件
    Android四大组件分别为activity、service、content provider、broadcast receive
    一、Activity
    这里写图片描述
    Activity生命周期的方法是成对出现的

    onCreate() & onDestory()
    onStart() & onStop()
    onResume() & onPause()

    每一个活动( Activity)都处于某一个状态,对于开发者来说,是无法控制其应用程序处于某一个状态的,这些均由系统来完成。 但是当一个活动的状态发生改变的时候,开发者可以通过调用onXX() 的方法获取到相关的通知信息。 在实现 Activity 类的时候,通过覆盖( override )这些方法即可在你需要处理的时候来调用。
    一,onCreate:当活动第一次启动的时候,触发该方法,可以在此时完成活动的初始化工作。onCreate方法有一个参数,该参数可以为空(null),也可以是之前调用onSaveInstanceState方法保存的状态信息。 二,在onStart:方法该触发的所属表示将活动被展现给用户四,在onPause:当一个正在前台运行的活动因为其他的活动需要前台运行而转入后台运行的时候,触发该方法。这时候需要将活动的状态持久化,比如正在编辑的数据库记录等。 五,onStop:当一个活动不再需要展示给用户的时候,触发该方法。如果内存紧张,系统会直接结束这个活动,而不会触发onStop方法。所以保存状态信息是应该在onPause时做,而不是onStop时做。活动如果没有在前台运行,都将被停止或者Linux管理进程为了给新的活动预留足够的存储空间而随时结束这些活动。因此对于开 者来说,在设计应用程序的时候,必须时刻牢记这一原则。在一些情况下,在onPause方法或许是活动触发的最后的方法,因此开发者需要在这个时候保存需要保存的信息。六,onRestart :当处于停止状态的活动需要再次展现给用户的时候,触发该方法。七,onDestroy:当活动销毁的时候,触发该方法。和onStop方法一样,如果内存紧张,系统会直接结束这个活动而不会触发该方法·。 的onSaveInstanceState:系统调用该方法,允许活动保存之前的状态,比如说在一串字符串中的光标所处的位置等活动栈

    每个Activity的状态是由它在Activity栈(是一个后进先出LIFO,包含所有正在运行Activity的队列)中的位置决定的。
    当一个新的Activity启动时,当前的活动的Activity将会移到Activity栈的顶部。
    这里写图片描述
    一个应用程序的优先级是受最高优先级的Activity影响的。当决定某个应用程序是否要终结去释放资源,Android内存管理使用栈来决定基于Activity的应用程序的优先级。
    Activity状态
    Activity有以下四种状态:
    活动的:当一个Activity在栈顶,它是可视的、有焦点、可接受用户输入的。Android试图尽最大可能保持它活动状态,杀死其它Activity来确保当前活动Activity有足够的资源可使用。当另外一个Activity被激活,这个将会被暂停。
    暂停:在很多情况下,你的Activity可视但是它没有焦点,换句话说它被暂停了。有可能原因是一个透明或者非全屏的Activity被激活。
    当被暂停,一个活动仍会当成活动状态,只不过是不可以接受用户输入。在极特殊的情况下,机器人将会杀死一个暂停的活动来为活动的活动提供充足的资源。当一个活动变为完全隐藏,它将会变成停止。停止:。当一个活动不是可视的,它“停止”了这个活动将仍然在内存中保存它所有的状态和会员信息尽管如此,当其它地方需要内存时,它将是最有可能被释放资源的。当一个活动停止后,一个很重要的步骤是要保存数据和当前UI状态。一旦一个活动退出或关闭了,它将变为待用状态。待用:。在一个活动被杀死后和被装在前,它是待用状态的待用胡亚蓉被移除活动栈,并且需要在显示和可用之前重新启动它的活性的四种加载模式在机器人的多活性开发中,活动之间的跳转可能需要有多种方式,有时是普通的生成一个新实例 有时希望跳转到原来某个活动实例,而不是生成大量的重复的活性。加载模式便是决定以哪种方式启动一个跳转到原来某个活动实例。在机器人里,有4种活性的启动模式,分别为:·standard:标准模式,一调用startActivity()方法就会产生一个新的实例。·singleTop:如果已经有一个实例位于Activity stack的顶部时,就不产生新的实例,而只是调用Activity中的newInstance()方法。如果不位于栈顶,会产生一个新的实例。·singleTask:会在一个新的任务中产生这个实例,以后每次调用都会使用这个,不会去产生新的实例了。

    ·singleInstance:这个跟singleTask基本上是一样,只有一个区别:在这个模式下的Activity实例所处的task中,只能有这个activity实例,不能有其他的实例。
    这些启动模式可以在功能清单文件AndroidManifest.xml中进行设置,中的launchMode属性。
    相关的代码中也有一些标志可以使用,比如我们想只启用一个实例,则可以使用Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
    标志,这个标志表示:如果这个activity已经启动了,就不产生新的activity,而只是把这个activity实例加到栈顶来就可以了。1、Intent intent = new Intent(ReorderFour.this, ReorderTwo.class);
    2、intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    3、startActivity(intent);
    复制代码
    Activity的加载模式受启动Activity的Intent对象中设置的Flag和manifest文件中Activity的元素的特性值交互控制。

    FLAG_ACTIVITY_NEW_TASK
    FLAG_ACTIVITY_CLEAR_TOP
    FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
    FLAG_ACTIVITY_SINGLE_TOP

    核心的特性有:

    taskAffinity
    launchMode
    clearTaskOnLaunch
    alwaysRetainTaskState
    finishOnTaskLaunch

    四种加载模式的区别 所属task的区别
    一般情况下,“standard”和”singleTop”的activity的目标task,和收到的Intent的发送者在同一个task内,就相当于谁调用它,它就跟谁在同一个Task中。
    除非Intent包括参数FLAG_ACTIVITY_NEW_任务。如果提供了FLAG 活动新_ TASK参数,会启动到别的任务里。“singleTask”和” singleInstance”总是把要启动的活动作为一个任务的根元素,他们不会被启动到一个其他任务里。是否允许多个实例“standard”和“singleTop”可以被实例化多次,并且是可以存在于不同的任务中;这种实例化时一个任务可以包括一个activity的多个实例; “ singleTask“和”singleInstance“则限制只生成一个实例,并且是task的根元素。singleTop要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不创建新的实例。是否允许其它活动存在于本任务内“singleInstance”独占一个任务,其它活动不能存在那个任务里; 如果它启动了一个新的活动,不管新的活动的启动模式如何,新的活动都将会到别的任务里运行(如同加了FLAG 活动 NEW_TASK参数)。而另 三种模式,则可以和其它活动共存。是否每次都生成新实例

    “standard”对于每一个启动Intent都会生成一个activity的新实例;
    “singleTop”的activity如果在task的栈顶的话,则不生成新的该activity的实例,直接使用栈顶的实例,否则,生成该activity的实例。
    “singleInstance”是其所在栈的唯一activity,它会每次都被重用。
    “singleTask” 如果在栈顶,则接受intent,否则,该intent会被丢弃,但是该task仍会回到前台。
    当已经存在的activity实例处理新的intent时候,会调用onNewIntent()方法,如果收到intent生成一个activity实例,那么用户可以通过back键回到上一个状态;如果是已经存在的一个activity来处理这个intent的话,用户不能通过按back键返回到这之前的状态。
    ———————————–
    二、Service
    service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务嘛,总是藏在后头的。
    Service是在一段不定的时间运行在后台,不和用户交互应用组件。每个Service必须在manifest中
    通过来声明。可以通过contect.startservice和contect.bindserverice来启动。
    Service和其他的应用组件一样,运行在进程的主线程中。这就是说如果service需要很多耗时或者阻塞的操作,需要在其子线程中实现。

    启动service,根据onStartCommand的返回值不同,有两个附加的模式:
    1、START_STICKY 用于显示启动和停止service。
    2、START_NOT_STICKY或START_REDELIVER_INTENT用于有命令需要处理时才运行的模式。
    服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。
    1、 使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。
    如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。
    如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。
    采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
    2、使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
    onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用的onDestroy()方法。拥有服务的进程具有较高的优先级1.如果service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以避免被杀死。2.如果当前服务已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着服务一般不会被杀死。3。如果客户端已经连接到service(bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。4.如果service可以使用startForeground(int,Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时被杀。如果有其他的应用组件作为服务,活动等运行在相同的进程中,那么将会增加该进程的重要性。本地服务1.不需和活动交互的本地服务2.本地服务和活动交互\ ———————–

    三、Broadcast Receiver
    是一个全局的监听器,属于Android四大组件之一
    作用是监听 / 接收 应用 App 发出的广播消息,并 做出响应
    应用场景为

    1、Android不同组件间的通信
    2、多线程通信
    3、与Android系统在特定情况下的通信


    四、Content Provider详解

    ContentProvider(内容提供者)是Android中的四大组件之一。主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对指定应用中的数据进行操作。ContentProvider分为系统的和自定义的,系统的也就是例如联系人,图片等数据。
    android中对数据操作包含有:
    file, sqlite3, Preferences,ContectResolver与ContentProvider前三种数据操作方式都只是针对本应用内数据,程序不能通过这三种方法去操作别的应用内的数据。
    android中提供ContectResolver与ContentProvider来操作别的应用程序的数据。
    使用方式:
    一个应用实现ContentProvider来提供内容给别的应用来操作,
    一个应用通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以。
    1.ContentProvider
    Android提供了一些主要数据类型的ContentProvider,比如音频、视频、图片和私人通讯录等。可在android.provider包下面找到一些Android提供的ContentProvider。通过获得这些ContentProvider可以查询它们包含的数据,当然前提是已获得适当的读取权限。

    2.ContentResolver
    当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用Context提供的getContentResolver()方法。
    3.Uri
    Uri指定了将要操作的ContentProvider,其实可以把一个Uri看作是一个网址,我们把Uri分为三部分。
    第一部分是”content://”。可以看作是网址中的”http://”。
    第二部分是主机名或authority,用于唯一标识这个ContentProvider,外部应用需要根据这个标识来找到它。可以看作是网址中的主机名,比如”blog.csdn.net”。
    第三部分是路径名,用来表示将要操作的数据。可以看作网址中细分的内容路径。

    作者:陈进杨

    原文链接:点击这里

    展开全文
  • Android四大组件的介绍

    千次阅读 2016-05-09 19:20:57
    Android 四大组件详细介绍

    一.Android四大组件详解

    Android四大组件分别是:activity,service,content provider,broadcast receiver.

    1.activity:

    1).一个应用程序一般由多个activity组成.

    2).任务栈(task stack)(别名(back stack)后退栈),记录存放用户开启的activity.

    3).一个应用程序被开启之后,系统就会分配一个任务栈,当所有activity都退出时,任务栈清空.

    4).任务栈的idinteger型的,是自增长的,它从1开始自增长,自增长值为1.

    5).activity之间通过intent进行通信.

    6).app中每一个activity都必须在AndroidManifest.xml文件中进行声明,否则系统将不识别不执行该activity.

    7).activity的启动模式,standard(标准启动模式),single Top(单一顶部),singletask(单一任务栈),singleInstance(单态,单例模式).

    (1).standard,默认的标准启动模式:每次startActivity都是创建一个新的activity,适用于绝大多数情况.

    (2).single Top,单一顶部,如果要开启的activity在任务栈的顶部已经存在,就不会创建新的实例,而是调用onNewIntent()方法.

    应用场景:浏览器书签.避免栈顶的activity被重复创建.

    (3).single task,单一任务栈,activity只会在任务栈里面存在一个实例,如果要激活的activity在任务栈中已经存在,调用onNewIntent()方法,并且清空当前任务里面的所有activity,

    应用场景:浏览器的activity,整个任务栈只有一个activity.

    (4).singlelnstance,单态单例模式,整个手机系统中只有一个任务实例,不同的应用去打开一个activity,共享这个activity.

    2.Service:

    1).service用于用户在后台完成独立的操作,service分为两种,started(启动),bound(绑定):

    a) Started(启动):当应用程序组件调用startService()方法时服务停止,服务处于started状态.

    b) Bound(绑定): 当用用程序组件调用boundService()方法绑定服务时,服务处于绑定状态.

    2).started Service(启动服务):是由其他组件调用startService()方法启动的,导致服务的onStartCommand()方法被调用.当服务处于started的状态时,其生命周期与启动它的组件无关,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止.

    3).使用bindService()方法启动服务,调用的组件与服务绑定在了一起,该组件一旦退出服务也就终止.

    4).开发人员需要在程序配置文件中生命全部的service,使用<service></service>标签.

    5).Service通常位于后台运行,一般不与用户交互,所以Service组件没有图形用户界面.Service组件需要继承Service基类.Service组件通常为其他组件提供后台服务或者监控其他组件的运行状态.

    6).长期在后台运行的组件不要在activity中开启子线程.应该是创建服务,在服务里面开启子线程.

    3.Content provider:

    1).android 平台提供了Content Provider 使一个程序的指定数据集提供给其他应用程序.其他应用可以通过contentResolver类从该类容中获取存入的数据.

    2).当多个应用程序中共享数组时可使用该组件.例如通讯录,

    3).ContentProvider实现数组共享.ContentProvider用于保存数组和获取数据,并对其所有应用程序可见.

    4).ContentProvider使用URL来唯一标识其数据集.

    4.Broadcast receiver:

    1).广播接收器专注于广播通知信息,并作出对应事件处理的组件.

    2).应用程序可以拥有任意数量的广播接收器.以对它感兴趣的所有消息作出响应.所有的接收器均继承自BroadcastReceiver基类.

    3).广播接收者注册有两种方式:

    a) 程序动态注册.

    b) AndroidManifest.xml文件中进行静态注册.

    4).动态注册的特点:activity销毁后,该广播失效.静态注册只要设备开启广播接收器就是打开的.

    展开全文
  • Android四大组件(整理相关知识点)

    万次阅读 多人点赞 2018-05-21 23:13:36
    Android 开发的四大组件分别是:活动(activity),用于表现功能;服务(service),后台运行服务,不提供界面呈现;广播接受者(Broadcast Receive),勇于接收广播;内容提供者(Content Provider),支持多个应用...
  • Android基础四大组件详解

    万次阅读 2016-09-05 23:46:27
    Android四大组件Activity,Service,BroadCastReceiver,ContentProvider详解
  • Android四大组件详解

    万次阅读 多人点赞 2019-06-14 09:31:39
    Android四大组件详解 Android四大组件分别为activity、service、content provider、broadcast receiver。 新的改变 1、activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent...
  • Android四大组件:Activity、Service、Broadcast Receiver、Content Provider1、activity(1)一个Activity通常就是一个单独的屏幕(窗口)。(2)Activity之间通过Intent进行通信。(3)android应用中每一个...
  • Android四大组件、六大布局、五大存储

    万次阅读 多人点赞 2016-09-06 16:15:36
    Android四大组件分别为activity、service、content provider、broadcast receiver。 一、android四大组件详解 1、activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent...
  • android四大组件(详细总结)

    万次阅读 多人点赞 2014-03-23 16:03:50
    android四大组件分别为activity、service、content provider、broadcast receiver。 一、android四大组件详解 1、activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent...
  • 四大组件、六大布局、五大存储,HandlerThread ,Services
  • Android四大组件:Service史上最全面解析

    千次阅读 多人点赞 2016-11-14 16:08:45
    Service作为Android四大组件之一,应用非常广泛 本文将介绍对Service进行全面介绍(基础认识、生命周期、使用和应用场景) 目录1. 基础知识 定义:服务,属于Android中的计算型组件 作用:提供需要在后台长期运行的...
  • Android 四大组件和五大布局

    万次阅读 多人点赞 2013-12-02 15:26:19
    Android四大组件:Activity、Service、Broadcast Receiver、Content Provider。  Activity 是Android程序与用户交互的窗口,从视觉效果来看,一个Activity占据当前的窗口,响应所有窗口事件,具备有控件,...
  • Android开发四大组件分别是:活动(Activity):用于表现功能。服务(Service):后台运行服务,不提供界面呈现。广播接收器(BroadcastReceiver):用于接收广播。内容提供商(ContentProvider):支持在多个应用中存储和...
  • Android四大组件及其用途

    千次阅读 2011-10-02 22:59:23
    四大组件 Activity, Service, Broadcast Receiver, Content provider 用途: Activity: 在Android中使用频率最高,实质就是一个可视化界面;一般一个Android应用由多个Activity组成,多个
  • Android四大组件分别为activity、service、content provider、broadcast receiver。 一、android四大组件详解 1、activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent...
  • Android开发学习

    千次阅读 多人点赞 2018-05-17 21:18:54
    Android开发学习 Android开发学习 什么是Android开发? Android开发用到的工具有哪些? Android开发用到的知识有哪些? Android开发学习路线 Android开发学习资源 什么是Android开发?     Android...
  • unity开发android游戏(一)搭建Unity安卓开发环境

    万次阅读 多人点赞 2014-03-23 16:14:38
    1,下载安装Java的JDK:...2,下载安装Android Studio:Google已经逐步转向Android studio了,我也比较推荐用这种来开发。ps. 2014年刚写这篇文章的时候,用的...
  • <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.WRITE_CONTACTS" /...
  • 四大组件,线程
  • Activity 、 Service 、 ContentProvider 如 果 要 使 用 则 必 须 在 AndroidManifest.xml 中 进 行 注 册 , 而BroadcastReceiver 则有两种注册方式,静态注册和动态注册。其中静态注册就是指在 AndroidManifest.... ...
  • 四大组件:Activity、BroadcastReceive、Service、Content Provider ; 五大存储: SharedPreferences、SDCard 、SQLite数据库存储数据、使用ContentProvider存储数据、IO存储 ; 六大布局 :LineartLayout 、...
  • 1.– 内容提供者(ContentProvider)是Android系统四大组件之一,用于保存和检索数据,是Android系统中不同应用程序之间共享数据的接口。– ContentProvider是不同应用程序之间进行数据交换的标准API,以Uri形式对外...
  • Android系统架构与四大组件

    千次阅读 2016-01-15 16:51:14
    本篇博文主要讲解Android的系统架构。 对于Android开发者来说,有...Android的体系架构鼓励系统组件重用,共享组件数据,并且定义组件的访问控制权限。可以说,这些层次结构即是相互独立,又是相互关联的。 一 Android
  • 新建一个Android项目,选择一个虚拟机运行Android项目 结果连虚拟机都启动不起来,启动时报如下错误: 2018/9/27 10:23 Emulator: emulator: ERROR: x86 emulation currently requires hardware acceleration! ...
  • 2019安卓开发环境配置教程(Eclipse+Android Studio)题外话面对的疑问功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定...
  • android 漂亮的UI界面 完整的界面设计

    万次下载 热门讨论 2012-02-17 22:19:40
    声明:这也是我学习时在网上下载的,鉴于分享精神,并且觉得很不错才上传...android 漂亮的UI界面 完整的界面设计 这是一个完整的UI设计,但是没写动作,这是一个公司程序员的公司任务设计,请别商用,要用请修改。。。
  • 安卓移动开发实验二:Android布局

    万次阅读 2019-03-18 14:38:51
    安卓移动开发实验二:Android布局一、试验内容二、试验代码与截图1.LinearLayout2.ConstraintLayout3.TableLayout 一、试验内容 通过Android studio,分别利用LinearLayout、ConstraintLayout和TableLayout实现老师...
  • 文章目录Android版本对照表 Android版本对照表 Android版本 API级别 VERSION_CODE ...可能会遇到无法保存图片到本地,或者造成Permission denied等问题,具体解决方案参考:《关于安卓open fai
  • 下载地址为:http://www.android-x86.org/download/,目前最新版本为“Android x86 4.4 r1”,这是Android-x86 4.4的首个稳定版本,下面合肥电脑维修网老刘就来用VMware虚拟机来为大家演示安装安卓Android)x86系统...

空空如也

1 2 3 4 5 ... 20
收藏数 2,932,871
精华内容 1,173,148
关键字:

android四大组件