Android常用布局、文件存储与权限、XML

时间:2021-09-01 18:44:26

常用的布局

LinearLayout

Android 2.2开始fill_parent改名为match_parent ,从API Level为8开始我们可以直接用match_parent来代替fill_parent

orientation方向;vertical垂直;horizontal水平
gravity: 对齐方式,子控件相对于当前控件的对齐方式
layout_gravity:当前控件相对于父控件的对齐方式
margin:当前控件相对于四周的间距。
padding:当前控件中的子控件相对于当前控件四周的间距。

注意:在LinearLayout中,match_parent是充满父布局剩余空间,剩余空间,剩余空间。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

上面的例子中,TextView能显示出来,然后Button占据了剩余的全部空间。

RelativeLayout

RelativeLayout 第一个控件默认都是从左上角开始布局,要控制位置需要设置每个控件相对于其他控件的位置。注意上例中改为RelativeLayout,第二个控件会把第一个控件遮住。

layout_below:位于哪个控件的下方
layout_above:位于哪个控件的上方
layout_toLeftOf:指定当前控件位于哪个控件的左边
layout_alignRight: 两个控件的右边边缘对齐
layout_alignParentRight:当前控件基于父窗体的对其方式
layout_centerHorizontal:水平居中
layout_centerVertical:垂直居中
layout_centerInParent:位于父窗体的中间

举个RelativeLayout的例子

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#ff0000"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <LinearLayout
       android:layout_centerInParent="true"
        android:background="#00ff00"
        android:layout_width="200dp"
        android:layout_height="200dp"
        />
    <LinearLayout
        android:layout_centerInParent="true"

        android:background="#0000ff"
        android:layout_width="300dp"
        android:layout_height="100dp"

    />
    <ImageView
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"/>
</RelativeLayout>

FrameLayout

帧布局中的子控件默认放在左上角,是一层一层向上叠加的。适合简单布局。

下面是帧布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#ff0000"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_gravity="center"
        android:background="#00ff00"
        android:layout_width="200dp"
        android:layout_height="200dp"
        />
    <LinearLayout
        android:layout_gravity="center"
        android:background="#0000ff"
        android:layout_width="300dp"
        android:layout_height="100dp"

    />
    <ImageView
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"/>
</FrameLayout>

和上例RelativeLayout两个的布局都长这样

Android常用布局、文件存储与权限、XML

可以看到确实是一层一层往上覆盖的,并且RelativeLayout也有这样的特性。

logcat

最好自定义一个log工具类,指定level就可以过滤日志信息,保留我们需要的部分。比如指定level为WARN,则只会打印WARN和ERROR的信息。将level指定为NOTHING,任何日志都不会打印。

package Utils;

import android.util.Log;

public class LogUtils {
    public static final int VERBOSE = 1;
    public static final int DEBUG = 2;
    public static final int INFO = 3;
    public static final int WARN = 4;
    public static final int ERROR = 5;
    public static final int NOTHING = 6;
    public static int level = VERBOSE;
    // 琐碎的意义不大的信息,等级最低
    public static void v(String tag , String msg){
        if(level <= VERBOSE){
            Log.e(tag, msg);
        }
    }
    // debug级
    public static void d(String tag , String msg){
        if(level <= DEBUG)
            Log.w(tag, msg);
    }
    // info级别,打印比较重要的数据,比debug高一级
    public static void i(String tag , String msg){
        if(level <= INFO)
            Log.i(tag, msg);
    }
    // warn警告信息
    public static void w(String tag , String msg){
        if(level <= WARN)
            Log.d(tag, msg);
    }
    // error错误信息
    public static void e(String tag , String msg){
        if(level <= ERROR)
            Log.v(tag, msg);
    }
}

登录样例-记住用户名密码

使用文件存储的方式,主要使用java.io,很简陋不推荐。

Android自带的方法openFileOutput和openFileInout默认保存在/data/data/包名/files/文件夹下。只能当前程序使用,安全。

values下的strings.xml

<resources>
    <string name="app_name">LayoutTest</string>
    <string name="account">手机/邮箱</string>
    <string name="password">密码</string>
    <string name="remember">记住我</string>
    <string name="login">登录</string>
