转存自下面路径,转载用大佬的原文
https://blog.csdn.net/qq_29966203/article/details/123868018?spm=1001.2014.3001.5502

  1. SD卡

路径

1
2
3
4
a).Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) 	//sd卡挂载状态
b).Environment.getExternalStorageDirectory().getPath() //sd卡路径 ->/storage/emulated/0
c).Environment.getDownloadCacheDirectory().getPath() //data/cache路径
d).Environment.getDataDirectory().getPath() //data

权限

1
2
<uses-permission android:name=“android.permission.WRITE_EXTERNAL_STORAGE”/>
<uses-permission android:name=“android.permission.MOUNT_UNMOUNT_FILESYSTEMS”/>
  1. 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
  • 网络
  1. 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
//隐式广播需要在manifest中加上
>>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) //发送普通广播,静态注册必须描述action标签
: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) //后续广播通过getResultExtras(true)取回Bundle
c).abortBroadcast() //终止广播传递
>>sendStickyBroadcast(intent) //发送粘性广播,粘性广播以最后一次的内容进行保留
:1.removeStickyBroadcast(intent)
:2.在Manifest中声明android.permission.BROADCAST_STICKY
  1. sp频繁操作会有什么后果?sp能存多少数据?

sp的底层由xml来实现,操作sp的过程就是xml的序列化和解析。xml是存在磁盘上的,因此要考虑io速度。另外dvm的内存是有限的,同时dvm的堆内存为16M,所以不能超过此数字

  1. 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,用位图标识第二阶段需要被重新标记的对象,在第二阶段完成后继续标记,此时禁止其他线程
  1. ART

//android4.4之后采用ART作为虚拟机
GC算法分为多种
:1.Mark-Sweep GC,标记-清楚
:2.Compacting GC,标记-压缩
新生代采用复制算法,老年代采用标记压缩 //复制算法:内存分为相等的两块,一块用于分配

  1. 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
//结合ActivityThread
->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
  1. Application能不能启动Activity

startActivity(intent:Intent,FLAG_ACTIVITY_NEW_TASK)
standard模式启动的Activity,该实例会存放在启动该Activity的Activity所在的任务栈中,而 ApplicationContext不存放在任务栈中

  1. Activity的状态都有哪些

->foreground activity
->visible activity
->background activity
->empty process

  1. 横竖屏切换时Activity的生命周期
  • 在没有配置activity的configChanges属性时
    Activity的生命周期:onPause->onStop->onCreate->onStart->onResume
  • 配置了activity的configChanges
    onConfigurationChanged
  1. 如何设置activity成窗口样式
1
android:theme=“@android:style/Theme.Dialog”
  1. Activity的启动方式
  • standard 不管有没有已存在的实例,都生成新实例
  • singleTop 如果发现有对应的Activity实例位于栈顶,则重复利用,否则创建实例
  • singleTask
    a)栈内复用,复用时具有clearTop机制
    b)single taskAffinity in task
  • singleInstance
    a)启用一个新的栈结构,将Activity放置于栈结构中,并保证不会有其它Activity实例进入
    b)方便多个应用共享全局唯一的实例
  1. 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(); //必须声明为static
@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;
}
}
}
  1. IntentService

//开启线程处理耗时操作

1
2
3
4
5
>>onCreate 				//创建单独的HandlerThread处理所有的intent请求
: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;//在于service挂掉了intent是否会回传
}
: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
//onHandleIntent,为自定义的类所实现的耗时操作
: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)//当id为最后一个startService的startId时,进行关闭
}
}

总结
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资源

  1. 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) //Fragment onCreate
  1. Service的onStartCommand有几种返回值
1
2
3
>>START_STICKY	当Service因内存不足被系统kill掉后,会尝试重建Service,创建成功后回调onStartCommand,但Intent是空的
>>START_NOT_STICKY 系统不会尝试重建Service
>>START_REDELIVER_INTENT 重建Service并传递最后一次Intent
  1. Service的onRebind什么情况下执行
  • 当Service刚启动时,通过调用bindService都会进入Service的onBind回调
  • 在Service启动过后,再进行绑定进入onRebind,并在onUnbind返回true
  1. 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);
}
}
}
  1. IntentFilter的匹配法则
  • action //匹配manifest中的其中一个
  • data //如果manifest声明了data匹配,则隐式intent必须设置data
1
2
<data android:scheme=‘content’ host=‘pwcong.me’ path=‘/hello’ port=‘8080’>
Uri.parse(‘content://pwcong.me:8080/hello')
  • 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						//Fragment与Activity发生关联
>>onCreate
>>onCreateView(LayoutInflater,ViewGroup,Bundle) //创建Fragment视图
>>onActivityCreated(Bundle) //当Activity的onCreate执行完
>>onStart->onResume->onPause->onStop
>>onDestroyView //Fragment视图被移除
>>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
//FragmentController mFragments = FragmentController.createController(new HostCallbacks())
b).HostCallbacks的初始化 //class HostCallbacks extends FragmentHostCallback<Activity>
>public HostCallbacks(){



super(Activity.this) //因为HostCallbacks为Activity的内部类
}
c).FragmentHostCallback
>FragmentHostCallback(Activity activity)
{



> FragmentHostCallback(activity,context,handler,windowAnimations) ->
mActivity=activity
mContext=context
mHandler=handler
}
>成员FragmentManagerImpl
d).FragmentController
>private FragmentController(FragmentHostCallback<?> callbacks) {


//至此,mFragments持有HostCallbacks
mHost = callbacks;
}
e).ActivityThread::performLaunchActivity
>attach…
>mFragments.attachHost(null) //主要是让mFragmentManager持有对外部HostCallbacks对象
Activity::performCreate //1.onCreate(bundle) 2.performCreateCommon()
Activity::onCreate //一般通过动态添加fragment时,都会在此调用getFragmentManager
{



super.onCreate(savedInstanceState)
//mFragments.dispatchCreate()->mFragmentManagerImpl.dispatchCreate()
//FragmentmnagerImpl::mCurState=Fragment.CREATED
getFragmentManager() //mFragments.getFragmentManager()-> mHost.getFragmentManagerImpl()
.beginTransaction() //生成BackStackRecord,并让其持有FragmentManagerImpl对象
.replace(R.id.container,mContentFragment)
//fragment.mContainerId =containerViewId
//Op op = new Op() -> op.cmd = opcmd -> op.fragment = fragment -> addOp(op)
.commit();//
}
  1. 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
  1. Fragment如何实现Activity栈的压栈和出栈
