1. 程式人生 > >基於Android系統OTG功能下將U盤作為資料來源的解決方案

基於Android系統OTG功能下將U盤作為資料來源的解決方案

一、問題來源

目前市面上多數APP都是基於網際網路來讀取資料,展示介面,但若在無網路環境,如展廳、政府機構中大多都不連線外網,那麼APP的資料只能以靜態或內建的方式儲存,這些資料(圖片、Excel等文件、音視訊等)無法重新整理和更改,非常不方便後期維護更新;

典型案例:某展廳有一臺觸控式螢幕(Android系統),提供該單位所管轄範圍下的客戶資訊,一旦客戶資訊變更,那麼就需要修改APP中的資料,問題來了,該資料是靜態寫入程式的,觸控式螢幕是不聯網的,只能重新提供APP安裝包,非常不方便。

二、分析原因

資料來源:

1 內建資料,就是所謂的單機版,靜態,不易修改,無法滿足後期更新維護的要求,無法滿足如上案例更新資料的要求;

2 伺服器提供,通過介面獲取伺服器儲存的資料,前提是需要一臺伺服器,在聯網的情況下通過介面把資料提供給APP,達到更新資料的目的,這種也無法滿足如上案例無網的情況;

案例分析:本案例在日常生活中經常遇到,如展廳的觸控式螢幕,內容資料更新頻率不高,裝置不聯網,只用來做簡單的查詢展示作用;

束手無策:怎麼辦,無法滿足案例需求?還好,可採用第三種解決方案,通過中介軟體提供資料,即無需裝置聯網,也無需一臺伺服器提供資料,也可實現更新資料的目的,那就是通過OTG技術。

什麼是OTG

OTG是一種USB傳輸技術,通過OTG轉接線,可以讓手機直接訪問U盤或數碼相機等裝置中的檔案,還可以連線到鍵盤、滑鼠等外接裝置;

三、解決方案

非常產品,就需非常手段,筆者已經解釋了OTG技術,那麼解決方案應該已經清楚了,簡單描述就是支援OTG的裝置(一般觸控式螢幕和部分手機都支援OTG技術),通過U盤提供資料來源給APP程式;

方案描述:首先將修改後的資料來源按照一定格式或檔案目錄存入U盤,APP啟動後從U盤讀取資料並存入裝置內建SD卡,拔掉U盤後,App從SD卡中讀取對應資料展示;

資料流向:U盤——SD卡——App程式——使用者介面。

附錄一張筆者在專案技術方案中的流程圖:


四、程式碼流程

基本思路已如上圖很清晰了,下面就是通過程式碼來一步一步實現了,核心程式碼如下:

1 許可權檢測和開源庫

涉及許可權,注意6.0的動態許可權

<!-- 開機啟動 -->

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


<!-- SD卡寫許可權 -->

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

<!-- SD卡讀許可權 -->

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

<!-- 在SDCard中建立與刪除檔案許可權 -->

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

U盤插入拔出檢測

private void registerReceiver() {
        //監聽otg插入 拔出
        IntentFilter usbDeviceStateFilter = new IntentFilter();

        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);

        usbDeviceStateFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);

        registerReceiver(mUsbReceiver, usbDeviceStateFilter);

        //註冊監聽自定義廣播

        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);

        registerReceiver(mUsbReceiver, filter);

    }


    private BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {

        public void onReceive(Context context, Intent intent) {

            String action = intent.getAction();

            switch (action) {

                case ACTION_USB_PERMISSION://接受到自定義廣播

                    setMsg("接收到自定義廣播");

                    UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {  //允許許可權申請

                        if (usbDevice != null) {  //Do something

                            setMsg("使用者已授權,可以進行讀取操作");

                            readDevice(getUsbMass(usbDevice));

                        } else {

                            setMsg("未獲取到裝置資訊");

                        }

                    } else {
                        setMsg("使用者未授權,讀取失敗");
                    }

                    break;

                case UsbManager.ACTION_USB_DEVICE_ATTACHED://接收到儲存裝置插入廣播

                    UsbDevice device_add = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                    if (device_add != null) {

                        setMsg("接收到儲存裝置插入廣播,嘗試讀取");
                        redDeviceList();
                    }

                    break;

                case UsbManager.ACTION_USB_DEVICE_DETACHED://接收到儲存裝置拔出廣播

                    UsbDevice device_remove = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                    if (device_remove != null) {

                        setMsg("接收到儲存裝置拔出廣播");

                        usbFiles.clear();//清除

                        cFolder = null;

                    }

                    break;

            }

        }

    };
2 讀取U盤資料

這裡用到開源庫:

//Excel解析庫

implementation 'com.hynnet:jxl:2.6.12.1'

//U盤

