Android適配全面總結(jié)(三)----ROM適配

版權(quán)聲明:本文為博主原創(chuàng)文章(部分引用他人博文,已加上引用說(shuō)明),未經(jīng)博主允許不得轉(zhuǎn)載。http://www.lxweimin.com/p/f9c67a4b908e

轉(zhuǎn)載請(qǐng)標(biāo)明出處:
http://www.lxweimin.com/p/f9c67a4b908e
本文出自 AWeiLoveAndroid的博客



第一篇文章講了 Android適配全面總結(jié)(一)----屏幕適配
上一篇文章講了 Android適配全面總結(jié)(二)----版本適配

這一篇文章講一下 ROM適配。


Android是開(kāi)源的,不同的手機(jī)廠商都有自己定制的系統(tǒng),所以這就給開(kāi)發(fā)者帶來(lái)了ROM適配難題。在一些群里面經(jīng)常看到有人因?yàn)槭謾C(jī)適配問(wèn)題,說(shuō)這個(gè)手機(jī)坑,那個(gè)手機(jī)坑,其實(shí)那是沒(méi)有對(duì)ROM定制系統(tǒng)的一些變更了解,導(dǎo)致了盲目的說(shuō)出這些指責(zé)的話(huà)。如果你熟悉了,也就會(huì)少走很多彎路。下面這篇文章就來(lái)講一下幾個(gè)主流手機(jī)的ROM適配問(wèn)題。


一、手機(jī)平臺(tái)相關(guān)文檔

(一)小米

(1)文檔

1、小米開(kāi)發(fā)者文檔
2、開(kāi)發(fā)人員必看:《小米應(yīng)用開(kāi)發(fā)者文檔》 ,在這里可以找到在小米手機(jī)上開(kāi)發(fā)、分發(fā)應(yīng)用的相關(guān)文檔。
3、常見(jiàn)問(wèn)題
4、小米帳號(hào)場(chǎng)景化登錄
5、技術(shù)文檔


(二)華為

(1)文檔

1、 APK固件及分辨率適配說(shuō)明
2、 華為全面屏適配技術(shù)指導(dǎo)
3、 華為劉海屏手機(jī)安卓O版本適配指導(dǎo)
4、 谷歌強(qiáng)制升級(jí)TargetSdkVersion適配指導(dǎo)
5、 Android P版本非SDK接口管控特性解讀及適配指導(dǎo)
6、 Android P版本應(yīng)用兼容性適配技術(shù)指導(dǎo)
7、華為桌面角標(biāo)開(kāi)發(fā)指導(dǎo)書(shū)

(2)問(wèn)題

1、華為部分設(shè)備不打印Log

部分的華為設(shè)備工程模式下log是關(guān)閉的,華為部分設(shè)備不打印Log的解決方案:

1.如果是華為手機(jī),進(jìn)入撥號(hào)界面輸入:*#*#2846579#*#*進(jìn)入頁(yè)面設(shè)置。
2.如果是華為pad,進(jìn)入計(jì)算器輸入:()()2846579()()= 進(jìn)入頁(yè)面設(shè)置。
2、華為手機(jī)獲取拍照權(quán)限后拍照,返回值為空

問(wèn)題起源:
開(kāi)發(fā)中遇到了需要拍照和從圖庫(kù)中選擇圖片展示并上傳的功能,其他手機(jī)測(cè)試沒(méi)問(wèn)題,華為手機(jī)獲取拍照權(quán)限后拍照,返回值為空。

問(wèn)題分析:
原來(lái)是華為在7.0以后的系統(tǒng)中,對(duì)于拍照后返回的圖片也做了權(quán)限處理。所以說(shuō),華為7.0在拍照的時(shí)候,不僅要拿到拍照 CAMERA 的權(quán)限,還要拿到讀寫(xiě)文件的權(quán)限 WRITE_EXTERNAL_STORAGE

解決方法部分代碼如下:

//聲明兩個(gè)常量
public static final int MY_PERMISSIONS_REQUEST_CALL_PHONE = 0x0001;
public static final int MY_PERMISSIONS_REQUEST_CALL_PHONE2 = 0x0002;