1
2
>>	transaction.addToBackStack(null)         	//指定transaction对象
>> getSupportFragmentManager().popBackStack()
  1. 什么情况下造成内存泄露
  • 资源释放问题 长期保存Context、Cursor的引用,资源得不到释放
  • 对象内存过大 Bitmap文件,如decodeStream不设置inSampleSize
  • static关键字的使用
    //考虑到static声明的对象不再属于类实例,而是属于类范围,因此对象的生命周期将会延长,用WeakReference进行替换
  • 线程内存溢出 1.将线程的内部类该为静态内部类,静态内部类不会对外部类对象拥有强引用
    2.在线程内部用WeakReference去引用Context
  • 采用软引用缓存图片//?
  1. 图片过大导致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
//事件起点是DecorView
ViewGroup和View的继承关系和接口
:1.ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。
:2.View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于View。

ViewGroup::dispatchTouchEvent
:1.intercepted = onInterceptTouchEvent(ev) //是否截取点击touch_down事件
-> 默认只有满足滑动条点击时时才返回true,可以重写
-> intercepted==true
调用super.dispatchTouchEvent(et) //View父类
:2.遍历子View
-> ArrayList<View> preorderedList=buildTouchDispatchChildList()//建立前序子view列表
>根据子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))//触点不在child上
continue;
newTouchTarget = getTouchTarget(child) //返回是否有匹配view的TouchTarget
dispatchTransformedTouchEvent(ev,..,child..) //当子View成功消费ev时,返true
> offsetX=mScrollX-child.mLeft
> offsetY=mScrollY-child.mTop
> boolean handled=child.dispatchTouchEvent(event)//此处递归
> return handled
-> 遍历结束
if(dispatchTransformedTouchEvent(…))//当子View在onTouch或者onTouchEvent上成功消费
{



newTouchTarget = addTouchTarget(child, idBitsToAssign)//新建TouchTarget
target.next=mFirstTouchTarget//将TouchTarget插入链首
mFristTouchTarget=target //设置为头指针
alreadyDispatchedToNewTouchTarget=true. //标记成功消费MotionEvent.ACTION_DOWN
}
-> 针对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 //如果没有经过重写,则调用View.onTouchEvent
>case MotionEvent.ACTION_UP:
if (mPerformClick == null) {



mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {



performClick(); //mOnClickListener.onClick(this)
}


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的区别

  • ArrayList
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; //elementData存在一些没使用的空间,所以声明为transient
:2.public void add(int index,E element)
{



ensureCapacityInternal(minCapacity:size + 1) //size代表当前非空元素的长度
>if (minCapacity - elementData.length > 0)
grow(minCapacity) //给数组开辟新的空间,
> int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容因子为3/2
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); // Increments modCount,并且如果有需要进行扩容
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) //时间复杂度为O(n)
{



> 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
}
  • 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
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; //为总内存的1/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)

}
  • 避免keydisptach timeout

    a).ui线程不做耗时操作
    b).使用handler来处理与其它thread的交互
    //new Thread(new Runnable(){ public void run(){…handler.sendMessage(msg)}})
    //匿名内部类持有对外部类实例的引用,导致不能被回收,因将其改为内部静态类,并且把Activity声明为WeakReference

  1. 描述Service的启动方式
