Android学习总结2
2015-11-09 08:32
886 查看
一、屏幕出现卡顿的原因:
表面原因:
1、在UI主线程中执行比如像网络下载等耗时的操作,致使CPU没有能力在16ms内完成对下一帧显示数据的处理
2、需要显示的界面太过复杂,比如布局的层次较深,界面控件过多等,给CPU与GPU的渲染造成压力
3、手机硬件GPU、CPU处理能力有限,FPS低于16ms,造成卡顿;Android 4.1后Triple Buffering三缓冲机制一定程度上缓解了该问题。
4、单个帧处理事件内JVM GC次数过多,由于GC 需要stop the world,GC 次数过多可能占用较多的CPU处理时间。而造成GC次数过多的原因可能有:1)内存抖动,即短时间内大量对象被创建又立即被销毁;2)瞬间创建大量对象,占用过多内存,使得JVM不得不提前进行GC来回收内存空间。
由Android的显示机制知,在下一个VSYNC信号到来时,由于前面所述的原因,下一个要显示的帧的信息在Back Buffer并未准备好,则Frame Buffer只能显示前一个帧,造成JANK这样的卡顿的情况。
解决方法:
1、降低界面的布局复杂度以及控件数量,优化界面布局(使用Hierarchy Viewer工具来分析)
1)使用<include>来重用layout
2)使用viewStub来延迟界面加载(相对于gone,viewstub只有在需要时才会被加载,而第一次加载后,viewStub就会转化为一个普通控件)
2、采用异步处理的方式,避免将耗时的任务放在UI线程中进行处理。
3、尽量减少Overdraw (使用Android开发者工具中的Enable GPU Overdraw来判断界面Overdraw的次数)
4、良好的Java编程习惯,避免创建大量的短生命周期对象(避免发生内存抖动的情况)。
CPU、GPU处理图片原理:
Resterization栅格化是绘制那些Button,Shape,Path,String,Bitmap等组件最基础的操作。它把那些组件拆分到不同的像素上进行显示。这是一个很费时的操作,GPU的引入就是为了加快栅格化的操作。
CPU负责把UI组件计算成Polygons,Texture纹理,然后交给GPU进行栅格化渲染。
CPU、GPU计算,绘制以及渲染都需要在一帧16ms中完成。
二、减少电量损耗的措施:
有下面一些措施能够显著减少电量的消耗:
- 我们应该尽量减少唤醒屏幕的次数与持续的时间,使用WakeLock来处理唤醒的问题,能够正确执行唤醒操作并根据设定及时关闭操作进入睡眠状态。
- 某些非必须马上执行的操作,例如上传歌曲,图片处理等,可以等到设备处于充电状态或者电量充足的时候才进行。
- 触发网络请求的操作,每次都会保持无线信号持续一段时间,我们可以把零散的网络请求打包进行一次操作,避免过多的无线信号引起的电量消耗。关于网络请求引起无线信号的电量消耗,还可以参考这里http://hukai.me/android-training-course-in-chinese/connectivity/efficient-downloads/efficient-network-access.html
三、Android保证Service不被杀死:
1、设置persistent属性为true,设置为常驻应用;Android boot时,就会启动这些persistent设置为true的应用,这些应用从开机开始,生命周期一直持续到关闭手机,当出现异常,系统会尝试立即重启;
2、重写onStartCommand方法,onStartCommand方法有三种返回值,设置返回值为START_REDELIVERY_INTENT,当Service因为异常或者内存紧张被系统杀死时,会立即重启;
3、设置为前台进程,设置Service的优先级为最高优先级,避免因为low memory而被系统杀死;
4、流氓用法:设置两个Service相关联,当一个Service被清理掉时,在onDestory中重启另一个Service
参考:http://www.android100.org/html/201406/06/19765.html
http://blog.csdn.net/mad1989/article/details/22492519
http://my.oschina.net/youranhongcha/blog/269591
四、如何设置手机黑名单:
1、设置:通过Content Resolver获取所有联系人信息,进行设置,将黑名单存储到一个Hash表中
2、创建PhoneStateListener,监听电话状态,判断来电是否是在黑名单中。
3、如果来电已被拉进黑名单,则需要挂断电话。因为Telephony挂断电话的API是不开放的,所以这里要通过反射来获取相应的method
http://blog.csdn.net/lg878398509/article/details/13005309
五、获取wrap_content控件的宽高:
六、获取屏幕的宽高:
方法一:使用WindowManager.getDefaultDisplay:
方法二:使用getDisplayMetrics:
方法三:与方法二类似,获取DisplayMetrics的方式如下:
七、实现一个View随手指的拖动效果:
八、设置窗口样式
九、使用占用百分比的布局分布来解决部分屏幕适配的问题
参考博客:
http://blog.csdn.net/lmj623565791/article/details/46695347
http://blog.csdn.net/lmj623565791/article/details/46767825
使用percent-support-lib库: compile 'com.android.support:percent:22.2.0'
在xml文件中使用:
十、使用ToolBar的title居中的问题,可以设置ToolBar的标题为空,然后再Bar布局中添加一个TextView使其居中显示即可;
十一、调用相机拍摄图片
十二、GridView和ListView嵌套问题。
GridView和ListView需要自定义并复写onMeasure方法。取消其滑动属性,如果不复写,GridView和ListView将只显示一行。
表面原因:
1、在UI主线程中执行比如像网络下载等耗时的操作,致使CPU没有能力在16ms内完成对下一帧显示数据的处理
2、需要显示的界面太过复杂,比如布局的层次较深,界面控件过多等,给CPU与GPU的渲染造成压力
3、手机硬件GPU、CPU处理能力有限,FPS低于16ms,造成卡顿;Android 4.1后Triple Buffering三缓冲机制一定程度上缓解了该问题。
4、单个帧处理事件内JVM GC次数过多,由于GC 需要stop the world,GC 次数过多可能占用较多的CPU处理时间。而造成GC次数过多的原因可能有:1)内存抖动,即短时间内大量对象被创建又立即被销毁;2)瞬间创建大量对象,占用过多内存,使得JVM不得不提前进行GC来回收内存空间。
由Android的显示机制知,在下一个VSYNC信号到来时,由于前面所述的原因,下一个要显示的帧的信息在Back Buffer并未准备好,则Frame Buffer只能显示前一个帧,造成JANK这样的卡顿的情况。
解决方法:
1、降低界面的布局复杂度以及控件数量,优化界面布局(使用Hierarchy Viewer工具来分析)
1)使用<include>来重用layout
2)使用viewStub来延迟界面加载(相对于gone,viewstub只有在需要时才会被加载,而第一次加载后,viewStub就会转化为一个普通控件)
2、采用异步处理的方式,避免将耗时的任务放在UI线程中进行处理。
3、尽量减少Overdraw (使用Android开发者工具中的Enable GPU Overdraw来判断界面Overdraw的次数)
4、良好的Java编程习惯,避免创建大量的短生命周期对象(避免发生内存抖动的情况)。
CPU、GPU处理图片原理:
Resterization栅格化是绘制那些Button,Shape,Path,String,Bitmap等组件最基础的操作。它把那些组件拆分到不同的像素上进行显示。这是一个很费时的操作,GPU的引入就是为了加快栅格化的操作。
CPU负责把UI组件计算成Polygons,Texture纹理,然后交给GPU进行栅格化渲染。
CPU、GPU计算,绘制以及渲染都需要在一帧16ms中完成。
二、减少电量损耗的措施:
有下面一些措施能够显著减少电量的消耗:
- 我们应该尽量减少唤醒屏幕的次数与持续的时间,使用WakeLock来处理唤醒的问题,能够正确执行唤醒操作并根据设定及时关闭操作进入睡眠状态。
- 某些非必须马上执行的操作,例如上传歌曲,图片处理等,可以等到设备处于充电状态或者电量充足的时候才进行。
- 触发网络请求的操作,每次都会保持无线信号持续一段时间,我们可以把零散的网络请求打包进行一次操作,避免过多的无线信号引起的电量消耗。关于网络请求引起无线信号的电量消耗,还可以参考这里http://hukai.me/android-training-course-in-chinese/connectivity/efficient-downloads/efficient-network-access.html
三、Android保证Service不被杀死:
1、设置persistent属性为true,设置为常驻应用;Android boot时,就会启动这些persistent设置为true的应用,这些应用从开机开始,生命周期一直持续到关闭手机,当出现异常,系统会尝试立即重启;
2、重写onStartCommand方法,onStartCommand方法有三种返回值,设置返回值为START_REDELIVERY_INTENT,当Service因为异常或者内存紧张被系统杀死时,会立即重启;
3、设置为前台进程,设置Service的优先级为最高优先级,避免因为low memory而被系统杀死;
4、流氓用法:设置两个Service相关联,当一个Service被清理掉时,在onDestory中重启另一个Service
参考:http://www.android100.org/html/201406/06/19765.html
http://blog.csdn.net/mad1989/article/details/22492519
http://my.oschina.net/youranhongcha/blog/269591
四、如何设置手机黑名单:
1、设置:通过Content Resolver获取所有联系人信息,进行设置,将黑名单存储到一个Hash表中
2、创建PhoneStateListener,监听电话状态,判断来电是否是在黑名单中。
3、如果来电已被拉进黑名单,则需要挂断电话。因为Telephony挂断电话的API是不开放的,所以这里要通过反射来获取相应的method
//获取android.os.ServiceManager类的对象的getService()方法 Method method=Class.forName("android.os.ServiceManager"). getMethod("getService",String.class); // 获取远程TELEPHONY_SERVICE的IBinder对象的代理 IBinder binder =(IBinder)method.invoke(null, new Object[] {TELEPHONY_SERVICE}); // 将IBinder对象的代理转换为ITelephony对象 ITelephony telephony=ITelephony.Stub.asInterface(binder); //挂断电话 telephony.endCall();参考:http://blog.csdn.net/fengyuzhengfan/article/details/38109597
http://blog.csdn.net/lg878398509/article/details/13005309
五、获取wrap_content控件的宽高:
ViewTreeObserver observer = myText.getViewTreeObserver(); observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { public boolean onPreDraw() { if (hasMeasured == false) { int width = myText.getMeasuredWidth(); int height = myText.getMeasuredHeight(); Log.d(TAG, "width:" + width + ";height:" + height); //获取到宽度和高度后,可用于计算 hasMeasured = true; } return true; } });
六、获取屏幕的宽高:
方法一:使用WindowManager.getDefaultDisplay:
// 获取屏幕宽高(方法1) int screenWidth = getWindowManager().getDefaultDisplay().getWidth(); // 屏幕宽(像素,如:480px) int screenHeight = getWindowManager().getDefaultDisplay().getHeight(); // 屏幕高(像素,如:800p) Log.d(TAG , "getDefaultDisplay:" + "screenWidth=" + screenWidth + "; screenHeight=" + screenHeight); // 由于getWidth与getHeight已废弃,宜使用下面的方法 Point point = new Point(); getWindowManager().getDefaultDisplay().getSize(point); Log.d(TAG, "getDefaultDisplay:" + "screenWidth_x=" + point.x + "; screenHeight_y=" + point.y);
方法二:使用getDisplayMetrics:
// 获取屏幕密度(方法2) DisplayMetrics dm = new DisplayMetrics(); dm = getResources().getDisplayMetrics(); float density = dm.density; // 屏幕密度(像素比例:0.75/1.0/1.5/2.0) int densityDPI = dm.densityDpi; // 屏幕密度(每寸像素:120/160/240/320) float xdpi = dm.xdpi; float ydpi = dm.ydpi; Log.d(TAG , " DisplayMetrics:" + "xdpi=" + xdpi + "; ydpi=" + ydpi); Log.d(TAG , " DisplayMetrics:" + "density=" + density + "; densityDPI=" + densityDPI); int screenWidth = dm.widthPixels; // 屏幕宽(像素,如:480px) int screenHeight = dm.heightPixels; // 屏幕高(像素,如:800px) Log.d(TAG , " DisplayMetrics(111):" + "screenWidth=" + screenWidth + "; screenHeight=" + screenHeight);
方法三:与方法二类似,获取DisplayMetrics的方式如下:
// 获取屏幕密度(方法3) dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm);
七、实现一个View随手指的拖动效果:
@Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getRawX(); int y = (int) event.getRawY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: int deltaX = x - mLastX; int deltaY = y - mLastY; int translationX = (int) getTranslationX() + deltaX; int translationY = (int) getTranslationY() + deltaY; setTranslationX(translationX); setTranslationY(translationY); break; case MotionEvent.ACTION_UP: break; default: break; } mLastX = x; mLastY = y; return super.onTouchEvent(event); }
八、设置窗口样式
super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); 使用supportRequestWindowFeature替代requestWindowFeature;
九、使用占用百分比的布局分布来解决部分屏幕适配的问题
参考博客:
http://blog.csdn.net/lmj623565791/article/details/46695347
http://blog.csdn.net/lmj623565791/article/details/46767825
使用percent-support-lib库: compile 'com.android.support:percent:22.2.0'
在xml文件中使用:
<?xml version="1.0" encoding="utf-8"?> <android.support.percent.PercentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 支持百分比进行空间分配 --> <TextView android:id="@+id/txt1" android:layout_width="0dp" android:layout_height="0dp" android:text="Text1" app:layout_heightPercent="15.5%" android:gravity="center" android:layout_gravity="center_horizontal" app:layout_widthPercent="30%" android:background="@color/aliceblue" /> <!-- 支持Layout的常规用法--> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Heel" android:layout_gravity="center" android:background="@color/aliceblue" android:gravity="center" /> <!-- PercentRelativeLayout的使用 --> <android.support.percent.PercentRelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" > <!-- 支持marginLeftPercent等距离属性 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Realt" android:gravity="center" android:background="@color/aliceblue" app:layout_marginLeftPercent="3%" /> </android.support.percent.PercentRelativeLayout> </android.support.percent.PercentFrameLayout>
十、使用ToolBar的title居中的问题,可以设置ToolBar的标题为空,然后再Bar布局中添加一个TextView使其居中显示即可;
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" > <TextView android:id="@+id/toolbar_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_gravity="center" android:text="@string/tab_name1" android:textColor="@android:color/white" android:textSize="20sp" android:textStyle="bold" /> </android.support.v7.widget.Toolbar>代码中使用:
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setTitle(""); setSupportActionBar(toolbar);
十一、调用相机拍摄图片
public class TestActivity extends AppCompatActivity implements View.OnClickListener{ private final int CAPTURE_TAG = 0; private final int PICTURE_HEIGHT = 500; private final int PICTURE_WIDTH = 500; private ImageView testImg; private String filePath; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); testImg = (ImageView) findViewById(R.id.testImg); testImg.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.testImg: // 保存到外部存储设备如SD卡中 filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/myPicture.jpg"; File imageFile = new File(filePath); Uri imageUri = Uri.fromFile(imageFile); // 调用手机中的Camera Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(intent, CAPTURE_TAG); break; default: break; } } // 处理拍照后的结果 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { testImg.setImageBitmap(decodeSampledBitmap(filePath, PICTURE_WIDTH, PICTURE_HEIGHT)); } } // 计算图片压缩比例 private int calculateInSampleSize(BitmapFactory.Options options, int picWidth, int picHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > picHeight || width > picWidth) { int heightRatio = Math.round((float) height/ (float) picHeight); int widthRatio = Math.round((float) width / (float) picWidth); inSampleSize = Math.min(heightRatio, widthRatio); } return inSampleSize; } // 压缩图片 private Bitmap decodeSampledBitmap(String imgFilePath, int picWidth, int picHeight) { // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小,此时返回的bitmap为null final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds= true; Bitmap bitmap = BitmapFactory.decodeFile(imgFilePath, options); // 获得压缩比 options.inSampleSize = calculateInSampleSize(options, picWidth, picHeight); // 解析获得图片 options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(imgFilePath, options); } }
十二、GridView和ListView嵌套问题。
GridView和ListView需要自定义并复写onMeasure方法。取消其滑动属性,如果不复写,GridView和ListView将只显示一行。
//自定义GridView,重写onMeasure方法,使其失去滑动属性,这样才能嵌套在同样具有滑动属性的ScrollView中了。 public class StaticGridView extends GridView { public StaticGridView(Context context) { super(context); } public StaticGridView(Context context, AttributeSet attrs) { super(context, attrs); } public StaticGridView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
相关文章推荐
- android 中View的优化
- Android图片加载神器之Fresco-圆形圆角二合一教程[Java代码实现圆形圆角效果]
- Android Animation之补间动画
- 关于下拉刷新你是否真的非常理解还是只会搬砖?附 Android 实例子源代码文件下载地址380个合集
- android88 录音机
- android打开各种文件setDateAndType
- Android 下删除手机呼叫记录的两种方法:
- AndroidScreenSlide项目切换view动画效果《IT蓝豹》
- 选择蜂窝Android培训 我的薪资过万了
- android文件存储的五种方式简介
- 《Android开发艺术探索》读后笔记1
- android87 Service
- Android 源码分析工具
- Android中layout_gravity和gravity属性的区别
- Android 实践项目开发一
- Windows10下搭建Android开发环境
- 【FastDev4Android框架开发】BaseAdapterHelper的基本使用介绍,让你摆脱狂写一堆Adapter烦恼(二十四)
- Android 无线接口层RIL
- Android 微光闪烁效果之更强Shimmer-android
- Android Context创建过程