• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • android學習筆記----pull解析與xml生成和應用申請權限模版

    先來個pull解析測試,然后是pull解析用法說明,文章末尾附有xml生成方式。

    學習目標:首先是解析測試例子給出的對于常用字段的理解,然后是pull解析常用套路方法,最后是xml的2種生成方式。

    經常寫代碼需要申請動態權限,在最后例子也順帶記錄下來,方便查閱。

    目錄

    pull解析測試:

    pull解析例子:

    xml生成方式(代碼添加申請權限示范模版):


     

    pull解析例子的源碼:https://github.com/liuchenyang0515/PullDemo

    xml生成方式的源碼:https://github.com/liuchenyang0515/SaveXmlInfo

     

     

    pull解析測試:

    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.util.Xml;
    import android.view.View;
    
    import org.xmlpull.v1.XmlPullParser;
    
    import java.io.InputStream;
    
    public class MainActivity extends AppCompatActivity {
        private String TAG = "MainActivity";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void onclick(View view) {
            try {
                InputStream is = getAssets().open("info.xml");
                // 解析info.xml文件
                // 1.得到xml文件的解析器
                XmlPullParser parser = Xml.newPullParser();
                
                // 2.設置輸入流和編碼
                parser.setInput(is, "utf-8");
                
                // 3.解析xml文件,獲取當前的事件類型
                int type = parser.getEventType();
                while (type != XmlPullParser.END_DOCUMENT){
                    // getText()是獲取內容,整個流程相當于一個指針指一次開始標簽再指一次內容,
                    // (內容的getName()為null, getText()才是取內容字符串,如果沒內容就是"")
                    // 再指一次結束標簽(如果沒遇到結束標簽就指向下一個開始標簽),然后再次指向內容,如果沒有getName()就是null
                    // 否則就指向下一個標簽,依次重復這個過程
                    // 經測試,事件類型依次為
                    // 最頂端document   0
                    // 開始標簽         2
                    // 內容             4
                    // 結束標簽         3
                    Log.d(TAG, parser.getEventType() + "..." + parser.getName() + "..." + parser.getText());
                    type = parser.next();
                }
                is.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    常用的字段:
    int START_DOCUMENT = 0;  
    int END_DOCUMENT = 1;  
    int START_TAG = 2;  
    int END_TAG = 3;  

    int TEXT = 4;  

    筆記批注:

        getText()是獲取內容,整個流程相當于一個指針指一次開始標簽再指一次內容,再指一次結束標簽(如果沒遇到結束標簽就指向下一個開始標簽),然后再次指向內容。

    標簽只有getName(),而getText()為null, 內容只有getText(),而getName()為null。getText()取字符串,如果字符串就是空串"",說明本行后面沒內容了,那么就開始進行下一行的解析。

    比如獲取開始標簽的下一次沒有內容而是另一個開始標簽,那么getText()是"",進行下一行解析,或者遇到結束標簽的下一次獲取內容getText()是"",說明后面沒內容了,進行下一行解析。

    如果getName()沒有,值就是null(比如TEXT字段)

    如下圖。

    即不管是開始還是結束標簽,只要遇到標簽,下一次就會嘗試獲取內容,getEventType()得到了START_DOCUMENT和END_TAG字段, 那么下一次getEventType()一定是TEXT字段。

    SAX解析和PULL解析原理是一樣的,可以見我另一篇博客之中寫到的SAX解析:SAX解析代碼原理分析

    xml如下:

    ?

    運行結果如下:

    ????

     

    pull解析例子:

    MainActivity.java:

    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.widget.TextView;
    
    import java.io.InputStream;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
        private String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 1.顯示天氣信息
            TextView tv_weather = (TextView) findViewById(R.id.tv_weather);
    
            try ( // 1.1獲取資產的管理者,通過上下文
                  InputStream inputStream = getAssets().open("weather.xml");) {
                // 2.調用我們定義的解析xml業務方法
                List<Channel> list = Weather.parserXml(inputStream);
                StringBuffer sb = new StringBuffer();
                for (Channel channel : list) {
                    sb.append(channel.toString() + "\n");
                }
                // 3.把數據展示到textview
                tv_weather.setText(sb);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    Weather.java:

    import org.xmlpull.v1.XmlPullParser;
    import org.xmlpull.v1.XmlPullParserFactory;
    
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    public class Weather {
        /**
         * 服務器是以流的形式把數據返回的
         */
        public static List<Channel> parserXml(InputStream in) throws Exception {
            List<Channel> weatherLists = null;
            Channel channel = null;
            // 1.獲取XmlPullParser解析的實例
            // 簡單工廠方法的例子
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser parser = factory.newPullParser();
    
            // 也可以直接一步寫XmlPullParser parser = Xml.newPullParser();
            // 通過Xml.newPullParser()獲得的解析器可能會有一個bug:調用nextText()并不總是前進到END_TAG
            // 一些app可能圍繞著這個問題,額外的調用next()或nextTag()方法:
            // 在Android Ice Cream Sandwich版本中,刪除了ExpatPullParser類來修復這個bug,
            // 不幸的是,app在Android4.0版本下使用它可能會導致應用crash
            // 官方說明文檔是用的2步,如我上面寫的,就當做是推薦這種寫法吧
    
            // 2.設置XmlPullParser參數
            parser.setInput(in, "utf-8");
            // 3.獲取事件類型
            int type = parser.getEventType();
            while (type != XmlPullParser.END_DOCUMENT) {
                String nodeName = parser.getName();
                // 4.具體判斷一下解析到哪里了
                switch (type) {
                    case XmlPullParser.START_TAG:
                        if ("weather".equals(nodeName)) {
                            // 5.創建一個集合對象
                            weatherLists = new ArrayList<Channel>();
                        } else if ("channel".equals(nodeName)) {
                            // 6.創建Channel對象
                            channel = new Channel();
                            // 7.獲取id值
                            // 也可以parser.getAttributeValue(null, "id");
                            String id = parser.getAttributeValue(0); // 0號屬性
                            channel.setId(id);
                        } else if ("city".equals(nodeName)) {
                            // 8.獲取city的數據
                            String temp = parser.nextText();// 剛剛測試過了getText()是獲得本次指向的內容
                            // 這里nextText()就是指向下一次指向的內容,即開始和結束標簽之間的內容
                            channel.setTemp(temp);
                        } else if ("wind".equals(nodeName)) {
                            // 9.獲取wind數據
                            String wind = parser.nextText();
                            channel.setWind(wind);
                        } else if ("pm250".equals(nodeName)) {
                            // 9.獲取wind數據
                            String pm250 = parser.nextText();
                            channel.setPm250(pm250);
                        }
                        break;
                    case XmlPullParser.END_TAG:
                        if ("channel".equals(nodeName)) { // 已經測試過了,結束標簽不會讀取前面的/
                            // 把javabean對象存到集合中
                            weatherLists.add(channel);
                        }
                        break;
                }
                // 不停的向下解析
                type = parser.next();
            }
            return weatherLists;
        }
    }

    筆記批注:

    // 1.獲取XmlPullParser解析的實例

    XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
    XmlPullParser parser = factory.newPullParser();

    這個就是簡單工廠方法模式的一個例子。

     也可以直接一步寫XmlPullParser parser = Xml.newPullParser(); 通過Xml.newPullParser()獲得的解析器可能會有一個bug:調用nextText()并不總是前進到END_TAG一些app可能圍繞著這個問題,額外的調用next()或nextTag()方法:在Android Ice Cream Sandwich版本中,刪除了ExpatPullParser類來修復這個bug,不幸的是,app在Android4.0版本下使用它可能會導致應用crash, 官方說明文檔是用的2步,如我上面寫的,就當做是推薦這種寫法吧

    詳情參考博客:https://blog.csdn.net/u013656135/article/details/49840125

    關于方法使用:

    getAttributeValue(int index);//大意就是返回指定位置的屬性值,位置從0開始

    getAttributeValue(String namespace,String name); // 大意就是返回指定的屬性名對應的屬性值,如果沒有使用命名空間,則第一個參數傳入null,第二個參數是屬性名,這個例子是"id"屬性

    Channel.java:

    public class Channel {
        private String id;
        private String city;
        private String temp;
        private String wind;
        private String pm250;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getCity() {
            return city;
        }
    
        public void setCity(String city) {
            this.city = city;
        }
    
        public String getTemp() {
            return temp;
        }
    
        public void setTemp(String temp) {
            this.temp = temp;
        }
    
        public String getWind() {
            return wind;
        }
    
        public void setWind(String wind) {
            this.wind = wind;
        }
    
        public String getPm250() {
            return pm250;
        }
    
        public void setPm250(String pm250) {
            this.pm250 = pm250;
        }
    
        @Override
        public String toString() {
            return "Channel [id=" + id + ", city=" + city + ", temp=" + temp
                    + ", wind=" + wind + ", pm250=" + pm250 + "]";
        }
    }

    assets目錄下的weather.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <weather>
        <channel id='1'>
            <city>北京</city>
            <temp>25°</temp>
            <wind>3</wind>
            <pm250>300</pm250>
    
        </channel>
    
        <channel id='2'>
            <city>鄭州</city>
            <temp>20°</temp>
            <wind>4</wind>
            <pm250>300</pm250>
    
        </channel>
    
        <channel id='3'>
            <city>長春</city>
            <temp>10°</temp>
            <wind>4</wind>
            <pm250>100</pm250>
    
        </channel>
    
        <channel id='4'>
            <city>沈陽</city>
            <temp>20°</temp>
            <wind>1</wind>
            <pm250>50</pm250>
        </channel>
    </weather>

    筆記批注:

    assets與res/raw不同:
        assets目錄是Android的一種特殊目錄,用于放置APP所需的固定文件,且該文件被打包到APK中時,不會被編碼到二進制文件。
        Android還存在一種放置在res下的raw目錄,該目錄與assets目錄不同。
    注意點:
        1、 assets目錄不會被映射到R中,因此,資源無法通過R.id方式獲取,必須要通過AssetManager進行操作與獲取;res/raw目錄下的資源會被映射到R中,可以通過getResource()方法獲取資源。
        2、 多級目錄:assets下可以有多級目錄,res/raw下不可以有多級目錄。
        3、 編碼(都不會被編碼):assets目錄下資源不會被二進制編碼;res/raw應該也不會被編碼。
     

    運行結果如下:

    ?

     

     

    xml生成方式(代碼添加申請權限示范模版):

    本demo的xml是一個Button,屬性添加android:onClick="save",然后幾個編輯框提交。如下圖:

    ?

    SaveXmlInfo的Demo主要代碼如下:

    import android.Manifest;
    import android.content.DialogInterface;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.os.Environment;
    import android.support.annotation.NonNull;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatActivity;
    import android.text.TextUtils;
    import android.util.Xml;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.Toast;
    
    import org.xmlpull.v1.XmlSerializer;
    
    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.util.ArrayList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        private EditText et_name, et_age, et_id;
        private final int MY_PER_CODE = 15;
        private String name, age, id;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            et_name = (EditText) findViewById(R.id.et_name);
            et_age = (EditText) findViewById(R.id.et_age);
            et_id = (EditText) findViewById(R.id.et_id);
        }
    
        public void save(View view) {
            name = et_name.getText().toString().trim();
            age = et_age.getText().toString().trim();
            id = et_id.getText().toString().trim();
            if (TextUtils.isEmpty(name) || TextUtils.isEmpty(age) || TextUtils.isEmpty(id)) {
                Toast.makeText(this, "信息不能為空", Toast.LENGTH_SHORT).show();
                return;
            } else {
                List<String> permissionList = new ArrayList<>();
                if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED) {
                    if (!permissionList.contains(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                        permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
                    }
                }
                if (!permissionList.isEmpty()) {
                    String[] permissions = permissionList.toArray(new String[permissionList.size()]);
                    ActivityCompat.requestPermissions(this, permissions, MY_PER_CODE);
                } else {
                    writeXml_1();
                    //writeXml_2();
                }
            }
        }
    
        private void writeXml_2() {
            // 1.創建一個xml文件的序列化器
            XmlSerializer serializer = Xml.newSerializer();
            try (FileOutputStream fos = new FileOutputStream(new File
                    (Environment.getExternalStorageDirectory(), "info1.xml"));) {
                // 設置文件的輸出和編碼方式
                // 設置為使用具有給定編碼的二進制輸出流。
                serializer.setOutput(fos, "utf-8");
                // 寫xml文件的頭
                // 使用編碼(if encoding not null)和獨立標志(if standalone not null)編寫<?xml聲明,此方法只能在setOutput之后調用。
                serializer.startDocument("utf-8", true);
                // 4.寫info結點
                // 使用給定的命名空間和名稱寫入開始標記。如果沒有為給定的命名空間定義前綴,則將自動定義前綴。
                // 如果名稱空間為NULL,則不打印名稱空間前綴,而只打印名稱。
                serializer.startTag(null, "info");
                // 5.寫student節點
                serializer.startTag(null, "student");
                // 6.寫屬性
                // 寫一個屬性。對Attribute()的調用必須立即跟隨對startTag()的調用。
                serializer.attribute(null, "id", id);
                // 7.寫name
                serializer.startTag(null, "name");
                serializer.text(name);
                serializer.endTag(null, "name");
                // 8.寫age
                serializer.startTag(null, "age");
                serializer.text(age);
                serializer.endTag(null, "age");
    
                serializer.endTag(null, "student");
                serializer.endTag(null, "info");
                // 寫完。所有未關閉的開始標記將被關閉,輸出將被刷新。在調用此方法之后,在下次調用setOutput()之前,不能序列化更多的輸出。
                serializer.endDocument();
                Toast.makeText(this, "保存學生信息成功", Toast.LENGTH_SHORT).show();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            switch (requestCode) {
                case MY_PER_CODE:
                    if (grantResults.length > 0) {
                        for (int result : grantResults) {
                            if (result != PackageManager.PERMISSION_GRANTED) { // 如果用戶不同意
                                // ActivityCompat.shouldShowRequestPermissionRationale用法見下面筆記批注
                                if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                                        Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                                    showPermissionDialog(permissions);
                                } else { // 不同意的情況下還勾選了“不再提醒”
                                    Toast.makeText(MainActivity.this, "您已拒絕權限,請在設置手動打開", Toast.LENGTH_SHORT).show();
                                }
                                return; // 用戶不同意的情況下,到這里直接返回
                            }
                        }
                        writeXml_1();
                        //writeXml_2();
                    } else {
                        Toast.makeText(this, "未知錯誤!", Toast.LENGTH_SHORT).show();
                        finish();
                    }
                    break;
                default:
                    break;
            }
        }
    
        private void showPermissionDialog(final String[] permissions) {
            AlertDialog.Builder dialog = new AlertDialog.Builder(this);
            dialog.setTitle("提示!");
            dialog.setMessage("這個權限關系到功能使用,如拒絕需要在設置手動打開!");
            dialog.setCancelable(false); // 點擊空白處不可取消
            dialog.setPositiveButton("授權", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    ActivityCompat.requestPermissions(MainActivity.this, permissions, MY_PER_CODE);
                }
            });
            dialog.setNegativeButton("拒絕", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                }
            });
            dialog.show();
        }
    
        private void writeXml_1() {
            StringBuffer sb = new StringBuffer();
            sb.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>");
            sb.append("<info>");
            sb.append("<student id=\"" + id + "\">");
            sb.append("<name>" + name + "</name>");
            sb.append("<age>" + age + "</age>");
            sb.append("</student>");
            sb.append("</info>");
            try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(new File(Environment.getExternalStorageDirectory(), "info.xml"))));) {
                bw.write(sb.toString());
                Toast.makeText(this, "保存學生信息成功", Toast.LENGTH_SHORT).show();
            } catch (IOException e) {
                e.printStackTrace();
                Toast.makeText(this, "保存學生信息失敗", Toast.LENGTH_SHORT).show();
            }
        }
    }

    writeXml_1()是通過文件寫xml,writeXml_2()是通過序列化器寫xml。

    筆記批注:

    ActivityCompat.shouldShowRequestPermissionRationale用法:
        應用安裝后第一次訪問,如果未開始獲取權限申請直接返回false;可能此時并未請求權限而執行到此方法
        第一次請求權限時,用戶拒絕了,下一次shouldShowRequestPermissionRationale()返回 true,這時候可以顯示一些為什么需要這個權限的說明;
        第二次以及之后請求權限時,用戶拒絕了,并選擇了“不再提醒”的選項時:shouldShowRequestPermissionRationale()返回 false;
        第二次以及之后請求權限時,用戶拒絕了,但沒有勾選“不再提醒”選項,返回true,繼續提醒

        設備的系統設置中禁止當前應用獲取這個權限的授權,shouldShowRequestPermissionRationale()返回false;

     

    =================================Talk is cheap, show me the code=================================

     

    版權聲明:本文為qq_34115899原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_34115899/article/details/80933772

    智能推薦

    Android權限申請的學習實踐

    1.引子 在換到Android手機之前,對Android系統的印象是這系統app的跑馬場,app可以任意索取各種權限,隨意竊取各種隱私,換手機后才知道Android系統對權限的管理已有很大的改觀,索取的每個危險權限都需要提示用戶,當然Android只是盡可能提示用戶,還是存在著用戶不同意就不給用的情況。 權限管理的改進給開發者增加了一定工作量,申請危險權限不再是簡單的在AndroidManifes...

    Android使用Pull方式進行Xml文件解析

    熟知的XML解析方式有SAX,DOM,Pull三種,筆者采用Pull方式對xml進行了解析操作。 以案例入手學習: 對本地xml文件進行解析,首先把xml文件放入到assets文件夾下。 1.xml文件格式及內容: (在這個xml文件中,我們發現在根標簽之中有:與,而且point在stroke之中,這種情況我們需要注意一下,首先把point內的x,y,time放入到集合中,再把point加上str...

    [Android面試] Xml 解析辨析---SAX、DOM、Pull

      在Android中提供了三種解析XML的方式: SAX(Simple API XML),DOM(Document Objrect Model),以及Android推薦的Pull解析方式。 解析概念 SAX: 是事件驅動型XML解析的一個標準接口,簡單地說就是對文檔進行順序掃描,當掃描到文檔(document)開始與結束、元素(element)開始與結束、文檔(document)結束等...

    Android開發:XML簡介及DOM、SAX、PULL解析對比

    目錄 目錄 定義 XML(extensible Markup Language) ,是一種數據標記語言 & 傳輸格式 作用 對數據進行標記(結構化數據) 對數據進行存儲 對數據進行傳輸 與html的區別:html用于顯示信息;xml用于存儲&傳輸信息 XML特點 標簽可進行自定義 XML允許作者定義自己的標簽和文檔結構 自我描述性 XML文檔實例 僅僅是一個純文本,有文本處理能力的...

    Android --pull解析XML,獲取省市區的例子

    舉個這樣的例子,這是一個XML的部分數據,現在需要解析   最后的效果圖 寫這個帖子是記錄下,解析這塊,說實話,我居然忘記怎么解析了,還是后面百度別人的文章,跟看同事的代碼,所以基礎啊 不牢固                  ...

    猜你喜歡

    Android使用Sax 及Pull 解析Xml demo

    客戶端開發中,越來越多的接口使用的數據傳輸類型時json了,xml相對較少。恰好當前要使用到了xml傳遞數據,寫個demo權當記錄安卓中如何使用sax 及 pull解析xml. 1.常用的幾種解析XML的方式   Dom ,Sax ,Pull  Dom解析Xml 是將整個Xm文檔當成一個對象來處理,會先把整個文檔讀入到內存里。是基于樹的結構,通常需要加載整文檔和構造DOM樹,然...

    【Unity&Android】安卓app自測應用隱私相關獲取和申請權限

    目錄 ① 在雷神模擬器4上-設置-開啟Root權限和USB權限,安裝你要自測的app。 ② 安裝python3 (或最新版本) ③ window安裝frida-server ④ 安裝手機使用的frida文件并運行起來 ⑤ 下載camille【Hook工具】 注意事項 通常,需要這種需求的情況就是因為XXXX應用獲取了XXX隱私或申請了XXX隱私權限導致拒審。 然后呢,我們就要去查到底是哪里獲取了或...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    精品国产乱码久久久久久蜜桃不卡