1
2
3
4
5
6
7
8
9
10
11
12
13
//主要针对Service和Activity的优先级比较
- Activity.startService
- bindService(intent, conn, flag)
:1.flag的种类
>BIND_AUTO_CREATE //当service不存在时自动创立
>BIND_ADJUST_WITH_ACTIVITY //service的优先级随着其绑定的Activity发生变换,back<->fore
>BIND_ABOVE_CLIENT //service优先级超过Activity
>BIND_IMPORTANT //service优先级相当于前台Activity
>BIND_NOT_FOREGROUND //service优先级不高于前台Activity,但至少会提高到客户端优先级之上
- onStartCommand的返回标志
:1.START_NOT_STICKY //在经过onStartCommand之后,service挂了也不会重启
:2.START_STICKY //重启
:3.START_REDELIVER_INTENT //重启,并传入最后的intent
  1. 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在哪列开始排列

  1. 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(){



//oldTab 为当前表的哈希桶
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//将原哈希桶置空以便GC
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e; //直接将这个元素放置在新的哈希桶里
else
{



//因为扩容是容量翻倍,所以原链表上的每个节点,现在可能存放在原来的下标,即low位, 或者扩容后的下标,
//即high位。 high位= low位+原哈希桶容量
//低位链表的头结点、尾节点
Node<K,V> loHead = null, loTail = null;
//高位链表的头节点、尾节点
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;//临时节点 存放e的下一个节点
do {



next = e.next;
//这里又是一个利用位运算 代替常规运算的高效点: 利用哈希值 与 旧的容量 可以得到哈希值去模后,
//是大于等于oldCap还是小于oldCap,等于0代表小于oldCap,应该存放在低位,否则存放在高位
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)
//将低位链表存放在原index处,
if (loTail != null) {



loTail.next = null;
newTab[j] = loHead;
}
//将高位链表存放在新index处
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
n = (tab = resize()).length;
//如果当前index的节点是空的,表示没有发生哈希碰撞。 直接构建一个新节点Node,挂载在index处即可。
//这里再啰嗦一下,index 是利用 哈希值 & 哈希桶的长度-1,替代模运算
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) {



// p 是待删除节点的前置节点
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;//将待删除节点引用赋给node
else if ((e = p.next) != null) {


//否则循环遍历 找到待删除节点,赋值给node
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);
}
}
//如果有待删除节点node, 且 matchValue为false,或者值也相等
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)//如果node == p,说明是链表头是待删除节点
tab[index] = node.next;
else//否则待删除节点在表中间
p.next = node.next;
++modCount;//修改modCount
--size;//修改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;
//查找过程和删除基本差不多, 找到返回节点,否则返回null
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {



if (first.hash == hash && // always check first node
((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;
//fail-fast策略
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
//依次取链表下一个节点,
if ((next = (current = e).next) == null && (t = table) != null) {



//如果当前链表节点遍历完了,则取哈希桶下一个不为null的链表头
do {


} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
40.LinkedHashMap
>>成员
:1.LinkedHashMapEntry //增加了before和after域,成为双向链表
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 //默认为false,是按照插入结点的顺序,若为true,则可构造LruCache
>>重写newNode,在HashMap调用putVal()方法被调用
>//在构建新节点时,构建的是`LinkedHashMap.Entry` 不再是`Node`.
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;
}
}
>>删除结点
>//在删除节点e时,同步将e从双向链表上删除
void afterNodeRemoval(Node<K,V> e) {


// unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
//待删除节点 p 的前置后置节点都置空
p.before = p.after = null;
//如果前置节点是null,则现在的头结点应该是后置节点a
if (b == null)
head = a;
else//否则将前置节点b的后置节点指向a
b.after = a;
//同理如果后置节点时null ,则尾节点应是b
if (a == null)
tail = b;
else//否则更新后置节点a的前置节点为b
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) {


// move node to last
LinkedHashMapEntry<K,V> last;//原尾节点
//如果accessOrder 是true ,且原尾节点不等于e
if (accessOrder && (last = tail) != e) {



//节点e强转成双向链表节点p
LinkedHashMapEntry <K,V> p =
(LinkedHashMapEntry <K,V>)e, b = p.before, a = p.after;
//p现在是尾节点, 后置节点一定是null
p.after = null;
//如果p的前置节点是null,则p以前是头结点,所以更新现在的头结点是p的后置节点a
if (b == null)
head = a;
else//否则更新p的前直接点b的后置节点为 a
b.after = a;
//如果p的后置节点不是null,则更新后置节点a的前置节点为b
if (a != null)
a.before = b;
else//如果原本p的后置节点是null,则p就是尾节点。 此时 更新last的引用为 p的前置节点b
last = b;
if (last == null) //原本尾节点是null 则,链表中就一个节点
head = p;
else {


//否则 更新 当前节点p的前置节点为 原尾节点last, last的后置节点是p
p.before = last;
last.after = p;
}
//尾节点的引用赋值成p
tail = p;
//修改modCount。
++modCount;
}
}
  1. 红黑树

性质
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是否位于其父结点的左子树,设置父结点的子树指针
x.p.left=y
else
x.p.right=y
y.left=x //左旋之后,x位于y的左分支
x.p=y //设置x父结点为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)
{



//1.结点z的叔结点、父结点皆为红色
//2.结点z和其父结点为红色,叔结点为黑色,结点z为左分支
//3.结点z和其父结点为红色,叔结点为黑色,结点z为右分支

}
  1. Handler Looper 工作原理
    成员
1
2
3
4
5
6
7
8
9
:1.class Looper{



static final ThreadLocal<Looper> sThreadLocal =new ThreadLocal()//why declare staic?
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()// 通过ThreadLocal获取Looper实例,通过Looper获取消息队列实例
final MessageQueue queue = me.mQueue
for (;;) {



Message msg = queue.next()//通过消息队列阻塞性地获取消息
if (msg == null) {


return ;}// 跳出循环
try{



msg.target.dispatchMessage(msg)//检查顺序:msg.callback->mCallback->handleMessage
}
finally{



}
msg.recycleUnChecked() //初始化msg属性,并放置在Message的sPool头部
}
}

最后Handler通过sendMessage将消息塞入mQueue队列
由Looper接收到了msg,然后调用Handler的dispatchMessage
Handler 检查mCallback->message.callback->mCallback的handleMessage

  1. Listview的几种adapter介绍
  • SimpleAdapter
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
//在一个行中能放入不同view组件
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);//drawable
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);
  • ArrayAdapter
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
//在一个行中只能放入TextView
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)
//inflate传参:1).viewGroup为null,此时R.layout.xxx的根节点的layout_width、layout_height失效
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); //mTag=holder
}else
{



holder = (ViewHolder)convertView.getTag(); //return mTag
}

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;
  1. 如何在ListView间添加分割线
