android常用的必备基础知识



发表时间:2019-01-04  投稿人:余瑛   部门:行政        编辑:    点击量:6960  

首先从四大组件说起: 
Activity: 
生命周期: 
activity三种状态:运行(运行在最前端)、停止(不可见,完全被覆盖)、暂停(可见,但前端还有其他activity) 
生命周期相关的方法:onCreate-onStart-onResume-onPause-onStop-onDestory-onRestart 
切换时如果要保存数据, 可以重写: onSaveInstanceState(); 
恢复数据时, 重写: onRestoreInstanceState(); 
启动模式: 
1.standard:默认的,每次调用startActivity()启动时都会创建一个新的activity放在栈顶 
2.singleTop:启动Activity时,指定Activity不在任务栈栈顶就创建,如在栈顶,则不会创建,会调用onNewInstance(),复用已经存在的实例。 
3.singleTask:在任务栈里面只允许一个实例,如果启动activity不存在就创建,如果存在直接跳转到指定Activity所在位置。 
4.singleInstance:开启一个新的任务栈来存放这个Activity的实例,此模式开启的Activity是运行在自己单独的任务栈中的。

2.BroadcastReceiver广播接收者 
有序广播用过调用abortBroadcast()方法来中断,接收者之间可以传递数据 
动态注册广播register,取消unregister 
广播接收者打开Activity,需要设置一下Intent.FLAG_ACTIVITY_NEW_TASK 
应为广播接收者是没有Activity任务栈的 
sendOrderBroadcast()发送有序广播 
1)静态注册:在AndroidManifest.xml注册,android不能自动销毁广播接收器,也就是说当应用程序关闭后,还是会接收广播。 
2)动态注册:在代码中通过registerReceiver()手工注册.当程序关闭时,该接收器也会随之销毁。当然,也可手工调用unregisterReceiver()进行销毁。

3.Service服务 
Service中的生命周期方法(Context调用执行): 
1)startService() 如果没创建就先onCreate()再startCommand(), 如果已创建就只执行startCommand(); 
2)stopService() 执行onDestroy() 
3)bindService() 如果没有创建就先onCreate()再onBind() 
4)unbindService() 如果服务是在绑定时启动的, 先执行onUnbind()再执行onDestroy(). 如果服务在绑定前已启动, 那么只执行onUnbind(); 
3、开启服务的2种方式 
2种不同开启方式的区别: 
1)startService: 
开启服务,服务一旦开启,就长期就后台运行,即使调用者退出来,服务还会长期运行; 
资源不足时,被杀死,资源足够时,又会复活; 
2)bindService: 
绑定服务,绑定服务的生命周期会跟调用者关联起来,调用者退出,服务也会跟着销毁; 
通过绑定服务,可以间接的调用服务里面的方法(onBind返回IBinder); 
4、服务混合调用生命周期 
一般的调用顺序: 
①、start -> stop 开启 –> 结束 
②、bind -> unbind 绑定(服务开启) -> 解绑(服务结束) 
混合调用: 
①、start –> bind -> stop->unbind->ondestroy 通常不会使用这种模式 
开启 –> 绑定 –> 结束(服务停不了)->解除绑定(服务才可停掉) 
②、start –> bind -> unbind -> stop 经常使用 
开启 –> 绑定 –> 解绑(服务继续运行)-> stop(不用时,再停止服务) 
这样保证了服务长期后台运行,又可以调用服务中的方法

