-
2021-05-26 08:38:50
大家好,我是时间财富网智能客服时间君,上述问题将由我为大家进行解答。
安卓四大组件:Activity、Service、BroadcastReceiver和ContentProvider,作用:
1、Activity组件的主要作用是展示一个界面并和用户交互,它扮演的是一种前台界面的角色
Activity是一种展示型组件,主要是向用户展示一个界面,并且可以接收用户的输入信息从而和用户进行交互。对用户来说,Activity就是Android应用的全部,因为其他三大组件对用户来说是不可感知的。Activity的启动由Intent触发,其中Intent分为显式启动和隐式启动。
2、Service组件的主要作用是在后台执行计算任务,执行任务的结果可以和外界进行通信
Service是一种计算型组件,用于在后台执行一系列计算任务。由于Service组件工作在后台,因此用户无法直接感知到它的存在。Service组件和Activity组件不同,Activity组件只有一种运行模式,即Activity处于启动状态,但是Service组件却有两种状态:启动状态和绑定状态。Service组件处于启动状态时,它的内部可以执行一些后台计算,并且不需要和外界有直接的交互。Service处于绑定状态,Service内部同样也可以执行后台计算,但是处于这种状态的Service可以很方便地和外界进行通信。
3、BroadcastReceiver组件的主要作用是消息的传递,该消息的传递可以在应用内,也可以在应用之间,它的角色是一个消息的传递者
BroadcastReceiver是一种消息型组件,用于在不同组件乃至不同应用之间传递消息。BroadcastReceiver同样无法被用户所感知,因为它工作在系统内部。BroadcastReceiver也叫做广播,广播的注册方式有两种:静态注册和动态注册。静态注册指在AndroidManifest中注册广播,这种广播在应用安装时被系统解析,此种形式的广播不需要应用启动就可以接收到相应的广播。动态广播需要通过Context.registerReceiver()来实现,并且在不需要的时候通过Context.unRegisterReceiver()解除广播,此种形态的广播必须要应用启动才能注册并接收广播。
4、ContentProvider组件的主要作用是作为一个平台,提供数据的共享,并且提供数据的增删改查功能。主要应用于应用之间的数据共享场景
ContentProvider是一种数据共享型组件,用于向其他组件乃至其他应用共享数据。同样的,它也无法被用户所感知。对于ContentProvider组件来说,它的内部需要实现增删改查这四种操作。需要注意的是,ContentProvider内部的delete、update和query方法需要处理好线程同步,因为这几个方法都是在Binder线程池中被调用的。ContentProvider组件不需要手动停止。
更多相关内容 -
Android四大组件详解
2019-06-14 09:31:39Android四大组件详解 Android四大组件分别为activity、service、content provider、broadcast receiver。 新的改变 1、activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent...第一次写博客,也是第一次接触安卓,很多格式什么的都不熟悉,如果比较乱还请多多指教。
关注微信公众号,一起探索
Android四大组件详解
Android四大组件分别为activity、service、content provider、broadcast receiver。
1、activity
(1)一个Activity通常就是一个单独的屏幕(窗口)。
(2)Activity之间通过Intent进行通信。
(3)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity。
2、service
(1)service用于在后台完成用户指定的操作。service分为两种:
(a)started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。
(b)bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。
(2)startService()与bindService()区别:
(a)started service(启动服务)是由其他组件调用startService()方法启动的,这导致服务的onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止。
(b)使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
(3)开发人员需要在应用程序配置文件中声明全部的service,使用标签。
(4)Service通常位于后台运行,它一般不需要与用户交互,因此Service组件没有图形用户界面。Service组件需要继承Service基类。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。
3、content provider
(1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。
(2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。
(3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。
(4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。
(5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。
4、broadcast receiver
(1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。
(2)广播接收者的注册有两种方法,分别是程序动态注册和AndroidManifest文件中进行静态注册。
(3)动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。
(4)Android还有一套本地广播机制,就是为了解决广播的安全问题,因为系统全局广播可以被其他任何程序接收到,一些携带关键性数据的广播就可能被其他应用程序截获。而本地广播机制发出的广播只能在应用程序的内部进行传递,并且只能接收来自本应用程序的广播,这样就不存在安全问题了。
(5)今天了解了Android的静态注册和动态注册,Android在8.0以后,为了提高效率,删除了静态注册,防止关闭App后广播还在,造成内存泄漏。现在静态注册的广播需要指定包名,而动态注册就没有这个问题。并且,无论是静态注册广播还是动态注册广播,在接收广播的时候都不能拦截广播,否则会报错。
谷歌官网的原文是:应用无法使用其清单注册大部分隐式广播。不过,是不能对大部分的广播进行注册,但还是有些广播可以进行静态注册的,比如对接收Android开机的广播通过静态注册还是能够正常接收的。5、Intent介绍
Intent是Android系统用来抽象描述要执行的一个操作,也可以在不同组件之间进行沟通和消息传递。
显式的Intent就是你已经知道要启动的组件名称,比如某个Activity的包名和类名,在Intent中明确的指定了这个组件(Activity),一般来说这种Intent经常用在一个应用中,因为你已经明确的知道要启动的组件名称。
隐式的Intent就是你不知道要启动的组件名称,只知道一个Intent动作要执行,比如:拍照,录像,查看地图。一般来说这种Intent用在不同的应用之间传递信息。 -
Android四大组件(整理相关知识点)
2018-05-21 23:13:36Android 开发的四大组件分别是:活动(activity),用于表现功能;服务(service),后台运行服务,不提供界面呈现;广播接受者(Broadcast Receive),勇于接收广播;内容提供者(Content Provider),支持多个应用...Android 开发的四大组件分别是:活动(activity),用于表现功能;服务(service),后台运行服务,不提供界面呈现;广播接受者(Broadcast Receive),勇于接收广播;内容提供者(Content Provider),支持多个应用中存储和读取数据,相当于数据库。
1.活动(activity)
(1)定义:Activity是Android的四大组件之一。是用户操作的可视化界面;它为用户提供了一个完成操作指令的窗口。当我们创建完毕Activity之后,需要调用
setContentView()
方法来完成界面的显示;以此来为用户提供交互的入口。在Android App 中只要能看见的几乎都要依托于Activity,所以Activity是在开发中使用最频繁的一种组件。(2)一个Activity通常就是一个单独的屏幕(窗口)。
(3)Activity之间通过Intent进行通信。
(4)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity。在android stdio会自动生成,但eclipse需要自己手动添加
(5)Activity的生命周期
在Android中会维持一个Activity Stack(Activity栈),当一个新的Activity创建时,它就会放到栈顶,这个Activity就处于运行状态。当再有一个新的Activity被创建后,会重新压人栈顶,而之前的Activity则会在这个新的Activity底下,就像枪梭压入子弹一样。而且之前的Activity就会进入后台。
一个Activity实质上有四种状态:
a.运行中(Running/Active):这时Activity位于栈顶,是可见的,并且可以用户交互。
b.暂停(Paused):当Activity失去焦点,不能跟用户交互了,但依然可见,就处于暂停状态。当一个新的非全屏的Activity或者一个透明的Activity放置在栈顶,Activity就处于暂停状态;这个时候Activity的各种数据还被保持着;只有在系统内存在极低的状态下,系统才会自动的去销毁Activity。
c.停止(Stoped):当一个Activity被另一个Activity完全覆盖,或者点击HOME键退入了后台,这时候Activity处于停止状态。这里有些是跟暂停状态相似的:这个时候Activity的各种数据还被保持着;当系统的别的地方需要用到内容时,系统会自动的去销毁Activity。
d.销毁(Detroyed):当我们点击返回键或者系统在内存不够用的情况下就会把Activity从栈里移除销毁,被系统回收,这时候,Activity处于销毁状态。2.服务(Service)
service(服务)是安卓中的四大组件之一,它通常用作在后台处理耗时的逻辑,与Activity一样,它存在自己的生命周期,也需要在AndroidManifest.xml配置相关信息。
服务(Service)是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。
不过需要注意的是,服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程。与某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。另外.也不要被服务的后台概念所迷惑,实际上服务并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。
(1)service用于在后台完成用户指定的操作。service分为两种:
(a)started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。
(b)bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。
(2)startService()与bindService()区别:
(a)started service(启动服务)是由其他组件调用startService()方法启动的,这导致服务的onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止。
(b)使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
(3)开发人员需要在应用程序配置文件中声明全部的service,使用<service></service>标签。(4)Service通常位于后台运行,它一般不需要与用户交互,因此Service组件没有图形用户界面。Service组件需要继承Service基类。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。
定义
Service是一个专门在后台处理长时间任务的Android组件,它没有UI。它有两种启动方式,startService和bindService。
这两种启动方式的区别:
startService只是启动Service,启动它的组件(如Activity)和Service并没有关联,只有当Service调用stopSelf或者其他组件调用stopService服务才会终止。
bindService方法启动Service,其他组件可以通过回调获取Service的代理对象和Service交互,而这两方也进行了绑定,当启动方销毁时,Service也会自动进行unBind操作,当发现所有绑定都进行了unBind时才会销毁Service。
不可以, Service的onCreate是在主线程(ActivityThread)中调用的,耗时操作会阻塞UI
如果需要做耗时的操作,你会怎么做?线程和Handler方式
是否知道IntentService,在什么场景下使用IntentService?
IntentService相比父类Service而言,最大特点是其回调函数onHandleIntent中可以直接进行耗时操作,不必再开线程。其原理是IntentService的成员变量 Handler在初始化时已属于工作线程,之后handleMessage,包括onHandleIntent等函数都运行在工作线程中。如果对IntentService的了解仅限于此,会有种IntentService很鸡肋的观点,因为在Service中开线程进行耗时操作也不麻烦。我当初也是这个观点,所以很少用IntentService。
但是IntentService还有一个特点,就是多次调用onHandleIntent函数(也就是有多个耗时任务要执行),多个耗时任务会按顺序依次执行。原理是其内置的Handler关联了任务队列,Handler通过looper取任务执行是顺序执行的。
这个特点就能解决多个耗时任务需要顺序依次执行的问题。而如果仅用service,开多个线程去执行耗时操作,就很难管理。
https://blog.csdn.net/zhengyin_tmac/article/details/52446696
3.广播接受者(Broadcast Receive)
在Android中,广播是一种广泛运用的在应用程序之间传输信息的机制。而广播接收器是对发送出来的广播进行过滤接受并响应的一类组件。可以使用广播接收器来让应用对一个外部时间做出响应。例如,当电话呼入这个外部事件到来时,可以利用广播接收器进行处理。当下载一个程序成功完成时,仍然可以利用广播接收器进行处理。广播接收器不NotificationManager来通知用户这些事情发生了。广播接收器既可以在AndroidManifest.xml中注册,也可以在运行时的代码中使用Context.registerReceive()进行注册。只要是注册了,当事件来临时,即使程序没有启动,系统也在需要的时候启动程序。各种应用还可以通过使用Context.sendBroadcast()将它们自己的Intent广播给其他应用程序。
(1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。
(2)广播接收者的注册有两种方法,分别是程序动态注册(在运行时的代码中使用Context.registerReceive()进行注册)和AndroidManifest文件中进行静态注册。
(3)动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。
4.内容提供者(Content Provider)
(1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。
(2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。
(3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。
(4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。(5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。
emmm看到就收集我也忘记哪是哪个网的了
面试题:有使用过ContentProvider码?能说说Android为什么要设计ContentProvider这个组件吗?
ContentProvider应用程序间非常通用的共享数据的一种方式,也是Android官方推荐的方式。Android中许多系统应用都使用该方式实现数据共享,比如通讯录、短信等。但我遇到很多做Android开发的人都不怎么使用它,觉得直接读取数据库会更简单方便。
那么Android搞一个内容提供者在数据和应用之间,只是为了装高大上,故弄玄虚?我认为其设计用意在于:
- 封装。对数据进行封装,提供统一的接口,使用者完全不必关心这些数据是在DB,XML、Preferences或者网络请求来的。当项目需求要改变数据来源时,使用我们的地方完全不需要修改。
- 提供一种跨进程数据共享的方式。
应用程序间的数据共享还有另外的一个重要话题,就是数据更新通知机制了。因为数据是在多个应用程序中共享的,当其中一个应用程序改变了这些共享数据的时候,它有责任通知其它应用程序,让它们知道共享数据被修改了,这样它们就可以作相应的处理。
ContentResolver接口的notifyChange函数来通知那些注册了监控特定URI的ContentObserver对象,使得它们可以相应地执行一些处理。ContentObserver可以通过registerContentObserver进行注册。
既然是对外提供数据共享,那么如何限制对方的使用呢?
android:exported属性非常重要。这个属性用于指示该服务是否能够被其他应用程序组件调用或跟它交互。如果设置为true,则能够被调用或交互,否则不能。设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。
对于需要开放的组件应设置合理的权限,如果只需要对同一个签名的其它应用开放content provider,则可以设置signature级别的权限。大家可以参考一下系统自带应用的代码,自定义了signature级别的permission:
<permission android:name="com.android.gallery3d.filtershow.permission.READ" android:protectionLevel="signature" /> <permission android:name="com.android.gallery3d.filtershow.permission.WRITE" android:protectionLevel="signature" /> <provider android:name="com.android.gallery3d.filtershow.provider.SharedImageProvider" android:authorities="com.android.gallery3d.filtershow.provider.SharedImageProvider" android:grantUriPermissions="true" android:readPermission="com.android.gallery3d.filtershow.permission.READ" android:writePermission="com.android.gallery3d.filtershow.permission.WRITE" />
如果我们只需要开放部份的URI给其他的应用访问呢?可以参考Provider的URI权限设置,只允许访问部份URI,可以参考原生ContactsProvider2的相关代码(注意path-permission这个选项):
<provider android:name="ContactsProvider2" android:authorities="contacts;com.android.contacts" android:label="@string/provider_label" android:multiprocess="false" android:exported="true" android:grantUriPermissions="true" android:readPermission="android.permission.READ_CONTACTS" android:writePermission="android.permission.WRITE_CONTACTS"> <path-permission android:pathPrefix="/search_suggest_query" android:readPermission="android.permission.GLOBAL_SEARCH" /> <path-permission android:pathPrefix="/search_suggest_shortcut" android:readPermission="android.permission.GLOBAL_SEARCH" /> <path-permission android:pathPattern="/contacts/.*/photo" android:readPermission="android.permission.GLOBAL_SEARCH" /> <grant-uri-permission android:pathPattern=".*" /> </provider>
ContentProvider接口方法运行在哪个线程中呢?
ContentProvider可以在AndroidManifest.xml中配置一个叫做android:multiprocess的属性,默认值是false,表示ContentProvider是单例的,无论哪个客户端应用的访问都将是同一个ContentProvider对象,如果设为true,系统会为每一个访问该ContentProvider的进程创建一个实例。
这点还是比较好理解的,那如果我要问每个ContentProvider的操作是在哪个线程中运行的呢(其实我们关心的是UI线程和工作线程)?比如我们在UI线程调用getContentResolver().query查询数据,而当数据量很大时(或者需要进行较长时间的计算)会不会阻塞UI线程呢?
要分两种情况回答这个问题:
- ContentProvider和调用者在同一个进程,ContentProvider的方法(query/insert/update/delete等)和调用者在同一线程中;
- ContentProvider和调用者在不同的进程,ContentProvider的方法会运行在它自身所在进程的一个Binder线程中。
但是,注意这两种方式在ContentProvider的方法没有执行完成前都会blocked调用者。所以你应该知道这个上面这个问题的答案了吧。
也可以看看CursorLoader这个类的源码,看Google自己是怎么使用getContentResolver().query的。
ContentProvider是如何在不同应用程序之间传输数据的?
这个问题点有些深入,大家要对Binder进程间通信机制比较了解。因为之前我们有提到过一个应用进程有16个Binder线程去和远程服务进行交互,而每个线程可占用的缓存空间是128KB这样,超过会报异常。ContentResolver虽然是通过Binder进程间通信机制打通了应用程序之间共享数据的通道,但Content Provider组件在不同应用程序之间传输数据是基于匿名共享内存机制来实现的。有兴趣的可以查看一下老罗的文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划。
作者:goeasyway
链接:https://www.jianshu.com/p/380231307070
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
-
android应用自动更新组件
2016-08-09 16:24:25android应用自动更新组件,封装好可以直接用,博客地址: http://blog.csdn.net/u012835548/article/details/52163691 -
Android 四大组件,五大存储,六大布局
2018-08-15 12:28:24Android 四大组件 android四大组件分别是:Activity, service,content provider,broadcast receiver ...Android 五大存储 SharedPreferences 方式 文件存储方式 SQList 存储...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所对应的的classIntent 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给内容提供器中的数据建立了唯一标识符,它主要由两部分组成:authority 和 path 。authority 是用于对不同的应用程序做区分的,一般为了避免冲突,都会采用程序包名的方式进行命名。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注意:margin 和 padding 的区别
* 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>
-
Android组件化方案
2017-02-15 19:01:52每个工程师都要熟悉如此之多的代码,将很难进行多人协作开发,而且Android项目在编译代码的时候电脑会非常卡,又因为单一工程下代码耦合严重,每修改一处代码后都要重新编译打包测试,导致非常耗时。 -
Android组件化原理
2022-01-07 14:33:49Android组件化原理什么是组件化?为什么使用组件化?一步步搭建组件化组件化开发要注意的几点问题1.新建模块2.统一Gradle版本号3.创建基础库4.组件模式和集成模式转换5.AndroidManifest的切换 什么是组件化? 一个... -
Android 自己实现组件间通信框架EventBus
2014-11-14 01:21:53代码为博客的实例代码:http://blog.csdn.net/lmj623565791/article/details/41096639 有问题请博客留言 -
Android 小组件完整案例
2022-04-15 19:46:36android小组件 -
《Android自定义组件开发详解》
2016-02-27 14:24:231.3.1 测量组件大小 16 1.3.2 确定子组件的位置 17 1.3.3 绘制组件 18 1.4、说点别的 22 1.5 练习作业 22 第二章 Graphics2D API 23 2.1、概述 23 2.2、Point类和PointF类 23 2.3、Rect类和RectF类 25 2.4、Bitmap... -
Android之四大组件、六大布局、五大存储
2016-09-06 16:15:36Android四大组件分别为activity、service、content provider、broadcast receiver。 一、android四大组件详解 1、activity (1)一个Activity通常就是一个单独的屏幕(窗口)。 (2)Activity之间通过Intent... -
Android组件设计思想
2013-10-23 01:11:07Android应用开发的哲学是把一切...这一节讲Android组件化设计的背景、理念、原则,以及Android在OS级别上提供的组件化支持,其中还会包含一个实验来验证这种组件化设计思想,可以对Android系统有一个高层次的抽象理解。 -
android动态添加删除组件(Layout)
2015-01-04 11:24:22android开发有时不知道该有多少个控件,需要动态添加或删除,比如:发帖页面。该例子工程实现了动态添加和删除一组控件(包含EditText和ImageButton按钮)。实现逻辑比较简单,希望有所启发! -
Android的组件化开发
2016-12-02 17:18:15如何在Android使用组件化开发 -
Android 一一 简述Android四大组件
2017-11-30 21:42:31Android四大基本组件:Activity、BroadcastReceiver广播接收器、ContentProvider内容提供者、Service服务。 Activity: 应用程序中,一个Activity就相当于手机屏幕,它是一种可以包含用户界面的组件,主要用于和... -
安卓 四大组件
2018-06-29 11:16:46Android开发的四大组件 Android四大组件分别为activity、service、content provider、broadcast receive 一、Activity Activity生命周期的方法是成对出现的 onCreate() & onDestory() onStart() ... -
Android UI组件实例集合
2012-09-11 11:31:29android:paddingTop="4px" android:paddingLeft="14px" android:enabled="false" /> 3-在代码中配置常用属性: // 从xml中得到GifView的句柄 gf1 = (GifView) findViewById(R.id.gif1); // 设置Gif图片源 gf1.... -
Android 组件化,从入门到不可自拔
2019-04-19 02:54:33组件化能够显著提高Android项目开发效率,支持不同业务组件单独打包或者组合打包,可以说是Android开发者必备技能。 本文通过一个极其简单的实践案例,梳理了组件化的配置过程,并辅以全部源码,希望对还没有应用... -
Android UI组件框架AndroidMaterialDesign详解
2019-01-27 11:36:30在使用中有任何问题,请在下方留言,或加入Android、Java开发技术交流群 QQ群:810970432 email:geduo_83@163.com 10.关于作者 var geduo_83 = { nickName : "门心叼龙", site : ... -
Android使用WebView组件显示网页
2022-03-25 12:42:54Android使用WebView组件显示网页 代码实现效果如下图所示: 1.xml布局文件代码 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ... -
Android组件化之组件通信
2018-11-20 23:37:28本文是续上一篇Android组件化方案实践与思考文章一些思考,主要是针对组件间通信,比如: 每个组件如何初始化各自的数据 Activity间如何跳转、Fragment实例获取、如何调用普通类的函数 如何在一个组件中通知... -
Android进阶——组件化开发实践(一)
2022-03-11 16:39:37一、组件化的意义 随着Android 项目代码和结构逐渐复杂,维护成本会指数型上升,通常我们会利用Android Studio自带的Module去拆分项目代码。但这种拆分显然需要基于一定逻辑和结构,目前主流的拆分思路有两种:分别... -
零基础学Android—按钮组件
2022-03-28 09:51:06Android Studio中按钮组件使用 -
Android组件化开发
2020-02-26 14:23:21上图是组件化项目中一个标准的业务组件目录结构,首先我们在main文件夹下创建一个module文件夹用于存放组件开发模式下业务组件的 AndroidManifest.xml,而 AndroidStudio 生成的 AndroidManifest.xml 则依然保留,... -
Android 开发优秀组件库整理
2018-09-16 22:59:29开源组件库整理 1. 缓存 开源组件 简介 最佳实践 参考文章 DiskLruCache ... 开源组件 ... Android Universal Image Loader 一个强大的加载,缓存,展示图片的库 - – P... -
Android 组件化架构-简谈
2022-03-14 11:11:24了解组件化 在了解组件化之前,我们需要先了解模块化。 模块化就是将整体业务集合按照功能的不同,抽离到不同的模块中,这样做的好处就是在团队协助中能够较好的区分各自负责的功能模块、也能使得整个工程显得不是... -
Android组件化开发简单示例
2020-11-21 15:01:521、通过一个简单的android项目初始架构图来了解组件化,如下图: 打个生动的比喻,把我们的APP当成一个电脑主机,那么app外壳就是主机外壳,main组件就是主板,其他各个组件就类似于硬盘、网卡、显卡之类的东西,... -
Android 12上焕然一新的小组件:美观、便捷和实用
2021-05-25 18:21:21Android 12上重新设计的桌面小部件:美观、易用和高效 封面 Todo iOS14上重磅推出的桌面小部件使得小部件这个设计重获关注,Google当然不会落人之后,在12里重新改造了小部件,为开发者为用户提供更加效率的更加...