1
2
3
4
5
6
7
8
9
10
//推荐用divider设置drawable的分割线
>>.设置全局属性
a).android:divider="#FFF" //设置为null可无视间距
b).android:dividerHeight="1px"
c).默认的list view不支持设置footerDevider,采取在每个item布局中添加
> <View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#999"
/>
  1. LinkedHashMap 源码解读
  2. Drawable转Bitmap
1
>>Bitmap bitmap= BitmapFactory.decodeResource(getResources(),R.drawable.icon_hint);
  1. 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

  1. android:layout_gravity和android:gravity的区别

android:layout_gravity 用于设置当前View在父容器的位置
android:gravity 用于设置当前View中文本、图像的位置

  1. 如何重用布局
    采用
  2. 使用merge减少include布局所造成的布局层次
1
<merge xmlns:android="http://schemas.android.com/apk/res/android">
  1. 如何优化布局

设置为invisible或者gone的View -> ViewStub
:1.ViewStub是一个不可见且大小为0的视图
:2.当ViewStub被设置为visible或者调用.inflate()时就会被指定的layout所代替
:3.inflatedId指定加载过来的layout id
:4.例子

  1. android:layout_gravity的使用规则

LinearyLayout 当orientation为horizontal时,存在 center\top\bottom
FrameLayout
a).android:layout_centerVertical

  1. TextView显示图像
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>android:drawableLeft 			//设置图像id
>>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)
{



//todo:利用intent执行跳转
}
},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);

  1. 在按钮上显示图像的方式有哪些
    Button android:drawableXXX(left|rigth|…)
    Button继承自TextView,可以通过采用SpannableString进行setText
    ImageButton
    a).layout_width&layout_height //设置了具体的宽高后,图像并不会拉伸,而是背景增大
    b).src //设置图片
    c).不设置宽高,表现与ImageView一致
  2. 如何动态改变Button的大小和位置
    View.layout(left,top,right,bottom)
  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
drawable	//通过selector标签构建xml
<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) {




}
});
  1. 如何实现图像的半透明度
    自定义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)
}
>>在图像上方覆盖一个半透明的图像 //设置android:background,父组件采用FrameLayout
  1. 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
//inputStream进行包装,可进行整行读取
a).BufferedReader br=new BufferedReader(is);
while((line=br.readLine())!=null)
result+=line+”\n”;
c).网页也是字符组织,故该方法可行

BitmapFactory.decodeStream(is)

1
//读取图片,返回的Bitmap用完要记得销毁,只有BufferedInputStream实现了reset
  1. ProgressBar
  • 属性设置
    a).style=“@android:style/Widget.ProgressBar.Horizontal”
    b).android:max=“100”
    c).android:progress=“50”
  • 属性更新
    a).setProgress(int v)
    b).incrementProgressBy(int step)
  1. AbsListView
    layoutChildren
    a).ListView::layoutChildren
  2. ListView,如何进行数据的增删改查

数据更新
a).Adapter::notifyDataSetChanged() //只使用于数据集的某项改变,更改引用将不会有效
b).Adapter::update //自定义方法,直接更新对应holder
BaseAdapter.notifyDataSetInvalidated()
a).针对adapter数据源改变引用的情况

  1. 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)			//重写子类继承CursorAdapter
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);
}
  • CursorAdapter源码
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) //mRowIDColumn==cursor.getColumnIndex(“_id”)
} 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
//declare-styleable name=“MyView”
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);
  1. 如何动态加载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; //用于匹配Uri
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)
{



//sqlite: mSqliteDatabase.execSQL(“update student set name=‘dd’ where ….”)
int affected=mSqliteDatabase.update(tableName,cv,selection,selectionArgs);
return affected;
}
e).public String getType(Uri uri)
>>ContentObserver //监听ContentProvider数据改变
:1.getContentResolver().registerContentObserver(Uri uri, boolean notifyForDescendents,ContentObserver co)
//设置监听,ContentObserver需要自己实现
:2.getContext().getContentResolver().notifyChange(uri,null) //在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
@Override
protected void onCreate(Bundle savedInstanceState)
{



super.onCreate(savedInstanceState)

Uri uri=Uri.parse(“content://com.ryg.chapter_2.book.provider”);
getContentResolver().query(uri,null,null,null,null,null);
getContentResolver().registerContentObserver(uri,true,Observer)
}

static class LObserver extends ContentObserver{



public LObserver(Handler handler)
{



super(handler); //handler作用是用于更新ui
}
public void onChange(boolean selfChange)
{



Cursor cursor=getContentResolver().query(…)
//do ui op
}
}
  1. 如何设置Activity为程序启动的默认Activity
1
2
3
4
>><intent-filter>
<action android:name=“android.intent.action.MAIN”/>
<category android:name=“android.intent.category.LAUNCHER”/>
</intent-filter>
  1. 启动Activity的几种方式
1
2
>>startActivity	 intent
>>startActivityForResult
  1. 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
  1. 如何将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
  1. 如何获取短信内容
    指定intent-filter
  2. 如何拦截手机屏幕休眠和唤醒
    通过程序里注册广播监听 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. 如何发送广播
1
2
3
4
5
Intent intent…
intent.addAction(‘…’)
intent.putExtra(key,val)
sendBroadcast(intent)
Bundle bundle=intent.getExtras()//当广播拿到消息
  1. 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)
}

//声明CREATOR常量
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];
}
}
}
>>
  1. 如何读取联系人信息
    Cursor cursor=getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null)
  2. 请描述Content Provider的uri由哪几部分组成