4、内容提供者 
这个问的非常少,但是一旦问道还是要知道怎么回事的。 
ContentProvider: 
在数据库中有对应的增删改查的方法,如果要让别的应用程序访问,需要有一个路径uri 
通过content:路径对外暴露,uri写法:content://主机名/表名 
1)ContentProvider:内容提供者 
把一个应用程序的私有数据(如数据库)信息暴露给别的应用程序,让别的应用程序可以访问; 
在数据库中有对应的增删改查的方法,如果要让别的应用程序访问,需要有一个路径uri: 
通过content:// 路径对外暴露,uri写法:content://主机名/表名 
2)ContentResolver:内容解析者 
根据内容提供者的路径,对数据进行操作(crud); 
3)ContentObserver:内容观察者 
可以理解成android系统包装好的回调,数据发送变化时,会执行回调中的方法; 
ContentResolver发送通知,ContentObserver监听通知; 
当A的数据发生变化的时候,A就会显示的通知一个内容观察者,不指定观察者,就会发消息给一个路径 
ContentProvider和sql的实现上的区别: 
1,Contentprovider屏蔽了数据存储的细节,内部实现对用户完全透明,用户只需要i、关心操作数据的uri就可以了,ContentProvider可以实现不同app之间的共享 
2,Sql也有增删改查的方法,但是sql只能查询本应用下的数据库。而Comtentprovider还可以去增删改查本地文件.xml文件的读取等

异步加载网络数据(AsyncTask) 
AsyncTask类,这个类中的任务是运行在后台线程中的,并可以将结果放在UI线程中去处理 
它定义了三种泛型,分别是Params、Progress和Result,分别表示请求的参数、任务的进度和获取的结果数据 
执行过程:1.onPreExecte():执行在主线程。这步操作是用于准备好任务的,作为任务加载的准备工作。建议在这个方法中弹出一个提示框。 
2.doInBackground():执行在子线程中。需要将请求的参数传递进来,发送给服务器,并将获取到的数据返回,数据回传给下一步的onProgressUpdate()中进行进度更新。 
3.onProgressUpdate():进度更新 
4.onPostExectue():界面更新 
实现原理: 
1.线程池的创建:默认创建一个线程池ThreadPoolExecutor,并默认创建出5个线程放入到线程池中,最多可放128个线程,且这个线程池是公共的唯一一份。 
2.任务的执行:执行run方法,执行完run方法后,会调用scheduleNext()不断的从双端队列中轮询,获取下一个任务并继续放到一个子线程中执行,直到异步任务执行完毕。 
3.消息的处理:在AsyncTask中维护了一个InternalHandler的类,这个类是继承Handler的,获取的数据是通过handler进行处理和发送的。其中handleMessage方法中,将消息传递给onProgressUpdate()进行进度的更新,也可以将结果发送到主线程中,进行界面的更新了。

LisView优化: 
ListView如何提高其效率 
1.复用ContentView 
2.自定义静态类viewholder 
3.使用分页加载 
4.使用weakReference引用ImageView对象 
listView可以显示多种类型的条目: 
Listview显示的每个条目都是通过baseAdapter的getView来展示的,理论上我们完全可以让每个条目都是不同类型的view,除此之外adapter还提供了getViewTypeCount()和getItemViewType(int position)两个方法。在getview方法中我们可以根据不同的viewtype加载不同的布局文件。

ListView中的数据分批及分页加载: 
设置ListView的滚动监听器:setOnScrollListener 
1.在监听器中有两个方法:滚动状态发生变化的方法onScrollStateChanged和listView被滚动时调用的方法 
2.在滚动状态发生改变的方法有三个状态:触摸滑动、惯性滚动、静止状态 
3.对不同的状态进行处理:分批加载数据,只关心静止状态:如果最后一个可见条目就是数据适

配器里的最后一个,此时可加载更多的数据。

ListView图片优化 
1.不要直接拿路径去循环decodeFile();使用Option保存图片大小,不要加载图片到内存 
2.拿到图片一定要经过边界压缩 
3.在listView去图片是也不要直接拿个路径去取图片,而是以WeakReference代替强引用 
4.在getView中做图片转换时,产生的中间变量一定及时释放 
异步加载图片基本思想: 
1.先从内存缓存中获取图片显示(内存缓冲) 
2.获取不到的话从SD卡里获取(SD卡缓存) 
3.都获取不到的话从网络下载图片并保存到SD卡同时加入内存并显示 
图片错位问题: 
本质是因为listView使用了缓存convertView。可见则显示,不可见则不显示。在imageLoader里有个imageViews的map对象,用于保存当前显示区域图像对应的url集,在显示前判断处理一下即可。 
内存缓冲机制: 
首先限制内存图片缓冲的堆内存大小,每次有图片往缓存里加时判断是否超过限制大小,超过的话取最少使用的图片并将其移除 
二级缓存:从LinkedHashMap里移除的缓存放在SoftReference里 
LinkedHashMap缓存在没有移除出去之前是不会被GC回收的,而SoftRefernce里的图片缓存在没有其他引用保存时随时都会被GC回收。 
ListView的其他优化: 
1.尽量避免在BaseAdapter中使用static来定义全局静态变量 
2.尽量使用getApplicationContext 
3.尽量避免在ListView适配器中使用线程