implementation 'com.github.mjdev:libaums:0.5.5'
private void readDevice(UsbMassStorageDevice device) {

        // before interacting with a device you need to call init()!

        try {

            device.init();//初始化

//          Only uses the first partition on the device

            Partition partition = device.getPartitions().get(0);

            FileSystem currentFs = partition.getFileSystem();

//          fileSystem.getVolumeLabel()可以獲取到裝置的標識

//          通過FileSystem可以獲取當前U盤的一些儲存資訊,包括剩餘空間大小,容量等等

//            Log.d(TAG, "Capacity: " + currentFs.getCapacity());

//            Log.d(TAG, "Occupied Space: " + currentFs.getOccupiedSpace());

//            Log.d(TAG, "Free Space: " + currentFs.getFreeSpace());

//            Log.d(TAG, "Chunk size: " + currentFs.getChunkSize());

            UsbFile root = currentFs.getRootDirectory();//獲取根目錄

            String deviceName = currentFs.getVolumeLabel();//獲取裝置標籤

            setMsg("正在讀取U盤" + deviceName);

            cFolder = root;//設定當前檔案物件

            addFile2SD();

        } catch (Exception e) {

            e.printStackTrace();

            setMsg("讀取失敗,異常:" + e.getMessage());

        }

    }
3 寫資料到SD卡
private void addFile2SD() {

    try {

        for (UsbFile file : cFolder.listFiles()) {

            //找到資料夾

            if (file.isDirectory() && file.getName().equals(SD_DIR_NAME)) {

                for (UsbFile subfile : file.listFiles()) {

                    if (subfile.isDirectory()) {//image 資料夾

                        for (UsbFile imgfile : subfile.listFiles()) {

                            usbFiles_img_list.add(imgfile);

                            readFile(imgfile,FileUtil.getSavePath(SD_DIR_NAME+File.separator+SD_DIR_IMAGE_NAME));

                        }

                    } else { //em.xls info.png

                        usbFiles_xls_list.add(subfile);

                        readFile(subfile,FileUtil.getSavePath(SD_DIR_NAME));
                    }

                }

                break;

            }

        }


    } catch (IOException e) {

        e.printStackTrace();

        setMsg("讀取出錯IO異常:" + e.getMessage());

    }

}
4 App讀取資料

根據路徑從SD卡中取出所需檔案,示例

private void loadExcelData() {

        if (!SDUtils.checkSDcard()) {

            Toast.makeText(getBaseContext(), "該裝置沒有SD卡,請檢查!", Toast.LENGTH_LONG).show();

            return;

        }


        String filePath = SDUtils.getSDPath() + File.separator + SDUtils.SD_DIR_NAME +

                File.separator + SDUtils.SD_FILE_NAME;

        Log.w("TAG", "filePath:" + filePath);


        //解析xls
        ExcelDataLoader excelDataLoader = new ExcelDataLoader();

        excelDataLoader.execute(filePath);
    }


    //在非同步方法中 呼叫

    private class ExcelDataLoader extends AsyncTask<String, Integer, ArrayList<ExcelBean>> {

        //執行子主執行緒,執行耗時操作,防止主執行緒阻塞,出現ANR

        @Override

        protected ArrayList<ExcelBean> doInBackground(String... strings) {

            return ExcelUtils.parseXlsDataFromSD(strings[0], 0, getBaseContext());

        }



        //運行於主執行緒,更新進度

        @Override

        protected void onProgressUpdate(Integer... values) {

            super.onProgressUpdate(values);

        }



        //運行於主執行緒,耗時操作結束後執行該方法

        @Override

        protected void onPostExecute(ArrayList<ExcelBean> excelBeans) {

            super.onPostExecute(excelBeans);



            initData(excelBeans);

        }

    }



    private void initData(ArrayList<ExcelBean> list) {



        Gson gson = new Gson();

        String jsonStr = gson.toJson(list);



        Log.i("TAG", "JSON_INFO:" + jsonStr);

    }