1
2
3
4
5
//content://mobile.android.mydata/product/20
>>scheme content://
>>authority 域名,mobile.android.mydata
>>path 路径,product
>>param 20
  1. 介绍开发ContentProvider的步骤
    编写一个类,继承ContentProvider
    实现query\update\delete\insert
    在static块中创建UriMatcher,add需要映射的uri,初始化SqliteOpenHelper和SqliteDatabase的实例
    manifest中注册provider并填写authorities属性,readPermission
  2. 如何为ContentProvider添加访问权限
1
2
3
4
5
6
7
8
9
10
>>在manifest中声明 自定义权限
<permission android:name=“xxx” android:protectionLevel=“normal”/>
>>添加权限限制 //provider可添加android:permission、android:readPermission、android:writePermission
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)		//不设置title不显示icon
.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();

//设置button的颜色和大小
dialog.getButton(dialog.BUTTON_NEGATIVE).setTextColor(Color.parseColor(“#009900”))

显示列表项

1
2
3
4
5
6
7
8
9
10
11
12
13
//与setMessage冲突
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)	//注意用ConvertView和ViewHolder提高性能

显示自定义View

1
a).setView(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)
}
  1. 如何使用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)

  1. 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
  2. 点击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); //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)
}
  1. 如何将上下文菜单绑定至可视组件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)
  1. 在单击菜单时,如何能跳转进别的Activity
1
2
MenuItem item=menu.add(0,1,0,name)
item.setIntent(new Intent(this,ActivityB.class)
  1. 菜单的回调函数有哪些
1
2
>>onOptionsItemSelected
>>onContextItemSelected
  1. 如何使用SharedPreferences存取数据
    读取
1
2
3
//存放文件位于/data/data/{package}/shared_prefs/PREFERENCE_NAME
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()
  1. 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); //将对象写入"employee.dat"中
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() //Product implements Serializable

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();
}
  1. 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)

  1. 如何将table1表的数据和结构复制到table2上
1
create table table2 as select * from table1

102.SQLite

  1. 多次插入开启事务 //节省回滚时间
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()
}
  1. 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”)
  1. 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()
  1. 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. 如何打开手机中的蓝牙功能
  • 使用Intent
1
2
Intent enableIntent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableIntent,1)
  • BluetoothAdapter
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)
{



//TODO:处理这些未绑定的设备
}
}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)
{



//todo:去电
}else{



//todo:来电
}
}
}

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>
  1. 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;
}
  1. 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://call_log/calls”)
>>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)
}
  1. 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()
  1. 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();
}
});
  1. 在工程路径下的res/drawable存放tree.png,如果将图片显示在View上
1
2
3
4
5
6
InputStream is=getResources().openRawResource(R.raw.xxx)  //只适合读取raw下的资源
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)	//调用setBounds设置范围,在调用draw
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
//lottey_animlist.xml
<?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作为载体显示
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://schemas.android.com/apk/res/android”
android:delay=“0.5
android:animationOrder=“normal”
android:animation=“@anim/anim_item”/>

b)为子元素指定具体动画

1
2
3
4
5
<set xmlns:android=“http://schemas.android.com/apk/res/android”
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

  • ObjectAnimator
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精确第计算由初始值到结束值的插值模式
ObjectAnimator
.ofInt(mSkyView,”backgroundColor”,mBlueSkyColor,mRedSkyColor)
.setDuration(3000)
.setEvaluator(new ArgbEvaluator()) //针对色彩的evaluator
.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);
}
}
  • ValueAnimator
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>(){




// fraction = t / duration
@Override
public PointF evaluate(float fraction, PointF startValue,
PointF endValue)
{



Log.e(TAG, fraction * 3 + "");
// x方向200px/s ,则y方向0.5 * 10 * t
PointF point = new PointF();
point.x = 200 * fraction * 3;
point.y = 0.5f * 200 * (fraction * 3) * (fraction * 3);
return point;
}

});
  • AnimatorSet 动画集合
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. 定义字符串数组资源
  • 在res/values 定义xxx.xml
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)
  1. 图层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://schemas.android.com/apk/res/android”>
<item>
<bitmap android:src=“” android:gravity=“center”/>
</item>
<item android:top=“20dp” android:left=“20dp”>
<bitmap android:src=“” android:gravity=“center”/>
</item>
</layer-list>
  1. Clip图像资源
  • 放置在res/drawable clip.xml
1
2
3
4
5
<clip xmlns=“http://schemeas.android.com/apk/res/android”
android:drawable=“”
android:orientation=“horizontal”
android:gravity=“left”
/>
  • ImageView imageView
1
2
ClipDrawable drawable=(ClipDrawable)imageView.getBackground()
drawable.setLevel(3000)//内部预设最大值为10000
  1. ShapeDrawable
    放置在res/drawable shape.xml
1
2
3
4
5
6
7
8
9
<shape xmlns=“http://schemas.android.com/apk/res/android” android:shape=“rectangle”>
<!— 定义渐变色 —>
<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>
  1. 如何统一设置多个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>
  1. 布局文件中的”@“、”+”、”?”含义
    引用资源
    +号代表所引用的资源id在R类不存在
    ?代表引用当前主题的属性
  2. 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); //进入onProgressUpdate回调
}
return i + params[0].intValue() + "";
}

//该回调处于ui主线程
@Override
protected void onProgressUpdate(Integer... values) {



int vlaue = values[0];
progressBar.setProgress(vlaue);
}

//该方法运行在UI线程当中
@Override
protected void onPreExecute() {



textView.setText("开始执行异步线程");
}

//该回调处于ui主线程,onPostExecute的形参类型由result决定
@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去进行提示
  1. 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);
//这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
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

  1. 自定义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);