//設(shè)置權(quán)限
public void setTakePhonePermissions(Context context) {  
    if (Build.VERSION.SDK_INT >= 23) {  
        int checkCallPhonePermission = ContextCompat.checkSelfPermission(context,  
                Manifest.permission.CAMERA);  
        if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) {  
            ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.CAMERA,}  
                    , MY_PERMISSIONS_REQUEST_CALL_PHONE);  
        } else if (ContextCompat.checkSelfPermission(context,  
                Manifest.permission.WRITE_EXTERNAL_STORAGE)  
                != PackageManager.PERMISSION_GRANTED) {  
            ActivityCompat.requestPermissions(context,  
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA},  
                    MY_PERMISSIONS_REQUEST_CALL_PHONE2);  
        } else {  
            takePhoto();  
        }  
    //小于23的不需要?jiǎng)討B(tài)權(quán)限
    } else {  
        takePhoto();  
    }  
}  
3、華為Android7.0手機(jī)打開(kāi)攝像頭拍照閃退問(wèn)題。
4、華為手機(jī)Android8.0 使用代碼安裝APK閃退問(wèn)題

更新版本APK自動(dòng)安裝的時(shí)候,在安卓6.0、7.0下都OK,唯獨(dú)在華為安卓8.0手機(jī)閃退。

解決方案: 只要在Mainfest.xml 中加入請(qǐng)求安裝權(quán)限就OK了

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />  

  • ★★ 關(guān)于閃退的小結(jié):
    1. 在Android 7.0(及以上)手機(jī)開(kāi)啟拍照功能,既要申請(qǐng)CAMERA 權(quán)限,還要申請(qǐng) WRITE_EXTERNAL_STORAGE 權(quán)限。
    1. Android 7.0(API24)以及以上版本不支持file://這種類(lèi)型的URI,而是使用content://這種類(lèi)型的URI。不然會(huì)報(bào)android.os.FileUriExposedException這個(gè)錯(cuò),使用Android 7.0(及以上)手機(jī)拍照功能時(shí),一定要注意這個(gè)api的變化。
    1. 使用Android 8.0(及以上)手機(jī)更新安裝apk時(shí),在Mainfest.xml 中請(qǐng)求安裝權(quán)限android.permission.REQUEST_INSTALL_PACKAGES

5、華為手機(jī)app閃退重啟清空l(shuí)og日志問(wèn)題

解決方案:

◆ 方式1(最全面的解決方案):

找到手機(jī)設(shè)置 ---> 最后的開(kāi)發(fā)人員選項(xiàng) ---> 在調(diào)試模塊,打開(kāi)USB調(diào)試。 
還是調(diào)試模塊內(nèi),找到日志記錄器緩沖區(qū)大小,改為1M(也可選擇更大),
然后進(jìn)入撥號(hào)界面輸入:*#*#2846579#*#*  ----> 選擇USB端口設(shè)置 ----> 選擇Google模式。

◆ 方式2:撥號(hào)鍵盤(pán) + 快捷鍵設(shè)置(這種方式不是所有l(wèi)og都能顯示)

進(jìn)入撥號(hào)界面輸入:*#*#2846579#*#*
依次選擇:后臺(tái)設(shè)置 ---> LOG設(shè)置 ---> AP 日志,重新啟動(dòng)手機(jī)。

◆ 方式3:錯(cuò)誤出現(xiàn)后,迅速拔掉USB線,這是一個(gè)拼手速的方法,成功率不敢保證。


6、關(guān)于華為手機(jī)App權(quán)限更改導(dǎo)致應(yīng)用重啟的坑(暫且我還沒(méi)有很好的解決方式)

問(wèn)題重現(xiàn):

  • 1.當(dāng)我們?cè)谌A為手機(jī)上打開(kāi)一個(gè)應(yīng)用,將應(yīng)用退至后臺(tái)進(jìn)程中。
  • 2.打開(kāi) “設(shè)置”去更改該應(yīng)用的權(quán)限(比如將“存儲(chǔ)”權(quán)限由授權(quán)狀態(tài)改為非授權(quán)狀態(tài))。
  • 3.再將該應(yīng)用重新切換到前臺(tái),會(huì)發(fā)現(xiàn)應(yīng)用進(jìn)行了重新啟動(dòng)。 (在該app中,啟動(dòng)的時(shí)候,F(xiàn)ragmentManager仍然會(huì)持有原有的fragment。)