可能有朋友需要看下Excel表格解析成資料模型的程式碼,程式碼如下:

 public static ArrayList<ExcelBean> parseXlsDataFromSD(String xlsPath, int index, Context
            context) {
        ArrayList<ExcelBean> excelBeanArrayList = new ArrayList<>();

        File file = new File(xlsPath);

        try {

            InputStream is = new FileInputStream(file);
            Workbook workbook = Workbook.getWorkbook(is);

            int sheetNum = workbook.getNumberOfSheets();

            Log.d(TAG, "the num of sheets is " + sheetNum);

            for (int i = 0; i < sheetNum; i++) {

                Sheet sheet = workbook.getSheet(i);

                int sheetRows = sheet.getRows();
                int sheetColumns = sheet.getColumns();

                Log.d(TAG, "the name of sheet is  " + sheet.getName());
                Log.d(TAG, "total rows is 行=" + sheetRows);
                Log.d(TAG, "total cols is 列=" + sheetColumns);

                for (int j = 1; j < sheetRows; j++) {
                    ExcelBean excelBean = new ExcelBean();
                    excelBean.setNum(Integer.valueOf(sheet.getCell(0, j).getContents()));
                    excelBean.setType(Integer.valueOf(sheet.getCell(1, j).getContents()));
                    excelBean.setName(sheet.getCell(2,j).getContents());

                    /*
                    * 如果是日期型別,可以使用DateCell物件提供的方法:

                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                        Cell cell = sheet.getCell(j, i);
                        DateCell dateCell = (DateCell) cell;
                        String date = sdf.format(dateCell.getDate());
                          如果是數值型別,可以使用NumberCell物件提供的方法:

                        Cell cell = sheet.getCell(j, i);
                        NumberCell numberCell = (NumberCell)cell;
                        Double numberValue = numberCell.getValue();
                        String number = numberValue.toString();
                    * */

                    Cell cell_lon = sheet.getCell(3, j);
                    NumberCell numberCell_lon = (NumberCell)cell_lon;
                    Double numberValue_lon = numberCell_lon.getValue();
                    excelBean.setLatitude(numberValue_lon);

                    Cell cell_lat = sheet.getCell(4, j);
                    NumberCell numberCell_lat = (NumberCell)cell_lat;
                    Double numberValue_lat = numberCell_lat.getValue();
                    excelBean.setLongitude(numberValue_lat);

                    excelBean.setContent(sheet.getCell(5,j).getContents());
                    excelBean.setImgPath(sheet.getCell(6,j).getContents());


                    Log.d(TAG, "setLongitude=" + sheet.getCell(3,j).getContents());

                    excelBeanArrayList.add(excelBean);
                }
            }


            workbook.close();

        } catch (Exception e) {
            Log.e(TAG, "read error=" + e, e);
        }

        return excelBeanArrayList;
    }
5 使用者介面展示

將獲取到的資料做進一步處理,展示到使用者介面。

五、方案總結

至此案例需求完美解決,通過Android系統的OTG功能,採用U盤作為資料來源提供者,實現了無網路、無伺服器來定期更新維護App的特殊要求,目前該解決方案已通過真實專案測試,筆者可提供專案原始碼或技術協助,注意提供技術或原始碼均不免費,聯絡方式:[email protected],其他問題可直接留言,感謝大家的支援以及前輩同行們總結的寶貴經驗。

附:筆者推薦優質APP:IT面試寶典 

相關推薦

基於Android系統OTG功能U作為資料來源解決方案

一、問題來源目前市面上多數APP都是基於網際網路來讀取資料,展示介面,但若在無網路環境,如展廳、政府機構中大多都不連線外網,那麼APP的資料只能以靜態或內建的方式儲存,這些資料(圖片、Excel等文件、音視訊等)無法重新整理和更改,非常不方便後期維護更新;典型案例:某展廳有一

android系統分享功能自己的APK加入可分分享的應用列表

android系統分享功能 第一步:在Manifest.xml進行配置,比普通的activity中多增加一個Intent過濾器 <application android:allowBackup="true" android:icon="@d

Jacoco在Android系統應用測試中覆蓋率一直為0的解決方案

問題 普通應用Gradle配置Jacoco,執行createDebugAndroidTestCoverageReport,能夠正常輸出覆蓋率報告,報告路徑為: build/reports/coverage/debug/index.html。檢視build/out

linux嵌入式系統實現U、SD卡自動掛載功能

在 Linux的嵌入式系統中我們經常用到U盤、SD卡的掛載,而每次都手動掛載或解除安裝非常麻煩,我們可以採取以下方法實現自動掛載或解除安裝U盤、SD卡 這就要用到mdev了 其具體操作如下: 1、首先在/etc/init.d/rsC中加入以下語句 echo /sbin/mdev > /proc/sys

基於Android系統的多點觸控式螢幕(MultiTouchScreen)驅動

理論: 輸入子系統由來 在Linux中, 應用層對於輸入裝置(滑鼠、鍵盤、觸控式螢幕等)的操作無非都是open、read、write、ioctl,然後呼叫驅動層的xxx_open、xxx_read、xxx_write、xxx_ioctl去操作具體的硬體輸入裝置。如果按照傳統的思路,每

呼叫android系統相機拍照並圖片傳指定路徑並獲取相片