//进行childView的宽高计算,会进入view的onMeasure回调
measureChildren(widthMeasureSpec, heightMeasureSpec)
//for+measureChild(child,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();
// 上面两个childView
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
  1. 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()
//todo....
Response response=chain.proceed(request)
return response
}

源码
:1.创建OkHttpClient

1
2
3
4
5
6
7
8
9
10
11
12
13
> public OkHttpClient() {


//直接创建的 OkHttpClient对象并且默认构造builder对象进行初始化
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); //获取scheme、host、port等信息
return url(parsed) //设置this.url成员
}
}

: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 /* for web socket */);
}
...
}
> 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{


//implements Runnable
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
> 	/** 最大并发请求数为64 */
private int maxRequests = 64;
/** 每个主机最大请求数为5 */
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) {



//maxRequest==64
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 {




//用来指定你使用的 java 版本。通常你应该返回
@Override
public SourceVersion getSupportedSourceVersion() {



return SourceVersion.latestSupported();
}

//会被处理器调用,可以在这里获取Filer,Elements,Messager等辅助类,后面会解释
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {



super.init(processingEnv);
}

//这个方法返回stirng类型的set集合,集合里包含了你需要处理的注解
@Override
public Set<String> getSupportedAnnotationTypes() {



Set<String> annotataions = new LinkedHashSet<String>();
annotataions.add("com.example.MyAnnotation");
return annotataions;
}


//核心方法,这个一般的流程就是先扫描查找注解,再生成 java 文件
//这2个步骤设计的知识点细节很多。
@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
//查找用BindView注解的元素
...for(Element element:env.getElementsAnnotatedWith(BindView.class)
try{



parseBindView(element, Map<TypeElement,BindingSet.Builder>builderMap,...)
....enclosingElement=(TypeElement)element.getEnclosingElement()
TypeMirror elementType = element.asType()//转换成TypeMirror才能获取类型等信息
if (elementType.getKind() == TypeKind.TYPEVAR) {


//变量类型
TypeVariable typeVariable = (TypeVariable) elementType
elementType = typeVariable.getUpperBound()//获取上限类型
}
//检查1!!!
if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) {



throw Error
}
int id = element.getAnnotation(BindView.class).value()//获取资源id
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)//变量类型
//往刚刚生成的BuildingSet.Builder加入field信息
builder.addField(resourceId, new FieldViewBinding(name, type, required))
... new FieldViewBinding(name, type, required)//分配变量名、变量类型

... viewId=new ViewBinding.Builder(id)
viewIdMap.put(id,viewId)//viewIdMap为BindingSet的私有变量
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
TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
.addModifiers(PUBLIC); //创建类
//设置类要实现的接口
if (parentBinding != null) {



result.superclass(parentBinding.bindingClassName);
} else {



result.addSuperinterface(UNBINDER);//设置实现Unbinder
}
if (hasTargetField()) {


//检查FieldViewBinding
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");//targetTypeName指向Activity
//构建bind
result.addMethod(createBindingConstructor(sdk, debuggable, useAndroidX))
... if (hasViewBindings()) {



for (ViewBinding binding : viewBindings) {



addViewBinding(constructor, binding, debuggable)
-> FieldViewBinding fieldBinding = binding.getFieldBinding()

}
}
//针对hasViewBinding,构建unbind函数
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();//获取变量的上界,比如TextView extends View
}
//判断是否为View的子类型,不是的话报错
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;
}
}
//获取@BindView的id值
int id = element.getAnnotation(BindView.class).value();
//检查是否有缓存,有则生成ViewBindings
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 {



//声称_ViewBinding类
bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement);
}
//name就是word
String name = element.getSimpleName().toString();
//类型的名字
TypeName type = TypeName.get(elementType);
//生成FieldViewBinding实体
FieldViewBinding binding = new FieldViewBinding(name, type, required)
//5.加入到 bindingClass 成员变量的集合中
bindingClass.addField(getId(id), binding);
1
:2.getOrCreateTargetClass			//获取或者创建BindingClass
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
>//遍历生存生成java 文件
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 //生成

  • BindingClass的数据结构
    :1.
  • 注解
