转存自下面路径,转载用大佬的原文 https://blog.csdn.net/qq_29966203/article/details/123868018?spm=1001.2014.3001.5502
上
SD卡
路径
1 2 3 4 a).Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) b).Environment.getExternalStorageDirectory().getPath() c).Environment.getDownloadCacheDirectory().getPath() d).Environment.getDataDirectory().getPath()
权限
1 2 <uses-permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE”/> <uses-permission android:name=“android.permission.MOUNT_UNMOUNT_FILESYSTEMS”/>
android的数据存储方式
文件存储 a).InputStream getResouces().openRawResource(int id) //返回InputStream,针对res/raw b).InputStream getAssets().open(String path) //open(“img/smail.jpg”) c).Bitmap BitmapFactory.decodeResouce(resources,int,option)
xml、SharedPreference //1).SharedPreference位于data/data/包名/shared_prefs的xxx.xml文件
SQLiteDatabase
ContentProvider
网络
BroadcastReceiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 >>class Receiver :BroadcastReceiver{ 。 onReceive(context:Context?,intent:Intent?){ val action=intent.getAction() when(action)->{ } } } >>IntentFilter if =IntentFilter() if .addAction("action1" ) registerReceiver(re:BroadcastReceiver,if ) override fun destroy () { unregisterReceiver(re:BroadcastReceiver) } >>隐式广播(通过action和category匹配) :1. 在Manifest上添加<category android:name="android.intent.category.DEFAULT" /> :2. 标识为android.intent.category.LAUNCHER,则不用添加DEFAULT >>sendBroadcast(intent) :1. val intent=Intent() intent.action="" intent.addCategory() >>sendOrderedBroadcast(intent,receivePermission) a).<receiver android:name=".smsReceiver" > <intent-filter android:priority="1000" > <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> b).setResultExtras(bundle:Bundle) c).abortBroadcast() >>sendStickyBroadcast(intent) :1. removeStickyBroadcast(intent) :2. 在Manifest中声明android.permission.BROADCAST_STICKY
sp频繁操作会有什么后果?sp能存多少数据?
sp的底层由xml来实现,操作sp的过程就是xml的序列化和解析。xml是存在磁盘上的,因此要考虑io速度。另外dvm的内存是有限的,同时dvm的堆内存为16M,所以不能超过此数字
dvm与jvm的区别
架构不同
JVM基于栈则意味着需要去栈中读写数据
DVM是基于寄存器的
字节码的执行顺序不同
执行顺序为: .java文件 -> .class文件 -> .jar文件
java文件 –>.class文件-> .dex文件
每个应用都运行在一个DVM实例中,且一个DVM运行在一个独立的进程空间
dvm的垃圾回收
采用标记清除 缺点:容易产生很多彼此分隔的小内存块
dvm可划分为Active Heap+Zygote Heap+Card Table+Live Heap Bitmap+MARK HEAP Bitamp ->Zygote Heap 为初始时的进程,而应用程序是由Zygote fork出来的 ->当创建了应用程序后,会从Zygote heap分出一部分创建Active Heap,之后分配的对象都在Active堆进行 ->堆的垃圾回收标记 采用Heap Bitmap位图来存储,划分为Live + Mark ->垃圾回收遍历采用递归,垃圾回收的标准为Live=1,Mark=0 ->垃圾回收的两个阶段,第一个阶段标记根对象 (静态对象、栈、全局变量、寄存器),不允许其他线程运行 第二个阶段遍历被根对象所引用的对象,并标记,此时为Concurrent GC ->CardTable,用位图标识第二阶段需要被重新标记的对象,在第二阶段完成后继续标记,此时禁止其他线程
ART
//android4.4之后采用ART作为虚拟机 GC算法分为多种 :1.Mark-Sweep GC,标记-清楚 :2.Compacting GC,标记-压缩 新生代采用复制算法,老年代采用标记压缩 //复制算法:内存分为相等的两块,一块用于分配
Activity的生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ->onCreate 创建时执行 a).mInstrumentation.callActivityOnCreate b).performStart c).mInstrumentation.callActivityOnRestoreInstanceState ->onStart 可见时执行 ->onResume 获取焦点时执行 a).handleResumeActivity >performResumeActivity ->onPause 失去焦点时执行 a).handlePauseActivity >performPauseActivity >if (!r.activity.mFinished&&r.saveState) mInstrumentation.callActivityOnSaveInstanceState ->onStop 不可见时执行 a).handleStopActivity >performStopActivityInner >performPauseActivityIfNeeded ->onDestroy 销毁 a).handleDestroyActivity ->案例分析:Activity A -> Activity B :1. Activity A的创建,A::onCreate , A:onStart ,A:onResume :2. 跳转 , A:onPause ,B::onCreate,B:onStart,B:onResume,A:onStop :3. Activity B返回,B:onPause,A:onRestart,A:onStart,A:onResume,B:onStop,B:onDestroy
Application能不能启动Activity
startActivity(intent:Intent,FLAG_ACTIVITY_NEW_TASK) standard模式启动的Activity,该实例会存放在启动该Activity的Activity所在的任务栈中,而 ApplicationContext不存放在任务栈中
Activity的状态都有哪些
->foreground activity ->visible activity ->background activity ->empty process
横竖屏切换时Activity的生命周期
在没有配置activity的configChanges属性时 Activity的生命周期:onPause->onStop->onCreate->onStart->onResume
配置了activity的configChanges onConfigurationChanged
如何设置activity成窗口样式
1 android:theme=“@android :style/Theme.Dialog”
Activity的启动方式
standard 不管有没有已存在的实例,都生成新实例
singleTop 如果发现有对应的Activity实例位于栈顶,则重复利用,否则创建实例
singleTask a)栈内复用,复用时具有clearTop机制 b)single taskAffinity in task
singleInstance a)启用一个新的栈结构,将Activity放置于栈结构中,并保证不会有其它Activity实例进入 b)方便多个应用共享全局唯一的实例
Service的生命周期
通过startService调用 onCreate->onStartCommand->onDestroy 通过bindService调用 onCreate->onBind->onUnbind->onDestroy
Service Binder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 >> a).public class LocalService extends Service { private static LBinder binder=new LBinder (); @Override public void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); } @Override public IBinder onBind (Intent intent) { return binder; } static class LBinder extends Binder { public int getCount () { return count; } } }
IntentService
//开启线程处理耗时操作
1 2 3 4 5 >>onCreate :1. HandlertThread thread = new HandlerThread ("" ) thread.start() mServiceLooper =thread.getLooper() mServiceHandler =new ServiceHandler (mServiceLooper)
onSTartCommand.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 :1. public int onStartCommand (Intent intent,int startId) { onStart(intent,startId) return mRedelivery?START_REDELIVER_INTENT:START_NOT_STICKY; } :2. public void onStart (Intent intent,int startId) { Message msg=mServiceHandler.obtainMessage() msg.obj=intent msg.arg1=startId mServiceHandler.sendMessage(msg) }
ServiceHandler.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 :1. private final class ServiceHandler extends Handler { public ServiceHandler (Looper looper) { super (looper);} public void handleMessage (Message msg) { onHandleIntent((Intent)msg.obj) stopSelf(msg.arg1) } }
总结 a).通过HandlerThread创建线程,并调用getLooper获取looper变量,以新建Handler b).IntentService通过onStartCommand接收新的intent,并放在message.obj发送过handler c).handler调用实现的onHandleIntent(intent) d).Message msg=mServiceHandler.obtainMessage() //obtainMessage和Looper调用msg.relycleUnchecked是以插入链表头部和拆除的形式分配、释放Message资源
Fragment和Activity的onCreateOptionsMenu
1 2 3 4 5 6 7 8 9 >>override fun onCreateOptionsMenu (menu:Menu) { getMenuInflater.inflate(R.menu.menu1,menu) return super .onCreateOptionsMenu(menu) } >>setHasOptionsMenu(true )
Service的onStartCommand有几种返回值
1 2 3 >>START_STICKY 当Service因内存不足被系统kill掉后,会尝试重建Service,创建成功后回调onStartCommand,但Intent是空的 >>START_NOT_STICKY 系统不会尝试重建Service >>START_REDELIVER_INTENT 重建Service并传递最后一次Intent
Service的onRebind什么情况下执行
当Service刚启动时,通过调用bindService都会进入Service的onBind回调
在Service启动过后,再进行绑定进入onRebind,并在onUnbind返回true
Handler防止内存泄露
原因 a).声明Handler为静态类,同时对于Activity的this引用封装成WeakReference 否则会导致匿名类引用了外部Activity,导致Activity不能被回收 b).如果这时对手机硬件进行横竖屏切换,讲导致Activity被创建,却不能被回收
措施
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 a).public void onDestroy () { mHandler.removeCallbacksAndMessages(null ); } >>static class MyHandler extends Handler { WeakReference<Activity > mActivityReference; MyHandler(Activity activity) { mActivityReference= new WeakReference <Activity>(activity); } @Override public void handleMessage (Message msg) { final Activity activity = mActivityReference.get(); if (activity != null ) { mImageView.setImageBitmap(mBitmap); } } }
IntentFilter的匹配法则
action //匹配manifest中的其中一个
data //如果manifest声明了data匹配,则隐式intent必须设置data
1 2 <data android:scheme=‘content’ host=‘pwcong.me’ path=‘/hello’ port=‘8080 ’> Uri.parse(‘content:
category //隐式Intent可以没有category a).隐式intent默认添加 android.intent.category.DEFAULT b).AndroidManifest c).隐式intent设置的category必须全部能匹配命中
例子 :1.val uri=Uri.parse(“http://www.baidu.com:80/2.jsp” ) val intent=Intent() intent.setDataAndType(uri,“abc/eft”)
19.Fragment与Activity传值
接口或者方法调用 :1.在Fragment中可以通过getActivity()直接获得其绑定的Activity
通过fragmentManager查找 :1.在Activity中可以通过FragmentManager获取fragment的实例
1 2 3 FragmentManager fragmentManager=getFragmentManager() Fragment fragment=fragmentManager.findFragmentByTag(tag) Fragment fragment=fragmentManager.findFragmentById(id)
:2.fragment的入栈行为
1 2 3 4 5 Fragment oneFragment=OneFragment.newInstance(arg1:String,arg2:String) val transaction=getFragmentManager().beginTransaction() transaction.replace(R.id.frament_hodler,oneFragment,tag:String?) transaction.addToBackStack(null ) transaction.commit()
20.Fragment 生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 >>onAttach >>onCreate >>onCreateView(LayoutInflater,ViewGroup,Bundle) >>onActivityCreated(Bundle) >>onStart->onResume->onPause->onStop >>onDestroyView >>onDetach >>Fragment使用 a). <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <fragment android:id="@+id/id_fragment_title" android:name="com.zhy.zhy_fragments.TitleFragment" android:layout_width="fill_parent" android:layout_height="45dp" /> <fragment android:layout_below="@id/id_fragment_title" android:id="@+id/id_fragment_content" android:name="com.zhy.zhy_fragments.ContentFragment" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout> b). a).FragmentManager fm = getFragmentManager(); ContentFragment contentFragment=(ContentFragment)fm.findFragmentById(R.id.id_fragment_container) if (contentFragment==null ) { contentFragment=new ContentFragment (); fm.beginTransaction().add(R.id.id_fragment_container,contentFragment).commit(); } b).宿主Activity必须实现OnFragmentInteractionListener >>ActivityThread关于Fragment的回调 a).Activity持有mFragments b).HostCallbacks的初始化 >public HostCallbacks () { super (Activity.this ) } c).FragmentHostCallback >FragmentHostCallback(Activity activity) { > FragmentHostCallback(activity,context,handler,windowAnimations) -> mActivity=activity mContext=context mHandler=handler } >成员FragmentManagerImpl d).FragmentController >private FragmentController (FragmentHostCallback<?> callbacks) { mHost = callbacks; } e).ActivityThread::performLaunchActivity >attach… >mFragments.attachHost(null ) Activity::performCreate Activity::onCreate { super .onCreate(savedInstanceState) getFragmentManager() .beginTransaction() .replace(R.id.container,mContentFragment) .commit(); }
Fragment的add和replace的区别 //replace==remove|append
1 >> transaction.add(R.id.fragment_container, oneFragment).commit()
1 2 3 第一个参为容器id,第二个为需要添加的fragment,添加不会导致容器的内容被清空, 同一个fragment添加多次会报错,添加进去的fragment都会按层级显示,大多数情况都是与remove、hide 搭配用
1 >> replace,replace(R.id.fragment_container, oneFragment).commit();
1 替换会把容器的所有内容都替换掉,只保存单独一个fragment
Fragment如何实现Activity栈的压栈和出栈
1 2 >> transaction.addToBackStack(null ) >> getSupportFragmentManager().popBackStack()
什么情况下造成内存泄露
资源释放问题 长期保存Context、Cursor的引用,资源得不到释放
对象内存过大 Bitmap文件,如decodeStream不设置inSampleSize
static关键字的使用 //考虑到static声明的对象不再属于类实例,而是属于类范围,因此对象的生命周期将会延长,用WeakReference进行替换
线程内存溢出 1.将线程的内部类该为静态内部类,静态内部类不会对外部类对象拥有强引用 2.在线程内部用WeakReference去引用Context
采用软引用缓存图片//?
图片过大导致OOM 等比例缩小图片 1.获取图片的原始宽高
1 2 3 BitmapFactory.Options opts=new BitmapFactory .Options() opts.inJustDecodeBounds=true BitmapFactory.decodeStream(is, null , opts)
2.计算图片的压缩比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Bitmap bitmap=null int i=0 while (true ){ if (opts.width>>i<=size&&opts.height>>I<=size) { BitmapFactory.Options options=new BitmapFactory .Options() options.inSampleSize=1 <<i Bitmap bmp=null bmp=BitmapFactory.decodeStream(is,null ,options) } }
25.SoftReference跟WeakRefrence的区别
WeakRefrence => 与强引用对象的生命周期一致,不会增加引用计数
SoftReference => 内存不足时才进行回收
26.dp与px
dp转px a).float scale=context.getResources().getDisplayMetrics().density (int)(dpValue*scale+0.5f)
px转dp a).float scale=context.getResources().getDisplayMetrics().density pxValue/scale+0.5f
27.设置布局为一半宽、高
1 2 3 4 DisplayMetrics metrics=new DisplayMetrics () getWindowManager().getDefaultDisplay().getMetrics(metrics) Constant.srceenHeight = metrics.heightPixels; Constant.srceenWidth = metrics.widthPixels;
28.多分辨率支持的清单文件配置
1 2 android:normalScreens=“true ” android:smallScreens=“true ” android:xlargeScreens=“true ” android:anyDensity=“true ”
29.Android的事件分发机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 ViewGroup和View的继承关系和接口 :1. ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。 :2. View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于View。 ViewGroup::dispatchTouchEvent :1. intercepted = onInterceptTouchEvent(ev) -> 默认只有满足滑动条点击时时才返回true ,可以重写 -> intercepted==true 调用super .dispatchTouchEvent(et) :2. 遍历子View -> ArrayList<View> preorderedList=buildTouchDispatchChildList() >根据子view下标进行遍历,z=elevation+translationZ >根据z的大小插入perorderedList -> 遍历子view,让其消费事件 for (int i = childrenCount - 1 ; i >= 0 ; i--) { View child=getAndVerifyPreorderedView(i,children) if (!isTransfromedTouchPointInView(x,y,child,null )) continue ; newTouchTarget = getTouchTarget(child) dispatchTransformedTouchEvent(ev,..,child..) > offsetX=mScrollX-child.mLeft > offsetY=mScrollY-child.mTop > boolean handled=child.dispatchTouchEvent(event) > return handled -> 遍历结束 if (dispatchTransformedTouchEvent(…)) { newTouchTarget = addTouchTarget(child, idBitsToAssign) target.next=mFirstTouchTarget mFristTouchTarget=target alreadyDispatchedToNewTouchTarget=true . } -> 针对MotionEvent.ACTION_UP和MontionEvent.ACTION_MOVE target=mFirstTouchTarget while (target!=null ) { next:TouchTarget=target.next if (dispatchTransformedTouchEvent(ev,...,target.child,...) ) handled=true target=next } :3. 是否需要自己处理MotionEvent if (mFirstTouchTarget == null ) { handled = dispatchTransformedTouchEvent(ev,.., null ,TouchTarget.ALL_POINTER_IDS) } View的事件分发顺序: :1. dispatchEvent流程 : onTouch > onTouchEvent >onTouchEvent(@Overide ) onTouchEvent检查ACTION_UP,查看是否有mOnClickListener,有则回调onClick :2. onTouchEvent >case MotionEvent.ACTION_UP: if (mPerformClick == null ) { mPerformClick = new PerformClick (); } if (!post(mPerformClick)) { performClick(); } touch事件的层级传递 a).在setOnTouchListener中只有当ACTION_DOWN返回true 后,才会继续传来ACTION_UP、ACTION_MOVE >>ViewGroup >>ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。 >>触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0 个。 >>当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true 。 >>当某个子View返回true 时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:如ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true ,它将被保存在ViewGroup1中,而ViewGroup1也会返回true ,被保存在ViewGroup0中。当Move和UP事件来时,会先从ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView。 >>当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super .dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。 >>onInterceptTouchEvent有两个作用:1. 拦截Down事件的分发。2. 中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。
32.ArrayList和LinkedList的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 :1. transient Object[] elementData; :2. public void add (int index,E element) { ensureCapacityInternal(minCapacity:size + 1 ) >if (minCapacity - elementData.length > 0 ) grow(minCapacity) > int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1 ); if (newCapacity - minCapacity < 0 ) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0 ) newCapacity = hugeCapacity(minCapacity) elementData = Arrays.copyOf(elementData, newCapacity) System.arraycopy(elementData,index,elementData,index+1 ,size-index) elementData[index] = element; size++; } :3. public boolean addAll (Collection<? extends E> c) { Object[] a=c.toArray() int numNew = a.length; ensureCapacityInternal(size + numNew); System.arraycopy(src:a, pos:0 , dat:elementData, pos:size, len:numNew); size += numNew; return numNew != 0 ; } :4. public boolean contains (Object o) -> return indexOf(o) { > if (o == null ) { for (int i = 0 ; i < size; i++) if (elementData[i]==null ) return i; } }else { for (int i = 0 ; i < size; i++) if (o.equals(elementData[i])) return i; } return -1 ; } :5. public E remove (int index) { modCount++ int s = size E oldValue = (E) elementData[index] int numMoved=size-index-1 if (numMoved>0 ) System.arraycopy(src:elementData, srcPos:index + 1 , dest:elementData, destPos:index, len:numMoved) elementData[—-size]=null return oldValue }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 :1. 成员 ->transient int size=0 ->transient Node<E> first,last :2. 在头部插入 public void linkFirst (E e) { final Node<E> f=first final Node<E> newNode=new Node <>(prev:null ,e,next:f) first=newNode if (f==null ) last=newNode else f.prev=newNode size++;modCount++; } :3. 在尾部插入 public void linkLast (E e) { final Node<E> l=last final Node<E> newNode=new Node <>(l,e,null ) last=newNode if (l==null ) first=newNode else l.next=newNode size++;modCount++; } e).boolean add (E e) { linkLast(e) return true } f).boolean remove (Object o) { for (Node<E> x=first;x!=null ;x=x.next) { if (o.equals(x.item)) { unlink(x);return true ; } } return false } g).E unlink (Node<E> x) { final E element=x.item final Node<E> next=x.next final Node<E> prev=x.prev if (prev==null ) first=next else { prev.next=next x.prev=null } if (next==null ) last=prev else { next.prev=prev x.next=null } x.item=null ;size—;modCount++; return element }
ArrayList和LinkedList在末尾增加一项的时间是固定的,但是ArrayList可能会造成数组空间的重新分配 在ArrayList中插入、删除元素会造成移动,而LinkedList不会 LinkedList不支持高效的随机元素访问 LinkedList的空间花费集中在内部类Node的开销,而ArrayList则体现在扩容
33.LruCache
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 >>实现 :1. 采用LinkedHashMap作为数据结构 :2. 放入缓存 >public final V put (K key,V value) { V previous; synchronized (this ) { putCount++ size+=safeSizeOf(key, value) previous=map.put(key, value) if (previous!=null ) size-=safeSizeOf(key, value) } trimToSize(maxSize) } >public void trimToSize (int maxSize) { while (true ) { K key; V value; synchronized (this ){ if (size<=maxSize||map.isEmpty()) break Map.Entry<K,V> evict=map.entrySet().iterator().next() key=evict.getKey() value=evict.getValue() map.remove(key) size-=safeSizeOf(key, value) } } } >>示例 :1. 初始化 int maxSize=(int )Runtime.getRuntime().maxMemory()/8 ; LruCache<String,Bitmap> cache=new LruCache <String,Bitmap>(maxSize) { @Override protected int sizeOf (String key, Bitmap value) { return value.getByteCount(); } } :2. 放入缓存 cache.put(key:String, value:Bitmap)
36.什么是ANR,如何规避
ANR application not responding a).keyDispatch timeout 5s b).broadcast timeout 10s c).service timeout 20s
UI线程
1 2 3 4 5 6 7 8 9 10 a).Activity&AsyncTask:onPreExecute、onPostExecute、onProgressUpdate >>public Result doInBackground (String …arg) { … publishProgress(int ) … }
描述Service的启动方式
1 2 3 4 5 6 7 8 9 10 11 12 13 - Activity.startService - bindService(intent, conn, flag) :1. flag的种类 >BIND_AUTO_CREATE >BIND_ADJUST_WITH_ACTIVITY >BIND_ABOVE_CLIENT >BIND_IMPORTANT >BIND_NOT_FOREGROUND - onStartCommand的返回标志 :1. START_NOT_STICKY :2. START_STICKY :3. START_REDELIVER_INTENT
Android有哪几种布局
LinearyLayout 按照水平或垂直的顺序排列子元素,android:layout_weight生效
FrameLayout 后来的子元素覆盖在之前子元素的上层,以左上角为基准点
AbsoluteLayout android:layout_x和android:layout_y会生效
RelativeLayout android:layout_below和android:layout_above
TableLayout //表格布局由TableRow构成,layout_column指定开始列,layout_span制定范围 a).全局属性 android:shrinkColumns=“0,1” //可收缩的列 android:stretchColumns=“0,1” //可拉伸的列 b).单元格属性 android:layout_span=“2” //该TableRow中的子View所横跨的列 android:layout_column=“1” //制定子view在哪列开始排列
HashMap、HashTable的区别 //从线程安全性、速度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 >>线程方面 :1. HashTable是线程安全的 >>HashTable的key、value不允许设置null ,而hashmap可以 >>是否可在遍历时进行操作 :1. HashMap:fail-fast a).既在遍历HashMap时,如果通过实例对象进行修改则报ConcurrentModificationException, expectedModCount==modCount >>解决冲突 :1. HashMap的装载因子是3 /4 ,链接法解决冲突,当链表过长时转化为红黑树 >>实现 :1. HashMap 通过数组+链表的结构 >transient Node<K,V>[] table; >static class Node <K,V> implements Map .Entry<K,V>{ final int hash; final K key; final V value; Node<K,V> next; public final int hashCode () { return Objects.hashCode(key)^Objects.hashCode(v) } } :2. hashCode 通过Objects.hashCode(key)^Objects.hashCode(value)计算 :3. 扩容 哈希桶的容量形若100. ..0 ,扩容容量为2 倍,阀值也为2 倍,需要rehash >final Node<K,V>[] resize(){ Node<K,V>[] oldTab = table; int oldCap=oldTab.length int oldThr = threshold; if (oldCap > 0 ) { if ((newCap = oldCap << 1 ) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) { newThr=oldThr<<1 ; } } threshold = newThr table= newTab if (oldTab!=null ) { for (int j = 0 ; j < oldCap; ++j) { Node<K,V> e; if ((e=oldTab[j])!=null ){ oldTab[j] = null if (e.next == null ) newTab[e.hash & (newCap - 1 )] = e; else { Node<K,V> loHead = null , loTail = null ; Node<K,V> hiHead = null , hiTail = null ; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0 ) { if (loTail == null ) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null ) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null ) if (loTail != null ) { loTail.next = null ; newTab[j] = loHead; } if (hiTail != null ) { hiTail.next = null ; newTab[j + oldCap] = hiHead; } } } } } :4. 插入节点 >public V put (K key,V value) { return putVal(hash(key),key,value)} >final V putVal (int hash,K key,V value,...) { Node<K,V>[] tab=table int n=tab.length Node<K,V> p; if ((tab = table) == null || (n = tab.length) == 0 ) n = (tab = resize()).length; if (p=tab[I=?(n-1 )&hash)==null ) tab[I]=newNode(hash,key,value,null ) else { Node<K,V> e;K k; if (p.hash==hash && ((k=p.key)==key || key.equals(k)) e=p; else if (p instance TreeNode) e=TreeNode<K,V)p.putTreeVal(this , tab,hash,key,value) else { for (int binCount=0 ;;++binCount) { if ((e=p.next)==null ) { p.next=newNode(hash, key, value) if (binCount>=TREEIFY_THRESHOLD-1 ) treeifyBin(tab,hash) break } if (e.hash==hash&&((k=e.key)==key) break p=e } } if (e!=null ) e.value=value } } :5. 删除结点 >final Node<K,V> removeNode (int hash, Object key, Object value, boolean matchValue, boolean movable) { Node<K,V>[] tab; Node<K,V> p; int n, index; if ((tab = table) != null && (n = tab.length) > 0 && (p = tab[index = (n - 1 ) & hash]) != null ) { Node<K,V> node = null , e; K k; V v if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) node = p; else if ((e = p.next) != null ) { if (p instanceof TreeNode) node = ((TreeNode<K,V>)p).getTreeNode(hash, key); else { do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { node = e; break ; } p = e; } while ((e = e.next) != null ); } } if (node != null && (!matchValue || (v = node.value) == value || (value != null && value.equals(v)))) { if (node instanceof TreeNode) ((TreeNode<K,V>)node).removeTreeNode(this , tab, movable); else if (node == p) tab[index] = node.next; else p.next = node.next; ++modCount; --size; return node; } } } :6. 查找 >final Node<K,V> getNode (int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1 ) & hash]) != null ) { if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null ) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null ); } } return null ; } :7. iterator遍历 > final Node<K,V> nextNode () { Node<K,V>[] t; Node<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException (); if (e == null ) throw new NoSuchElementException (); if ((next = (current = e).next) == null && (t = table) != null ) { do { } while (index < t.length && (next = t[index++]) == null ); } return e; } 40. LinkedHashMap >>成员 :1. LinkedHashMapEntry static class LinkedHashMapEntry <K,V> extends HashMap .Node<K,V> { LinkedHashMapEntry<K,V> before, after; LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) { super (hash, key, value, next); } } :2. head tail >transient LinkedHashMap.Entry<K,V> head >transient LinkedHashMap.Entry<K,V> tail :3. accessOrder >>重写newNode,在HashMap调用putVal()方法被调用 > Node<K,V> newNode (int hash, K key, V value, Node<K,V> e) { LinkedHashMap.Entry<K,V> p = new LinkedHashMap .Entry<K,V>(hash, key, value, e); linkNodeLast(p); return p; } private void linkNodeLast (LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null ) head = p; else { p.before = last; last.after = p; } } >>删除结点 > void afterNodeRemoval (Node<K,V> e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.before = p.after = null ; if (b == null ) head = a; else b.after = a; if (a == null ) tail = b; else a.before = b; } >>get >public V get (Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null ) return null ; if (accessOrder) afterNodeAccess(e); return e.value; } >若accessOrder==true void afterNodeAccess (Node<K,V> e) { LinkedHashMapEntry<K,V> last; if (accessOrder && (last = tail) != e) { LinkedHashMapEntry <K,V> p = (LinkedHashMapEntry <K,V>)e, b = p.before, a = p.after; p.after = null ; if (b == null ) head = a; else b.after = a; if (a != null ) a.before = b; else last = b; if (last == null ) head = p; else { p.before = last; last.after = p; } tail = p; ++modCount; } }
红黑树
性质 a).每个结点是红色的或者黑色的 b).根结点是黑色的 c).每个叶结点(空结点)都是黑色的 d).如果一个结点是红色的,则它的子结点都是黑色的 e).对于每个结点而言,从该结点到其所有叶结点的简单路径上,包含相等数目的黑结点 引理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 a).n>=2 ^bh(x)-1 >>左旋 LEFT-ROTATE(T,x) { y=x.right x.right=y.left if y.left!=T.nil y.left.p=x y.p=x.p if x.p==T.nil T.root=y else if x==x.p.left x.p.left=y else x.p.right=y y.left=x x.p=y } >>右旋 RIGHT-ROTATE(T,x) { y=x.p if y.p==T.nil T.root=x else if y.p.left==y y.p.left=x else y.p.right=x x.p=y.p y.p=x y.left=x.right x.right=y } >>插入 RB-INSERT(T,z) { y=T.nil x=T.root while x!=T.nil y=x if z.key<x.key x=x.left else x=x.right z.p=y if y==T.nil T.root=z else if z.key<y.key y.left=z else y.right=z z.left=T.nil z.right=T.nil RB-INSERT-FIXUP(T,z) } RB-INSERT-FIXUP(T,z) { }
Handler Looper 工作原理 成员
1 2 3 4 5 6 7 8 9 :1. class Looper { static final ThreadLocal<Looper> sThreadLocal =new ThreadLocal () static Looper sMainLooper final MessageQueue mQueue final Thread mThread }
初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 :1. Looper.prepare() if (sThreadLocal.get()!=null throw new RuntimeException sThreadLocal.set(new Looper (true )) :2. public Looper (boolean quitAllowed) { mQueue=new MessageQueue (quitAllowed) mThread=Thread.currentThread() } :3. 开启事件循环 public static Looper.loop() { final Looper me=myLooper() final MessageQueue queue = me.mQueue for (;;) { Message msg = queue.next() if (msg == null ) { return ;} try { msg.target.dispatchMessage(msg) } finally { } msg.recycleUnChecked() } }
最后Handler通过sendMessage将消息塞入mQueue队列 由Looper接收到了msg,然后调用Handler的dispatchMessage Handler 检查mCallback->message.callback->mCallback的handleMessage
Listview的几种adapter介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 a).SimpleAdapter(Context context,List<? extends Map <String, ?>> data,int resource,String[] from,int [] to) b). for (int i = 0 ; i < 20 ; i++) { Map<String, Object> item = new HashMap <String, Object>(); item.put("touxiang" , R.drawable.ic_launcher); item.put("name" , "某某" + i); item.put("message" , "今晚我请客吃饭" ); data.add(item); } listView = (ListView) findViewById(R.id.listview1); simAdapt = new SimpleAdapter ( this , data, R.layout.listview_item, new String [] { "touxiang" , "name" , "message" }, new int [] { R.id.touxiang, R.id.tv_name, R.id.tv_message }); listView.setAdapter(simAdapt);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 a).List<String> items=new ArrayList <>(); items.add("item1" ); items.add("item2" ); listView.setAdapter(new ArrayAdapter <String>(this ,android.R.layout.simple_list_item_1,items)); >>BaseAdapter a).static class LAdapter extends BaseAdapter { private LayoutInflater mInflater; private Context mContext; public LAdapter (Context context) { mContext=context; mInflater=LayoutInflater.from(context); } @Override public int getCount () { return 0 ; } @Override public Object getItem (int position) { return null ; } @Override public long getItemId (int position) { return 0 ; } @Override public int getItemViewType (int position) { } @Override public View getView (int position, View convertView, ViewGroup parent) { View v= LayoutInflater.from(mContext).inflate(R.layout.xxx,parent,false ) 2 ).viewGroup不为null ,attachToRoot=false ,layout_width、layout_height有效 3 ).viewGroup不为null ,attachToRoot=true ,直接添加到父容器 return v; } } b).使用convertView进行优化 >>if (convertView==null ) convertView=mInflater.inflate(R.layout.xx,null ); c).使用ViewHolder进行优化 >>static class ViewHolder { public ImageView img; public TextView title; public TextView info; } >>if (convertView == null ) { holder = new ViewHolder (); convertView = mInflater.inflate(R.layout.list_item, null ); holder.img = (ImageView)item.findViewById(R.id.img) holder.title = (TextView)item.findViewById(R.id.title); holder.info = (TextView)item.findViewById(R.id.info); convertView.setTag(holder); }else { holder = (ViewHolder)convertView.getTag(); } holder.img.setImageResource((Integer) data.get(position).get("img" )); holder.title.setText((String)data.get(position).get("title" )); holder.info.setText((String)data.get(position).get("info" )); return convertView;
如何在ListView间添加分割线
1 2 3 4 5 6 7 8 9 10 >>.设置全局属性 a).android:divider="#FFF" b).android:dividerHeight="1px" c).默认的list view不支持设置footerDevider,采取在每个item布局中添加 > <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#999" />
LinkedHashMap 源码解读
Drawable转Bitmap
1 >>Bitmap bitmap= BitmapFactory.decodeResource(getResources(),R.drawable.icon_hint);
Layout_weight
根据比重划分布局 a).根据LinearLayout的orientation,设置layout_width=0或者layout_height=0 b).layout_weight=xx,直接进行布局划分 根据剩余布局叠加进行划分 a).首先计算layout_width的布局之和layout_sum,layout_remain=width-layout_sum b).得出子view的宽度:layout_width+layout_remain*layout_weight/sum
android:layout_gravity和android:gravity的区别
android:layout_gravity 用于设置当前View在父容器的位置 android:gravity 用于设置当前View中文本、图像的位置
如何重用布局 采用
使用merge减少include布局所造成的布局层次
1 <merge xmlns:android="http://schemas.android.com/apk/res/android" >
如何优化布局
设置为invisible或者gone的View -> ViewStub :1.ViewStub是一个不可见且大小为0的视图 :2.当ViewStub被设置为visible或者调用.inflate()时就会被指定的layout所代替 :3.inflatedId指定加载过来的layout id :4.例子
android:layout_gravity的使用规则
LinearyLayout 当orientation为horizontal时,存在 center\top\bottom FrameLayout a).android:layout_centerVertical
TextView显示图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>android:drawableLeft >>CharSequence charSequence=Html.fromHtml(html,new ImageGetter (){ @Override public Drawable getDrawable (String source) { Drawable drawable=getResources().getDrawable(getResourceId(source)) drawable.setBounds(0 ,0 ,drawable.getIntrinsicWidth(),drawable.getIntrinsicHeight()) return drawable } },null ) >>textView.setText(charSequence)
53.SpannableString的使用
支持局部字符样式化 a). b).setSpan(Object what,int start,int end,int flags) flags:Spanned.SPAN_INCLUSIVE_EXCLUSIVE //不包括终止下标 :Spanned.SPAN_INCLUSIVE_INCLUSIVE //包括起始下标、终止下标
采用ImageSpan设置文本图标 a).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Drawable drawable = getResources().getDrawable(R.mipmap.a9c); drawable.setBounds(0 , 0 , 42 , 42 ); ImageSpan imageSpan = new ImageSpan (drawable); spannableString.setSpan(imageSpan, 6 , 8 , Spanned.SPAN_INCLUSIVE_EXCLUSIVE) >>ClickableSpan 设置响应点击 a).setSpan(new ClickableSpan (){ @Override public void onClick (View v) { } },0 ,text.length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
BackgroundColorSpan设置背景色 a).
1 2 BackgroundColorSpan bcSpan=new BackgroundColorSpan (Color.YELLOW) spannableString.setSpan(bcSpan,start,end,Scannable.SPAN_EXCLUSIVE_EXCLUSIVE)
54.AutoCompleteTextView的使用
android:completionThreshold //至少用户输入多少个字符才显示 android:dropDownHeight //下拉列表的高度 android:dropDownWidth android:completionHint //提示 setAdapter a).String [] arr={“aa”,“aab”,“aac”}; ArrayAdapter arrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,arr); autotext.setAdapter(arrayAdapter);
在按钮上显示图像的方式有哪些 Button android:drawableXXX(left|rigth|…) Button继承自TextView,可以通过采用SpannableString进行setText ImageButton a).layout_width&layout_height //设置了具体的宽高后,图像并不会拉伸,而是背景增大 b).src //设置图片 c).不设置宽高,表现与ImageView一致
如何动态改变Button的大小和位置 View.layout(left,top,right,bottom)
如何让一个显示图像的按钮在不同状态下显示不同图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 drawable <selector> <item android:state_pressed=“true ” android:drawable=“@drawable /pressed”/> <item android:state_focused=“true ” android:drawable=“@drawable /focused”/> <item android:drawable=“@drawable /normal”/> </selector> >>程序里设置监听 a).button.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View v) { Drawable drawable=getResources().getDrawable(R.mipmap.ic_launcher); v.setBackground(drawable); } }); b).button.setOnFocusChangeListener(new View .OnFocusChangeListener() { @Override public void onFocusChange (View v, boolean hasFocus) { } });
如何实现图像的半透明度 自定义View 在onDraw中绘制到canvas上
1 2 3 4 5 6 7 8 9 10 11 12 InputStream is=getResources().openRawResource(R.drawable.image) Bitmap bitmap=BitmapFactory.decodeStream(is) public void onDraw (Canvas canvas) { Paint paint=new Paint (); paint.setAlpha(180 ); canvas.drawBitmap(bitmap,….,paint) } >>在图像上方覆盖一个半透明的图像
HttpURLConnection URL
1 2 3 4 5 6 url=new URL ("http://www.baidu.com" ); HttpURLConnection connection= (HttpURLConnection) url.openConnection(); connection.setDoInput(true ); InputStream inputStream= connection.getInputStream(); byte [] buffer=new byte [1024 ]; int hasRead=0 ;
BufferedReader
1 2 3 4 5 a).BufferedReader br=new BufferedReader (is); while ((line=br.readLine())!=null ) result+=line+”\n”; c).网页也是字符组织,故该方法可行
BitmapFactory.decodeStream(is)
ProgressBar
属性设置 a).style=“@android:style/Widget.ProgressBar.Horizontal” b).android:max=“100” c).android:progress=“50”
属性更新 a).setProgress(int v) b).incrementProgressBy(int step)
AbsListView layoutChildren a).ListView::layoutChildren
ListView,如何进行数据的增删改查
数据更新 a).Adapter::notifyDataSetChanged() //只使用于数据集的某项改变,更改引用将不会有效 b).Adapter::update //自定义方法,直接更新对应holder BaseAdapter.notifyDataSetInvalidated() a).针对adapter数据源改变引用的情况
ListView中如何显示数据库中的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 setAdapter(CursorAdapter adapter) a).@Override public View newView (Context context, Cursor cursor, ViewGroup parent) { ViewHolder viewHolder= new ViewHolder (); LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View view=inflater.inflate(R.layout.item_contacts ,parent,false ); viewHolder.tv_name=(TextView) view.findViewById(R.id.tv_showusername ); view.setTag(vieHolder); return view; } @Override public void bindView (View view, Context context, Cursor cursor) { Log. i("cursor" ,"bindView=" +view); ViewHolder viewHolder=(ViewHolder) view.getTag(); String name=cursor.getString(cursor.getColumnIndex(PersonInfo.NAME)); String phoneNumber=cursor.getString(cursor.getColumnIndex(PersonInfo.PHONENUMBER)); viewHolder. tv_name.setText(name); viewHolder. tv_phonenumber.setText(phoneNumber); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 a).class CursorAdapter extends BaseAdapter { public Object getItem (int position) { if (mDataValid && mCursor != null ) { mCursor.moveToPosition(position); return mCursor; } else { return null ; } } public int getCount () { if (mDataValid && mCursor != null ) { return mCursor.getCount(); } else { return 0 ; } } public long getItemId ( int position) { if (mDataValid && mCursor != null ) { if ( mCursor.moveToPosition(position)) { return mCursor.getLong( mRowIDColumn) } else { return 0 ; } } else { return 0 ; } } public View getView ( int position, View convertView, ViewGroup parent) { if (!mDataValid) { throw new IllegalStateException ( "this should only be called when the cursor is valid" ); } if (!mCursor.moveToPosition(position)) { throw new IllegalStateException ( "couldn't move cursor to position " + position); } View v; if (convertView == null ) { v = newView( mContext, mCursor, parent); } else { v = convertView; } bindView(v, mContext, mCursor); return v; } }
66.android TypedArray
在res/value下新建attr.xml文件
1 2 3 4 5 6 7 8 a).<?xml version="1.0" encoding="utf-8" ?> <resources> <declare-styleable name="MyView" > <attr name="myTextSize" format="dimension" /> <attr name="myColor" format="color" /> <attr name=“title” format=“string”/> </declare-styleable> </resources>
在布局中声明自己的类空间,并进行组件引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:myapp="http://schemas.android.com/apk/res/com.eyu.attrtextdemo" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <com.eyu.attrtextdemo.MyView android:layout_height="wrap_content" android:layout_width="wrap_content" myapp:myTextSize="20sp" myapp:myColor="#324243" /> </LinearLayout>
在自定义view的构造函数中获取属性
1 2 3 4 a).TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.MyView); int textColor = a.getColor(R.styleable.MyView_myColor, 003344 ); float textSize = a.getDimension(R.styleable.MyView_myTextSize, 33 );
如何动态加载apk文件(未安装)的类
1 2 3 4 >>DexFile dexFile=dalvik.system.DexFile.loadDex(‘/sdcard/my.apk’,’/sdcard/my_temp.apk’,0 ); >>Object obj=dexFile.loadClass(‘mobile.android.file.explorer.widget.Test’,null ).newInstance() >>Method method=obj.getClass().getDeclaredMethod(‘getName’,null ) >>String result=String.valueOf(method.invoke(obj,null ))
69.Android ContentProvider
1 2 3 4 5 :1. <permission android:name="top.shixinzhang.permission.READ_CONTENT" android:label="Permission for read content provider" android:protectionLevel="normal" />
设置访问provider所需要的权限
在应用中注册权限
继承ContentProvider
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 :1. 成员 private static final UriMatcher matcher; public static final String AUTHORITY="com.danding.lProvider" public static final Uri CONTENT_URI=Uri.parse("content://lprovider/person" ) :2. 初始化 >static { mUriMatcher.addURI(AUTHORITY,"person" ,1 ) } >onCreate(){ mDatabase = new DbOpenHelper (mContext).getWritableDatabase(); } :3. 查询 public Cursor query (Uri uri,String[] projection,String selection,String[] selectionArgs) { Cursor c=null ; switch (matcher.match(uri)){ case 1 : c=mSqliteDatabase.query(tableName,projection,selection,selectionArgs, groupBy:null ,having:null ,orderBy:null ) break ; } } b).public Uri insert (Uri uri,ContentValues cv) { low rowId=mSqliteDatabase.insert(tableName,nullColumnHack:”_id”,cv) if (rowId>0 ) { Uri insertedUri=Uri.withAppendedPath(Uri.parse(authorities),”/user/“+rowId) getContext().getContentResolver().notifyChange(insertedUri,null ) } } c).public int delete (Uri uri,String selection,String[] selectionArgs) d).public int update (Uri uri,ContentValues cv,String selection,String[] selectionArgs) { int affected=mSqliteDatabase.update(tableName,cv,selection,selectionArgs); return affected; } e).public String getType (Uri uri) >>ContentObserver :1. getContentResolver().registerContentObserver(Uri uri, boolean notifyForDescendents,ContentObserver co) :2. getContext().getContentResolver().notifyChange(uri,null )
客户端调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState) … Uri uri=Uri.parse(“content: getContentResolver().query(uri,null ,null ,null ,null ,null ); getContentResolver().registerContentObserver(uri,true ,Observer) } static class LObserver extends ContentObserver { public LObserver (Handler handler) { super (handler); } public void onChange (boolean selfChange) { Cursor cursor=getContentResolver().query(…) } }
如何设置Activity为程序启动的默认Activity
1 2 3 4 >><intent-filter> <action android:name=“android.intent.action.MAIN”/> <category android:name=“android.intent.category.LAUNCHER”/> </intent-filter>
启动Activity的几种方式
1 2 >>startActivity intent >>startActivityForResult
Activity传递数据的方式
Intent //setData是用于设置uri a).intent.putExtra(key,value) getIntent().getExtras().getString(key)
类的静态变量
剪切板 a).
1 2 3 4 5 6 ClipboardManager cm=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE) >>设置 ClipboardManager cm=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE); cm.setPrimaryClip(ClipData.newPlainText(“name”,”dancing”)); >>获取 cm.getPrimaryClip().getItemAt(0 ).getText().toString()
全局对象 继承Application,同时修改manifest的的android:name
如何将Activity设置成半透明的模态框
1 2 3 4 >>在style.xml定义继承自@android :style/Theme.Dialog的主题 >><style name=‘test’ parent=‘@android :style/Theme.Dialog’> <item name=‘android:windowBackground”>@color /colorAccent</item> </style>
74.如何接收广播 继承BroadcastReceiver,在onReceive(Context context,Intent intent)中处理广播消息
1 2 3 4 5 6 7 8 9 10 11 12 13 a).public class LReceiver extends BroadcastReceiver { @Override public void onReceive (Context context,Intent intent) { String msg=intent.getExtras().get(“msg”).toString(); } }
orderedBroadcast
1 2 a).设置传播结果 -> setResultExtras(bundle) b).获取传播结果 -> Bundle bundle= getResultExtras(makeup:true )
在AndroidManifest.xml通过intent-filter指定action,category,data
如何获取短信内容 指定intent-filter
如何拦截手机屏幕休眠和唤醒 通过程序里注册广播监听 ScreenOnOffReceiver ScreenOnOffReceiver receiver=new ScreenOnOffReceiver()
1 2 3 4 IntentFilter filter=new IntentFilter () filter.addAction(Intent.ACTION_SCREEN_ON) filter.addAction(Intent.ACTION_SCREEN_OFF) registerRecei ver (receiver,filter)
如何发送广播
1 2 3 4 5 Intent intent… intent.addAction(‘…’) intent.putExtra(key,val) sendBroadcast(intent) Bundle bundle=intent.getExtras()
AIDL与Service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 >>AIDL的创建 a).public class Product implements Parcelable { private int id; private String name; private float price; public Product () { } public Product (Parcel in) { id=in.readInt() name=in.readString() price=in.readFloat() } public void writeToParcel (Parcel dest,flags) { dest.writeInt(id) dest.writeString(name) dest.writeFloat(price) } public static final Parcelable.Creator<Product> CREATOR=new Parcelable .Creator<Product>{ public Product createFromParcel (Parcel in) { return new Product (in) } @Override public Product[] newArray(int size) { return new Product [size]; } } } >>
如何读取联系人信息 Cursor cursor=getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null)
请描述Content Provider的uri由哪几部分组成
1 2 3 4 5 >>scheme content: >>authority 域名,mobile.android.mydata >>path 路径,product >>param 20
介绍开发ContentProvider的步骤 编写一个类,继承ContentProvider 实现query\update\delete\insert 在static块中创建UriMatcher,add需要映射的uri,初始化SqliteOpenHelper和SqliteDatabase的实例 manifest中注册provider并填写authorities属性,readPermission
如何为ContentProvider添加访问权限
1 2 3 4 5 6 7 8 9 10 >>在manifest中声明 自定义权限 <permission android:name=“xxx” android:protectionLevel=“normal”/> >>添加权限限制 a)<provider android:name=".MyContentProvider" android:authorities=“….” ...... android:readPermission=“com.danding.lPermission” /> b)在应用中去使用权限 <uses-permission android:name=“com.danding.lPermission”/>
84.AlertDialog
构建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 a).AlertDialog dialog= new AlertDialog .Builder(this ) .setIcon(R.drawable.menu_contact) .setTitle("title" ) .setMessage("message" ) .setPositiveButton("确定" , new DialogInterface .OnClickListener() { @Override public void onClick (DialogInterface dialog, int which) { } }) .setNegativeButton("返回" , new DialogInterface .OnClickListener() { @Override public void onClick (DialogInterface dialog, int which) { } }) .create(); dialog.show(); dialog.getButton(dialog.BUTTON_NEGATIVE).setTextColor(Color.parseColor(“#009900”))
显示列表项
1 2 3 4 5 6 7 8 9 10 11 12 13 a).builder.setItems(Items, new DialogInterface .OnClickListener() { @Override public void onClick (DialogInterface dialogInterface, int i) { Toast.makeText(getApplicationContext(), "You clicked " +Items[i], Toast.LENGTH_SHORT).show(); } });
显示单选列表项
1 a).setSingleChoiceItems(CharSequence[] items,int checkedItem,DialogInterface.OnClickListener)
显示多选列表
1 2 3 4 5 6 7 a).setMultiChoiceItems(items,new boolean []{ true ,true ,false ,true },new DialogInterface .OnMultiChoiceClickListener() { })
显示自定义列表
1 a).setAdapter(? extends BaseAdapter )
显示自定义View
85.如何自己控制对话框的关闭时机
控制mShowing 当它为false时对话框关闭不起作用
1 2 3 4 5 6 7 8 try { Field field=dialog.getSuperClass().getDeclaredField(‘mShowing’) field.setAccessible(true ) field.set(dialog,false ) }
86.如何改变对话框的透明度
1 2 3 4 >> Window window=dialog.getWindow() WindowManager.LayoutParams lp=window.getAttributes() lp.alpha=0.7f window.setAttributes(lp)
87.如何自己控制Toast的显示和关闭
1 2 3 4 5 6 7 8 9 10 11 12 >>Toast.TN.show、Toast.TN.hide 打开和关闭对话框,对话框不会自动关闭 >>Toast toast=Toast.makeText(this ,’…’,Toast.LENGTH_LONG) try { Field field=toast.getClass().getDeclaredField(’mTN’) field.setAccessible(true ) Object ob=field.get(toast) Method method=ob.getClass().getDeclaredMethod(‘show’,null ) method.invoke(ob,null ) }
如何使用Notification /属于remoteView 构建notification
1 2 3 4 5 6 7 8 9 10 11 12 :1. NotificationManager nm=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE) Notification.Builder builder=new Notification .Builder(this ) Notification notification=builder .setAutoCancel(true ) .setContentTitle(‘title’) .setContentText(‘something here’) .setContentIntent(PendingIntent:pi) .setDefaults(Notification.DEFAULT_SOUND|Notification.DEFAULT_VIBRATE) .setWhen(System.currentTimeMillis()) .setProgress(max:int ,progress:int ,indeterminate:false ) .build(); nm.notify(1 ,notification)
Notification的重复发送 a).nm.notify(id:int,Notification:xx) //覆盖已有的相同id的Notification,可用于更新progress b).contentIntent(PendingIntent:pi) //可针对Flag动态更新PendingIntent:Intent的内容
PendingIntent pi=PendingIntent.getActivity(context:this, requestCode:2,intent,PendingIntent.FLAG_UPDATE_CURRENT) //每次都更新intent的内容,requestCode用于判断是否是相同的pi 取消Notification a).nm.cancel(id)
PendingIntent使用 //跨进程intent 跳转到Activity getActivity(Context context,int requestCode,Intent intent,int flags) 发送广播 getBroadcast(Context context,int requestCode,Intent intent,int flags) 启动Service getService(Context context,int requestCode,Intent intent,int flags) Flag变量说明 //requestCode和notifyId作为识别一个PendingIntent的条件 FLAG_CANCEL_CURRENT //取消当前已存在的PendingIntent FLAG_UPDATE_CURRENT //更新之前相同PendingIntent的内容 FLAG_ONE_SHOT //PendingIntent只能有效一次 FLAG_NO_CREATE
点击Notification触发Activity跳转
contentIntent PendingIntent contentIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT) notification.contentIntent=contentIntent
91.如何自定义Notification的view
设置RemoteViews
RemoteViews remoteViews=new RemoteViews(getPakcageName(),R.layout.xxx) remoteViews.setTextViewText(R.id.xxx,’xxx’) remoteViews.setOnClickPendingIntent(R.id.xxx,pendingIntent) //设置点击的响应 notification.contentView=remoteViews
92.然后为一个Activity添加选项菜单 menu.add(charSequence) //添加1级菜单子项 menu.addSubMenu(charSequence) //添加子菜单 menu.add((int groupId, int itemId, int order, charsequence title) //itemId用于在回调时识别这个MenuItem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 :1. public void onCreateOptionsMenu (Menu menu) { menu.add(groupId:0 ,itemId:1 ,order:0 ,title:”red”) menu.add(0 ,2 ,0 ,”green”).setIcon(R.drawable.xxx) return super .onCreateOptionsMenu(menu); } :2. getMenuInflater().inflate(R.menu.main, menu); :3. public boolean onOptionsItemSelected (MenuItem item) { int groupId=item.getGroupId() int itemId=item.getItemId() Log.v("options_item" ,itemId+"" ) return super .onOptionsItemSelected(item) } :4. public boolean onCreateOptionsMenu (menu:Menu?) { menuInflater.inflate(R.menu.xxx,menu) return super .onCreateOptionsMenu(menu) }
如何将上下文菜单绑定至可视组件View
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { getMenuInflater().inflate(R.menu.menc_main, menu); super .onCreateContextMenu(menu, v, menuInfo); } public boolean onContextItemSelected (MenuItem item) { switch (item.getItemId()) { case R.id.xxx: break ; } return super .onContextItemSelected(item); } registerForContextMenu(button)
在单击菜单时,如何能跳转进别的Activity
1 2 MenuItem item=menu.add(0 ,1 ,0 ,name) item.setIntent(new Intent (this ,ActivityB.class)
菜单的回调函数有哪些
1 2 >>onOptionsItemSelected >>onContextItemSelected
如何使用SharedPreferences存取数据 读取
1 2 3 SharedPreferences sharedPreferences=Context.getSharedPreferences(PREFERENCE_NAME,Context.MODE_PRIVATE) sharedPreferences.getXXX(key,null )
写入
1 2 3 SharedPreferences.Editor editor=sharedPreference.edit() editor.putXXX(key,value) editor.commit()
SharedPreferences //构造函数(string name,int mode)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 >>mode a).MODE_PRIVATE b).MODE_APPEND >>ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream ("employee.dat" )); out.writeObject(staff); out.close(); >>写入SharedPreferences Product product=new Product () ByteArrayOutputStream bos=new ByteArrayOutputStream () ObjectOutputStream oos=new ObjectOutputStream (bos) oos.writeObject(product) SharedPreferences sp=Context.getSharedPreferences(this ,Context.MODE_PRIVATE) String product64=new String (Base64.encodeBase64(bos.toByteArray())) SharedPreferences.Editor editor=sharedPreferences.edit() editor.putString(‘product’,product64) editor.commit() >>从SharedPreferences中装载 String product64=sharedPreferences.getString(‘product’,’’) byte [] bytes=Base64.decode(product64.getBytes(),Base64.DEFAULT); ByteArrayInputStream bis=new ByteArrayInputStream (bytes) ObjectInputStream ois=new ObjectInputStream (bis) product product=(Product) ois.readObject()
99.Android如何解析xml文件 xml文件放置在res/xml/xxx.xml 采用XmlResourceParser进行解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 XmlResourceParser xrp=getResources().getXml(R.xml.books); try { StringBuilder sb=new StringBuilder ("" ); while (xrp.getEventType()!=XmlResourceParser.END_DOCUMENT) { if (xrp.getEventType()==XmlResourceParser.START_TAG) { String tagName=xrp.getName(); if (tagName.equals("book" )) { String bookPrice=xrp.getAttributeValue(namespace:null ,name:”price"); sb.append(" price:"); sb.append(bookPrice); String bookYear=xrp.getAttributeValue(null," date"); sb.append(" date:"); sb.append(bookYear); sb.append(" name:"); sb.append(xrp.nextText()); } sb.append(" \n"); }else if(xrp.getEventType()==XmlResourceParser.TEXT) { sb.append(xrp.getText()); } xrp.next(); } }catch (Exception e) { e.printStackTrace(); }
gson 解析json
1 2 :1. val gson=Gson() val goAbroad:AuditGoAbroad=gson.fromJson(str,AuditGoAbroad::class.java)
转为string
1 2 :1. val gson=Gson() val str=gson.toJson(tmp)
下
如何将table1表的数据和结构复制到table2上
1 create table table2 as select * from table1
102.SQLite
多次插入开启事务 //节省回滚时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 try { mSqliteDatabase.beginTransaction() … mSqliteDatabase.execSQL(….) … mSqliteDatabase.setTransactionSuccessful() }catch (Exception e) { e.printStackTrace() }finally { mSqliteDatabase.endTransaction() }
alter操作 a).alter table student add sex integer default 0 //增加sex column b).目前不支持drop column //采用新建临时表,复制数据,删除旧表
1 2 3 mSqliteDatabase.execSQL(“create table temp as select … from student”) mSqliteDatabase.execSQL(“drop table if exists student”) mSqliteDatabase.execSQL(“alter table temp rename to student”)
SQLiteOpenHelper.getReadableDatabase和SQLiteOpenHelper.getWritableDatabase将数据库文件放哪了 保存在/data/data//databases/DATABASE_NAME
104.然后将带数据的SQLite同apk一起发布
SQLite放置在res/raw a).InputStream is= getResources().openRawResource(R.id.filename) b).InputStream is= getAssets().oepn(path) //path基于assets文件夹
Android不能打开放置在apk的数据库文件,因此在初始化时将文件拷贝到sdcard中
String databaseFilename=‘/sdcard/test.db’ if(!new File(databaseFilename).exists())
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { InputStream is=getResources().openRawResource(R.raw.test) FileOutputStream fos=new FileOutputStream (databaseFiename) byte [] buffer= new byte [1024 ] int count=0 while ((count=is.read(buffer))!=-1 ) { fos.write(buffer,0 ,count) } fos.close() is.close() } SQLiteDatabase database=SQLiteDatabase.createOrOpenDatabase(databaseFilename,null )
105.Socket连接成功后,怎么获取服务器的ip和域名
1 2 socket.getInetAddress().getHostName() socket.getInetAddress().getHostAddress()
BufferedInputStream mark //只有BufferedInputStream实现了mark方法
mark(int readmit) //设置在标记位置失效前能够读取的字节数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 a)bis.mark(is.available()); byte [] buffer=new byte [1024 ]; String result="" ; int len; while ((len=bis.read(buffer))>0 ) { result+=new String (buffer,0 ,len); } bis.reset(); while ((len=bis.read(buffer))>0 ) { result+=new String (buffer,0 ,len); } System.out.println(result); is.close(); bis.close();
如何打开手机中的蓝牙功能
1 2 Intent enableIntent=new Intent (BluetoothAdapter.ACTION_REQUEST_ENABLE) startActivityForResult(enableIntent,1 )
1 2 BluetoothAdapter bluetoothAdapter=BluetoothAdapter.getDefaultAdapter() bluetoothAdapter.enable()
111.如何获取已绑定的蓝牙设备
1 Set<BluetoothDevice> devices=bluetoothAdapter.getBoundedDeviecs()
112.搜索蓝牙的过程中经过哪些状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 IntentFilter filter=new IntentFilter (BluetoothDevice.ACTION_FOUND) this .registerReceiver(receiver,filter) filter=new IntentFilter (BluetoothDevice.ACTION_DISCOVERY_FINISHED) this .registerReceiver(receiver,filter) private final BroadcastReceiver receiver=new BroadcastReceiver () { @Override public void onReceive (Context context,Intent intent) { String action=intent.getAction() if (BluetoothDevice.ACTION_FOUND.equals(action) { BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) if (device.getBondState()!=BluetoothDevice.BOND_BONDED) { } }else if (BluetoothDevice.ACTION_DISCOVERY_FINISHED.equals(action) { } } } adapter.startDiscovery()
113.隐式Intent 系统默认 Intent a).Intent.ACTION_DIAL 拨打电话,需要添加权限android.permission.CALL_PHONE b).Intent.ACTION_NEW_OUTGOING_CALL 用户主动发起拨打电话 c).Intent.ACTION_SCREEN_ON|Intent.ACTION_SCREEN_OFF
114.如何采用广播监听去电和来电
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class MyReceiver extends BroadcastReceiver { @Override public void onReceive (final Context context,Intent intent) { if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL) { }else { } } }
xml描述
1 2 3 4 5 6 7 8 <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <receiver android:name=".PhoneBroadcastReceiver" > <intent-filter android:priority="1000" > <action android:name="android.intent.action.NEW_OUTGOING_CALL" /> <action android:name="android.intent.action.PHONE_STATE" /> </intent-filter> </receiver>
Android支持的电话状态
1 2 3 4 5 6 7 8 9 10 11 12 13 TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE) switch (tm.getCallState()) { case TelephonyManager.CALL_STATE_RINGING: break ; case TelephonyManager.CALL_STATE_IDLE: break ; }
Android如何控制接听和挂断电话
1 2 3 4 5 6 7 8 9 10 11 12 13 TelephonyManager telephonyManager=(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); Class<TelephonyManager> telephonyManagerClass=TelephonyManager.class; Method telephonyMethod=telephonyManagerClass.getDeclaredMethod("getITelephony" ,null ); telephonyMethod.setAccessible(true ); Object obj=telephonyMethod.invoke(telephonyManager,null ); Method endCallMethod=obj.getClass().getDeclaredMethod("endCall" ); endCallMethod.setAccessible(true ); endCallMethod.invoke(obj,null ); Method answerRinginCallMethod=obj.getClass().getDeclaredMethod("answerRinginCallMethod" ,null ); answerRinginCallMethod.setAccessible(true ); answerRinginCallMethod.invoke(obj,null );
117.请给出访问通话记录的Content Provider URI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 >><uses-permission android:name=“READ_CALL_LOG”/> >>Uri.parse(“content: >>M版本以上进行动态权限申请 a).if (PackageManager.PERMISSION_GRANTED==checkSelfPermission(Manifest.permission.READ_CALL_LOG)) { Cursor cursor = getContentResolver().query(Uri.parse("content://call_log/calls" ), null , null , null , null , null ); cursor.moveToFirst(); while (!cursor.isAfterLast()) { int phone = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.NUMBER)); String name=cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_NAME)); Log.v(“call_log”,phone+””); cursor.moveToNext(); } }else { requestPermissions(new String []{ Manifest.permission.READ_CALL_LOG},1 ); } public void onRequestPermissionsResult (int requestCode, @NonNull String[] permissions, @NonNull int [] grantResults) { super .onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case 1 : if (grantResuls[0 ]==PackageManager.PERMISSION_GRANTED) … break ; } }
119.发送短信 //需要动态请求权限:android.permission.SEND_SMS
1 2 3 4 5 6 7 8 9 10 SmsManager smsManager=SmsManager.getDefault() String message=“i gonna fuck u” List<String> msges=smsManager.divideMessage(message); for (int i=0 ;i<msges.size();i++) { smsManager.sendTextMessage(“12345678 ”,null ,”你好吗”,PendingIntent pi,PendingIntent di) }
ContactsContract
Contacts&CommonDataKinds.Phone //存储通讯录中每个人的概要信息 a).查询姓名
1 2 Cursor c=getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null ,null ,…) String name=c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY))
b).查询电话
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Cursor c=getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null ,…) int id=c.getInt(c.getColumnIndex(ContactsContract.Contacts._ID))c.close() c=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null , ContactsContract.CommonDataKinds.Phone.CONTACT_ID+”=?”,id,null ) c.moveToFirst() while (!c.isAfterLast()){ String phone=c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER)) … } c.close()
VideoView 播放视频
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Uri uri = Uri.parse(path) videoView.setVideoURI(uri) videoView.start() videoView.setOnPreparedListener(new MediaPlayer .OnPreparedListener() { @Override public void onPrepared (MediaPlayer mp) { mp.start(); Toast.makeText(MainActivity.this , "开始播放!" , Toast.LENGTH_LONG).show(); } }); videoView.setOnCompletionListener(new MediaPlayer .OnCompletionListener() { @Override public void onCompletion (MediaPlayer mp) { Toast.makeText(MainActivity.this , "播放完毕" , Toast.LENGTH_SHORT).show(); } });
在工程路径下的res/drawable存放tree.png,如果将图片显示在View上
1 2 3 4 5 6 InputStream is=getResources().openRawResource(R.raw.xxx) BitmapFactory.Options options=new BitmapFactory .Options() options.inSampleSize=2 Bitmap bitmap=BitmapFactory.decodeStream(is,null ,opts) canvas.drawBitmap(bitmap,10 ,10 ,null ) Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.tree)
123.调用Drawable进行绘图
1 2 3 4 Drawable.draw(canvas) Drawable drawable=getResources().getDrawable(R.drawable.tree) drawable.setBounds(x,x,x,x) drawable.draw(canvas)
124.如何设置图像透明度
1 2 3 Paint paint=new Paint () paint.setAlpha(0.5 ) canvas.drawBitmap(bitmap,sourceRect,destRect,paint)
125.如何旋转View
1 2 3 4 Matrix Matrix matrix=new Matrix () matrix.setRotate(120 ,80 ,80 ) canvas.setMatrix(matrix)
126.Activity 切换
1 2 3 4 5 6 7 8 9 10 11 overridePendingTransition(int enterAnim,int exitAnim) a).Intent intent=new Intent (this ,xxx.class) startActivity(intent); overridePendingTransition(R.anim.enter,R.anim.exit) b).public void finish () { overridePendingTransition(R.anim.enter,R.anim.exit) }
127.Android
xml //定义animation-list,位于res/drawable,属于帧动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?xml version="1.0" encoding="utf-8" ?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false" ><!— oneshot 代表只会播放一次,默认为循环播放-> <item android:drawable="@mipmap/lottery_1" android:duration="200" /> <item android:drawable="@mipmap/lottery_2" android:duration="200" /> <item android:drawable="@mipmap/lottery_3" android:duration="200" /> <item android:drawable="@mipmap/lottery_4" android:duration="200" /> <item android:drawable="@mipmap/lottery_5" android:duration="200" /> <item android:drawable="@mipmap/lottery_6" android:duration="200" /> </animation-list> imageView.setImageResource(R.drawable.lottery_animlist) AnimationDrawable animationDrawable=(AnimationDrawable)button.getBackground() animationDrawable.start()
AnimationDrawable //api .start() .stop() .isRunning() .getNumberOfFrames() .setOneShot()
onAnimationEnd 接口 采用hashCode()进行区分
128.动画
AlphaAnimation 透明度渐变,针对 a)动画声明 =>
1 2 3 4 5 <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:fromAlpha="1.0" android:toAlpha="0.1" android:duration="2000" />
b)动画调用 =>
1 2 Animation animation= AnimationUtils.loadAnimation(this ,R.anim.alpha_demo) imageView.startAnimation(animation)
RotateAnimation 旋转渐,针对 a)动画声明 =>
1 2 3 4 5 6 7 <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:fromDegrees="0" android:toDegrees="360" android:duration="1000" android:repeatCount="1" android:repeatMode="reverse" />
ScaleAnimation 放缩渐变,需要指定参考点,针对 a)动画声明 =>
1 2 3 4 5 6 7 8 9 <scale xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_interpolator" android:fromXScale="0.2" android:toXScale="1.5" android:fromYScale="0.2" android:toYScale="1.5" android:pivotX="50%" android:pivotY="50%" android:duration="2000" />
TranslateAnimation 位移渐变,需要指定开始和结束坐标,针对 a)动画声明 =>
1 2 3 4 5 6 7 <translate xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/accelerate_decelerate_interpolator" android:fromXDelta="0" android:toXDelta="320" android:fromYDelta="0" android:toYDelta="0" android:duration="2000" />
LayoutAnimation //在ViewGroup中指定,并作用于它的各个子元素
a)动画声明 => //res/anim/anim_layout.xml
1 2 3 4 5 <layoutAnimation xmlns:android=“http: android:delay=“0.5 ” android:animationOrder=“normal” android:animation=“@anim /anim_item”/>
b)为子元素指定具体动画
1 2 3 4 5 <set xmlns:android=“http: android:duration=“3000 ” > <alpha android:fromAlpha=“0.0 android:toAlpha=“1.0 ”/> <translate android:fromXDelta=500 ” android:toXDelta=“0 ”/> </set>
c)设置父容器属性
1 <ListView android:layoutAnimation=“@anim /anim_layout”/>
AnimationSet 动画集合,针对 a)动画声明 =>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/decelerate_interpolator" android:shareInterpolator="true" > <scale android:duration="2000" android:fromXScale="0.2" android:fromYScale="0.2" android:pivotX="50%" android:pivotY="50%" android:toXScale="1.5" android:toYScale="1.5" /> <rotate android:duration="1000" android:fromDegrees="0" android:repeatCount="1" android:repeatMode="reverse" android:toDegrees="360" /> <translate android:duration="2000" android:fromXDelta="0" android:fromYDelta="0" android:toXDelta="320" android:toYDelta="0" /> <alpha android:duration="2000" android:fromAlpha="1.0" android:toAlpha="0.1" /> </set>
129.属性动画 Animator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 a)ObjectAnimator .ofFloat(view,”rotateX”,0f ,360f ) .setDuration(500 ) .start(); b)ObjectAnimator .ofFloat(view,”y”,sunYStart,sunYEnd) .setDuration(3000 ) .setRepeatCount(Animation.INFINITE) .setRepeatMode(Animation.REVERSE) .setInterpolator(new AccelerateInterpolator ()); oa.start(); c)TypeEvaluator ObjectAnimator .ofInt(mSkyView,”backgroundColor”,mBlueSkyColor,mRedSkyColor) .setDuration(3000 ) .setEvaluator(new ArgbEvaluator ()) .start(); d)添加插值监听 oa.start(); oa.addUpdateListener(new AnimatorUpdateListener () { @Override public void onAnimationUpdate (ValueAnimator animator) { float cVal = (Float) animator.getAnimatedValue(); view.setAlpha(cVal); view.setScaleX(cVal); view.setScaleY(cVal); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 a)添加插值监听 ValueAnimator animator=ValueAnimator.ofFloat(0 ,mScreenHeight-mBlueBall.getHeight()); animator.setDuraton(3000 ).start(); animator.addUpdateListener(new AnimatorUpdateListener (){ @Override public void onAnimationUpdate (ValueAnimator animation) { mBlueBall.setTranslationY((Float)animation.getAnimatedValue()) } }); va.setDuration(3000 ) b)返回自定义类型的估值 ValueAnimator animator=new ValueAnimator (); animator.setDuration(3000 ); animator.setObjectValues(new PointF (0 ,0 )); animator.setEvaluator(new TypeEvaluator <PointF>(){ @Override public PointF evaluate (float fraction, PointF startValue, PointF endValue) { Log.e(TAG, fraction * 3 + "" ); PointF point = new PointF (); point.x = 200 * fraction * 3 ; point.y = 0.5f * 200 * (fraction * 3 ) * (fraction * 3 ); return point; } });
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ObjectAnimator anim1 = ObjectAnimator.ofFloat(mBlueBall, "scaleX" ,1.0f , 2f ); ObjectAnimator anim2 = ObjectAnimator.ofFloat(mBlueBall, "scaleY" , 1.0f , 2f ); AnimatorSet animSet=new AnimatorSet (); animSet.setDuration(2000 ) animSet.setInterpolator(new LinearInterpolator ()); animist.playTogether(anim1,anim2); animist.start(); a)AnimatorSet 设置动画执行秩序 animSet.play(anim1).with(anim2) animSet.play(anim2).before(anim3) b)AnimatorSet xml设置 Animator animator=AnimatorInflater.loadAnimator(this ,R.animator.property_t); animator.setTarget() animator.start()
定义字符串数组资源
1 2 3 4 5 6 7 <resources> <string-array name=‘planet_array’> <item>venus</item> <item>mars</item> <item>earth</item> </string-array> </resources>
1 String[] names=getResources().getStringArray(R.array.names)
图层layer资源 在res/drawable layer.xml
1 2 3 4 5 6 7 8 9 <?xml version=“1.0 ” encoding=“utf-8 ”?> <layer-list xmlns:android=“http: <item> <bitmap android:src=“” android:gravity=“center”/> </item> <item android:top=“20dp” android:left=“20dp”> <bitmap android:src=“” android:gravity=“center”/> </item> </layer-list>
Clip图像资源
1 2 3 4 5 <clip xmlns=“http: android:drawable=“” android:orientation=“horizontal” android:gravity=“left” />
1 2 ClipDrawable drawable=(ClipDrawable)imageView.getBackground() drawable.setLevel(3000 )
ShapeDrawable 放置在res/drawable shape.xml
1 2 3 4 5 6 7 8 9 <shape xmlns=“http: <!— 定义渐变色 —> <gradient android:startColor=“#00f ” android:endColor=“#f00” android:angle=“45 ”/> <padding android:left=“7dp” android:right=“7dp”/> <corners android:radius=“8dp”/> <size android:height="20dp" android:width="20dp" /> <solid android:color=“”/> <stroke android:color=“” android:width=“”/> </shape>
如何统一设置多个View的android:textSize和android:textColor 放置于res/values style.xml
1 2 3 4 5 6 <resources> <style name=“customText” parent=“@style /Text”> <item name=“android:textSize”>20sp</item> <item name=“android:textColor”>#008</item> </style> </resources>
布局文件中的”@“、”+”、”?”含义 引用资源 +号代表所引用的资源id在R类不存在 ?代表引用当前主题的属性
android获取屏幕高度和宽度的办法 :1.通过获取DisplayMetrics
1 2 3 DisplayMetrics dm=getResources().getDisplayMetrics() int width=dm.widthPixels int height=dm.heightPixels
:2.通过windowManager
1 2 3 >DisplayMetrics dm=new DisplayMetrics () getWindowManager().getDefaultDisplay(dm) dm.widthPixels , dm.heightPixels
138.AsyncTask 在ui主线程 执行.execute(T… params, T …progress,T result) 对应的参数会再doInBackground接收到回调 => onPreExecute、onPostExecute、doInBackground、onProgressUpdate AsyncTask的模板参数 <Params,Progress,Result>
示例: :1.ProgressBarAsyncTask asyncTask = new ProgressBarAsyncTask(textView, progressBar); asyncTask.execute(1000); //AsyncTask子类 public class ProgressBarAsyncTask extends AsyncTask<Integer,Integer,String>{
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 private TextView textView; private ProgressBar progressBar; public ProgressBarAsyncTask (TextView textView, ProgressBar progressBar) { super (); this .textView = textView; this .progressBar = progressBar; } @Override protected String doInBackground (Integer... params) { NetOperator netOperator = new NetOperator (); int i = 0 ; for (i = 10 ; i <= 100 ; i+=10 ) { netOperator.operator(); publishProgress(i); } return i + params[0 ].intValue() + "" ; } @Override protected void onProgressUpdate (Integer... values) { int vlaue = values[0 ]; progressBar.setProgress(vlaue); } @Override protected void onPreExecute () { textView.setText("开始执行异步线程" ); } @Override protected void onPostExecute (String result) { textView.setText("异步操作执行结束" + result); } }
139.Intent可传递的数据类型
8种基本数据类型:double、int、float、char、long、byte、short、String 实现了Parcelable接口的类 实现了Serializable接口的类
141.AlarmManager //闹钟
初始化 :1.AlarmManager am=(AlarmManager)getSystemService(Context.ALARM_SERVICE)
类型 :1.AlarmManager.RTC_WAKEUP | AlarmManager.RTC //按系统时钟计算,WAKEUP表示睡眠可用 :2.AlarmManager.ELAPSED_REALTIME | AlarmManager.ELAPSED_REALTIME_WAKEUP//按真实流逝时间计算 :3.AlarmManager.POWER_OFF_WAKEUP //关机也可使用
设定闹钟 a). am.set(int type,long startTime,PendingIntent pi) b). am.setRepeating(int type,long startTime,long interval,PendingIntent pi) //设置重复闹钟 c).pi //当闹钟触发后,执行pi。可以通过Service或者Activity去进行提示
HandlerThread //例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 HandlerThread mThread=new HandlerThread (“xx”); mThread.start(); handler = new Handler ( myHandlerThread.getLooper() ){ @Override public void handleMessage (Message msg) { super .handleMessage(msg); Log.d( "handler " , "消息: " + msg.what + " 线程: “+ Thread.currentThread().getName() ) ; } }; handler.send(xxx)
Thread
1 2 a)没有Looper 所以需要在Thread的run中去执行Looper.prepare()和Looper.loop() b)Hanlder的初始化需要当前线程具有looper,所以也得放置在Looper的循环中
HandlerThread a)HandlerThread自行进行Looper的prepare、loop等api b)允许调用getLooper()获取Looper实例 c)可以在handleMessage执行耗时 d)HandlerThread自己创建Looper,分担了主Looper的压力 e)异步请求是排队调用的,不适合网络IO
自定义ViewGroup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public ViewGroup.LayoutParams generateLayoutParams (AttributeSet attrs) { return new MarginLayoutParams (getContext(),attrs); } onMeasure => a)子View的宽高测量调用 b)setMeasuredDimension设置宽高 @Override protected void onMeasure (int widthMeasureSpec,int heightMeasureSpec) { int widthMode=MeasureSpec.getMode(widthMeasureSpec); int heightMode=MeasureSpec.getMode(heightMeasureSpec); int sizeWidth=MeasureSpec.getSize(widthMeasureSpec); int sizeHeight=MeasureSpec.getSize(heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec) int cCount = getChildCount(); int cWidth = 0 ; int cHeight = 0 ; MarginLayoutParams cParams = null ; for (int i = 0 ; i < cCount; i++) { View childView = getChildAt(i); cWidth = childView.getMeasuredWidth(); cHeight = childView.getMeasuredHeight(); cParams = (MarginLayoutParams) childView.getLayoutParams(); if (i == 0 || i == 1 ) { tWidth += cWidth + cParams.leftMargin + cParams.rightMargin; } if (i == 2 || i == 3 ) { bWidth += cWidth + cParams.leftMargin + cParams.rightMargin; } width=Math.max(tWidth,bWidth) } setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : width, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight : height); } onLayout => 作为虚函数需要由子类实现 a)放置各个子View protected void onLayout (boolean changed,int l,int t,int r,int b) { int cCount = getChildCount(); MarginLayoutParams cParams = null ; for (int i=0 ;i<cCount;i++) { View childView = getChildAt(i); int cWidth=childView.getMeasuredWidth() int cHeight=childView.getMeasuredHeight() childView.layout(cl, ct, cr, cb); } }
147.JVM的GC算法
标记-清除 //只清除可回收内存,使得在清除后出现很多不连续的小空间碎片 a)标记对象分为:可用内存、可回收内存、存活对象 b)释放可回收内存 c)缺点:清除标记后产生大量的不连续空间
标记-整理 //需要进行整理,同时将存活对象移动到边界的一端 a)标记对象: 略 b)增加存活对象的整理过程
复制算法 //新生代采用复制算法 - > 8:1:1 a)它将内存分为大小相等的2块,每次只是使用其中一块。当一块内存用完之后,就将存活的对象复制到另外一块内存区域并将本块内存清理 b)新生代的复制算法: ’8’比例和一块’1’比例的内存作为活动空间,剩余的’1’比例作为空闲空间 每次新建对象放置于’8’比例的内存中,GC时清除活动空间的可回收对象,并把存活对象移动到空闲空间
活动空间和空闲空间的轮换,始终在’1’比例的内存块中进行
分代收集 //新生代的对象有’年龄’计算,当超过阀值,转入老年代内存 a)新生代=>Eden+s0+1 老生代=>Perm
OkHttp 使用 :1.get方式 //new OkHttpClient -> 构造Request对象 -> newCall ->enqueue or execute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 > String url = "http://wwww.baidu.com" OkHttpClient okHttpClient = new OkHttpClient () final Request request = new Request .Builder() .url(url) .get() .build(); Response response= okHttpClient.newCall(request).execute() String result= response.body().toString > String url = "http://wwww.baidu.com" OkHttpClient okHttpClient = new OkHttpClient () final Request request = new Request .Builder() .url(url) .get() .build() okHttpClient.newCall(request).enqueue(ne Callback () { @Override public void onFailure (Call call, IOException e) { Log.d(TAG, "onFailure: " ); } @Override public void onResponse (Call call, Response response) throws IOException { Log.d(TAG, "onResponse: " + response.body().string()); } })
:2.post方式 //new OkHttpClient -> 构造RequestBody、指定mimeType -> 构造request
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 > OkHttpClient okHttpClient= new OkHttpClient () RequestBody requestBody=new FormBody .Builder() .add("username" ,"danding" ) .build() Request request= new RequestBody .Builder() .url(url) .post(requestBody) .build() okHttpClient.newCall(request).enqueue(new CallBack (){ @Override public void onFailure (Call call, IOException e) { Log.d(TAG, "onFailure: " ); } @Override public void onResponse (Call call, Response response) throws IOException { Log.d(TAG, "onResponse: " + response.body().string()); } })
:3.自定义Interceptor
1 2 3 4 5 6 7 8 9 10 > @Override public Response intercept (Chain chain) throws IOException { Request request=chain.request() Response response=chain.proceed(request) return response }
源码 :1.创建OkHttpClient
1 2 3 4 5 6 7 8 9 10 11 12 13 > public OkHttpClient () { this (new Builder ()); } OkHttpClient(Builder builder) { this .interceptors = Util.immutableList(builder.interceptors)... }
:2.创建Request //代码new Request.Builder().url(url).build()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 > public final class Request { public Builder () { this .method = "GET" ; this .headers = new Headers .Builder(); } public Builder url (String url) { HttpUrl parsed = HttpUrl.parse(url); return url(parsed) } }
:3.okHttpClient.newCall(request)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 > public class OkHttpClient implements Cloneable , Call.Factory, WebSocket.Factory { @Override public Call newCall (Request request) { return new RealCall (this , request, false ); } ... } > final class RealCall implements Call { @Override public void enqueue (Callback responseCallback) { synchronized (this ) { if (executed) throw new IllegalStateException ("Already Executed" ); executed = true ; } captureCallStackTrace(); client.dispatcher().enqueue(new AsyncCall (responseCallback)) } } > class AsyncCall extends NamedRunnable { protected void execute () { try { Response response = getResponseWithInterceptorChain() ... List<Interceptor> interceptors = new ArrayList <>() interceptors.addAll(client.interceptors()) interceptors.add(retryAndFollowUpInterceptor) interceptors.add(new BridgeInterceptor (client.cookieJar())) interceptors.add(new ConnectInterceptor (client)) interceptors.add(new CallServerInterceptor (forWebSocket)) ... Interceptor.Chain chain = new RealInterceptorChain ( interceptors, null , null , null , 0 , originalRequest); return chain.proceed(originalRequest) ... public Response proceed (Request request, StreamAllocation streamAllocation, HttpCodec httpCodec ,RealConnection connection) throws IOException { if (index >= interceptors.size()) throw new AssertionError () val interceptor=interceptos.get(index) Response response = interceptor.intercept(next) } responseCallback.onResponse(RealCall.this , response) } }
:4.Dispatcher线程池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 > private int maxRequests = 64 ;private int maxRequestsPerHost = 5 ;private ExecutorService executorService;private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque <>();private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque <>();private final Deque<RealCall> runningSyncCalls = new ArrayDeque <>();> enqueue synchronized void enqueue (AsyncCall call) { if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) { runningAsyncCalls.add(call); executorService().execute(call); } else { readyAsyncCalls.add(call); } }
:5.Interceptor
1 2 3 >BridgeInterceptor -> 添加Content-Type、Content-Length、User-Agent等协议头 >ConnectInterceptor -> .... >CallServerInterceptor -> 实际发送网络请求
149.ButterKnife //源码阅读 初始化 :1.定义处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 ```java public class MyProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion () { return SourceVersion.latestSupported(); } @Override public synchronized void init (ProcessingEnvironment processingEnv) { super .init(processingEnv); } @Override public Set<String> getSupportedAnnotationTypes () { Set<String> annotataions = new LinkedHashSet <String>(); annotataions.add("com.example.MyAnnotation" ); return annotataions; } @Override public boolean process (Set<? extends TypeElement> annoations, RoundEnvironment env) { return false ; } }
:2.注册处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 @AutoService(Processor.class) Public class xxx extends AbstractProcessor { } >>流程 :1. process (annotations:Set , env:RoundEnvironment) ->Map<TypeElement, BindingSet> targetClassMap = findAndParseTargets(env) ...Map<TypeElement,BindingSet> targetClassMap ...for (Element element:env.getElementsAnnotatedWith(BindView.class) try { parseBindView(element, Map<TypeElement,BindingSet.Builder>builderMap,...) ....enclosingElement=(TypeElement)element.getEnclosingElement() TypeMirror elementType = element.asType() if (elementType.getKind() == TypeKind.TYPEVAR) { TypeVariable typeVariable = (TypeVariable) elementType elementType = typeVariable.getUpperBound() } if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) { throw Error } int id = element.getAnnotation(BindView.class).value() BindingSet.Builder builder = builderMap.get(enclosingElement) if (builder!=null ){ } else builder = getOrCreateBindingBuilder(builderMap, enclosingElement) ... builder=BindingSet.newBuilder(enclosingElement) ->packageName= getPackage(enclosingElement).getQualifiedName() className=enclosingElement.getQualifiedName().toString() bindingClassName = ClassName.get(packageName, className + "_ViewBinding" ) ... builderMap.put(enclosingElement,builder) String name = simpleName.toString(); TypeName type = TypeName.get(elementType) builder.addField(resourceId, new FieldViewBinding (name, type, required)) ... new FieldViewBinding (name, type, required) ... viewId=new ViewBinding .Builder(id) viewIdMap.put(id,viewId) viewId.setFieldBinding(biding) } :2. for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) { TypeElement typeElement = entry.getKey(); BindingSet binding = entry.getValue(); binding.brewJava ... BuildingSet.createType TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName()) .addModifiers(PUBLIC); if (parentBinding != null ) { result.superclass(parentBinding.bindingClassName); } else { result.addSuperinterface(UNBINDER); } if (hasTargetField()) { result.addField(targetTypeName, "target" , PRIVATE); } if (isActivity) result.addMethod(createBindingConstructorForActivity(useAndroidX)); .... MethodSpec.Builder builder = MethodSpec.constructorBuilder() .addAnnotation(useAndroidX ? UI_THREAD_ANDROIDX : UI_THREAD) .addModifiers(PUBLIC) .addParameter(targetTypeName, "target" ); result.addMethod(createBindingConstructor(sdk, debuggable, useAndroidX)) ... if (hasViewBindings()) { for (ViewBinding binding : viewBindings) { addViewBinding(constructor, binding, debuggable) -> FieldViewBinding fieldBinding = binding.getFieldBinding() } } if (hasViewBindings() || parentBinding == null ) { result.addMethod(createBindingUnbindMethod(result, useAndroidX)); } }
概念
Element //包含程序中的包、类、方法等 TypeElement //类 VariableElement //变量 ExecutableElement //函数
TypeElement 不含有类的信息,转换为Element后,调用element.asType()获取TypeMirror :3.Typemirror //tm=element.asType() 包含元素信息 //比如TYPEVAR等 getKind() //DECLARED|EXECUTE toString() //返回类型完全限定名 :4.FieldViewBinding 类 constructor FieldViewBinding(String name, TypeName type, boolean required) { this.name = name; this.type = type; this.required = required; } b).getModifiers //返回Modifier修饰 c).getSimpleName //返回注释元素的变量名 d).getEnclosingElement //返回上一级Element,Enclosing(Activity)= Annotation,Enclosing(Button)=Activity e). processingEnv.getElementUtils().getPackageOf(Element ) //获取包名 解析BindView的流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 :1. parseBindView(Element element,....) TypeMirror elementType = element.asType() if (elementType.getKind() == TypeKind.TYPEVAR) { TypeVariable typeVariable = (TypeVariable) elementType; elementType = typeVariable.getUpperBound(); } if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) { if (elementType.getKind() == TypeKind.ERROR) { note(element, "@%s field with unresolved type (%s) " + "must elsewhere be generated as a View or interface. (%s.%s)" , BindView.class.getSimpleName(), elementType, enclosingElement.getQualifiedName(), element.getSimpleName()); } else { error(element, "@%s fields must extend from View or be an interface. (%s.%s)" , BindView.class.getSimpleName(), enclosingElement.getQualifiedName(), element.getSimpleName()); hasError = true ; } } int id = element.getAnnotation(BindView.class).value(); BindingClass bindingClass = targetClassMap.get(enclosingElement); if (bindingClass != null ) { ViewBindings viewBindings = bindingClass.getViewBinding(getId(id)); if (viewBindings != null && viewBindings.getFieldBinding() != null ) { FieldViewBinding existingBinding = viewBindings.getFieldBinding(); error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)" , BindView.class.getSimpleName(), id, existingBinding.getName(), enclosingElement.getQualifiedName(), element.getSimpleName()); return ; } } else { bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement); } String name = element.getSimpleName().toString(); TypeName type = TypeName.get(elementType); FieldViewBinding binding = new FieldViewBinding (name, type, required) bindingClass.addField(getId(id), binding);
1 :2. getOrCreateTargetClass
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >BindingClass bindingClass = targetClassMap.get(enclosingElement); if (bindingClass == null ) { TypeName targetType = TypeName.get(enclosingElement.asType()) String packageName = getPackageName(enclosingElement); String className = getClassName(enclosingElement, packageName) ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding" ) boolean isFinal = enclosingElement.getModifiers().contains(Modifier.FINAL) bindingClass = new BindingClass (targetType, bindingClassName, isFinal) targetClassMap.put(enclosingElement, bindingClass); }
:3.process //生成java文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 > for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) { TypeElement typeElement = entry.getKey(); BindingClass bindingClass = entry.getValue(); JavaFile javaFile = bindingClass.brewJava(); try { javaFile.writeTo(filer); } catch (IOException e) { } }
:4.brewJava //生成
1 2 3 4 5 6 7 8 9 10 11 @Target({ ElementType.FIELD}) @Retention(RetentionPolicy.CLASS) public @interface BindView{ @IdRes int value () ; }
1 2 3 4 5 6 7 8 9 10 11 >public final class HelloWorld { public static void main (String[] args) { System.out.println(“hello,JavaPoet!”); } }
b).? extends AbstractProcessor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 { private Filer filer;@Override public synchronized void init (ProcessingEnvironment processingEnv) { super .init(processingEnv); filer = processingEnv.getFiler(); } public boolean process (Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (TypeElement element : annotations) { if (element.getQualifiedName().toString().equals(JPHelloWorld.class.getCanonicalName())) { MethodSpec main = MethodSpec.methodBuilder("main" ) .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void .class) .addParameter(String[].class, "args" ) .addStatement("$T.out.println($S)" , System.class, "Hello, JavaPoet!" ) .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld" ) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); try { JavaFile javaFile = JavaFile.builder("com.example" , helloWorld) .addFileComment(" This codes are generated automatically. Do not modify!" ) .build(); javaFile.writeTo(filer); } catch (IOException e) { e.printStackTrace(); } } } return true ; } }
addViewBinding //赋值BindView对应的View id
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 :1. CodeBlock.Builder builder=CodeBlock.builder() .add("target.$L" ,fieldBinding.getName()) :2. 赋值 if (requiresCast) { builder.add("($T) " , fieldBinding.getType()); } builder.add("source.findViewById($L)" , binding.getId().code); - bind :1. public static Unbinder bind (Activity target) { View sourceView=target.getWindow().getDecorView() return createBinding(target,sourceView) } :2. public static Unbinder crateBinding (Object target,View source) { Class<?> targetClass =target.getClass() if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName()); Constructor<? extends Unbinder > constructor = findBindingConstructorForClass(targetClass) if (constructor == null ) { return Unbinder.EMPTY; } try { return constructor.newInstance(target, source); } catch (IllegalAccessException e) { …. } } :3. Constructor<? extends Unbinder > findBindingConstructorForClass(Class<?> cls){ Constructor<? extends Unbinder > bindingCtor = BINDINGS.get(cls) if (bindingCtor != null ) { if (debug) Log.d(TAG, "HIT: Cached in binding map." ); return bindingCtor; } String clsName = cls.getName() try { Class<?> bindingClass = cls.getClassLoader().loadClass(clsName +"_ViewBinding”) bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class); } catch (ClassNotFoundException e) { bindingCtor = findBindingConstructorForClass(cls.getSuperclass()); } catch (NoSuchMethodException e) { } BINDINGS.put(cls, bindingCtor) }
150.Dalvik内存模型
Heap=Active Heap + Zygote Heap
Bitmap来标记引用情况 :1.采用一个unsigned long数组来维护Heap Bitmap, Bitmap的某个位为1则标记该对象正在使用 :2.采用Live Bitmap+ Mark Bitmap来描述堆对象
Live Bitmap用于标记上一次GC时被引用的对象,也就是未回收对象 Mark Bitmap标记当前GC有被引用的对象 回收Live Bitmap=1, Mark Bitmap=0的对象
垃圾收集 :1.mark阶段
标记根对象(非并行) -> 标记栈变量、静态成员、方法区常量、本地方法被引用的对象 标记被根集所引用的对象(允许并行) -> 增加Card Table,以免其他线程更改了该对象的引用情况
Card Table :1.Card Table 由Card组成,每个Card可以为CLEAN或者DIRTY,每个Card占用一个字节 :2.在堆中,连续GC_CARD_SIZE地址的对象共用一个Card,Dalvik设置为128,在32位机器上就是4个对象
Mark Stack //递归方式的标记对象 :1.避免函数递归层次过深占用内存 :2.在第一次标记的过程中,先找到根集对象,然后将其压入Mark Stack
在之后的过程中,弹出Mark Stack的对象,并标记在Bitamap之中,并将该对象的引用也压入Mark Stack
151.ART垃圾回收
Image Space、Zygote Space、Allocation Space、Large Object Space、Card Table
Bitmap=Live Bitmap +Mark Bitmap
mod_union_table :1.image_mod_union_table -> 记录在并行GC阶段,image space上所分配的对于在Zygote、Allocation对象的引用 :2.zygote_mod_union_table -> 记录在并行Gc阶段,zygote space上分配的对于Allocation堆的对象的引用 :3.跟Card Table配合,使得Card Table可以在标记阶段重复使用
第一步是调用ModUnionTable类的成员函数ClearCards清理Card Table里面的Dirty Card,并且将这些Dirty Card记录在Mod Union Table中。第二步是调用ModUnionTable类的成员函数Update将遍历记录在Mod Union Table里面的Drity Card,并且找 到对应的被修改对象,然后将被修改对象引用的其它对象记录起来。第三步是调用ModUnionTable类的成员函数MarkReferences标记前 面第二步那些被被修改对象引用的其它对象。通过这种方式,就可以使用Card Table可以在标记阶段重复使用,即在执行第二步之前,重 复执行第一步,最后通过Mod Union Table将所有被被修改对象引用的其它对象收集起来统一进行标记,避免对相同对象进行重复标记
stack :1.Mark Stack,用于在GC过程中递归标记对象
151.2.JVM 可达性分析中哪些对象可以作为根节点
栈的引用对象
本地方法栈中的引用对象 //指jni
类的静态成员
方法区重的常量引用对象
HashSet
采用HashMap进行实现,值为new出的Object
采用HashMap的put进行不重复性的保证
SSL中的对称加密和非对称加密
对称加密 a).当通过浏览器向服务器请求安全网页时(https://…) b).服务器同时把证书和公钥发送过来 c).浏览器检查证书是不是由信赖机构颁发的 d).浏览器随机生成对称密钥,并采用服务器发送的公钥加密自身的公钥,同时采用公钥加密请求,一并发送至服务器 e).服务器用自己的私钥解密了发过来的密钥,并用该密钥解析加密后的请求 f).服务器用收到的密钥加密返回的数据 G).浏览器用自身的私钥解密服务器返回的加密消息 //那么中间人有没有可能解密出信息呢,关注点主要在客户端发来的信息 //从d步骤开始检测,中间人截取到了被加密的浏览器密钥,但是无法猜测对应服务器的公钥,故无法解密 // a)假设中间人清除目标服务器,首先获取了服务器的公钥,但是只有私钥才能解密被该公钥加密后的信息,故也无法解密
Handler的内存泄露问题
当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可 能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收
改善 :1.Activity的引用包装为WeakReference<>弱引用 :2.声明Handler为静态类
Android volatile关键字
JVM对于volatile所提供的内存屏障 //内存屏障的作用 -> 阻止屏障2侧的指令重排 a).Load Barrier //在指令前插入Load Barrier,可以让高速缓存的数据失效,强制从主存加载 b).Store Barrier //在指令后插入Store Barrier,强制写回主存 c).在每个volatile写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障 在每个volatile读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障
多线程工作时,线程对于特定变量采取对主存读取后拷贝到自身内存区的办法进行操作
volatile保证该变量是通过共享内存进行同步的
volatile不能替代synchronized,因为不能保证原子性操作
EventBus 源码阅读 //反射Class通常采用通配符 =>Class<?>或者Class<? extends T>
成员 :1.subscriptionsByEventType:HashMap //subscription = subscriber+subscriberMethod :2.typesBySubscriber :3.subscriberMethodFinder :4.METHOD_CACHE:HashMap<Subscriber,List> //缓存 :5.PostingThreadState //带有事件队列,当前线程的posting状态
流程 :1.register
1 2 3 4 5 6 7 8 9 10 11 12 val subscriberClass=subscriber.getClass() List<SubscriberMethod> subscriberMethods=subscriberMethodFinder.findSubscriberMethods(cls) { ...findUsingReflection ...moveToSuperclass ...findUsingReflectionInSingleClass -> 检查参数是否只有一个 -> getAnnotation -> 获取Subscribe注解信息 -> 生成subscriberMethod }
:2.subscribe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >for (subscriberMethod in subscriberMethods) { subscribe(subscriber,subscriberMethod) ...Class<?> eventType = subscriberMethod.eventType; subscription newSubscription = new Subscription (subscriber, subscriberMethod) val subscriptions=subscriptionsByEventType.get(eventType) if (subscriptions.contains(newSubscription)) throw new Error subscriptionsByEventType.put(eventType,subscriptions) ...List<Class<?>> events=typesBySubscriber.get(subscriber) events.add(eventType) }
:3.post //发送事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 >public void post (Object event) { ..PostingThreadState postingState=currentPostingThreadState.get() List<Object> eventQueue=postingState.eventQueue eventQueue.add(event) ..if (!postingState.isPosting) { postingState.isMainThread=Looper.getMainLooper()==Looper.myLooper() try { while (!eventQueue.isEmpty()) postingSingleEvent(eventQueue.remove(0 ),postingState) ... List<Class<?>> eventTypes = lookupAllEventTypes(eventClass) for (clazz in eventTypes) { postSingleEventForEventType(event , postingState , clazz ) -> CopyOnWriteArrayList<Subscription> subscriptions subscriptions=subscriptionsByEventType.get(clazz) for (subscription in subscriptions) postToSubscription(subscription, event, postingState.isMainThread) ..区别一下主进程的派送方式 -> 根据getMainLooper创建handler再sendMessage } }catch (){ } } }
EventBus使用 a).EventBus.getDefault().register(subscriber: this) //注册 b).单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 >public static EventBus getDefault () { if (defaultInstance == null ) { synchronized (EventBus.class) { if (defaultInstance == null ) { defaultInstance = new EventBus (); } } } return defaultInstance; }
c).@Subscribe(threadMode=ThreadMode.POSTING)
1 2 3 4 5 6 7 public void handleSomethingElse (Msg msg) { …. }
d).线程指定
ThreadMode.POSTING //在信息发送的线程进行响应 ThreadMode.MAIN //ui主线程进行响应 e).StickyEvent EventBus.getDefault().postSticky(new Msg(“Hello everyone!”)) Msg msg= EventBus.getDefault().getStickyEvent(Msg.class) EventBus.getDefault().removeStickyEvent(msg)
register //注册 :1.获取SubscriberMethods
1 2 3 4 5 6 7 8 9 10 List<SubscriberMethod> findSubscriberMethods (Class<?> subscriberClass) { List<SubscriberMethod> subscriberMethods=METHOD_CACHEA.get(subscriberClass) if (subscriberMethods!=null ) return subscriberMethods subscriberMethods=findUsingReflection(subscriberClass) METHOD_CACHE.put(subscriberClass, subscriberMethods); Return subscriberMethods }
:3.根据反射遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private List<SubscriberMethod> findUsingReflection (Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null ) { findUsingReflectionInSingleClass(findState); findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
:4.获取注解以及注解的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 private void findUsingReflectionInSingleClass (FindState findState) { Method[] methods try { methods=findState.clazz.getDeclaredMethods() } catch (Throwable th){ } for (Method method : methods) { int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0 ) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1 ) { Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class) if (subscribeAnnotation != null ) { Class<?> eventType = parameterTypes[0 ]; ThreadMode threadMode = subscribeAnnotation.threadMode(); findState.subscriberMethods.add(new SubscriberMethod (method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } } }
Subscription //封装注册信息 a).Object subscriber b).SubscriberMethod subscriberMethod
SubscriberMethod a).Method method b).int priority c).boolean sticky d).ThreadMode threadMode e).Class<?> eventType
注释类 //@Retention定义了该Annotation被保留的时间长短,@Target({ElementType.METHOD})注释对象为方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 public @interface Subscribe{ ThreadMode threadMode () default ThreadMode.POSTING; boolean sticky () default false ; int priority () default 0 ; } >>subscribe :1. private void subscribe (Object subscriber, SubscriberMethod subscriberMethod) { Class<?> eventType = subscriberMethod.eventType; Subscription newSubscription = new Subscription (subscriber, subscriberMethod) CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null ) { subscriptions = new CopyOnWriteArrayList <>(); subscriptionsByEventType.put(eventType, subscriptions); } else { if (subscriptions.contains(newSubscription)) { throw new EventBusException ("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } int size = subscriptions.size(); for (int i = 0 ; i <= size; i++) { if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { subscriptions.add(i, newSubscription); break ; } } List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber) if (subscribedEvents == null ) { subscribedEvents = new ArrayList <>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); if (subscriberMethod.sticky){ if (eventInheritance) { } else { Object stickyEvent = stickyEvents.get(eventType) checkPostStickyEventToSubscription(newSubscription, stickyEvent) } } }
post //发送事件 :1.PostingThreadState //本线程用于保存事件队列信息
1 2 3 4 5 6 7 8 9 10 11 final static class PostingThreadState { final List<Object> eventQueue = new ArrayList <Object>(); boolean isPosting; boolean isMainThread; Subscription subscription; Object event; boolean canceled; }
:2.public void post(Object event) //主循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 { PostingThreadState postingState = currentPostingThreadState.get()List<Object> eventQueue = postingState.eventQueue eventQueue.add(event) if (!postingState.isPosting) { postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); postingState.isPosting = true ; if (postingState.canceled) { throw new EventBusException ("Internal error. Abort state was not reset" ); } try { while (!eventQueue.isEmpty()) { postSingleEvent(eventQueue.remove(0 ), postingState) } } finally { postingState.isPosting = false ; postingState.isMainThread = false ; } } }
:3.分发单个事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private void postSingleEvent (Object event, PostingThreadState postingState) { Class<?> eventClass = event.getClass() boolean subscriptionFound = false if (eventInheritance) { List<Class<?>> eventTypes = lookupAllEventTypes(eventClass) int countTypes = eventTypes.size() for (int h = 0 ; h < countTypes; h++) { Class<?> clazz = eventTypes.get(h); subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); } } }
D).反射调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private boolean postSingleEventForEventType (Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions synchronized (this ) { subscriptions=subscriptionsByEventType.get(eventClass) } if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription:subscriptions) { postingState.event=evnet postingState.subscription=subscription postToSubscription (subscription,event,postingState.isMainThread) } return ture } return false }
e).postToSubscription(subscription,event,isMainThread) //isMainThread代表是否post的线程为主线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 { switch (subscription.subscriberMethod.threadMode){ case ThreadMode.POSTING: invokeSubscriber(subscription,event) -> >try { subscription.subscriberMethod.method.invoke(subscription.subscriber,event) }catch (e) { } break ; case ThreadMode.MAIN: if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break ; case BACKGROUND: if (isMainThread) { backgroundPoster.enqueue(subscription, event); } else { invokeSubscriber(subscription, event); } break ; } }
f).mainThreadPoster //HandlerPoster实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 class HandlerPoster extends Handler { private final PendingPostQueue queueprivate EventBus eventBus… HandlerPoster(EventBus eventBus,Looper looper,..) { super (looper) this .eventBus=eventBus } void enqueue (Subscription subscription,Object event) { PendingPost pendingPost=PendingPost.obtainPendingPost(subscription,event) synchronized (this ) { queue.enqueue(pendingPost) if (!handlerActive) { handlerActive=true sendMessage (obtainMessage() ) } } } @Override public void handleMessage (Message msg) { PendingPost pendingPost = queue.poll() if (pendingPost == null ) { synchronized (this ) { pendingPost = queue.poll(); if (pendingPost == null ) { handlerActive = false ; return ; } } } eventBus.invokeSubscriber(pendingPost) } }
g).BackgroundPoster //如果派发线程不是主线程则直接invokeSubscriber,否则调用线程池execute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 final class BackgroundPoster implements Runnable { } >>sticky事件 public void postSticky (Object event) { synchronized (stickyEvents) { stickyEvents.put(event.getClass(), event); } post(event) } >>executorService = Executors.newCachedThreadPool() >>eventTypesCache a).
String a=“ abc”;String b=“ abc”; 创建了几个对象 //栈存放引用变量 堆存在new出来的对象,并且堆划分出一部分作为常量池
当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个X在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加 b)Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象 c)使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象 d)使用包含变量的表达式来创建String对象,会在堆栈创建新的
java的单例创建 a)懒汉式 //线程不安全,指令重排
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 >>public class Singleton { private static Singleton instance; private Singleton () { } public static Singleton getInstance () { if (instance == null ) { instance = new Singleton (); } return instance } }
b)饿汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 >>public static synchronized Singleton getInstance () { if (instance == null ) { instance = new Singleton (); } return instance; }
c)双重检测锁 //效率提高,双重指的是2次检查instance是否为null
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 >>public class SafeDoubleCheckedLocking { private volatile static Instance instance; public static Instance getInstance () { if (instance == null ) { synchronized (SafeDoubleCheckedLocking.class) { if (instance == null ) instance = new Instance (); } } return instance; } }
163.Android进程优先级 a)Foreground processes 前台进程 进程中包含处于前台的正与用户交互的activity; 进程中包含与前台activity绑定的service; 进程中包含调用了startForeground()方法的service; 进程中包含正在执行onCreate(), onStartCommand(), 或onDestroy()方法的service; 进程中包含正在执行onReceive()方法的BroadcastReceiver. b)Visiable processes 可视进程
进程中包含未处于前台但仍然可见的activity(调用了activity的onPause()方法, 但没有调用onStop()方法)。 典型的情况是:运行>>activity时弹出对话框(类似对话框,将activity遮挡), 此时的activity虽然不是前台activity, 但其仍然可见。 进程中包含与可见activity绑定的service.可视进程不会被系统杀死, 除非为了保证前台进程的运行而不得已为之. c)Service processes 服务进程 正在运行的Service(不在onCreate(),onStartCommand(),onDestroy()状态中) d)background processes 后台进程 如:不可见状态的activity
164.ThreadLocal 源码 ThreadLocalMap :1.由Thread维护
ThreadLocal.ThreadLocalMap threadLocals,装载因子为2/3,超过即扩容 :2.ThreadLocalMap //结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 a).static class Entry extends WeakReference <ThreadLocal<?>> { Object value; Entry(ThreadLocal<?>k,Object v) { super (k) value=v } } b).private Entry[] table c).private static final int INITIAL_CAPCITY=16 d).ThreadLocalMap(ThreadLocal<?> firstKey,Object firstValue) { table=new Entry [INITIAL_CAPACITY] int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1 ) table[i] = new Entry (firstKey, firstValue); } e).线性探查 >寻找下个索引 private static int nextIndex (int i ,int len) { return i+1 <len?i+1 :0 } >寻找上个索引 private static int prevIndex (int i ,int len) { return i-1 >=0 ?i-1 :len-1 } g).获取元素 > getEntry(ThreadLocal<?> key) { int i=key.threadLocalHashCode & table.length-1 Entry e=table[i] if (e!=null &&e.get()==key) return e while (e!=null ) { ThreadLocal<?>k = e.get() if (k==key) return e if (k==null ) expungeStaleEntry(i) else i=nextIndex(i,len) e=tab[i] } } > expungeStaleEntry(int slot) { Entry[] tab=table int len=tab.length tab[slot].value=null tab[slot]=null for (int i=nextIndex(slot,len);(e=tab[i])!=null ;i=nextIndex(i,len)) { ThreadLocal<?> k=e.get() if (k==null ) { e.value=null tab[i]=null } else { int h= k.threadLocalHashCode()&(len-1 ) if (h!=i) { tab[i]=null while (tab[h]!=null ) { h=nextIndex(h,len) } tab[h]=e } } } } h).设置元素 > private void set (ThreadLocal<?> key,Object value) { Entry[] tab=table int len=tab.length int i= key.threadLocalHashCode & (len-1 ) for (Entry e=tab[i]; e!=null ;e=tab[i=nextIndex(i,len)]) { ThreadLocal<?> k=e.get() if (k==key) e.value=value return if (k==null ) replaceStaleEntry(key, value ,i) return } } > 取代旧有的entry private void replaceStaleEntry (ThreadLocal<?> key, Object value, int staleSlot) { Entry[] tab=table int len=tab.length for (int i=nextIndex(staleSlot,len) ;(e=tab[i])!=null ;i=nextIndex(i,len)) { ThreadLocal<?> k= e.get() if (k==key) { e.value=value tab[i]=tab[staleSlot] tab[staleSlot]=e } } } >>ThreadLocal a).ThreadLocal::ThreadLocal() b).public T get () { Thread t=Thread.currentThread() ThreadLocalMap map=t.getMap(t) if (map!=null ) { ThreadLocalMap.Entry e=map.getEntry(this ) if (e!=null ) { T result=(T)e.value return result } } return setInitialValue() } c).private Entry getEntry (ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1 ) Entry e=table[i] if (e != null && e.get() == key) return e else return getEntryAfterMiss(key, i, e) } d).private Entry getEntryAfterMiss (ThreadLocal<?> key,int i,Entry e c) .ThreadLocal::set(T) d).ThreadLocal::remove() >>set b).ThreadLocal.set() => Thread t= Thread.currentThread(); ThreadLocalMap map=getMap(t); c).当ThreadLocal初始化时,会自动调用nextHashCode生成,以便随后根据hashCode进行数组元素的索引 d).如果当前thread的threadLocalMap不为空 => map.set(this ,value) 如果为空 => createMap >>t.threadLocals= new ThreadLocalMap (this ,firstValue); >> ThreadLocal<String> name=new ThreadLocal <>(); name.set(“dancing”)
165.自定义View对象
自定义View的属性 :1.res/values/styles.xml
1 2 3 4 5 6 7 > <resources> <!--name为声明的"属性集合" 名,可以随便取,但是最好是设置为跟我们的View一样的名称--> <declare-styleable name="MyView" > <!--声明我们的属性,名称为default_size,取值类型为尺寸类型(dp,px等)--> <attr name="default_size" format="dimension" /> </declare-styleable> </resources>
在View的构造函数中获取自定义属性 :1.public CustomTitleView(Context context, AttributeSet attrs, int defStyle)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 { super (context, attrs, defStyle); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0 ); int n = a.getIndexCount(); for (int i = 0 ; i < n; i++) { int attr = a.getIndex(i); switch (attr) { case R.styleable.CustomTitleView_titleText: mTitleText = a.getString(attr); break ; case R.styleable.CustomTitleView_titleTextColor: mTitleTextColor = a.getColor(attr, Color.BLACK); break ; case R.styleable.CustomTitleView_titleTextSize: mTitleTextSize = a.getDimensionPixelSize(attr, (int ) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, 16 , getResources().getDisplayMetrics())); break ; } } a.recycle(); mPaint = new Paint (); mPaint.setTextSize(mTitleTextSize); mBound = new Rect (); mPaint.getTextBounds(mTitleText, 0 , mTitleText.length(), mBound); }
c).重写onMeasure //非必需
1 2 3 4 5 6 7 8 9 >>@Override protected void onMeasure (int widthMeasureSpec,int heightMeasureSpec) { super .onMeasure(widthMeasureSpec,heightMeasureSpec); }
d).重写onDraw
1 2 3 4 5 6 7 8 9 >>@Override protected void onDraw (Canvas canvas) { mPaint.setColor(Color.YELLOW); canvas.drawRect(0 , 0 , getMeasuredWidth(), getMeasuredHeight(), mPaint); }
Android @Override //重写 a).重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载 b).重写方法的访问修饰符一定要大于被重写方法的访问修饰符 public>protected>default>private c).重写的方法的返回值必须和被重写的方法的返回一致 d).重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类 e).被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写
Android Semaphore机制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 >>final Semaphore semaphore=new Semaphore (1 ) semaphore.acquire() semaphore.release() >>例子 final Semaphore sema = new Semaphore (3 ); for (int index = 1 ; index < 10 ; index++) { new Thread (new Runnable (){ @Override public void run () { sema.acquire() sema.release() } }) }
169.Android Lock同步 ReentrantLock :1.比synchronized更加适用于描述多个线程互相获取资源的场景,对象级别声明为static有效与否未知 unlock //相当于V lock //相当于P b).ReadWriteLock //读写锁 private ReentrantReadWriteLock rw1=new ReentrantReadWriteLock() rw1.readLock().lock() //unlock释放 rw1.writeLock().lock() //unlock释放 实例 :1.ReentrantLock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 > class Outputter { private Lock lock = new ReentrantLock (); public void output (String text) throws InterruptedException { lock.lock(); try { for (int i = 0 ; i < text.length(); i++) { System.out.println(text.charAt(i)); Thread.sleep(1000 ); } } finally { lock.unlock(); } } } :2. ReadWriteLock >class Data { private int data; private ReadWriteLock readWriteLock = new ReentrantReadWriteLock (); public void set (int data) throws Exception { readWriteLock.writeLock().lock(); try { Thread.sleep(50 ); this .data = data; } finally { readWriteLock.writeLock().unlock(); } } public void get () throws Exception { readWriteLock.readLock().lock(); try { Thread.sleep(50 ); } finally { readWriteLock.readLock().unlock(); } } }
170.ThreadPoolExecutor 线程池 a)newFixedThreadPool //返回一个固定线程数量的线程池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 >>ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3 ); for (int i = 1 ; i <= 10 ; i++) { final int index = i; fixedThreadPool.execute(new Runnable () { @Override public void run () { String threadName = Thread.currentThread().getName(); Log.v("zxy" , "线程:" +threadName+",正在执行第" + index + "个任务" ); try { Thread.sleep(2000 ); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
b)newSingleThreadExecutor //该池只跑一个线程,对所有耗时任务进行FIFO排队 c)newScheduledThreadPool //创建一个可以定时或者周期性执行任务的线程池
1 2 3 4 5 6 7 8 9 10 11 12 13 >> scheduledThreadPool.schedule(new Runnable () { @Override public void run () { } }, 2 , TimeUnit.SECONDS);
d)自定义ThreadPoolExecutor //可以根据优先级进行排队
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 >>public abstract class PriorityRunnable implements Runnable , Comparable<PriorityRunnable> { private int priority; public PriorityRunnable (int priority) { if (priority < 0 ) throw new IllegalArgumentException (); this .priority = priority; } @Override public int compareTo (PriorityRunnable another) { int my = this .getPriority(); int other = another.getPriority(); return my < other ? 1 : my > other ? -1 : 0 ; } @Override public void run () { doSth(); } public abstract void doSth () ; public int getPriority () { return priority; } } >> ExecutorService priorityThreadPool = new ThreadPoolExecutor (3 , 3 , 0L , TimeUnit.SECONDS, new PriorityBlockingQueue <Runnable>()); for (int i = 1 ; i <= 10 ; i++) { final int priority = i; priorityThreadPool.execute(new PriorityRunnable (priority) { @Override public void doSth () { String threadName = Thread.currentThread().getName(); Log.v("zxy" , "线程:" + threadName + ",正在执行优先级为:" + priority + "的任务" ); try { Thread.sleep(2000 ); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
171.Android下载图片通过bitmap设置ImageView
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 >>public class ActivityMain extends Activity { String imageUrl = "http://i.pbase.com/o6/92/229792/1/80199697.uAs58yHk.50pxCross_of_the_Knights_Templar_svg.png" ; Bitmap bmImg; ImageView imView; Button button1; @Override public void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); imView = (ImageView) findViewById(R.id.imview); imView.setImageBitmap(returnBitMap(imageUrl)); } public Bitmap returnBitMap (String url) { URL myFileUrl = null ; Bitmap bitmap = null ; try { myFileUrl = new URL (url); } catch (MalformedURLException e) { e.printStackTrace(); } try { HttpURLConnection conn = (HttpURLConnection) myFileUrl .openConnection(); conn.setDoInput(true ); conn.connect(); InputStream is = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(is); is.close(); } catch (IOException e) { e.printStackTrace(); } return bitmap; } }
ImageView ScaleType :1.FIT_CENTER 默认,图片等比例缩放到宽或者高能够填充控件大小 :2.FIT_START,FIT_END 等比例缩放,并放置在控件的左边或者上方 :3.FIT_XY 完全填充控件大小,但不是等比例 :4.CENTER -> 图片大小为原始大小,如果大于ImageView组件,则截取中间部分,若小于则将图片居中显示 :5.CENTER_CROP ->图片等比例缩放,使图像的短边填满,截取中间部分
onSaveInstanceState //保存app数据,当系统因内存不足时而销毁app,会得到调用
1 2 3 4 5 6 7 8 9 10 11 12 13 >>public void onSaveInstanceState (Bundle outState) { outState.put(“index”,3 ); } >>public void onCreate (Bundle saveInstanceState) { int curIndex=saveInstanceState.get(“index”,0 ); }
177.ViewPager&PagerAdapter&TabFragmentPagerAdapter 实例 :1.fragments=mutableListOf(Fragment1(),Fragment2()) val adapter= TagFragmentPagerAdapter(getSupportFragmentManager(),fragments) view_pager.adapter=adapter :2.view_pager.addOnPageChangeListener :3.view_pager.setCurrentItem //设置默认选中的item
Fragment在横竖屏切换时的数据保存 onCreate-> setRetainInstance(true) 当设置retain后,Fragment的生命周期不再经过onCreate、onDestroy
DialogFragment //采用子类继承的方式进行使用,相比AlertDialog由FragmentManager负责自动重建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 >>public class DatePickerFragment extends DialogFragment { a).public Dialog onCreateDialog (Bundle saveInstanceState) { Date date = (Date) getArguments().getSerializable(ARG_DATE); Calendar calendar = Calendar.getInstance(); calendar.setTime(date); int year = calendar.get(Calendar.YEAR); int month = calendar.get(Calendar.MONTH); int day = calendar.get(Calendar.DAY_OF_MONTH); return new DatePickerDialog (context, this ,year,month,day) } }
180.SQLiteDatabase helper //通过helper来获取database实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 a).public class DbOpenHelper extends SQLiteOpenHelper { private static final int VERSION=1 ; private static final String DATABASE_NAME="crimeBase.db" ; public DbOpenHelper (Context context) { super (context, DATABASE_NAME, null , VERSION); } @Override public void onCreate (SQLiteDatabase db) { db.execSQL("create table crimes (" + "_id integer primary key autoincrement ," + "title varchar(10) ," + "date datetime," + "solved boolean" + ")" ); } @Override public void onUpgrade (SQLiteDatabase db, int oldVersion, int newVersion) { } }
getWritableDatabase、getReadableDatabase a).getWritableDatabase //以读写方式打开数据库,一旦数据库的磁盘空间满了就只能读,因此会报错 b).getReadableDatabase //首先以读写方式打开,失败后转换为只读方式打开 c).锁 //SqliteOpenHelper,针对数据库进行了加锁,故遵循读写-锁原则,可多个读只有一个写 CursorWrapper //1).自动针对Cursor实现了getItem、getCount 2).通过convertView去调用createView|bindView a).bindView(view,context,cursor) b).newView(context,cursor,parent) DateTime的数据存储 //cursor不支持直接Date类型的数据存取 String dateStr =DateFormat.getDateTimeInstance().format(date) //getDateTimeInstance获取针对DateTime的转换器 cv.put(“date”,dateStr) //SQLite中设置date为datetime
IntentFilter的匹配规则
1 2 3 4 5 6 7 8 9 10 11 >><intent-filter> :1. <action android:name=“…”/> :2. <category android:name=“…”/> :3. <data android:scheme=“string” android:host=“string” android:port=“string” android:path=“string” android:mimeType=“string”/> >>预查询能匹配的Activity a)getPackageManager().queryIntentActivities(intent,0 ) b)getPackageManager().resolveActivity(intent,0 )
183.Android多进程
Messenger :1.服务器端所持有的Messenger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 >private Messenger serviceMessenger = new Messenger (new ServiceHandler ()); private class ServiceHandler extends Handler { @Override public void handleMessage (Message msg) { switch (msg.what){ case 0x01 : break ; } } } @Override public IBinder onBind (Intent intent) { Log.i("DemoLog" , "MyServivce -> onBind" ); return serviceMessenger.getBinder(); }
:2.设置在service在别的进程
1 <service android:name=".MessengerService" android:process=":remote" />
:3.客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 >>private Messenger mService; private ServiceConnection mConnection=new ServiceConnection (){ public void onServiceConnected (ComponentName xx,IBinder service) { mService=new Messenger (service); Message msg=Message.obtain(null ,what); Bundle data=new Bundle (); data.put(“”,”xx”); msg.setData(data); try { mService.send(data); }catch (RemoteException e) { e.printStackTrace(); } } } @Override protected void onCreate (Bundle savedInstanceState) { bindService(intent,mConnection,Context.BIND_AUTO_SERVICE); }
:4.服务器回应客户端
1 2 3 4 5 6 7 8 9 10 >客户端设置replyTo Message msg=Message.obtain() msg.replyTo=new Messenger (new Handler (){ new Runnable (){ }}) serverMessenger.send(msg)
AIDL a)服务器 -> 创建Service来监听客户端的连接请求,创建AIDL文件,将暴露给客户端的接口在该文件中声明,最后Service实现 b)客户端 -> 绑定Service,将返回的Binder转换为AIDL类型,调用AIDL方法 c)AIDL接口的创建 //引入一个接口和声明2个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 >>import com.ryg.chapter_2.aidl.Book; interface IBookManager { List<Book> getBookList () ; void addBook (in Book book) ; } >>AIDL支持的数据类型 -> 基本数据类型、String和CharSequence、ArrayList、HashMap、Parcelable、AIDL >>AIDL不支持声明静态常量 d)针对AIDL服务端Service的创建 public class BookManagerService extends Service { private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList <Book>(); private Binder mBinder=new IBookManager .Stub(){ @Override public List<Book> getBookList () throws RemoteException{ return mBookList; } @Override public void addBook (Book book) throws RemoteException{ mBookList.add(book); } } @Override public IBinder onBind (Intent intent) { return mBinder; } } e)针对AIDL客户端 private ServiceConnection mConnection=new ServiceConnection (){ public void onServiceConnected (ComponentName className,IBinder service) { IBookManager manager=IBookManager.Stub.asInterface(service); } } @Override public void onCreate (Bundle savedInstanceState) { bindService(intent,mConnection,Context.BIND_AUTO_CREATE); } >>RemoteCallbackList a)final int N=mListenerList.beginBroadcast() for (int i=0 ;i<N;i++) { IOnNewBookArrivedListener l=mListenerList.getBroadcastItem(i) l.onNewBookArrived(Book); } mListenerList.finishBroadcast() >>权限验证 a)自定义权限 -> <permission android:name=“com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE” android:protectionLevel=“normal”/> b)检查申请应用是否有对应权限 ->public IBinder onBind (Intent intent) { int check=checkCallingOrSelfPermission(“com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE”) if (check==PackageManager.PERMISSION_DENIED) return null else return mBinder; } >>ContentProvider
186.Android Scroller
1 2 3 4 5 6 7 8 9 10 11 12 13 >>Scroller mScroller=new Scroller (context) >>mScroller.startScroll(); invalidate(); >>@Override public void computeScroll () { if (mScroller.computeScrollOffset) scrollTo(mScroller.getCurX(),mScroller.getCurY()) invalidate(); }
188.Sqlite 连接操作
1 2 3 4 5 >>cross join a)select e.no,e.name from emp e cross join dept d >>inner join >>natural join >>outer join
190.Spinner
1 2 >>setOnItemSelectedListener >>setAdapter
191.RelativeLayout 源代码
sortChildren() a).DependGraph,形成依赖图和依赖子图 b).findRoots返回当前没有依赖的view结点集合roots c).通过roots集合采用bfs遍历所有view结点,具体是访问某个view时去除该view的相邻边,sorted[index++]=view
getLayoutDirection() //RTL,主要针对阿拉伯地区,默认LayoutDirection.LTR a).LayoutDirection.LTR,容器内容左对齐 b).LayoutDirection.RTL, 容器内容右对齐
水平方向和垂直方向的尺寸测量
192.Message //static Message sPool,指向队列前头的可用Message对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static Message obtain () { synchronized (sPoolSync) { if (sPool != null ) { Message m = sPool sPool = m.next; m.next = null ; m.flags = 0 ; sPoolSize--; return m; } } return new Message () }
1 2 3 4 5 6 7 8 9 10 11 12 13 synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool sPool = this sPoolSize++ } }
193.ArrayDeque 源代码阅读
成员 //transient标识的在通过ObjectOutputStream进行持久化时不进行该成员处理 a).private transient E[] elements b).private transient int head c).private transient int tail d).private static final int MIN_INITIAL_CAPACITY = 8
初始化时的分配 a).allocateElements(int numElements) //后续操作保证capacity为2的n次方 b).elements = new Object[initialCapacity]
doubleCapacity //扩容,当tail+1==head时,进行扩容 a).int p = head; int n = elements.length; int r = n - p; int n = elements.length int newCapacity = n << 1 System.arraycopy(elements, p, a, 0, r); System.arraycopy(elements, 0, a, r, p);
插入 a).addFirst //头部插入数据 elements[head = (head - 1) & (elements.length - 1)] = e;//elements.length-1 equals 1111…111 if (head == tail) doubleCapacity(); b).addLast //尾部插入数据 elements[tail] = e; if ( (tail = (tail + 1) & (elements.length - 1)) == head) doubleCapacity();
遍历 a).成员 //cursor,fence,lastRet b).public E next()
1 2 3 4 >if (cursor==fence) throw new Exception >lastRet=cursor >cursor = (cursor + 1 ) & (elements.length - 1 ) >return result
c).public void remove() delete(lastRet) //调用私有方法delete删除lastRet对应下标
final int front = (i - h) & mask; final int back = (t - i) & mask; if(front<back) //删除某一下标的元素后,需要对前半和后半进行移动,这时选择较短部分进行操作省时
194.RecyclerView
1 2 3 >mAttachedScrap >mChangedScrap >mCachedViews
:2.getViewForPosition //1).从mChangedScrap 2).mAttachedScrap 3).mCachedViews :3.recycleView
recycleViewHolderInternal(holder)-> mCachedViews.add -> mRecyclerPool
RecyclerView::onMeasure //测量 a).dispatchLayoutStep2 -> mLayout.onLayoutChildren … b).dispatchLayoutStep3 //执行动画
LayoutManager a).onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 { if (mAnchorInfo.mLayoutFromEnd){ }else { firstLayoutDirection=LayoutState.ITEM_DIRECTION_TAIL } fill(recycler, mLayoutState,RecyclerVie.State::state, false )-> { while ((layoutState.mInfinite || remainingSpace > 0 ) && layoutState.hasMore(state)) { layoutChunk(recycler, state, layoutState, layoutChunkResult)—> > addView } } }
RecycledViewPool //存放ViewHolder a).public ViewHolder getRecycledView(int viewType)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType); if (scrapHeap != null && !scrapHeap.isEmpty()) { final int index = scrapHeap.size() - 1 ; final ViewHolder scrap = scrapHeap.get(index); scrapHeap.remove(index); return scrap; } return null ; }
b).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public void putRecycledView (ViewHolder scrap) { final int viewType = scrap.getItemViewType(); final ArrayList scrapHeap = getScrapHeapForType(viewType); if (mMaxScrap.get(viewType) <= scrapHeap.size()) { return ; } if (DEBUG && scrapHeap.contains(scrap)) { throw new IllegalArgumentException ("this scrap item already exists" ); } scrap.resetInternal(); scrapHeap.add(scrap); }
195.LinearLayoutManager //RecyclerView的布局管理器
onLayoutChildren :1.更新layoutState的状态,并等待getItemCount()的刷新
1 2 3 mAnchorInfo.mLayoutFromStart=true updateLayoutStateToFillEnd -> mLayoutSTate.mAvailable = mOrientationHelper.getEndAfterPadding()-offset
:2.填充子view
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fill(recycler , mLayoutState,state,false ) ... int start=layoutState.mAvailable int remainSpace=layoutState.mAvailable while (remainSpace>0 &&layoutState.hasMore(state)) layoutChunk(recycler, state, layoutState, layoutChunkResult) ... View view=layoutState.next(recycler) if (mScrapList!=null ) return nextViewFromScrapList() View view=recycler.getViewForPosition(mCurrentPosition) ... holder=getScrapOrHiddenOrCachedHolderForPosition(position) if (holder==null ) holder=getRecycledViewPool().getRecycledView(type) if (holder==null &&mAdapter!=null ) holder=mAdapter.createViewHolder() ... mAdapter.onBindViewHolder() addView(view) measureChildWithMargins(view) result.mConsumed=view.height if (mOrientation==VERTICAL)
196.1 GridLayoutManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 >>mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager .SpanSizeLookup(){ override public getSpanSize (position:int ) :int { val type=mAdapter.getItemViewType(position) switch (type) { ... } } })
196.2.RecyclerView 使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 >>封装adapter :1. public class MyRecyclerViewAdapter extends RecyclerView .Adapter<MyAdapter.ViewHolder> { private List<String> list; public MyAdapter (List<String> list) { this .list = list; } @Override public MyAdapter.ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_base_use, parent, false ); MyAdapter.ViewHolder viewHolder = new MyAdapter .ViewHolder(view); return viewHolder; } @Override public void onBindViewHolder (MyAdapter.ViewHolder holder, int position) { holder.mText.setText(list.get(position)); } @Override public int getItemCount () { return list.size(); } class ViewHolder extends RecyclerView .ViewHolder { TextView mText; ViewHolder(View itemView) { super (itemView); mText = itemView.findViewById(R.id.item_tx); } } }
196.1 Binder
BpBinder :1.val in:Parcel ,val out:Parcel
1 2 3 4 5 6 7 8 9 >>MediaPlayerService :1. 调用了BpServiceManager::addService(String16& name,const sp<IBinder> &service) ... Parcel data, reply; data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); data.writeString16(name) data.writeStrongBinder(service) remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply) :2. BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) ... status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 :1. 打开Binder设备文件,分配内存 -> 告诉Binder驱动自己成为Binder的上下文管理者 -> binder_loop(bs,svcmgr_handler) ... struct binder_state *bs void *svcmgr = BINDER_SERVICE_MANAGER bs = binder_open(128 *1024 ) if (binder_become_context_manager(bs)) { } svcmgr_handle = svcmgr binder_loop (bs, svcmgr_handler) :2. fd是文件描述符,mapped是把设备文件/dev/binder映射到进程空间的起始地址;mapsize是上述内存映射空间的大小 .... struct binder_state { int fd; void *mapped; unsigned mapsize; }
197.ActivityThread //Android应用的主线程
成员 :1.mActivities :2.mServices :3.mApplication :4.mProviderMap
流程 :1.main
1 2 3 4 5 6 7 8 9 10 >Looper.prepareMainLooper() ActivityThread thread = new ActivityThread () if (sMainThreadHandler == null ) { sMainThreadHandler =thread.getHandler(); } ... Looper.loop()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 >>public static void main (String[] args) { Looper.prepareMainLooper() ActivityThread thread=new ActivityThread () … sMainThreadHandler = thread.getHandler(); Looper.loop() } >>private class H extends Handler { public void handleMessage (Message msg) { switch (msg.what) { a).case LAUNCH_ACTIVITY: handleLaunchActivity(r,Intent customIntent (null ) , "LAUNCH_ACTIVITY" ) -> >performLaunchActivity(r,customIntent) >检查r.packageInfo >检查r.intent.mComponent > activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent); >Application app = r.packageInfo.makeApplication(false , mInstrumentation) >Context appContext= createBaseContextForActivity(r, activity) >CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()) >activity.attach(appContext…) >mInstrumentation.callActivityOnCreate(activity, r.state); >activity.performStart() >mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state) >r.paused = true b).case RESUME_ACTIVITY: handleResumeActivity(IBinder token,boolean clearHide,boolean isForward)-> >ActivityClientRecord r = mActivities.get(token) >r = performResumeActivity(token, clearHide, reason) >if (r.pendingIntents != null ) { deliverNewIntents(r, r.pendingIntents) r.pendingIntents = null ; } >r.activity.performResume() c).case PAUSE_ACTIVITY: handlePauseActivity(IBinder token,boolean finished …) -> >performPauseActivity(ActivityClientRecord r,boolean finished,..) > 检查r.paused > if (r.paused &&r.activity.mFinished) { return null } > if (r.paused){ RuntimeExceptio e} > 检查finished > if (!r.activity.mFinished && saveState) { mInstrumentation.callActivityOnSaveInstanceState(r.activity,r.state) } > performPauseActivityIfNeeded(r,reason) if (r.paused){ return ;} mInstrumentation.callActivityOnPause(r.activity); d).case STOP_ACTIVITY_SHOW: handleStopActivity(IBinder token,boolean show) -> >performStopActivityInner(ActivityClientRecord r,StopInfo info,show…) >performPauseActivityIfNeeded(r,reason) >if (!r.activity.mFinished && saveState) { if (r.state == null ) { callCallActivityOnSaveInstanceState(r); } } e).case DESTROY_ACTIVITY: handleDestroyActivity(IBinder token,boolean finishing…) -> >performDestroyActivity(token, finishing,configChanges, getNonConfigInstance) >performPauseActivityIfNeeded } } } >>private class ApplicationThread { a).public final void scheduleLaunchActivity { ActivityClientRecord r = new ActivityClientRecord () … sendMessage(H.LAUNCH_ACTIVITY, r) } }
198.CountDownLatch 线程同步 199.retrofit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 >>建立retrofit对象 :1. Retrofit retrofit = new Retrofit .Builder() .baseUrl("http://fy.iciba.com/" ) .addConverterFactory(GsonConverterFactory.create()) .build(); :2. 动态代理 Public<T> T create (final Class<T> service) { return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class <?>[] { service }, new InvocationHandler () { @Override public Object invoke (Object proxy,Method method,Object[] args) { if (method.getDeclaringClass()==Object.class) return method.invoke(this , args) } }) } >>建立ServiceMethod :1. 建立CallAdapter >retrofit.callAdapter(returnType,annotations)
200.ClassLoader 类加载器
JVM :1.Bootstrap ClassLoader /jre/lib,用于加载java虚拟机所需要的系统类,java.lang.* :2.Ext ClassLoader /jre/lib/ext :3.App ClassLoader
ART :1.BootClassLoader ->加载常用类 :2.PathClassLoader ->extends DexClassLoader,加载系统类和应用程序的类dex文件、apk等 :3.DexClassLoader ->以加载dex文件以及包含dex的apk文件或jar文件,也支持从SD卡进行加载
201.RxJava
1 2 3 4 5 6 7 8 :1. create(ObservableOnSubscribe<T> source) { RxJavaPlugins.onAssembly(new ObservableCreate <T>(source)) }