</resources>

activity_main布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp" >

    <ImageView
        android:id="@+id/head_pic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="40dp"
        android:src="@mipmap/ic_launcher_round"/>
    <EditText
        android:id="@+id/account"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/account"
        android:layout_marginTop="30dp"/>
    <EditText
        android:id="@+id/password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/password"
        />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <CheckBox
            android:layout_centerVertical="true"
            android:id="@+id/remember_me"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/remember"/>
        <Button
            android:id="@+id/login"
            android:layout_centerVertical="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="@string/login"
            android:layout_alignParentRight="true"/>
    </RelativeLayout>
</LinearLayout>

MainActivity

package com.example.layouttest;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class MainActivity extends AppCompatActivity {

    private EditText etAccount;
    private EditText etPassword;
    private CheckBox checkRemember;
    private Button buttonLogin;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // context很常用, 直接给mContext,以后都用它就行
        mContext = this;
        // 1. 获取控件
        etAccount = (EditText) findViewById(R.id.account);
        etPassword = (EditText) findViewById(R.id.password);
        checkRemember = (CheckBox) findViewById(R.id.remember_me);
        buttonLogin = (Button) findViewById(R.id.login);
        // 6. 下次进入页面从本地读取用户名密码,显示,checkbox状态还是选中状态
        String userinfo = getUserInfo();
        if (!TextUtils.isEmpty(userinfo)) {
            String username = userinfo.split("-")[0];
            String password = userinfo.split("-")[1];
            Log.d("pass", username);
            Log.d("pass", password);
            etAccount.setText(username);
            etPassword.setText(password);

            checkRemember.setChecked(true);
        }
        // 2. 设置登录按钮点击事件
        buttonLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                login();
            }
        });
    }

    private void login() {
        // 3. 获取用户名密码,checkbox是否选中
        String username = etAccount.getText().toString().trim();
        String password = etPassword.getText().toString().trim();
        boolean isRemember = checkRemember.isChecked();
        // 4. 判断用户名和密码是否为空,不为空请求服务器;为空就提示输入

        if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
            Toast.makeText(mContext, "请输入用户名或密码", Toast.LENGTH_SHORT).show();
            return;
        }

        // 5. 判断checkbox是否选中,选中则保存用户名密码到本地
        if (isRemember) {
            boolean result = saveUserInfo(username, password);
            if (result) {
                Toast.makeText(mContext, "保存成功", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(mContext, "保存失败", Toast.LENGTH_SHORT).show();

            }
        } else {
            FileOutputStream out = null;
            try {
                // 若用户没有勾选保存用户名密码,用一个同名的文件覆盖,但里面没有内容,下次进入,输入框就被清空了
                out =  openFileOutput("userinfo", MODE_PRIVATE);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (out != null) {
                        out.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        Intent intent = new Intent(mContext, WelconmeActivity.class);
        startActivity(intent);
        finish();
    }

    private boolean saveUserInfo(String username, String password) {
        String userInfo = username + "-" + password;
        FileOutputStream out = null;
        BufferedWriter writer = null;
        try {
            out = openFileOutput("userinfo", MODE_PRIVATE);
            // 将字节流包装为字符流再包装成缓冲流
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write(userInfo);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                    e.printStackTrace();
            }
        }
        return false;
    }

    private String getUserInfo() {
        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuilder content = new StringBuilder();
        try {
            in = openFileInput("userinfo");
            reader = new BufferedReader(new InputStreamReader(in));
            String line;
            while ((line=reader.readLine()) != null) {
                content.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                    e.printStackTrace();
            }
        }
        return content.toString();
    }
}

SharedPreferences存储

上面的存储方法不推荐,现使用SharedPreferences。

SharedPreferences是通过xml文件来做数据存储的。一般用来存放一些标记性的数据,一些设置信息。

用法如下

// 1.通过Context对象创建一个SharedPreference对象
//name:sharedpreference文件的名称    mode:文件的操作模式
SharedPreferences sharedPreferences = getSharedPreferences("userinfo", Context.MODE_PRIVATE);
// 2.通过sharedPreferences对象获取一个Editor对象
Editor editor = sharedPreferences.edit();
// 3.往Editor中添加数据
editor.putString("username", username);
editor.putString("password", password);
// 4.提交Editor对象
editor.apply(); // editor.commit();这个是有返回值的,但是返回值用处不大,推荐使用apply() 

    // *********使用sharedPreferences读取数据**********

// 1.通过Context对象创建一个SharedPreference对象
SharedPreferences sharedPreferences = context.getSharedPreferences("userinfo", Context.MODE_PRIVATE);
// 2.通过sharedPreference获取存放的数据
//key:存放数据时的key   defValue: 没找到key时候的默认值,根据业务需求来写
String username = sharedPreferences.getString("username", "");
String password = sharedPreferences.getString("password", "");

通过PreferenceManager可以获取一个默认的sharepreferences对象,默认以包名+ _shared_preferences.xml这样的名称命名。

SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
package com.example.layouttest;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private EditText etAccount;
    private EditText etPassword;
    private CheckBox checkRemember;
    private Button buttonLogin;
    private Context mContext;
    private SharedPreferences pref;
    private SharedPreferences.Editor editor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // context很常用, 直接给mContext,以后都用它就行
        mContext = this;

        // 获取SharedPreferences
        pref = PreferenceManager.getDefaultSharedPreferences(mContext);
        // 1. 获取控件
        etAccount = (EditText) findViewById(R.id.account);
        etPassword = (EditText) findViewById(R.id.password);
        checkRemember = (CheckBox) findViewById(R.id.remember_me);
        buttonLogin = (Button) findViewById(R.id.login);
        // 没有取到就默认为false
        boolean isRemember = pref.getBoolean("remember_password", false);
        // 6. 下次进入页面从本地读取用户名密码,显示,checkbox状态还是选中状态
        if (isRemember) {
            String username = pref.getString("username", "");
            String password = pref.getString("password", "");
            etAccount.setText(username);
            etPassword.setText(password);
            checkRemember.setChecked(true);
        }

        // 2. 设置登录按钮点击事件
        buttonLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                login();
            }
        });
    }
    private void login() {
        // 3. 获取用户名密码,checkbox是否选中
        String username = etAccount.getText().toString();
        String password = etPassword.getText().toString();
        // 4. 判断用户名和密码是否为空,不为空请求服务器;为空就提示输入

        if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
            Toast.makeText(mContext, "请输入用户名或密码", Toast.LENGTH_SHORT).show();
            return;
        }
        // 5. 判断checkbox是否选中,选中则保存用户名密码到本地
        editor = pref.edit();
        if (checkRemember.isChecked()) {
            editor.putString("username", username);
            editor.putString("password", password);
            editor.putBoolean("remember_password", true);

        } else {
            // 不勾选,下次进入不应该显示用户名密码,应该clear掉
            editor.clear();
        }
        // 不管checkbox有没有选中,都提交
        editor.apply();
        Intent intent = new Intent(mContext, WelconmeActivity.class);
        startActivity(intent);
        finish();
    }
}