1
2
3
4
5
6
7
8
9
10
11
@Target({


ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface BindView{



@IdRes int value();
}
  • JavaPoet用法
    a).目标模板
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是个接口,支持通过注解处理器创建新文件
filer = processingEnv.getFiler();
}

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {



for (TypeElement element : annotations) {



if (element.getQualifiedName().toString().equals(JPHelloWorld.class.getCanonicalName())) {



// 创建main方法
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();
// 创建HelloWorld类
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();

try {



// 生成 com.example.HelloWorld.java
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); //getId对应设置时的数字
- bind //运行时
:1.public static Unbinder bind(Activity target){


//针对Activity和DecorView
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() //cls属于Class,调用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
  • 类的静态成员
  • 方法区重的常量引用对象
  1. HashSet
  • 采用HashMap进行实现,值为new出的Object
  • 采用HashMap的put进行不重复性的保证
  1. SSL中的对称加密和非对称加密
  • 对称加密
    a).当通过浏览器向服务器请求安全网页时(https://…)
    b).服务器同时把证书和公钥发送过来
    c).浏览器检查证书是不是由信赖机构颁发的
    d).浏览器随机生成对称密钥,并采用服务器发送的公钥加密自身的公钥,同时采用公钥加密请求,一并发送至服务器
    e).服务器用自己的私钥解密了发过来的密钥,并用该密钥解析加密后的请求
    f).服务器用收到的密钥加密返回的数据
    G).浏览器用自身的私钥解密服务器返回的加密消息
    //那么中间人有没有可能解密出信息呢,关注点主要在客户端发来的信息
    //从d步骤开始检测,中间人截取到了被加密的浏览器密钥,但是无法猜测对应服务器的公钥,故无法解密
    // a)假设中间人清除目标服务器,首先获取了服务器的公钥,但是只有私钥才能解密被该公钥加密后的信息,故也无法解密
  1. 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为静态类
  1. 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,因为不能保证原子性操作
  1. 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))//检查1!!!
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()//ThreadLocal变量保存
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)//发送单个事件!!!
...//查找eventClass类所有的父类以及接口
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)		//Msg为普通po,handleSomethingElse名字不固定
{



….
}

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 findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {



//通过反射来获得订阅方法信息
findUsingReflectionInSingleClass(findState);
//查找父类的订阅方法
findState.moveToSuperclass();
}
//获取findState中的SubscriberMethod(也就是订阅方法List)并返回
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){


}
//遍历Method
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();
//实例化SubscriberMethod对象并添加
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对象
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);
}
}
//根据优先级priority来添加Subscription对象
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;
}
}
//将订阅者对象以及订阅的事件保存到typesBySubscriber里,以便后续取消订阅
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber)
if (subscribedEvents == null) {



subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//区别sticky事件,如果为sticky事件则立即分发
if(subscriberMethod.sticky){



if(eventInheritance)//是否考虑监听事件的父类也作为事件去监听
{


}
else{



Object stickyEvent = stickyEvents.get(eventType)//stickEvents为全局对象
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()//ThreadLocal变量
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); //如果post线程就是主线程
} 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 queue
private 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) {



// Check again, this time in synchronized
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);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event)
}

>>executorService = Executors.newCachedThreadPool()

>>eventTypesCache //Map<Class<?>, List<Class<?>>>
a).
  1. String a=“ abc”;String b=“ abc”; 创建了几个对象 //栈存放引用变量 堆存在new出来的对象,并且堆划分出一部分作为常量池

当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个X在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加
b)Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象
c)使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象
d)使用包含变量的表达式来创建String对象,会在堆栈创建新的

  1. 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();//此句会出现问题,因为构造函数存在多次赋值,而第1层检测是允许重入的,
//所以其它线程可能会去引用一个未初始化完成的对象,造成系统崩溃
//加入volatile后保证在多线程环境下对变量的写操作优先于读操作
}
}
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 //Entry表
c).private static final int INITIAL_CAPCITY=16 //必须为2的幂
d).ThreadLocalMap(ThreadLocal<?> firstKey,Object firstValue)
{



table=new Entry[INITIAL_CAPACITY]
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1)
//当新建ThreadLocal实例时会生成threadLocalHashCode,主要针对ThreadLocal的开地址解决冲突策略
// 初始化该节点
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) //清理staleEntry
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
//遇到空的entry即停止
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{



//get the hash of this new slot
int h= k.threadLocalHashCode()&(len-1)
if(h!=i)//rehash,鉴于该hash有可能是基于线性探查才决定的位置
{



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)//该entry的引用已被GC回收
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:
// 默认设置为16sp,TypeValue也可以把sp转化为px
mTitleTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
break;

}

}
a.recycle();

/**
* 获得绘制文本的宽和高
*/
mPaint = new Paint();
mPaint.setTextSize(mTitleTextSize);
// mPaint.setColor(mTitleTextColor);
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);
//当自定义view采用wrap_content匹配时需要重写 a).setMeasuredDimension(width,height)
}

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);
}
  1. Android @Override //重写
    a).重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载
    b).重写方法的访问修饰符一定要大于被重写方法的访问修饰符 public>protected>default>private
    c).重写的方法的返回值必须和被重写的方法的返回一致
    d).重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类
    e).被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写
  2. 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)//资源数量为1
semaphore.acquire() //相当于P操作
semaphore.release() //相当于V操作
>>例子
final Semaphore sema = new Semaphore(3);
for (int index = 1; index < 10; index++) {



new Thread(new Runnable(){



@Override
public void run (){



sema.acquire()
//todo...
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
>> //延迟1秒后,每隔2秒执行一次该任务
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); //容易OOM
is.close();
} catch (IOException e) {



e.printStackTrace();
}
return bitmap;
}
}
  1. ImageView
    ScaleType
    :1.FIT_CENTER 默认,图片等比例缩放到宽或者高能够填充控件大小
    :2.FIT_START,FIT_END 等比例缩放,并放置在控件的左边或者上方
    :3.FIT_XY 完全填充控件大小,但不是等比例
    :4.CENTER -> 图片大小为原始大小,如果大于ImageView组件,则截取中间部分,若小于则将图片居中显示
    :5.CENTER_CROP ->图片等比例缩放,使图像的短边填满,截取中间部分
  2. 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);//默认值是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

  1. Fragment在横竖屏切换时的数据保存
    onCreate-> setRetainInstance(true)
    当设置retain后,Fragment的生命周期不再经过onCreate、onDestroy
  2. 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

  1. IntentFilter的匹配规则