ScrollView和ListView的冲突问题: 
在ScrollView添加ListView会导致listView控件显示不全,两个控件的滚动事件冲突导致。通过listView中的item数量去计算listView的显示高度。

熟悉XML/Json解析数据,以及数据存储方式 
数据存储方式包括:File、SharedPreference、XML/Json、数据库、网络 
XML/Json解析数据

Handler机制和事件分发机制 
Message:消息,由MessageQueue统一列队,终由Handler处理 
Handler:处理者,负责Message发送消息及处理。Handler通过与Looper进行沟通,从而使用Handler时,需要实现handlerMessage方法来对特定的Message进行处理 
MessageQuene:消息队列,用来存放Handler发送过来的消息,按照先入先出规则执行。 
Looper:消息泵,不断从MessageQueue中取出Message执行。因此,一个线程中的MessaeQueue需要一个Looper进行管理。 
耗时操作,比如网络请求、文件处理、多媒体处理需要在子线程中操作,手机显示的刷新频率60Hz,一秒钟刷新60次,没16.7毫秒刷新一次,为了不丢帧,主线程处理代码最好不要超过16毫秒。 
Handler消息机制 
在主线程中 Android 默认已经调用了 Looper.preper()方法,调用该方法的目的是在 Looper 中创建MessageQueue 成员变量并把 Looper 对象绑定到当前线程中。当调用 Handler 的 sendMessage(对象)方法的时 
候就将 Message 对象添加到了 Looper 创建的 MessageQueue 队列中,同时给 Message 指定了 target 对象,其实这个 target 对象就是 Handler 对象。主线程默认执行了 Looper.looper()方法,该方法从 Looper 的成员变量MessageQueue 中取出 Message,然后调用 Message 的 target 对象的 handleMessage()方法。这样就完成了整个消息机制。

事件分发机制 
onTouch和onTouchEvent 
这两个方法都是在View的dispathTouchEvent中调用的,onTouch优先于onTouchEvent执行,在onTouch中返回true将事件消费掉,onTouchEvent将不会再执行。 
依次下发,下发的过程是调用View的dispatchTouchEvent方法实现的。简单来说,就是viewgroup遍历包含者的子view,调用每个View的dispatchTouchEvent方法,而当子view为viewgroup时,又会调用viewgroup的dispatchTouchEvent方法继续调用内部的view的dispatchTouchEvent方法。 
touch事件分发有两个主角:viewGroup和view。 
viewgroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent 
view包含dispatchTouchEvent、onTouchEvent两个 
onInterceptTouchEvent有两个作用:1.拦截down事件的分发 2.中止Up和move事件向目标view传递,使得目标view所在的viewgroup捕获up和move事件 
触摸事件是有action_Down、Action_Move、Action_Up组成,其中一次完整的触 
摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。

自定义控件 
1.自定义组合控件 
声明一个view对象,继承相对布局,或者线性布局或者其他的viewGroup 
在自定义的view对象里面重写它的构造方法,在构造方法里面把布局初始化 
根据业务需要添加一些api方法,扩展自定义组合控件 
在布局文件里面可以自定义一些属性 
声明自定义属性的命名空间 
在res目录下的values目录下创建attrs.xml的文件声明我们写的属性 
在布局文件中写自定义的属性 
使用自定义的属性 
2.view的绘制过程 
mesarue()过程:为整个view树计算实际的大小 
view : mMeasuredHeight高和mMeasureWidth()宽 
viewGroup:重写onMeasure()方法,遍历measure()过程,通过调用父类ViewGroup类里面的measureChildWithMargins()方法去实现 
layout()根据子视图的大小以及布局参数将view树放到合适的位置上 
view: 设置该view视图位于父视图的坐标抽即mLeft,mTop,mLeft,mBottom,回调onLayout方法 
viewGroup:遍历每个子视图childView,调用该子视图的layout()方法去设置它的坐标值 
3.draw()绘制过程 
viewRoot对象的performTraversals()方法调用draw()方法发起绘制view树不需要全部重绘只需要绘制需要绘制的部分