Android常用布局、文件存储与权限、XML

SD卡

读写SD卡需要配置权限android.permission.WRITE_EXTERNAL_STORAGE

// 得到SD卡的路径
String path = Environment.getExternalStorageDirectory().getPath();
Log.d("storage", path); // 这里小米手机是 /storage/emulated/0
// SD卡的状态,是否挂载?
Log.d("storage", Environment.getExternalStorageState()); // 这里小米手机是 mounted,但不是实际的SD卡
//
File sdFile = Environment.getExternalStorageDirectory();
// File对象可以查看可用空间和已用空间
long usableSpace = sdFile.getUsableSpace();
long totalSpace = sdFile.getTotalSpace();
// 转换成用户能看懂的格式(M,G等)
String usableSpace_str = Formatter.formatFileSize(mContext, usableSpace);
String totalSpace_str = Formatter.formatFileSize(mContext, totalSpace);
Log.d("storage", usableSpace_str);
Log.d("storage", totalSpace_str);

/data/data/和/sdcard

  • /data/data/package-name/files: getFileDir().getPath();是一个应用程序的私有目录,只有当前应用程序有权限访问读写,其他应用无权限访问。一些安全性要求比较高的数据存放在该目录,一般用来存放size比较小的数据。
  • /sdcard: Enviroment.getExternalStorageDirectory().getPath();是一个外部存储目录,只要应用声明了<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>的一个权限,就可以访问读写sdcard目录;所以一般用来存放一些安全性不高的数据,文件size比较大的数据。