1
2
3
4
5
6
7
8
9
10
11
>><intent-filter>			//进行匹配
:1.<action android:name=“…”/> //目标intent-filter可以有多个action,intent只要符合其中一个
:2.<category android:name=“…”/> //addCategory,intent可以没有category,但是intent的category都必须在目标中存在
:3.<data android:scheme=“string”
android:host=“string”
android:port=“string”
android:path=“string”
android:mimeType=“string”/> //intent.setDataAndType(Uri.parse(…),”image/png”)
>>预查询能匹配的Activity //返回ResolveInfo
a)getPackageManager().queryIntentActivities(intent,0)//0代表MATCH_DEFAULT_ONLY
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");
//获取Service自身Messenger所对应的IBinder,并将其发送共享给所有客户端
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); //在onDestroy中调用unbindService
}
>>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刷新界面
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 //内连接,table1 (inner) join table2 on …
>>natural join //自然连接,自动检测相同属性是否值相等
>>outer join //外连接,table1 left outer join table2 on ….

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对象

  • obtain
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; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message()
}
  • recycleUnchecked
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

  • ViewHolder复用
    :1.Recycler
1
2
3
>mAttachedScrap 		//仍然依赖于RecyclerView,但已经被标记移除的vh(例如已经滑出可视范,但还没被移除)
>mChangedScrap //存储notifyXXX时需要改变的vh
>mCachedViews //mAttachedScrap不再依赖RecyclerView的vh

: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
{



//1)确认锚点信息
if(mAnchorInfo.mLayoutFromEnd)
{



}else{



firstLayoutDirection=LayoutState.ITEM_DIRECTION_TAIL//向底部填充
}
//2)填充
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))//判断是否有剩余空间&&mItemCount>position
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() //!!,调用Recycler.Adapter
...
mAdapter.onBindViewHolder() //!!,调用
addView(view) //加入子view
measureChildWithMargins(view) //view.measure(..,..)
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	//class MediaPlayerService : public BnMediaPlayerService,媒体服务  
: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)//remote是BpBinder
: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)
  • Service Manager
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 //句柄为0
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();//mH
}
...
Looper.loop()
  • ActivityInfo //封装了所有在AndroidManifest.xml解析出的关于Activity、receiver的信息

  • ActivityClientRecord
    a).intent

    mAction=“android.intent.action.MAIN”
    mComponent={mClass=“com.pill.interview.annotation.Home”}
    b).activityInfo
    taskAffinity=“com.pill.interview”
    processName=“com.pill.interview”

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() //1.新建looper,并放置于ThreadLocal中 2.赋值在Looper的sMainLooper
ActivityThread thread=new ActivityThread()//1.初始化成员变量mH,mH=new H()2.初始化ApplicationThread

sMainThreadHandler = thread.getHandler(); //将mH赋值给sMainThreadHandler
Looper.loop()
}
>>private class H extends Handler{



public void handleMessage(Message msg)//包含: 对Activity、Service、Application的操作
{



switch(msg.what)
{



a).case LAUNCH_ACTIVITY:
handleLaunchActivity(r,Intent customIntent(null), "LAUNCH_ACTIVITY") ->
>performLaunchActivity(r,customIntent)//通过Instrumentation反射得到Activity对象,新建Application
>检查r.packageInfo //无效的话通过r.applicationInfo去创建
>检查r.intent.mComponent//无效的话通过r.intent.resolveActivity去创建
> activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
//(Activity)cl.loadClass(className).newInstance() -> 反射创建Activity
>Application app = r.packageInfo.makeApplication(false, mInstrumentation)
>Context appContext= createBaseContextForActivity(r, activity)//创建Context
>CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager())//设置标题
>activity.attach(appContext…)
>mInstrumentation.callActivityOnCreate(activity, r.state);
//进入Activity的onCreate回调,设置ActionBar和恢复Fragments的状态
>activity.performStart() //调用Activity的onStart()回调
>mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state)
//进入Activity的onRestoreInstanceState回调
>r.paused = true //代表当前Activity的状态是pause
b).case RESUME_ACTIVITY: //当进入Resume后
handleResumeActivity(IBinder token,boolean clearHide,boolean isForward)->
>ActivityClientRecord r = mActivities.get(token)
//mActivities -> ArrayMap<IBinder,ActivityClientRecord>
>r = performResumeActivity(token, clearHide, reason)
>if(r.pendingIntents != null) {



deliverNewIntents(r, r.pendingIntents)
//mInstrumentation.callActivityOnNewIntent(r.activity, intent),进入onNewIntent回调
r.pendingIntents = null;
}
>r.activity.performResume()//进入onResume()
c).case PAUSE_ACTIVITY:
handlePauseActivity(IBinder token,boolean finished …) ->
>performPauseActivity(ActivityClientRecord r,boolean finished,..)
> 检查r.paused //1)r.paused==true,检查r.activity.mFinished是否为true,否则报异常
> if (r.paused &&r.activity.mFinished) {


return null}
> if(r.paused){


RuntimeExceptio e}
> 检查finished //if(finished) -> r.activity.mFinished=true
> if (!r.activity.mFinished && saveState) {


//如果需要saveState
mInstrumentation.callActivityOnSaveInstanceState(r.activity,r.state)
//进入Activity的onSaveInstanceState回调
}
> performPauseActivityIfNeeded(r,reason)
if(r.paused){


return ;}
mInstrumentation.callActivityOnPause(r.activity); //进入onPause回调
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); //进入Activity的onSaveInstanceState回调
}
}
e).case DESTROY_ACTIVITY:
handleDestroyActivity(IBinder token,boolean finishing…) ->
>performDestroyActivity(token, finishing,configChanges, getNonConfigInstance)
>performPauseActivityIfNeeded //如果必要,使Activity进入mPaused==true的状态

}

}
}
>>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/") //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

  • 创建Observable
1
2
3
4
5
6
7
8
:1.create(ObservableOnSubscribe<T> source)
{



RxJavaPlugins.onAssembly(new ObservableCreate<T>(source)) //this.source = source

}
  • Action
  • Function
  • map操作符