Android學習總結2

一、屏幕出現卡頓的原因
表面原因:
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

四、如何設置手機黑名單:
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


五、獲取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;


九、使用佔用百分比的佈局分佈來解決部分屏幕適配的問題
使用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); 
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章