文件权限

linux下一个文件的权限由10位标示:

  • 第1位:文件的类型。
d:文件夹
l:快捷方式
 -:文件

其他9位每3位分为一组,每一组的3位按照rwx的顺序排列,没有该项权限就会"-"表示。如果用二进制表示,如果不是-就用1标示,是-用0标示;chmod指令赋权限。

  • 2-4: 该文件所属用户对本文件的权限。
  • 5-7:该文件所属用户组对本文件的权限。
  • 8-10:其他用户对该文件的权限。

如常用的chmod -R 777,777怎么来的?

rwx三个分别是1 1 1,转换成十进制位7,2-10一共三组。每组都赋予 全部权限,就是777

Android常用布局、文件存储与权限、XML

xml的生成

直接写

写一个备份短信(模拟)和恢复短信的功能。业务逻辑如下

a.备份
    1.封装短信数据到list中
    2.将list中的数据写到xml文件中。
b.恢复
    1.解析xml文件中短信数据,封装到list集合中
    2.将解析数据打印。
  1. 先写一个工具类SmsUtils来处理短信数据,实现备份和恢复的功能。
  2. 备份需要获取全部短信数据。写一个JavaBean封装短信数据,再将这些数据放入一个List里面(由SmsDao完成),再返回List给SmsUtils。

**一般JavaBean是使用私有的(private)的成员变量,使用get/set方法加以封装。Android里面用JavaBean,不要用get、set,因为get/set用到了反射机制,而android内存有限。所以成员变量设置为public,*可以用"."点运算符直接引用。**

package bean;

public class SmsBean {
    /*
    一般JavaBean是使用私有的(private)的成员变量,使用get/set方法加以封装。
    Android里面用JavaBean,不要用get、set,因为get/set用到了反射机制,而android内存有限。
    所以成员变量设置为public,可以用"."点运算符直接引用
     */
    public int id;
    public int number;
    public String date;
    public String msg;

   @Override
    public String toString() {
        return "SmsBean{" +
                "id=" + id +
                ", number=" + number +
                ", date='" + date + '\'' +
                ", msg='" + msg + '\'' +
                '}';
    }

}

假设这就是我们的所有短信数据,这里共五条,以列表的形式返回

package dao;

import java.util.ArrayList;
import java.util.List;

import bean.SmsBean;

public class SmsDao {
    public static List<SmsBean> getAllSms() {
        List<SmsBean> msgs = new ArrayList<>();
        for (int i = 10; i > 0; i--) {
            SmsBean msg = new SmsBean();
            msg.id = i;
            msg.msg = "请你吃饭,快出来!给你十秒 " + i;
            msg.number = 123456;
            msg.date = "2017/4/9";
            msgs.add(msg);
        }
        return msgs;
    }

}
package utils;

import android.content.Context;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;

import bean.SmsBean;
import dao.SmsDao;

public class SmsUtils {
    public static boolean backupSms(Context mContext) {
        // 获取所有sms
        List<SmsBean> allSms = SmsDao.getAllSms();

        //将数据以xml格式封装到一个StringBduilder中
        StringBuilder buffer = new StringBuilder();

        //封装一个声明头
        buffer.append("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>");
        //封装根节点
        buffer.append("<Smss>");
        //循环遍历list集合封装所有的短信
        for (SmsBean smsBean : allSms) {

            buffer.append("<Sms id = \"").append(smsBean.id).append("\">");

            buffer.append("<number>");
            buffer.append(smsBean.number);
            buffer.append("</number>");

            buffer.append("<msg>");
            buffer.append(smsBean.msg);
            buffer.append("</msg>");

            buffer.append("<date>");
            buffer.append(smsBean.date);
            buffer.append("</date>");

            buffer.append("</Sms>");

        }
        buffer.append("</Smss>");

        FileOutputStream out = null;
        BufferedWriter writer = null;
        // 需要文件名后缀,注意和SharedPreferences(不用后缀名,默认xml)区别开来
        try {
            out = mContext.openFileOutput("smss.xml", mContext.MODE_PRIVATE);
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write(buffer.toString());
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                    e.printStackTrace();
            }
        }
        return false;
    }
    // TODO: 恢复短信
    public static void restoreSms() {
    }
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    tools:context="com.example.backrestore.MainActivity">

    <Button
        android:id="@+id/backup_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/backup"
        android:padding="16dp"
        />

    <Button
        android:id="@+id/restore_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/restore"
        android:layout_below="@id/backup_message"
        android:padding="16dp"/>

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="恢复的短信数据"
        android:layout_below="@id/restore_message"/>

    <TextView
        android:id="@+id/restore_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/text"/>