这个控件中,父视图使用unspecified dimensions来将它的每个子视图都测量一次来算出它们到底需要多大尺寸,而这些子视图没被限制的尺寸的和太大或太小,所以会用精确数值再次调用measure()(也就是说,如果子视图不满意它们获得的区域大小,那么父视图将会干涉并设置第二次测量规则)。其中measure()方法会调用onMeasure()方法。 
代码中,由于把每行剩余空间重新分配,会调用了requestLayout()方法,这个方法又会导致measure()和onLayout()方法的再次调用。 
最后你会发现 onMeasure()方法调用了 1次*2*2=4次 onLayout()方法调用了 1次*2 =2次

谈一谈android的安全机制 
1,在安卓是有文件权限的控制,在清单文件声明 
2,每个android中每个应用都有自己的/data/data/包名 文件夹,该文件夹只能该应用访问,而其他应用则无权访问 
3,Android的代码混淆保护了开发者的劳动成果

多线程断点续传下载 
多线程下载的实现过程: 
1,首先得到下载文件的长度,然后设置本地文件的长度。 
HttpURLConnection.getContentLength(); 
RandomAccessFile file = new RandomAccessFile(“FeiQ.exe”,”rwd”); 
file.setLength(filesize); //设置本地文件的长度 
2,根据文件长度和线程数计算每条线程下载的数据长度和下载位置。 
如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如图所示。 
3,使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止. 
如:指定从文件的2M位置开始下载,下载到位置4M为止,代码如下: 
HttpURLConnection.setRequestProperty(“Range”, “bytes=2097152-4194303”); 
4,保存文件 
使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。 
RandomAccessFile threadfile = new RandomAccessFile(“QQWubiSetup.exe “,”rwd”); 
threadfile.seek(2097152); //从文件的什么位置开始写入数据

数据库操作 
在Android系统,提供了一个SQLiteOpenHelper抽象类,该类用于对数据库版本进行管理.该类中常用的方法: 
onCreate 数据库创建时执行(第一次连接获取数据库对象时执行) 
onUpgrade 数据库更新时执行(版本号改变时执行) 
onOpen 数据库每次打开时执行(每次打开数据库时调用,在 onCreate,onUpgrade方法之后) 
Android提供了一个名为SQLiteDatabase的类,该类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。对SQLiteDatabase的学习,我们应该重点掌握execSQL()和rawQuery()方法。 execSQL()方法可以执行insert、delete、update和CREATE TABLE之类有更改行为的SQL语句; rawQuery()方法用于执行select语句。

aidl:接口声明语言,跨进程通信 
bindService有一个ServiceConnec接口覆写onServiceConnect方法,把第二个参数IBinder对象强制转换为aidl中的接口类

- 写服务类

1. 定义一个接口文件, 声明一个方法forwardPayMoney方法, 把后缀名修改为.aidl, 并且把修饰词去掉.

2. 在服务中定义一个内部类MyBinder, 继承Stub类, 并且把抽象方法forwardPayMoney实现了.

3. 在onBind方法中把第二部定义的内部类对象MyBinder返回.

- 另一个程序的Activity

4. 使用隐式的方式绑定服务, 传递过去一个连接桥对象.

5. 把服务程序中的aidl文件拷贝当前工程中, 包名要保留一致.

6. 在连接桥对象中的onServiceConnected方法中, 把IBinder对象转换成aidl接口对象

mAlipayRemoteService = Stub.asInterface(service);

7. 使用aidl接口对象, 调用接口中的抽象方法, 实际上会调用到远程服务中内部类中的forwardPayMoney方法.
android中的动画 
1.View Animation: 视图动画/ Frame动画/属性动画:这种动画是可扩展的,