public void onClick(View v) { if (v.getId() == R.id.btn_take) { // 調系統相機 if (Environment.getExterna

ubuntu/centos環境android系統(二): linuxandroid模擬器執行!

      上一篇完成了Android的編譯,想必一定心裡癢癢了!這一篇將開始玩轉android模擬器! 如果不喜歡在linux下執行 emulator ,可直接看第三篇,第三篇講述windows下的模擬器,相對linux下,要簡單很多! 一.下載Andoird SDK,

如何在不重灌win10系統的情況intel主機板的RAID設定改為ACHI設定

剛裝電腦的時候,為了資料的安全性,安裝了兩塊3T機械硬碟,並且在intel主機板中將磁碟設定為了raid0模式 結果用著發現raid問題很多 1.非正常關機後,重啟完畢需要同步資料,非常耗時 2.無法準確看到硬碟smart資訊,硬碟有報警了都不知道 3.噪音大,一塊硬碟沒什麼感覺,兩塊硬碟一塊動,就能

Linux實現U、SD卡自動掛載功能 .

在 Linux中我們經常用到U盤、SD卡的掛載問題,每次都手動掛載或解除安裝非常麻煩,我們可以採取以下方法實現自動掛載或解除安裝U盤、SD卡 1、首先在/etc/init.d/rsC中加入以下語句 echo /sbin/mdev > /proc/sys/kernel/

使用adb命令刪除Android系統data目錄檔案及資料夾

使用命令刪除data目錄下的資料夾,和刪除一般檔案不同。 data目錄下的檔案需要777許可權,所以需要的命令不同 data目錄: C:\Users\aw>adb shell roo

基於Android系統的平板電腦電子地圖系統開發

http://www.sbsm.gov.cn/article/gzdt/201204/20120400100590.shtml 4月1日上午,陝西省測繪地理資訊局組織陝西省發展和改革委員會相關專家對“基於android系統的平板電腦電子地圖系統開發”、“陝西省測繪成果目錄匯

Android獲取可用記憶體(系統,sd卡,u

/** *通過反射獲取不同儲存卡的路徑,主要是用來獲取外掛sd卡路徑,內建sd卡路徑可通過Environment類的方法獲得 */ public static boolean hasEnoughStor

linux製作U系統啟動

我們都知道 Windows 下我們可以使用 UltraISO(中文版叫軟碟通)製作U盤系統啟動盤,使用方法可能N多人都會,但是本文主要想簡單介紹下 Linux 製作U盤啟動盤的方法,所以不贅述了,如果有不會的請百度/谷歌去,但是如果你仔細看了本文,估計以後你也不想用

在Linux系統製作U啟動

我的當前的系統是Linux(版本Xubuntu),之前是通過軟通牒(UltraISO)在Windows系統下製作了這個Linux系統的U盤啟動盤,然而在Linux系統下通過系統本身整合的DD命令,來實現在Linux系統下製作Linux系統的ISO系統U盤啟動盤,一行簡單的程式

檢視基於Android 系統單個程序記憶體和CPU使用情況的幾種方法

Total PSS by OOM adjustment:     16839 kB: System                16839 kB: system (pid 791)      9279 kB: Persistent                 9279 kB: com.android.s

android USB OTG功能實現

                一、檢查HW原理圖,確認是否支援OTG功能(vbus是否供上電,IDDIG pin連線是否正確)二、若HW確認支援OTG功能,則按照以下方法分別開啟USB OTG功能及實現掛載:如何開啟USB OTG功能:1).在alps/mediatek/config/[project]/a

基於Android系統Api封裝常用工具類

專案地址 https://github.com/h4de5ing/AndroidCommon/blob/master/README-cn.md gradle使用方式 compile 'com.code19.library:library:0.1.2' 常用工具類 使用示

Linux掛載u

成功 掛載 消失 linux下 spa 一個 fat 執行 class 1.先進入/mnt/目錄新建一個usb目錄   cd /mnt/   mkidr usb 2.先fdisk -l,然後插上U盤,fdisk -l   查看是否有新的硬盤添加上來了 3.執行掛載   mo

怎麽U格式化的文件恢復

  備份文件,我們第一時間就會想到用U盤來將文件備份,如果我們將U盤裏的文件不小心格式化了怎麽辦呢?怎麽將U盤格式化的文件恢復 如果我們想要恢復U盤誤刪的文件,那麽我們可以使用互盾數據恢復軟件進行恢復,這款軟件是一款專業的數據恢復軟件,利用這款軟件我們可以輕松的將我們U盤裏刪除的文件恢復,這款軟件操作簡

Ubuntu使用U安裝Ubuntu

ubuntu制作U盤啟動1. 安裝U盤制作工具sudo apt-get install unetbootin使用 sudo df -l 命令查看U盤盤符,這裏假設為/dev/sdb12. 格式化U盤使用 sudo umount /dev/sdb1 命令卸載U盤使用 sudo mkfs.vfat /dev/sd