網(wǎng)上有人說(shuō)出了一種原因和一種 解決方案:當(dāng)應(yīng)用的權(quán)限發(fā)生變化的時(shí)候,華為手機(jī)發(fā)出廣播,導(dǎo)致應(yīng)用重新啟動(dòng)。 解決辦法(比較笨):在Activity的onCreate()方法中,根據(jù)FragmentManager獲取到已經(jīng)存在的fragment,并將它們移除掉。重新再創(chuàng)建一下需要展示的fragment

但是我想知道framework層是如何操作的?不知道有沒(méi)有大佬能夠分析一下源碼?


(三)魅族

(1)文檔

魅族開(kāi)發(fā)者文檔


(四)oppo

(1)文檔

oppo開(kāi)發(fā)者文檔

(2)問(wèn)題

2、關(guān)于開(kāi)發(fā)者選項(xiàng)

oppo手機(jī)的開(kāi)發(fā)者模式很惡心,開(kāi)啟“設(shè)置”》其他設(shè)置》開(kāi)發(fā)者選項(xiàng)》USB調(diào)試 待機(jī),然后狀態(tài)欄有個(gè)黃色的提醒窗口,提示10分鐘后自動(dòng)關(guān)閉開(kāi)發(fā)者選項(xiàng)。

3、關(guān)于驗(yàn)證碼

裝個(gè)應(yīng)用要驗(yàn)證碼,打開(kāi)開(kāi)發(fā)者選項(xiàng)需要驗(yàn)證碼。。很惡心。。

4、oppo手機(jī)的R9系列和A系列的5.1系統(tǒng)存在嚴(yán)重的bug,類(lèi)似以下這種的gc導(dǎo)致的釋放超時(shí)很多。

(五)vivo

(1)文檔

vivo開(kāi)發(fā)者文檔

(2)問(wèn)題

  • 關(guān)于as項(xiàng)目無(wú)法在vivo中安裝的問(wèn)題:

最近適配vivo手機(jī) 用的是vivo x9 發(fā)現(xiàn)應(yīng)用無(wú)法在手機(jī)上安裝 已經(jīng)打開(kāi)了開(kāi)發(fā)者模式還是不行,報(bào)以下錯(cuò):

【解決方案】
關(guān)掉Android Studio的Instant Run功能,然后把開(kāi)發(fā)者模式中的USB安全模式(在USB調(diào)試下面)和USB調(diào)試一起打開(kāi)。(其他手機(jī)遇到同樣問(wèn)題,也可以用這個(gè)方式解決。)


(六)錘子

(1)文檔

暫且沒(méi)找到相關(guān)文檔。。。


二、開(kāi)發(fā)中遇到的問(wèn)題在不同手機(jī)上的處理方式

(一)沉浸式狀態(tài)欄適配

  • 這里講一下華為手機(jī)沉浸式狀態(tài)欄和虛擬鍵盤(pán)沖突問(wèn)題怎么解決:

由于字?jǐn)?shù)限制,詳細(xì)代碼請(qǐng)看我的github https://github.com/AweiLoveAndroid/Solve-StatusBar-VirtualKeyBoard

(二)沉浸式狀態(tài)欄圖標(biāo)的適配

  • 2.2.1 小米MIUI系統(tǒng)適配

之前做沉浸式狀態(tài)欄,由于公司APP底色是白色,所以對(duì)MIUI進(jìn)行特殊處理。在MIUI V6及以上版本,調(diào)用MIUI的方法將狀態(tài)欄圖標(biāo)改為黑色。發(fā)現(xiàn)部分小米手機(jī),這樣的設(shè)置不管用,導(dǎo)致頭上一片白,狀態(tài)欄上的東西基本看不到。

調(diào)整過(guò)程中發(fā)現(xiàn)以下情況:

手機(jī)型號(hào) MIUI版本 Android版本 系統(tǒng)方法是否生效 MIUI的方法是否生效
紅米 NOTE 1LTE MIUI 8 8.2.1穩(wěn)定版 4.4 生效
小米5 MIUI 8 8.5.3穩(wěn)定版 7.0 生效
MI 3W MIUI 9 7.9.14開(kāi)發(fā)版 6.0.1 生效

參考官方文檔: http://www.miui.com/thread-8946673-1-1.html