图片处理框架 
imageLoader:imageLoaderEngine,cache及 
imageDownloader,imageDecoder,bitmaodisplayer,bitmapProcessor五大模块 
简单来说就是imageLoader收到加载及显示图片的任务,并将它交给 
imageLoaderEngine,ImageLoaderEngine分发任务到具体线程池去执行,任务通过cache及imageDownloder获取图片。 
优点:1,支持下载监听2,可以在view滚动中暂停图片加载3.默认实现多种内存缓存算法4.支持本地缓存文件名规则定义 
picasso 
这个库分为dispatcher/requestHandler及Downloader,picassoDrawable等模块 
picasso收到加载及显示图片的任务,创建Request并将他交给 
Dispatcher,dispatcher分发任务到具体的requesthandler,任务memoryCache及Handler获取图片,图片获取成功后通过picassoDrawable显示到Target中 
优点:1.自带统计监控功能2.支持优先级处理3.支持延迟加载4.支持飞行模式、并发线程数根据网络类型而变

图片缓存处理: 
LruCache类:主要算法原理是把最近使用的对象用强引用存储在在LinkedHashMap中,并且把最近最少使用的对象在缓存值达到预定值之前从内存中移除。

内存溢出原因: 
1.资源释放问题 
2.对象内存过大 
3.static 
4.线程导致内存溢出

图片占用进程的内存算法简介 
android中处理图片的基础类是Bitmap,顾名思义,就是位图。占用内存的算法如下: 
图片的width*height*Config。 
如果Config设置为ARGB_8888,那么上面的Config就是4。一张480*320的图片占用的内存就是480*320*4 byte。 
在默认情况下android进程的内存占用量为16M,因为Bitmap除了Java中持有数据外,底层C++的 skia图形库还会持有一个SKBitmap对象,因此一般图片占用内存推荐大小应该不超过8M。这个可以调整,编译源代码时可以设置参数。

Activity的启动与生命周期的监控 
应用程序被开启后,是需要开启并创建Activity,加载相应的view,从而展示出应用程序 
1、Activity是通过startActivity开启起来的,startActivity是由有Context调用的,其具体的实现类是ContextImpl 
在ContextImpl中的startActivity方法中,会调用ActivityThread的相关方法【mMainThread.getInstrumentation().execStartActivity()】;可以追溯到Instrumentation这个类,其中的execStartActivity()的方法中实现了startActivity的调用:ActivityManagerNative.getDefault().startActivity,由此可以看出是底层进行处理。 
2、ActivityMonitor监控Activity 
当Activity实例创建的时候,就会给Activity配置一个监视器ActivityMonitor,监控Activity的声明周期: 
在Instrumentation的execStartActivity()的方法中,上来先判断ActivityMonitor是否为null:在第一次开启Activity的时候,ActivityMonitor还是null的,就会调用ActivityManagerNative.getDefault().startActivity(…..),是在操作native底层的信息,从而执行startActivity,再去开启一个Activity。 
简单来说,就是通过调用JNI,调用startActivity方法,开启Activity;创建好了之后,随即也创建好了Activity的监视器ActivityMonitor 
3、在ActivityMonitor中就有Activity各种生命周期的监控 
①、在newActivity方法中: 
可以通过拿到Activity的字节码,创建一个Activity,并将这个Activity返回 
还会调用attach方法,传入ActivityThread的线程 
②、在各种生命周期的方法中,调用activity的各自的生命周期的方法

总结: 
1、通过PackageManagerService将所有用到的资源加载进内存中 
2、在Launcher中,将view等控件加载到ViewGroup中,点击每个item会有相应的操作 
3、在公开的文档中是找不到具体调用startActivity的类的,而是由系统完成调用的,实现了Activity的启动
实际就是通过Context的实现类ContextImpl进行调用的,一步步转到底层(ActivityManagerNative)实现调用 
4、另一个重要的类就是ActivityMonitor,监控Activity生命周期的;在其newActivity方法中创建了Activity,并调用了attach方法; 
也就是说当一个Activity被创建的时候,就会绑定一个ActivityMonitor,用来监控Activity的生命周期
 

上一篇 下一篇