</RelativeLayout>
package com.example.backrestore;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import utils.SmsUtils;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button btBackup;
    private Button btRestore;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        btBackup = (Button) findViewById(R.id.backup_message);
        btRestore = (Button) findViewById(R.id.restore_message);
        btBackup.setOnClickListener(this);
        btRestore.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.backup_message:
                boolean isBackup = SmsUtils.backupSms(mContext);
                if (isBackup) {
                    Toast.makeText(mContext, "备份成功", Toast.LENGTH_SHORT).show();
                } else  {
                    Toast.makeText(mContext, "备份失败", Toast.LENGTH_SHORT).show();

                }
                break;
            case R.id.restore_message:
                SmsUtils.restoreSms();
                break;
            default:
        }
    }
}

XmlSerializer序列化xml文件

backupSms()方法修改成即可

public static boolean backupSms_android(Context context){

        try {
            /* 使用Android的Xml包,可以直接Xml.newSerializer()和Xml.newPullParser()而不用
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlSerializer xmlSerializer = factory.newSerializer(); */

            //0.获取短信数据
            List<SmsBean> allSms = SmsDao.getAllSms();
            //1.通过Xml获取一个XmlSerializer对象
            XmlSerializer serializer = Xml.newSerializer();
            //2.设置XmlSerializer的一些参数,比如:设置xml写入到哪个文件中
            //os:xml文件写入流   encoding:流的编码
            serializer.setOutput(context.openFileOutput("backupsms2.xml", Context.MODE_PRIVATE), "utf-8");
            //3.序列化一个xml的声明头
            //encoding:xml文件的编码  standalone:是否独立
            serializer.startDocument("utf-8", true);
            //4.序列化一个根节点的开始节点
            //namespace:命名空间  name: 标签的名称。这里不指定就是null
            serializer.startTag(null, "Smss");
            //5.循环遍历list集合序列化一条条短信

            for (SmsBean smsBean : allSms) {
                serializer.startTag(null, "Sms");
                //name:属性的名称  value:属性值
                serializer.attribute(null, "id", smsBean.id+"");

                serializer.startTag(null, "number");
                //写一个标签的内容
                serializer.text(smsBean.number+"");
                serializer.endTag(null, "number");

                serializer.startTag(null, "msg");
                serializer.text(smsBean.msg);
                serializer.endTag(null, "msg");

                serializer.startTag(null, "date");
                serializer.text(smsBean.date);
                serializer.endTag(null, "date");

                serializer.endTag(null, "Sms");
            }

            //6.序列化一个根节点的结束节点
            serializer.endTag(null, "Smss");
            //7.将xml写入到文件中,完成xml的序列化,写入结束
            serializer.endDocument();
            return true;

        }catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

方便了不少,也不容易写错。

xml解析

解析方式

DOM Document Object Model 文档对象模型

核心思想: 把文档中所有内容都 封装成对象。

对象种类:

Document 整个(HTML,XML)文档
Element  文档中每一个标签都会被封装成Element对象
Attribute 标签上的每一个属性都会被封装成Attribute
Text     文档中,标签内的文本都会封装成Text对象
Common   注释,文档中的注释 ,会被封装成common.
  • Dom的优势:将文档结构(所有内容)都以对象的形式保留在了内存中。我们可以对内存中的(Dom树)进行增删改查操作并且操作很方便。
  • Dom的劣势:因为保留了全部文档内容,资源消耗比较大。

SAX解析思想

在读取xml文档时, 已经根据定义好的事件,对xml内容进行了筛选.解析完成后,内存中只保留了我们想要的内容。比较节约资源.。在资源比较匮乏的平台使用,比如手机。

缺点: 没有保留文档的结构, 无法进行增删改的操作。只能查询。

文档开始事件: startDocument
文档结束事件: endDocument
元素开始事件: startElement
元素结束事件: endElement
文本事件:    character

Pull解析

与Sax一样,都属于事件驱动的解析方式,相比Sax解析过程更加灵活。

  • SAX一旦开始解析就是从头读到尾,不解析完整个文档不会停.
  • pull解析较为灵活,是以事件为单位,手动向下继续.。如果获得到我们要找的内容,可以停止继续解析。

上面序列化后的xml大概长这样

<Smss>
    <Sms id="5">
        <numbe>123456</number>
        <msg>"请你吃饭,快出来!给你5秒 5"</msg>
        <date>"2017/4/10"</date>
    </Sms>

    <Sms id="5">
        <number>123456</number>
        <msg>"请你吃饭,快出来!给你5秒 5"</msg>
        <date>"2017/4/10"</date>
    </Sms>

    <Sms id="3">
        <number>123456</number>
        <msg>"请你吃饭,快出来!给你5秒 5"</msg>
        <date>"2017/4/10"</date>
    </Sms>
</Smss>

SmsUtils里面写恢复短信的方法

public static List<SmsBean> restoreSms(Context context) {
    List<SmsBean> smsBeanList = null;
    SmsBean sms = null;
    try {
        //1.通过Xml获取一个XmlPullParser对象
        XmlPullParser xmlPullParser = Xml.newPullParser();
        //2.设置XmlPullParser对象的参数,需要解析的是哪个xml文件,设置一个文件读取流
        xmlPullParser.setInput(context.openFileInput("smss.xml"), "utf-8");
        // 3. 获得当前事件类型
        int type = xmlPullParser.getEventType();
        // 4. 不是文档结尾就一直解析
        while (type != XmlPullParser.END_DOCUMENT) {
            // 获取当前元素/节点的名字
            String nodeName = xmlPullParser.getName();
            switch (type) {
                // 开始标签<abc>
                case XmlPullParser.START_TAG:
                    // 找到这个标签就新建列表
                    if (nodeName.equals("Smss")) {
                        smsBeanList = new ArrayList<>();
                        // 找到这个标签就新建sms
                    } else if (nodeName.equals("Sms")) {
                        sms = new SmsBean();
                        sms.id = Integer.parseInt(xmlPullParser.getAttributeValue(null, "id"));
                    } else if (nodeName.equals("number")) {
                        sms.number = Integer.parseInt(xmlPullParser.nextText());
                    } else if (nodeName.equals("msg")) {
                        sms.msg = xmlPullParser.nextText();
                    } else if (nodeName.equals("date")) {
                        sms.date = xmlPullParser.nextText();
                    }
                    break;

                // 结束标签</abc>
                case XmlPullParser.END_TAG:
                    // 如果结束标签是Sms则一条短信数据封装完毕封装
                    if (nodeName.equals("Sms")) {
                        smsBeanList.add(sms);
                    }
                    break;
                default:
            }
            // 下一个元素,返回事件类型
            type = xmlPullParser.next();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return smsBeanList;
}

再修改MainActivity,在case R.id.restore_message里处理逻辑

@Override
public void onClick(View v) {
    switch (v.getId()) {
    ...
        case R.id.restore_message:
            List<SmsBean> smss =  SmsUtils.restoreSms(mContext);
            if (smss != null) {
                restoreText.setText(smss.toString());
                Toast.makeText(mContext, "成功恢复"+smss.size()+"条短信", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(mContext, "恢复失败", Toast.LENGTH_SHORT).show();
            }

            break;
        default:
    }
}

看下结果,备份时候是5条。成功恢复!

Android常用布局、文件存储与权限、XML


by @sunhaiyu

2017.4.10