(三)應(yīng)用卸載然后安裝更新的適配

  • 2.3.1 華為適配
    華為手機(jī)程序卸載,安裝更新包,還是提醒更新包與安裝應(yīng)用簽名不一致。

  • 2.3.2 魅族適配

    • 問(wèn)題1. 測(cè)試的簽名和你正式出包的簽名如果不一致就不能安裝,卸載應(yīng)用也沒(méi)用。
    • 問(wèn)題2. 用as安裝過(guò)應(yīng)用,卸載后安裝正式的apk就安裝不了,用adb命令卸載后就行了。

(四)改變狀態(tài)欄字體顏色為黑色的適配

  • 2.4.1 小米適配
/**
 * 改變小米的狀態(tài)欄字體顏色為黑色,要求MIUI6以上
 * tested on: MIUI V7 5.0 Redmi-Note3
 */
private void processMIUI(boolean lightStatusBar) throws Exception{
    Window window = getWindow();
    Class<? extends Window> clazz = window.getClass();
    int darkModeFlag;
    Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
    Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
    darkModeFlag = field.getInt(layoutParams);
    Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
    extraFlagField.invoke(window,lightStatusBar ? darkModeFlag : 0, darkModeFlag);
}
  • 2.4.2 魅族適配
/**
 * 改變魅族的狀態(tài)欄字體為黑色,要求FlyMe4以上
 */
private void processFlyMe(boolean isLightStatusBar) throws Exception{
    Window window = getWindow();
    WindowManager.LayoutParams layoutParams = window.getAttributes();
    Class<?> instance = Class.forName("android.view.MiuiWindowManager$LayoutParams");
    int value = instance.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON").getInt(layoutParams);
    Field field = instance.getDeclaredField("meizuFlags");
    field.setAccessible(true);
    int origin = field.getInt(layoutParams);
    if(isLightStatusBar){
        field.set(layoutParams, origin | value);
    }else{
        field.set(layoutParams, -value | origin);
    }

}

下面來(lái)一張示例圖:

(五)屏幕圓角實(shí)現(xiàn)和適配

示例圖
  • 實(shí)現(xiàn)原理:利用WindowManager將我們的圓角加到屏幕的四個(gè)角,圓角顏色設(shè)置為黑色,形成視覺(jué)圓角屏幕。

下面簡(jiǎn)單的把一些核心代碼講一下:

  • 自定義圓角View,這里以左上角為例:
// top left
case Gravity.TOP | Gravity.LEFT:
    path.moveTo(0.0f, 0.0f);
    path.lineTo(0.0f, (float) h);
    path.arcTo(new RectF(0.0f, 0.0f,
            ((float) w) * 2.0f, ((float) h) * 2.0f), 180.0f, 90.0f, true);
    path.lineTo((float) w, 0.0f);
    path.lineTo(0.0f, 0.0f);
    path.close();
    break;
  • windowmanager在添加view的時(shí)候需要設(shè)置一個(gè)WindowManager.LayoutParams。下面初始化這個(gè)Params:
// window manager
manager = (WindowManager) this.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
params = new WindowManager.LayoutParams();
/*
 系統(tǒng)提示類(lèi)型:7.0以前可以直接用TOAST的類(lèi)型,不用申請(qǐng)權(quán)限,直接添加
 7.0以后不行了,需要申請(qǐng)SYSTEM_ALERT_WINDOW權(quán)限,window type最好
 設(shè)置為ERROR 或者 PHONE
 */
if (Utilities.isCanUseToastType()) {
    params.type = WindowManager.LayoutParams.TYPE_TOAST;
} else {
    params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
}
params.format = 1;
params.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN // 全屏
        | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS // 覆蓋到status bar
        | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION // 覆蓋到導(dǎo)航欄

        // 以下屬性設(shè)置加載我們圓角window 不搶焦點(diǎn),不攔截事件
        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.alpha = 1.0f;
params.x = 0;
params.y = 0;
// 設(shè)置  大小為全屏
params.width = ViewUtil.getScreenSize(this).x;
params.height = ViewUtil.getScreenSize(this).y;


  • 圓角加到屏幕上:
public void addCornerViewByPosition(String position){
    boolean enable = true;
    switch (position) {
        case LEFT_TOP:
            enable = leftTopEnable;
            params.gravity = Gravity.TOP | Gravity.LEFT;
            break;
        case RIGHT_TOP:
            enable = rightTopEnable;
            params.gravity = Gravity.TOP | Gravity.RIGHT;
            break;
        case LEFT_BOTTOM:
            enable = leftBottomEnable;
            params.gravity = Gravity.BOTTOM | Gravity.LEFT;
            break;
        case RIGHT_BOTTOM:
            enable = rightBottomEnable;
            params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
            break;
    }
    CornerView corner = buildCorner(enable,params.gravity);
    if(!corners.containsValue(corner)) {
        corners.put(position, corner);
        manager.addView(corner, params);
    }
}

屏幕圓角實(shí)現(xiàn)和適配,詳細(xì)的可以點(diǎn)擊這里:http://mp.weixin.qq.com/s/h5qRvfgVj04f_xExTtrIHg

(六)在帶虛擬按鍵的手機(jī)上,虛擬按鍵會(huì)遮擋全屏圖片的底部的解決。

在做splash頁(yè)面的時(shí)候,通過(guò)windowBackground設(shè)置背景圖片,在帶虛擬按鍵的手機(jī)上,虛擬按鍵會(huì)遮擋圖片的底部,這個(gè)問(wèn)題的解決方式:

參考:http://blog.csdn.net/c15522627353/article/details/52452490

究竟如何適配Android底部虛擬按鍵,可以參考這篇博文:http://www.lxweimin.com/p/b499628e0ae0

(七)懸浮窗權(quán)限設(shè)置了,dialog還是不提示。

(八)在Nexus 手機(jī),原生Android 8.0上,使用掃碼的時(shí)候顯示的拍照預(yù)覽方向不正,有180度的旋轉(zhuǎn)并且變形的,解決方案:

private void surfaceIsChanged() {
    if (mHolder.getSurface() == null) {
        System.out.println("getSurface,nullnull");
        return;
    }
    try {
        mCamera.stopPreview();
    } catch (Exception e) {
        e.printStackTrace(); 
    }
    try {
        Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
        int dataBufferSize = (int) (previewSize.height * previewSize.width
              * (ImageFormat.getBitsPerPixel(mCamera.getParameters()
             .getPreviewFormat()) / 8.0));
        mCamera.addCallbackBuffer(new byte[dataBufferSize]);
        mCamera.addCallbackBuffer(new byte[dataBufferSize]);
        mCamera.addCallbackBuffer(new byte[dataBufferSize]);
        mCamera.setPreviewDisplay(mHolder);
        mCamera.setPreviewCallback(previewCallback);
        mCamera.startPreview();
        mCamera.autoFocus(autoFocusCallback);
        
        // 核心代碼:根據(jù)照相的內(nèi)容進(jìn)行設(shè)置顯示方向。
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        // DO your logic to get front or back camera...or loop through all avaialable.
        int camIdx = 0; 
        Camera.getCameraInfo(camIdx, cameraInfo);

        try {
            // If using back camera then simply rotate what CameraInfo tells you.
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK){
                mCamera.setDisplayOrientation(cameraInfo.orientation);
            }else{
                // If using front camera note that image might be flipped
                //  to give users the impresion the are looking at a mirror.
                mCamera.setDisplayOrientation( (360 - cameraInfo.orientation) % 360);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //開(kāi)始掃描
        // Toast.makeText(QRZbarActivity.this, "開(kāi)始掃描",Toast.LENGTH_SHORT).show(); 
        // 打開(kāi)閃光燈,這個(gè)方法自己去實(shí)現(xiàn),這里不是重點(diǎn),就不寫(xiě)了。
        autoOpenLight();
    } catch (Exception e) {
        Toast.makeText(BaseScanActivity.this, R.string.account_toast_not_open_camera, 
                   Toast.LENGTH_SHORT).show();
        // showTip("您未允許" + getResources().getString(R.string.app_name) 
        // + "訪問(wèn)您的相冊(cè)\n請(qǐng)?jiān)凇鞍踩行?-授權(quán)管理”中更改設(shè)置");
        Log.d("DBG", "Error starting camera preview: " + e.getMessage());
    }
}

這個(gè)解決方案來(lái)自:https://stackoverflow.com/questions/12017148/android-camera-setdisplayorientation90-fails-in-different-devices#


(九)獲取手機(jī)里所有存儲(chǔ)設(shè)備盤(pán)符,不同廠商手機(jī)的路徑可能不一樣。

問(wèn)題描述:華為手機(jī)很變態(tài),存儲(chǔ)路徑跟原生系統(tǒng)的不一樣,所以需要對(duì)其做特別處理。

解決方案: 需要用到一個(gè)被系統(tǒng)隱藏的方法,即StorageManager下的getVolumePaths()方法。具體通過(guò)反射可以得到,其中mPath、mRemovable、mEmulated、mState這幾個(gè)屬性是我們需要關(guān)注的。

具體代碼:

public class StorageUtils {
    /*
    獲取全部存儲(chǔ)設(shè)備信息封裝對(duì)象
     */
    public static ArrayList<Volume> getVolume(Context context) {
        ArrayList<Volume> list_storagevolume = new ArrayList<Volume>();

        StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);

        try {
            Method method_volumeList = StorageManager.class.getMethod("getVolumeList");

            method_volumeList.setAccessible(true);

            Object[] volumeList = (Object[]) method_volumeList.invoke(storageManager);
            if (volumeList != null) {
                Volume volume;
                for (int i = 0; i < volumeList.length; i++) {
                    try {
                        volume = new Volume();
                        volume.setPath((String) volumeList[i].getClass().getMethod("getPath").invoke(volumeList[i]));
                        volume.setRemovable((boolean) volumeList[i].getClass().getMethod("isRemovable").invoke(volumeList[i]));
                        volume.setState((String) volumeList[i].getClass().getMethod("getState").invoke(volumeList[i]));
                        list_storagevolume.add(volume);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    }

                }
            } else {
                Log.e("null", "null-------------------------------------");
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }

        return list_storagevolume;
    }


    /*
     存儲(chǔ)設(shè)備信息封裝類(lèi)
     */
    public static class Volume {
        protected String path;
        protected boolean removable;
        protected String state;

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public boolean isRemovable() {
            return removable;
        }

        public void setRemovable(boolean removable) {
            this.removable = removable;
        }

        public String getState() {
            return state;
        }

        public void setState(String state) {
            this.state = state;
        }
    }
}

測(cè)試代碼:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ArrayList<StorageUtils.Volume> list_volume = StorageUtils.getVolume(this);
        for (int i=0;i<list_volume.size();i++){
            Log.e(i+"","path:"+list_volume.get(i).getPath()+"----"+
                        "removable:"+list_volume.get(i).isRemovable()+"---"+
                        "state:"+list_volume.get(i).getState());
        }
    }
}

需要添加權(quán)限:<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>


本文部分內(nèi)容引用了該博客: http://www.lxweimin.com/p/7609d5a62c45


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評(píng)論 3 417
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,423評(píng)論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,991評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,761評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,207評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,419評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,959評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,782評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,983評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,222評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,653評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,901評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,678評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,978評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,660評(píng)論 25 708
  • 本文章轉(zhuǎn)載于搜狗測(cè)試 Android應(yīng)用適配測(cè)試淺談 什么是Android適配測(cè)試 Android適配測(cè)試是為了讓...
    夜境閱讀 1,380評(píng)論 0 1
  • 數(shù)據(jù)庫(kù)分類(lèi): 關(guān)系型數(shù)據(jù)庫(kù) 關(guān)系數(shù)據(jù)庫(kù),是建立在關(guān)系模型基礎(chǔ)上的數(shù)據(jù)庫(kù),借助于集合代數(shù)等數(shù)學(xué)概念和方法來(lái)處理數(shù)據(jù)庫(kù)...
    夜幕繁華閱讀 521評(píng)論 0 1
  • 什么是異步編程 什么是異步編程呢?舉個(gè)簡(jiǎn)單的例子: 上面這段代碼中,Main方法中的代碼是按照自上而下的順序執(zhí)行的...
    雪飛鴻閱讀 4,424評(píng)論 0 12
  • 1.《想你》 夜深人靜時(shí), 我想起你, 內(nèi)心獨(dú)白了一遍又一遍。 2.《39.4》 39.4度, 一個(gè)好厲害的溫度,...
    無(wú)語(yǔ)呤咽閱讀 399評(píng)論 2 4