文章目录
前言
本篇内容比较快捷短小。属于快问快答形式。大家可以下载下来,每天一遍。
记住,答案只是引导,在回答完后发散自己的引导思维。去引申。
准备好挑战了吗?那么我们开始
- 如何将table1表的数据和结构复制到table2上
create table table2 as select * from table1
102.SQLite
- 多次插入开启事务 //节省回滚时间
try{
mSqliteDatabase.beginTransaction()
…
mSqliteDatabase.execSQL(….)
…
mSqliteDatabase.setTransactionSuccessful()
}catch(Exception e)
{
e.printStackTrace()
}finally{
mSqliteDatabase.endTransaction()
}
- alter操作
a).alter table student add sex integer default 0 //增加sex column
b).目前不支持drop column //采用新建临时表,复制数据,删除旧表
mSqliteDatabase.execSQL(“create table temp as select … from student”)
mSqliteDatabase.execSQL(“drop table if exists student”)
mSqliteDatabase.execSQL(“alter table temp rename to student”)
- SQLiteOpenHelper.getReadableDatabase和SQLiteOpenHelper.getWritableDatabase将数据库文件放哪了
保存在/data/data//databases/DATABASE_NAME
104.然后将带数据的SQLite同apk一起发布
- SQLite放置在res/raw
a).InputStream is= getResources().openRawResource(R.id.filename)
b).InputStream is= getAssets().oepn(path) //path基于assets文件夹 - Android不能打开放置在apk的数据库文件,因此在初始化时将文件拷贝到sdcard中
- String databaseFilename=‘/sdcard/test.db’
if(!new File(databaseFilename).exists())
{
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和域名
socket.getInetAddress().getHostName()
socket.getInetAddress().getHostAddress()
- BufferedInputStream mark //只有BufferedInputStream实现了mark方法
- mark(int readmit) //设置在标记位置失效前能够读取的字节数
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();
- 如何打开手机中的蓝牙功能
- 使用Intent
Intent enableIntent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableIntent,1)
- BluetoothAdapter
BluetoothAdapter bluetoothAdapter=BluetoothAdapter.getDefaultAdapter()
bluetoothAdapter.enable()
111.如何获取已绑定的蓝牙设备
Set<BluetoothDevice> devices=bluetoothAdapter.getBoundedDeviecs()
112.搜索蓝牙的过程中经过哪些状态
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.如何采用广播监听去电和来电
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描述
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<receiver android:name=".PhoneBroadcastReceiver">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
- Android支持的电话状态
TelephonyManager tm =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)
switch(tm.getCallState())
{
case TelephonyManager.CALL_STATE_RINGING:
break;
case TelephonyManager.CALL_STATE_IDLE:
break;
}
- Android如何控制接听和挂断电话
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
>><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
SmsManager smsManager=SmsManager.getDefault()
String message=“i gonna fuck u”
List<String> msges=smsManager.divideMessage(message);
for(int i=0;i<msges.size();i++)
{
smsManager.sendTextMessage(“12345678”,null,”你好吗”,PendingIntent pi,PendingIntent di)
}
- ContactsContract
Contacts&CommonDataKinds.Phone //存储通讯录中每个人的概要信息
a).查询姓名
Cursor c=getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null,null,…)
String name=c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY))
b).查询电话
Cursor c=getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null,…)
int id=c.getInt(c.getColumnIndex(ContactsContract.Contacts._ID))
c.close()
c=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID+”=?”,id,null)
c.moveToFirst()
while(!c.isAfterLast())
{
String phone=c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER))
…
}
c.close()
- VideoView 播放视频
Uri uri =Uri.parse(path)
videoView.setVideoURI(uri)
videoView.start()
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();// 播放
Toast.makeText(MainActivity.this, "开始播放!", Toast.LENGTH_LONG).show();
}
});
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText(MainActivity.this, "播放完毕", Toast.LENGTH_SHORT).show();
}
});
- 在工程路径下的res/drawable存放tree.png,如果将图片显示在View上
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进行绘图
Drawable.draw(canvas) //调用setBounds设置范围,在调用draw
Drawable drawable=getResources().getDrawable(R.drawable.tree)
drawable.setBounds(x,x,x,x)
drawable.draw(canvas)
124.如何设置图像透明度
Paint paint=new Paint()
paint.setAlpha(0.5)
canvas.drawBitmap(bitmap,sourceRect,destRect,paint)
125.如何旋转View
Matrix
Matrix matrix=new Matrix()
matrix.setRotate(120,80,80)
canvas.setMatrix(matrix)
126.Activity 切换
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,属于帧动画
//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)动画声明 =>
<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)动画调用 =>
Animation animation= AnimationUtils.loadAnimation(this,R.anim.alpha_demo)
imageView.startAnimation(animation)
RotateAnimation 旋转渐,针对
a)动画声明 =>
<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)动画声明 =>
<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)动画声明 =>
<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
<layoutAnimation
xmlns:android=“http://schemas.android.com/apk/res/android”
android:delay=“0.5”
android:animationOrder=“normal”
android:animation=“@anim/anim_item”/>
b)为子元素指定具体动画
<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)设置父容器属性
<ListView android:layoutAnimation=“@anim/anim_layout”/>
AnimationSet 动画集合,针对
a)动画声明 =>
<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
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
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 动画集合
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()
- 定义字符串数组资源
- 在res/values 定义xxx.xml
<resources>
<string-array name=‘planet_array’>
<item>venus</item>
<item>mars</item>
<item>earth</item>
</string-array>
</resources>
- 程序加载
String[] names=getResources().getStringArray(R.array.names)
- 图层layer资源
在res/drawable layer.xml
<?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>
- Clip图像资源
- 放置在res/drawable clip.xml
<clip xmlns=“http://schemeas.android.com/apk/res/android”
android:drawable=“”
android:orientation=“horizontal”
android:gravity=“left”
/>
- ImageView imageView
ClipDrawable drawable=(ClipDrawable)imageView.getBackground()
drawable.setLevel(3000)//内部预设最大值为10000
- ShapeDrawable
放置在res/drawable shape.xml
<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>
- 如何统一设置多个View的android:textSize和android:textColor
放置于res/values style.xml
<resources>
<style name=“customText” parent=“@style/Text”>
<item name=“android:textSize”>20sp</item>
<item name=“android:textColor”>#008</item>
</style>
</resources>
-
布局文件中的”@“、”+”、”?”含义
引用资源
+号代表所引用的资源id在R类不存在
?代表引用当前主题的属性 -
android获取屏幕高度和宽度的办法
:1.通过获取DisplayMetrics
DisplayMetrics dm=getResources().getDisplayMetrics()
int width=dm.widthPixels
int height=dm.heightPixels
:2.通过windowManager
>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>{
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去进行提示
- HandlerThread
//例子
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
a)没有Looper 所以需要在Thread的run中去执行Looper.prepare()和Looper.loop()
b)Hanlder的初始化需要当前线程具有looper,所以也得放置在Looper的循环中
HandlerThread
a)HandlerThread自行进行Looper的prepare、loop等api
b)允许调用getLooper()获取Looper实例
c)可以在handleMessage执行耗时
d)HandlerThread自己创建Looper,分担了主Looper的压力
e)异步请求是排队调用的,不适合网络IO
- 自定义ViewGroup
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
- OkHttp
使用
:1.get方式 //new OkHttpClient -> 构造Request对象 -> newCall ->enqueue or execute
> 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
> 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
> @Override
public Response intercept(Chain chain) throws IOException {
Request request=chain.request()
//todo....
Response response=chain.proceed(request)
return response
}
源码
:1.创建OkHttpClient
> public OkHttpClient() { //直接创建的 OkHttpClient对象并且默认构造builder对象进行初始化
this(new Builder());
}
OkHttpClient(Builder builder) {
this.interceptors = Util.immutableList(builder.interceptors)
...
}
:2.创建Request //代码new Request.Builder().url(url).build()
> 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)
> 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线程池
> /** 最大并发请求数为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
>BridgeInterceptor -> 添加Content-Type、Content-Length、User-Agent等协议头
>ConnectInterceptor -> ....
>CallServerInterceptor -> 实际发送网络请求
149.ButterKnife //源码阅读
初始化
:1.定义处理器
```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.注册处理器
@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.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);
:2.getOrCreateTargetClass //获取或者创建BindingClass
>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文件
>//遍历生存生成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. -
注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface BindView{
@IdRes int value();
}
- JavaPoet用法
a).目标模板
>public final class HelloWorld{
public static void main(String[] args){
System.out.println(“hello,JavaPoet!”);
}
}
b).? extends AbstractProcessor
{
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.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
- 类的静态成员
- 方法区重的常量引用对象
- HashSet
- 采用HashMap进行实现,值为new出的Object
- 采用HashMap的put进行不重复性的保证
- SSL中的对称加密和非对称加密
- 对称加密
a).当通过浏览器向服务器请求安全网页时(https://…)
b).服务器同时把证书和公钥发送过来
c).浏览器检查证书是不是由信赖机构颁发的
d).浏览器随机生成对称密钥,并采用服务器发送的公钥加密自身的公钥,同时采用公钥加密请求,一并发送至服务器
e).服务器用自己的私钥解密了发过来的密钥,并用该密钥解析加密后的请求
f).服务器用收到的密钥加密返回的数据
G).浏览器用自身的私钥解密服务器返回的加密消息
//那么中间人有没有可能解密出信息呢,关注点主要在客户端发来的信息
//从d步骤开始检测,中间人截取到了被加密的浏览器密钥,但是无法猜测对应服务器的公钥,故无法解密
// a)假设中间人清除目标服务器,首先获取了服务器的公钥,但是只有私钥才能解密被该公钥加密后的信息,故也无法解密
- Handler的内存泄露问题
- 当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可 能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收
- 改善
:1.Activity的引用包装为WeakReference<>弱引用
:2.声明Handler为静态类
- Android volatile关键字
- JVM对于volatile所提供的内存屏障 //内存屏障的作用 -> 阻止屏障2侧的指令重排
a).Load Barrier //在指令前插入Load Barrier,可以让高速缓存的数据失效,强制从主存加载
b).Store Barrier //在指令后插入Store Barrier,强制写回主存
c).在每个volatile写操作前插入StoreStore屏障,在写操作后插入StoreLoad屏障
在每个volatile读操作前插入LoadLoad屏障,在读操作后插入LoadStore屏障 - 多线程工作时,线程对于特定变量采取对主存读取后拷贝到自身内存区的办法进行操作
- volatile保证该变量是通过共享内存进行同步的
- volatile不能替代synchronized,因为不能保证原子性操作
- EventBus 源码阅读 //反射Class通常采用通配符 =>Class<?>或者Class<? extends T>
- 成员
:1.subscriptionsByEventType:HashMap //subscription = subscriber+subscriberMethod
:2.typesBySubscriber
:3.subscriberMethodFinder
:4.METHOD_CACHE:HashMap<Subscriber,List> //缓存
:5.PostingThreadState //带有事件队列,当前线程的posting状态 - 流程
:1.register
val subscriberClass=subscriber.getClass()
List<SubscriberMethod> subscriberMethods=subscriberMethodFinder.findSubscriberMethods(cls)
{
...findUsingReflection
...moveToSuperclass //从子类到父类都去查找注解信息
...findUsingReflectionInSingleClass
-> 检查参数是否只有一个 -> getAnnotation -> 获取Subscribe注解信息 -> 生成subscriberMethod
}
:2.subscribe
>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 //发送事件
>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).单例模式
>public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
c).@Subscribe(threadMode=ThreadMode.POSTING)
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
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.根据反射遍历
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.获取注解以及注解的值
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})注释对象为方法
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 //本线程用于保存事件队列信息
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) //主循环
{
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.分发单个事件
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).反射调用方法
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的线程为主线程
{
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实例
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
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).
- String a=“ abc”;String b=“ abc”; 创建了几个对象 //栈存放引用变量 堆存在new出来的对象,并且堆划分出一部分作为常量池
当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个X在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加
b)Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象
c)使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象
d)使用包含变量的表达式来创建String对象,会在堆栈创建新的
- java的单例创建
a)懒汉式 //线程不安全,指令重排
>>public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance
}
}
b)饿汉式
>>public static synchronized Singleton getInstance() //效率浪费,单例对象只有在创建阶段才需要同步
{
if (instance == null) {
instance = new Singleton();
}
return instance;
}
c)双重检测锁 //效率提高,双重指的是2次检查instance是否为null
>>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 //结构
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
> <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)
{
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 //非必需
>>@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
//当自定义view采用wrap_content匹配时需要重写 a).setMeasuredDimension(width,height)
}
d).重写onDraw
>>@Override
protected void onDraw(Canvas canvas)
{
mPaint.setColor(Color.YELLOW);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
}
-
Android @Override //重写
a).重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载
b).重写方法的访问修饰符一定要大于被重写方法的访问修饰符 public>protected>default>private
c).重写的方法的返回值必须和被重写的方法的返回一致
d).重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类
e).被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写 -
Android Semaphore机制
>>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
> 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 //返回一个固定线程数量的线程池
>>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秒执行一次该任务
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
}
}, 2, TimeUnit.SECONDS);
d)自定义ThreadPoolExecutor //可以根据优先级进行排队
>>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
>>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;
}
}
-
ImageView
ScaleType
:1.FIT_CENTER 默认,图片等比例缩放到宽或者高能够填充控件大小
:2.FIT_START,FIT_END 等比例缩放,并放置在控件的左边或者上方
:3.FIT_XY 完全填充控件大小,但不是等比例
:4.CENTER -> 图片大小为原始大小,如果大于ImageView组件,则截取中间部分,若小于则将图片居中显示
:5.CENTER_CROP ->图片等比例缩放,使图像的短边填满,截取中间部分 -
onSaveInstanceState //保存app数据,当系统因内存不足时而销毁app,会得到调用
>>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
-
Fragment在横竖屏切换时的数据保存
onCreate-> setRetainInstance(true)
当设置retain后,Fragment的生命周期不再经过onCreate、onDestroy -
DialogFragment //采用子类继承的方式进行使用,相比AlertDialog由FragmentManager负责自动重建
>>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实例
a).public class DbOpenHelper extends SQLiteOpenHelper {
private static final int VERSION=1;
private static final String DATABASE_NAME="crimeBase.db";
public DbOpenHelper(Context context) {
super(context, DATABASE_NAME, null , VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table crimes (" +
"_id integer primary key autoincrement ," +
"title varchar(10) ," +
"date datetime," +
"solved boolean" +
")");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
getWritableDatabase、getReadableDatabase
a).getWritableDatabase //以读写方式打开数据库,一旦数据库的磁盘空间满了就只能读,因此会报错
b).getReadableDatabase //首先以读写方式打开,失败后转换为只读方式打开
c).锁 //SqliteOpenHelper,针对数据库进行了加锁,故遵循读写-锁原则,可多个读只有一个写
CursorWrapper //1).自动针对Cursor实现了getItem、getCount 2).通过convertView去调用createView|bindView
a).bindView(view,context,cursor)
b).newView(context,cursor,parent)
DateTime的数据存储 //cursor不支持直接Date类型的数据存取
String dateStr =DateFormat.getDateTimeInstance().format(date) //getDateTimeInstance获取针对DateTime的转换器
cv.put(“date”,dateStr) //SQLite中设置date为datetime
- IntentFilter的匹配规则
>><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
>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在别的进程
<service android:name=".MessengerService" android:process=":remote" />
:3.客户端
>>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.服务器回应客户端
>客户端设置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个方法
>>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
>>Scroller mScroller=new Scroller(context) //创建实例
>>mScroller.startScroll(); //开始滑动,并需要调用invalidate刷新界面
invalidate();
>>@Override //滑动开始后的回调
public void computeScroll()
{
if(mScroller.computeScrollOffset)
scrollTo(mScroller.getCurX(),mScroller.getCurY())
invalidate();
}
188.Sqlite 连接操作
>>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
>>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
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
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()
>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
>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)确认锚点信息
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)
{
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).
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()的刷新
mAnchorInfo.mLayoutFromStart=true
updateLayoutStateToFillEnd ->
mLayoutSTate.mAvailable = mOrientationHelper.getEndAfterPadding()-offset//一开始就是很大的
:2.填充子view
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
>>mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup(){
override public getSpanSize(position:int):int{
val type=mAdapter.getItemViewType(position)
switch(type)
{
...
}
}
})
196.2.RecyclerView 使用
>>封装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
>>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.打开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
>Looper.prepareMainLooper()
ActivityThread thread = new ActivityThread()
if (sMainThreadHandler == null) {
sMainThreadHandler =thread.getHandler();//mH
}
...
Looper.loop()
- ActivityInfo //封装了所有在AndroidManifest.xml解析出的关于Activity、receiver的信息
- ActivityClientRecord
a).intentmAction=“android.intent.action.MAIN”
mComponent={mClass=“com.pill.interview.annotation.Home”}
b).activityInfo
taskAffinity=“com.pill.interview”
processName=“com.pill.interview”
>>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
>>建立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.create(ObservableOnSubscribe<T> source)
{
RxJavaPlugins.onAssembly(new ObservableCreate<T>(source)) //this.source = source
}
- Action
- Function
- map操作符