zoukankan      html  css  js  c++  java
  • 04_数据存储

    1. 理论概述

    Android数据存储方式:

    • SharedPreferences存储

    • 手机内部文件存储

    • 手机外部文件存储

    • sqlite数据库存储

    • 远程服务器存储

    2. 数据存储开发

    2.1 SharedPreferences存储

    说明

    • SP存储专门用来存储一些单一的小数据

    • 存储数据的类型:boolean,float,int,long,String

    • 数据保存的路径:/data/data/packageName/shared_prefs/yyy.xml

    • 可以设置数据只能是当前应用读取,而别的应用不可以

    • 应用卸载时会删除数据

    相关API

    • SharedPrefences:对应sp文件的接口

      • context.getSharedPreferences(String name,int mode):得到SP对象

        • name:文件名(不带.xml)

        • mode:生成的文件模式(是否是私有的,即其它应用是否可以访问)

      • Editor sp.edit():得到Editor对象

      • Xxx sp.getXxx(name, defaultValue):根据name得到对应的数据

    • Editor:能更新SP文件的接口

      • Editor put(name, value):保存一个键值对,没有真正保存到文件中

      • Editor remove(name)

      • commit():提交,数据真正保存到文件中 

    示例

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
    
        <EditText
            android:id="@+id/et_sp_key"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="存储的key" />
    
        <EditText
            android:id="@+id/et_sp_value"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="存储的value" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
    
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="save"
                android:text="保 存" />
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="read"
                android:text="读 取" />
        </LinearLayout>
    
    </LinearLayout>
    activity_sp.xml
    /**
     * 测试sp存储的界面
     */
    public class SpActivity extends Activity {
    
        private EditText et_sp_key;
        private EditText et_sp_value;
    
        private SharedPreferences sp;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_sp);
    
            et_sp_key = (EditText) findViewById(R.id.et_sp_key);
            et_sp_value = (EditText) findViewById(R.id.et_sp_value);
    
            //1. 得到sp对象
            sp = getSharedPreferences("atguigu", Context.MODE_PRIVATE);
        }
    
        public void save(View v) {
            //2. 得到editor对象
            SharedPreferences.Editor edit = sp.edit();
            //3. 得到输入的key/value
            String key = et_sp_key.getText().toString();
            String value = et_sp_value.getText().toString();
            //4. 使用editor保存key-value
            edit.putString(key, value).commit();
            //5. 提示
            Toast.makeText(this, "保存完成!", 0).show();
        }
    
        public void read(View v) {
            //1. 得到输入的key
            String key = et_sp_key.getText().toString();
            //2. 根据key读取对应的value
            String value = sp.getString(key, null);
            //3. 显示
            if(value==null) {
                Toast.makeText(this, "没有找到对应的value", 0).show();
            } else {
                et_sp_value.setText(value);
            }
        }
    }
    SpActivity.java
    package com.atguigu.l04_datastorage;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        // 测试sp存储
        public void onClickSP(View v) {
            startActivity(new Intent(this, SpActivity.class));
        }
    
        // 测试手机内部文件存储
        public void onClickIF(View v) {
            startActivity(new Intent(this, IFActivity.class));
        }
    
        // 测试手机外部文件存储
        public void onClickOF(View v) {
            startActivity(new Intent(this, OFActivity.class));
        }
    
        public void onClickDB(View v) {
    
        }
    
        public void onClickNW(View v) {
    
        }
    }
    MainActivity.java

    2.2 手机内部file存储

    说明

    • 应用运行需要的一些较大的数据或图片可以用文件保存在手机内部

    • 文件类型:任意

    • 数据保存的路径:/data/data/projectPackage/files/

    • 可以设置数据只能是当前应用读取,而别的应用不可以

    • 应用卸载时会删除此数据

    相关API

    • 读取文件

      • FileInputStream fis = openFileInput("logo.png");

    • 保存文件
      • FileOutputStream fos = openFileOutput("logo.png",MODE_PRIVATE);
    • 得到files文件夹对象
      • File filesDir = getFilesDir();  
    • 操作asserts下的文件
      • 得到Assetmanager:context.getAssets();    
      • 读取文件:InputStream open(filename);      
    • 加载图片文件
      • Bitmap BitmapFactory.decodeFile(String pathName)//.bmp/.png/.jpg
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
    
        <TextView
            android:id="@+id/textView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:text="1. 将asserts下的logo.png保存到手机内部
    2. 读取手机内部图片文件显示"
            android:textColor="#ff0000"
            android:textSize="15sp" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
    
            <Button
                android:id="@+id/btn_if_save"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="save"
                android:text="保 存" />
    
            <Button
                android:id="@+id/btn_if_read"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="read"
                android:text="读 取" />
        </LinearLayout>
    
        <ImageView
            android:id="@+id/iv_if"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher" />
    
    </LinearLayout>
    activity_if.xml
    package com.atguigu.l04_datastorage;
    
    import android.app.Activity;
    import android.content.Context;
    import android.content.res.AssetManager;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.Toast;
    
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    /**
     * 测试手机内部文件存储
     */
    public class IFActivity extends Activity {
    
        private ImageView iv_if;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_if);
    
            iv_if = findViewById(R.id.iv_if);
        }
    
        public void save(View view) throws IOException {
            //1.得到InputStream ->读取assets下的logo.png
            //得到AssetManager
            AssetManager manager = getAssets();
            //读取文件
            InputStream is = manager.open("logo.png");
            //2.得到OutputStream->/data/data/packageName/files/logo.png
            FileOutputStream fos = openFileOutput("logo.png", Context.MODE_PRIVATE);
            //3.边读边写
            byte[] buffer = new byte[1024];
            int len = 1;
            while((len = is.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }
            fos.close();
            is.close();
            //4.提示
            Toast.makeText(this,"保存完成",Toast.LENGTH_SHORT).show();
        }
    
        //  /data/data/packageName/files/logo.png
        public void read(View view){
            //1. 得到图片文件的路径  /data/data/packageName/files
            String filesPath = getFilesDir().getAbsolutePath();
            String imagePath = filesPath + "/logo.png";
            //2.读取加载图片文件得到bitmap对象
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            //3.将其设置到imageView中显示
            iv_if.setImageBitmap(bitmap);
        }
    }
    IFActivity.java

    2.3 手机外部file存储

    说明

    • 应用运行用到的数据文件(如图片)可以保存到sd卡中

    • 文件类型:任意

    • 数据保存的路径:

      • 路径1:/storage/sdcard/Android/data/packageName/files/

      • 路径2:/storage/sdcard/xx/

    • 路径1:其它应用可以访问,应用卸载时删除

    • 路径2:共它应用可以访问,应用卸载时不会删除

    • 必须保证sd卡挂载在手机上才能读写,否则不能操作

    相关API

    • Environment:操作SD卡的工具类

      • 得到SD卡的状态:Environment.getExternalStorageState()

      • 提到SD卡的路径:Environment.getExternalStorageDirectory()

      • SD卡可读写的挂载状态值:Enviroment.MEDIA_MOUNTED

    • context.getExternalFilesDir():

      • 得到/mnt/sdcard/Android/data/package_name/files/xxx.txt

    • 操作SD卡的权限:

      • android.permission.WRITE_EXTERNAL_STORAGE

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
    
        <EditText
            android:id="@+id/et_of_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="存储的文件名" />
    
        <EditText
            android:id="@+id/et_of_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="存储的文件内容" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
    
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="save"
                android:text="保 存" />
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="read"
                android:text="读 取" />
        </LinearLayout>
        
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
    
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="save2"
                android:text="保 存2" />
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="read2"
                android:text="读 取2" />
        </LinearLayout>
    
    </LinearLayout>
    activity_of.xml
    package com.atguigu.l04_datastorage;
    
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Environment;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.Toast;
    
    /**
     * 测试手机外部文件存储
     */
    public class OFActivity extends Activity {
    
        private EditText et_of_name;
        private EditText et_of_content;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_of);
            
            et_of_name = (EditText) findViewById(R.id.et_of_name);
            et_of_content = (EditText) findViewById(R.id.et_of_content);
        }
    
        public void save(View v) throws IOException {
            //1. 判断sd卡状态, 如果是挂载的状态才继续, 否则提示
            if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                //2. 读取输入的文件名/内容
                String fileName = et_of_name.getText().toString();
                String content = et_of_content.getText().toString();
                //3. 得到指定文件的OutputStream
                    //1).得到sd卡下的files路径
                String filesPath = getExternalFilesDir(null).getAbsolutePath();
                    //2).组成完整路径
                String filePath = filesPath+"/"+fileName;
                    //3). 创建FileOutputStream
                FileOutputStream fos = new FileOutputStream(filePath);
                //4. 写数据 
                fos.write(content.getBytes("utf-8"));
                fos.close();
                //5. 提示
                Toast.makeText(this, "保存完成", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "sd卡没有挂载", Toast.LENGTH_SHORT).show();
            }
            
        }
    
        public void read(View v) throws Exception {
            
            // 1. 判断sd卡状态, 如果是挂载的状态才继续, 否则提示
            if (Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED)) {
                // 2. 读取输入的文件名
                String fileName = et_of_name.getText().toString();
                // 3. 得到指定文件的InputStream
                    // 1).得到sd卡下的files路径
                String filesPath = getExternalFilesDir(null).getAbsolutePath();
                    // 2).组成完整路径
                String filePath = filesPath + "/" + fileName;
                    // 3). 创建FileInputStream
                FileInputStream fis = new FileInputStream(filePath);
                // 4. 读取数据, 成String
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = -1;
                while((len=fis.read(buffer))!=-1) {
                    baos.write(buffer, 0, len);
                }
                String content = baos.toString();
                
                // 5. 显示
                et_of_content.setText(content);
            } else {
                Toast.makeText(this, "sd卡没有挂载", Toast.LENGTH_SHORT).show();
            }
        }
    
        //  /storage/sdcard/atguigu/xxx.txt
        public void save2(View v) throws IOException {
            //1. 判断sd卡状态, 如果是挂载的状态才继续, 否则提示
            if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                //2. 读取输入的文件名/内容
                String fileName = et_of_name.getText().toString();
                String content = et_of_content.getText().toString();
                //3. 得到指定文件的OutputStream
                    //1). /storage/sdcard/
                String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();
                    //2). /storage/sdcard/atguigu/(创建文件夹)
                File file = new File(sdPath+"/atguigu");
                if(!file.exists()) {
                    file.mkdirs();//创建文件夹
                }
                    //3). /storage/sdcard/atguigu/xxx.txt
                String filePath = sdPath+"/atguigu/"+fileName;
                    //4). 创建输出流
                FileOutputStream fos = new FileOutputStream(filePath);
                //4. 写数据 
                fos.write(content.getBytes("utf-8"));
                fos.close();
                //5. 提示
                Toast.makeText(this, "保存完成", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "sd卡没有挂载", Toast.LENGTH_SHORT).show();
            }
        }
    
        public void read2(View v) throws Exception {
            // 1. 判断sd卡状态, 如果是挂载的状态才继续, 否则提示
            if (Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED)) {
                // 2. 读取输入的文件名
                String fileName = et_of_name.getText().toString();
                // 3. 得到指定文件的InputStream
                String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();
                String filePath = sdPath+"/atguigu/"+fileName;
                FileInputStream fis = new FileInputStream(filePath);
                // 4. 读取数据, 成String
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = -1;
                while((len=fis.read(buffer))!=-1) {
                    baos.write(buffer, 0, len);
                }
                String content = baos.toString();
                fis.close();
                // 5. 显示
                et_of_content.setText(content);
            } else {
                Toast.makeText(this, "sd卡没有挂载", Toast.LENGTH_SHORT).show();
            }
        }
    }
    OFActivity.java

    比较内部文件与外部文件存储?

    1. 存储空间的大小

    2. 是否是私有的

    3. 应用卸载是否自动删除

    2.4 SQLite数据库存储

    说明

    • 应用运行需要保存一系列有一定结构的数据,比如说公司员工信息

    • 文件类型:db

    • 数据保存的路径 :/data/data/projectPackage/databases/xxx.db

    • 默认情况下其它应用不能访问,当前应用可以通过ContentProvider提供其它应用操作

    • 应用卸载时会删除此数据

    SQLite数据库

    SQLite(http://www.sqlite.org),是一款轻型的关系型数据库服务器,移动设备的数据库存储都使用SQLite,它的特点:

    • 安装文件小:最小只有几百K,Android系统已经安装

    • 支持多操作系统:Android,WP,IOS,Windows,Linux等

    • 支持多语言:比如Java、PHP、C#等

    • 处理速度快:处理速度比Mysql,Oracle,SQLServer都要快(数据量不是特别大)

    • SQLite中的一个数据库就是一个.db文件(本质上.db的后缀都可以不指定)

    SQLite数据库命令行

    • adb shell 进入系统根目录

    • cd data/data/…/databases:进入包含数据库文件的文件夹下

    • sqlite3 contacts2.db:使用sqlite3命令连接指定的数据库文件,进入连接模式

    • help:查看命令列表

    • tables:查看所有表的列表

    • 执行 insert/delete/update/select语句

    • exit:退出数据库连接模式

    • Ctrl+C:直接退出shell模式

    数据类型

    SQLite支持的数据类型与MySQL相似,常用的数据类型

    • INT/INTEGER:整数

    • FLOAT/DOUBLE:小数

    • CHAR/VARCHAR/TEXT:字符串文本

    • BLOB:文件

    • DATE/DATETIME:日期/日期时间

    SQLite建表

    SQLite操作数据库的sql语句基本与mysql一样,但需要注意下面2点:

    • 最大的不同在于创建表时可以不用指定字段类型,sqlite可以适时的自动转换,但除varchar类型外最好指定类型

    • sqlite中的主键名称建议使用_id

    create table employee(
      _id integer primary key autoincrement,/*主键,自增长*/
      name varchar,                         /* 字符串*/
      salary double,                        /* 小数 */
      birthday date                       /* 日期,可直接插入日期格式字符串*/
    )
    sqlite建表

    SQLite的CRUD语句

    /*插入*/
    INSERT INTO employee(name,salary,birthday) VALUES('Tom',8000,'1988-09-21');
    /*删除*/
    DELETE FROM employee WHERE _id=2
    /*更新*/
    UPDATE employee SET name='Jack',salary=salary+1000 WHERE _id=1
    /*查找*/
    SELECT * FROM employee WHERE _id=3
    View Code

    相关API

    • SQLiteOpenHepler:数据库操作的抽象帮助类
      • SQLiteOpenHelper(Context context,String name, CursorFactory

      • factory, int version):构造方法,指定数据库文件名和版本号

      • abstract void onCreate(SQLiteDatabases db):用于创建表

      • abstract void onUpgrade():用于版本更新

      • SqliteDatabase getReadableDatabase():得到数据库连接

    • sqliteDatabase:代表与数据库的连接的类

      • long insert():用于执行insert SQL,返回id值

      • int update():用于执行update SQL

      • int delete():用于执行delete SQL

      • Cursor query():用于执行select SQL,返回包含查询结果数据的Cursor

      • void execSql(sql):执行sql语句

      • beginTransaction():开启事务

      • setTransactionSuccessful():设置事务是成功的

      • endTransaction():结束事务,可能提交事务或回滚事务

      • openDatabase(String path, CursorFactory factory, int flags):得到数据库连接

    • Cursor:包含所有查询结果记录的结果集对象(光标,游标)

      • int getCount():匹配的总记录数

      • boolean moveToNext():将游标移动到下一条记录的前面

      • Xxx getXxx(columnIndex):根据字段下标得到对应值

      • int getColumnIndex(columnname):根据字段名得到对应的下标

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="testCreateDB"
            android:text="Create DB" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="testUpdateDB"
            android:text="Update DB" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="testInsert"
            android:text="Insert" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="testUpdate"
            android:text="Update" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="testDelete"
            android:text="Delete" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="testQuery"
            android:text="query" />
    
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="testTransaction"
            android:text="Test Transaction" />
    
    </LinearLayout>
    activity_db.xml
    package com.atguigu.l04_datastorage;
    
    import android.app.Activity;
    import android.content.ContentValues;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Toast;
    
    /**
     * 测试Sqlite数据库存储
     * 
     */
    public class DBActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_db);
        }
    
        /*
         * 创建库
         */
        public void testCreateDB(View v) {
            DBHelper dbHelper = new DBHelper(this, 1);
            //获取连接
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            
            Toast.makeText(this, "创建数据库", 0).show();
        }
    
        /*
         * 更新库
         */
        public void testUpdateDB(View v) {
            DBHelper dbHelper = new DBHelper(this, 2);
            //获取连接
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            
            Toast.makeText(this, "更新数据库", 0).show();
        }
    
        /*
         * 添加记录
         */
        public void testInsert(View v) {
            //1. 得到连接
            DBHelper dbHelper = new DBHelper(this, 2);
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            //2. 执行insert  insert into person(name, age) values('Tom', 12)
            ContentValues values = new ContentValues();
            values.put("name", "Tom");
            values.put("age", 12);
            long id = database.insert("person", null, values);
            //3. 关闭
            database.close();
            //4. 提示
            Toast.makeText(this, "id="+id, 1).show();
        }
    
        /*
         * 更新
         */
        public void testUpdate(View v) {
            DBHelper dbHelper = new DBHelper(this, 2);
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            //执行update  update person set name=Jack, age=13 where _id=4
            ContentValues values = new ContentValues();
            values.put("name", "jack");
            values.put("age", 13);
            int updateCount = database.update("person", values , "_id=?", new String[]{"4"});
            database.close();
            Toast.makeText(this, "updateCount="+updateCount, 1).show();
        }
    
        /*
         * 删除
         */
        public void testDelete(View v) {
            // 1. 得到连接
            DBHelper dbHelper = new DBHelper(this, 2);
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            // 2. 执行delete delete from person where _id=2
            int deleteCount = database.delete("person", "_id=2", null);
            // 3. 关闭
            database.close();
            // 4. 提示
            Toast.makeText(this, "deleteCount=" + deleteCount, 1).show();
        }
    
        /*
         * 查询
         */
        public void testQuery(View v) {
            // 1. 得到连接
            DBHelper dbHelper = new DBHelper(this, 2);
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            // 2. 执行query select * from person
            Cursor cursor = database.query("person", null, null, null, null, null, null);
            //cursor = database.query("person", null, "_id=?", new String[]{"3"}, null, null, null);
            //得到匹配的总记录数
            int count = cursor.getCount();
            
            //取出cursor中所有的数据
            while(cursor.moveToNext()) {
                //_id
                int id = cursor.getInt(0);
                //name
                String name = cursor.getString(1);
                //age
                int age = cursor.getInt(cursor.getColumnIndex("age"));
                Log.e("TAG", id+"-"+name+"-"+age);
            }
            // 3. 关闭
            cursor.close();
            database.close();
            // 4. 提示
            Toast.makeText(this, "count=" + count, 1).show();
        }
    
        /*
         * 测试事务处理
         * update person set age=16 where _id=1
         * update person set age=17 where _id=3
         * 
         * 一个功能中对数据库进行的多个操作: 要就是都成功要就都失败
         * 事务处理的3步:
         * 1. 开启事务(获取连接后)
         * 2. 设置事务成功(在全部正常执行完后)
         * 3. 结束事务(finally中)
         */
        public void testTransaction(View v) {
            
            SQLiteDatabase database = null;
            try{
                DBHelper dbHelper = new DBHelper(this, 2);
                database = dbHelper.getReadableDatabase();
                
                //1. 开启事务(获取连接后)
                database.beginTransaction();
                
                //执行update  update person set age=16 where _id=1
                ContentValues values = new ContentValues();
                values.put("age", 16);
                int updateCount = database.update("person", values , "_id=?", new String[]{"1"});
                Log.e("TAG", "updateCount="+updateCount);
                
                //出了异常
                boolean flag = true;
                if(flag) {
                    throw new RuntimeException("出异常啦!!!");
                }
                
                //执行update  update person set age=17 where _id=3
                values = new ContentValues();
                values.put("age", 17);
                int updateCount2 = database.update("person", values , "_id=?", new String[]{"3"});
                Log.e("TAG", "updateCount2="+updateCount2);
                
                //2. 设置事务成功(在全部正常执行完后)
                database.setTransactionSuccessful();
                
            } catch(Exception e) {
                e.printStackTrace();
                Toast.makeText(this, "出异常啦!!!", 1).show();
            } finally {
                //3. 结束事务(finally中)
                if(database!=null) {
                    database.endTransaction();
                    database.close();
                }
            }
            
        }
    
    }
    DBActivity
    package com.atguigu.l04_datastorage;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    import android.util.Log;
    
    /**
     * 数据库操作的帮助类
     */
    public class DBHelper extends SQLiteOpenHelper {
    
        public DBHelper(Context context,int version) {
            super(context, "atguigu.db", null, version);
        }
    
        /**
         * 什么时候才会创建数据库文件?
         *     1). 数据库文件不存在
         *  2). 连接数据库
         * 
         * 什么时候调用?
         *     当数据库文件创建时调用(1次)
         * 在此方法中做什么?
         *     建表
         *     插入一些初始化数据
         */
        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.e("TAG", "DBHelper onCreate()");
            //建表
            String sql = "create table person(_id integer primary key autoincrement, name varchar,age int)";
            db.execSQL(sql);
            //插入一些初始化数据
            db.execSQL("insert into person (name, age) values ('Tom1', 11)");
            db.execSQL("insert into person (name, age) values ('Tom2', 12)");
            db.execSQL("insert into person (name, age) values ('Tom3', 13)");
        }
    
        //当传入的版本号大于数据库的版本号时调用
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.e("TAG", "DBHelper onUpgrade()");
        }
    
    }
    DBHelper.java

    Android中的Junit测试

    1. 添加配置信息

    <application>
        <!-- 使用android测试包 -->
        <uses-library android:name="android.test.runner" />
    </application>
    
    <!-- android:targetPackage的值应与manifest的package的值一致 -->
    <instrumentation
          android:name="android.test.InstrumentationTestRunner"
          android:targetPackage="com.atguigu.app04_sqlite" />
    View Code

    2. 编写测试类 

    class StudentTest extends AndroidTestCase

    2.5 远程服务器存储

    说明

    • 对于联网的APP来说,可能需要通过请求向服务器提交请求数据,也可能需要从服务器端获取数据显示
    • 如何编码实现客户端与服务器端的交互呢?

        - JDK内置的原生API

          HttpUrlConnection

        - Android内置的包装API

          HttpClient  浏览器

      - 异步网络请求框架

      • Volley
      • Xutils
    • 注意:
      • 访问网络,需要声明权限:android.permission.INTERNET
      • 访问网络的程序必须在分线程执行

    使用HttpConnection

    1. URL : 包含请求地址的类

      • URL(path) : 包含请求路径的构造方法

      • openConnection() : 得到连接对象

    2. HttpURLConnection : 代表与服务器连接的类

      • setMethod(“GET/POST”) : 设置请求方式

      • setConnectTimeout(time) : 设置连接超时时间, 单位为ms

      • setReadTimeout(time): 设置读取服务器返回数据的时间

       

      • connect() : 连接服务器

       

      • int getResponseCode(): 得到服务器返回的结果码

      • Int getContentLength() : 得到服务器返回数据的长度(字节)

      • getOutputStream() : 返回一个指向服务器端的数据输出流

      • getInputStream() : 返回一个从服务器端返回的数据输入流

      • disconnect() : 断开连接

    使用HttpClient

    1. HttpClient/DefaultHttpClient : 能提交请求的客户端对象

      • HttpResponse execute (HttpUriRequest request) 执行包含请求数据的请求对象, 返回包含响应数据的响应对象

      • HttpParams getParams() 得到包含请求参数的对象

    2. HttpConnectionParams : 设置请求参数的工具类

      • static setConnectionTimeout(params, time) : 设置获取连接的超时时间

      • static setSoTimeout(params, time): 设置读取数据的超时时间

    3. HttpGet : Get请求

      • HttpGet(String path) : 包含请求路径的构造方法

    4. HttpPost : Post请求

      • HttpPost(String path) : 包含请求路径的构造方法

      • setEntity(HttpEntity entity) : 设置请求体

    1. NameValuePair/BasicNameValuePair : 包含参数键值对

      • BasicNameValuePair (String name, String value)

    2. HttpResponse : 服务器返回的响应

      • getStatusLine() : 得到响应状态行, 从而得到状态码

      • getEntity() : 得到响应体数据对象

    3. EntityUtils : 解析HttpEntity的工具类

      • toString(httpEntity): 解析响应体, 得其内容字符串

    4. 关闭连接, 释放资源:

      • client.getConnectionManager().shutdown();

    Volley

    • Volley是Google 2013年的 I/O大会 上,发布了的一个框架

    • Volley是Android上的网络通信库,能使网络通信更快,更简单,更健壮

    • Volley特别适合数据量不大但是通信频繁的场景: 带图片的列表

    Volley相关API

    1. RequestQueue : 请求队列, 会自动执行队列中的请求

      • Volley. newRequestQueue(context) : 创建一个请求队列

      • addReqeust(Request reqeust) : 将请求添加到请求队列

    2. Request<T>: 代表请求的接口

      • StringRequest : 获取字符串结果的请求

      • JsonRequest : 获取Json数据结果的请求

      • ImageRequest : 获取图片结果的请求

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >
    
        <EditText
            android:id="@+id/et_network_url"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="textUri"
            android:text="@string/url" >
        </EditText>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="1. 测试HttpUrlConnection"
            android:textSize="20dp" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
    
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="testConnectionGet"
                android:text="GET请求" />
    
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="testConnectionPost"
                android:text="POST请求" />
        </LinearLayout>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="2. 测试HttpClient"
            android:textSize="20dp" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
    
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="testClientGet"
                android:text="GET请求" />
    
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="testClientPost"
                android:text="POST请求" />
        </LinearLayout>
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:text="3. 测试Volley框架"
            android:textSize="20dp" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
    
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="testVolleyGet"
                android:text="GET请求" />
    
            <Button
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="testVolleyPost"
                android:text="POST请求" />
        </LinearLayout>
    
        <EditText
            android:id="@+id/et_network_result"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:hint="用来显示网络请求返回的结果数据" >
        </EditText>
    
    </LinearLayout>
    activity_network.xml
    package com.atguigu.l04_datastorage;
    
    import java.io.ByteArrayOutputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.http.HttpEntity;
    import org.apache.http.HttpResponse;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.params.HttpConnectionParams;
    import org.apache.http.params.HttpParams;
    import org.apache.http.util.EntityUtils;
    
    import com.android.volley.Request.Method;
    import com.android.volley.AuthFailureError;
    import com.android.volley.RequestQueue;
    import com.android.volley.Response;
    import com.android.volley.toolbox.StringRequest;
    import com.android.volley.toolbox.Volley;
    
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.EditText;
    
    public class NetworkActivity extends Activity {
    
        private EditText et_network_url;
        private EditText et_network_result;
        private RequestQueue queue;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_network);
            
            et_network_url = (EditText) findViewById(R.id.et_network_url);
            et_network_result = (EditText) findViewById(R.id.et_network_result);
            
            queue = Volley.newRequestQueue(this);
        }
    
        /*
         * 使用httpUrlConnection提交get请求
         */
        /*
            1. 显示ProgressDialog
            2. 启动分线程
            3. 在分线程, 发送请求, 得到响应数据
                1). 得到path, 并带上参数name=Tom1&age=11
                2). 创建URL对象
                3). 打开连接, 得到HttpURLConnection对象
                4). 设置请求方式,连接超时, 读取数据超时
                5). 连接服务器
                6). 发请求, 得到响应数据
                    得到响应码, 必须是200才读取
                    得到InputStream, 并读取成String
                7). 断开连接
            4. 在主线程, 显示得到的结果, 移除dialog
         */
        public void testConnectionGet(View v) {
            //1. 显示ProgressDialog
            final ProgressDialog dialog = ProgressDialog.show(this, null, "正在请求中...");
            //2. 启动分线程
            new Thread(){
                //3. 在分线程, 发送请求, 得到响应数据
                public void run() {
                    try {
                        //1). 得到path, 并带上参数name=Tom1&age=11
                        String path = et_network_url.getText().toString()+"?name=Tom1&age=11";
                        //2). 创建URL对象
                        URL url = new URL(path);
                        //3). 打开连接, 得到HttpURLConnection对象
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        //4). 设置请求方式,连接超时, 读取数据超时
                        connection.setRequestMethod("GET");
                        connection.setConnectTimeout(5000);
                        connection.setReadTimeout(6000);
                        //5). 连接服务器
                        connection.connect();
                        //6). 发请求, 得到响应数据
                            //得到响应码, 必须是200才读取
                        int responseCode = connection.getResponseCode();
                        if(responseCode==200) {
                            //得到InputStream, 并读取成String
                            InputStream is = connection.getInputStream();
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            byte[] buffer = new byte[1024];
                            int len = -1;
                            while((len=is.read(buffer))!=-1) {
                                baos.write(buffer, 0, len);
                            }
                            final String result = baos.toString();
                            
                            baos.close();
                            is.close();
                            
                            //4. 在主线程, 显示得到的结果, 移除dialog
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    et_network_result.setText(result);
                                    dialog.dismiss();
                                }
                            });
                        }
                        //7). 断开连接
                        connection.disconnect();
                    } catch (Exception e) {
                        e.printStackTrace();
                        //如果出了异常要移除dialog
                        dialog.dismiss();
                    }
                }
            }.start();
        }
    
        /*
         * 使用httpUrlConnection提交post请求
         */
        /*
            1. 显示ProgressDialog
            2. 启动分线程
            3. 在分线程, 发送请求, 得到响应数据
                1). 得到path
                2). 创建URL对象
                3). 打开连接, 得到HttpURLConnection对象
                4). 设置请求方式,连接超时, 读取数据超时
                5). 连接服务器
                6). 发请求, 得到响应数据
                    得到输出流, 写请求体:name=Tom1&age=11
                    得到响应码, 必须是200才读取
                    得到InputStream, 并读取成String
                7). 断开连接
            4. 在主线程, 显示得到的结果, 移除dialog
         */
        public void testConnectionPost(View v) {
            //1. 显示ProgressDialog
            final ProgressDialog dialog = ProgressDialog.show(this, null, "正在加载中...");
            //2. 启动分线程
            new Thread(new Runnable() {
                //3. 在分线程, 发送请求, 得到响应数据
                @Override
                public void run() {
                    try {
                        //1). 得到path
                        String path = et_network_url.getText().toString();
                        //2). 创建URL对象
                        URL url = new URL(path);
                        //3). 打开连接, 得到HttpURLConnection对象
                        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        //4). 设置请求方式,连接超时, 读取数据超时
                        connection.setRequestMethod("POST");
                        connection.setConnectTimeout(5000);
                        connection.setReadTimeout(5000);
                        //5). 连接服务器
                        connection.connect();
                        //6). 发请求, 得到响应数据
                            //得到输出流, 写请求体:name=Tom1&age=11
                        OutputStream os = connection.getOutputStream();
                        String data = "name=Tom2&age=12";
                        os.write(data.getBytes("utf-8"));
                            //得到响应码, 必须是200才读取
                        int responseCode = connection.getResponseCode();
                        if(responseCode==200) {
                            //得到InputStream, 并读取成String
                            InputStream is = connection.getInputStream();
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            byte[] buffer = new byte[1024];
                            int len = -1;
                            while((len=is.read(buffer))!=-1) {
                                baos.write(buffer, 0, len);
                            }
                            final String result = baos.toString();
                            
                            baos.close();
                            is.close();
                            
                            //4. 在主线程, 显示得到的结果, 移除dialog
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    et_network_result.setText(result);
                                    dialog.dismiss();
                                }
                            });
                        }
                        os.close();
                        //7). 断开连接
                        connection.disconnect();
                    } catch (Exception e) {
                        e.printStackTrace();
                        dialog.dismiss();
                    }
                }
            }).start();
        }
    
        /*
         * 使用httpClient提交get请求
         */
        public void testClientGet(View v) {
            //1. 显示ProgressDialog
            final ProgressDialog dialog = ProgressDialog.show(this, null, "正在请求中...");
            //2. 启动分线程
            new Thread(){
                //3. 在分线程, 发送请求, 得到响应数据
                public void run() {
                    try {
                        //1). 得到path, 并带上参数name=Tom1&age=11
                        String path = et_network_url.getText().toString()+"?name=Tom3&age=13";
                        
                        //2). 创建HttpClient对象
                        HttpClient httpClient = new DefaultHttpClient();
                        //3). 设置超时
                        HttpParams params = httpClient.getParams();
                        HttpConnectionParams.setConnectionTimeout(params, 5000);
                        HttpConnectionParams.setSoTimeout(params, 5000);
                        //4). 创建请求对象
                        HttpGet request = new HttpGet(path);
                        //5). 执行请求对象, 得到响应对象
                        HttpResponse response = httpClient.execute(request);
                        
                        int statusCode = response.getStatusLine().getStatusCode();
                        if(statusCode==200) {
                            //6). 得到响应体文本
                            HttpEntity entity = response.getEntity();
                            final String result = EntityUtils.toString(entity);
                            //4. 要主线程, 显示数据, 移除dialog
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    et_network_result.setText(result);
                                    dialog.dismiss();
                                }
                            });
                        }
                        //7). 断开连接
                        httpClient.getConnectionManager().shutdown();
                    } catch (Exception e) {
                        e.printStackTrace();
                        //如果出了异常要移除dialog
                        dialog.dismiss();
                    }
                }
            }.start();
        }
    
        /*
         * 使用httpClient提交post请求
         */
        public void testClientPost(View v) {
            //1. 显示ProgressDialog
            final ProgressDialog dialog = ProgressDialog.show(this, null, "正在请求中...");
            //2. 启动分线程
            new Thread(){
                //3. 在分线程, 发送请求, 得到响应数据
                public void run() {
                    try {
                        //1). 得到path
                        String path = et_network_url.getText().toString();
                        
                        //2). 创建HttpClient对象
                        HttpClient httpClient = new DefaultHttpClient();
                        //3). 设置超时
                        HttpParams params = httpClient.getParams();
                        HttpConnectionParams.setConnectionTimeout(params, 5000);
                        HttpConnectionParams.setSoTimeout(params, 5000);
                        //4). 创建请求对象
                        HttpPost request = new HttpPost(path);
                        //设置请求体
                        List<BasicNameValuePair> parameters = new ArrayList<BasicNameValuePair>();
                        parameters.add(new BasicNameValuePair("name", "Tom4"));
                        parameters.add(new BasicNameValuePair("age", "14"));
                        HttpEntity entity = new UrlEncodedFormEntity(parameters);
                        request.setEntity(entity);
                        
                        //5). 执行请求对象, 得到响应对象
                        HttpResponse response = httpClient.execute(request);
                        
                        int statusCode = response.getStatusLine().getStatusCode();
                        if(statusCode==200) {
                            //6). 得到响应体文本
                            entity = response.getEntity();
                            final String result = EntityUtils.toString(entity);
                            //4. 要主线程, 显示数据, 移除dialog
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    et_network_result.setText(result);
                                    dialog.dismiss();
                                }
                            });
                        }
                        //7). 断开连接
                        httpClient.getConnectionManager().shutdown();
                    } catch (Exception e) {
                        e.printStackTrace();
                        //如果出了异常要移除dialog
                        dialog.dismiss();
                    }
                }
            }.start();
        }
    
        /*
         * 使用Volley提交get请求
         */
        /*
         1. 创建请求队列对象(一次)
         2. 创建请求对象StringRequest
         3. 将请求添加到队列中
         */
        public void testVolleyGet(View v) {
            
            final ProgressDialog dialog = ProgressDialog.show(this, null, "正在请求中...");
            
            //创建请求对象StringRequest
            String path = et_network_url.getText().toString()+"?name=Tom5&age=15";
            StringRequest request = new StringRequest(path, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {//在主线程执行
                    et_network_result.setText(response);
                    dialog.dismiss();
                }
            }, null);
            //将请求添加到队列中
            queue.add(request);
        }
    
        /*
         * 使用Volley提交post请求
         */
        public void testVolleyPost(View v) {
            final ProgressDialog dialog = ProgressDialog.show(this, null, "正在请求中...");
            
            //创建请求对象StringRequest
            String path = et_network_url.getText().toString();
            StringRequest request = new StringRequest(Method.POST, path, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    et_network_result.setText(response);
                    dialog.dismiss();
                }
            }, null){
                //重写此方法返回参数的map作为请求体
                @Override
                protected Map<String, String> getParams() throws AuthFailureError {
                    Map<String, String> map = new HashMap<String, String>();
                    map.put("name", "Tom6");
                    map.put("age", "16");
                    return map;
                }
            };
            //将请求添加到队列中
            queue.add(request);
        }
    
    }
    NetworkActivity.java
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <string name="app_name">L04_DataStorage</string>
        <string name="hello_world">Hello world!</string>
        <string name="title_activity_sp">SpActivity</string>
        <string name="title_activity_if">IFActivity</string>
        <string name="title_activity_of">OFActivity</string>
        <string name="title_activity_db">DBActivity</string>
        <string name="title_activity_network">NetworkActivity</string>
        <string name="url">http://192.168.10.165:8080/Web_Server/index.jsp</string>
    </resources>
    strings.xml

    3. 应用练习

    3.1 修改防盗名称

    • 功能描述:

      1. 长按手机防盗, 显示修改的Dialog

      2. 通过dialog修改手机防盗名称

    • 关键技术点:

      1. SharedPreferences的使用

      2. AlertDialog的使用

      3. GridView+BaseAdapter的使用

    3.2 手机黑名单管理

    黑名单的管理

    • 功能描述:

      1. 黑名单添加

      2. 显示所有黑名单列表

      3. 删除指定黑名单

      4. 修改黑名单

    • 关键技术点:

      1. SQLite数据库的操作

      2. ListView列表显示

      3. AlertDialog的使用

      4. contextMenu的使用

    1. 界面布局
    	ListView
    2. DBHelper
    	数据库
    	表
    3. 实体类
    4. DAO并单元测试
    5. 显示列表
    6. 添加
    	1. 显示添加的dialog(带输入框)
    	2. 在确定的回调方法实现: 
    		1). 保存数据表中
    		2). 保存数据到List
    		3). 通知更新列表
    	问题1: 新添加的没有显示在第一行
    		add到集合中的第一位
    	问题2: 初始显示的列表顺序不对
    		查询根据_id倒序
    7. 删除
    	1. 显示ContextMenu
    	2. 响应对item的选择
    		1). 删除数据表对应的数据
    		2). 删除List对应的数据
    		3). 通知更新列表 
    	问题: 如何得到长按的position?	
    8. 更新
    	1. 显示更新的Dialog
    	2. 点击确定的响应
    		1). 更新数据表对应的数据
    		2). 更新List对应的数据
    		3). 通知更新列表 
    
    9. 使用ListActivity优化功能
    	1. extends ListActivity
    	2. 布局文件中的<ListView>的id必须是系统定义的id: list
    	3. 如果想在没有数据时显示一个提示文本, 可以在布局中定义 一个<TextView>(id必须为empty)
    
    /*
    一个功能的主要工作
    */
    	1. 内存的操作(集合)
    	2. 存储的操作(sp/数据库/文件)
    	3. 界面的操作(列表)
    		
    		
    思路
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
    
        <ListView
            android:id="@android:id/list"
            android:layout_width="match_parent"
            android:layout_height="0dp" 
            android:layout_weight="1">
        </ListView>
    
        <TextView
            android:id="@android:id/empty"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="还没有一个黑名单"
            android:gravity="center"/>
        
        <Button
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="添  加" 
            android:onClick="add"/>
    
    </LinearLayout>
    activity_main.xml
    package com.atguigu.app04_sqlite;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    import android.util.Log;
    
    /**
     * 数据库操作的帮助类
     *
     */
    public class DBHelper extends SQLiteOpenHelper {
    
        public DBHelper(Context context) {
            super(context, "atguigu.db", null, 1);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.i("TAG", "DBHelper onCreate()");
            //创建表
            db.execSQL("create table black_number(_id integer primary key autoincrement, number varchar)");
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    
    }
    DBHelper.java

    实体类BlackNumber.java

    package com.atguigu.app04_sqlite;
    
    /**
     * black_number表对应的实体类
     */
    public class BlackNumber {
    
        private int id;
        private String number;
    
        public BlackNumber(int id, String number) {
            super();
            this.id = id;
            this.number = number;
        }
    
        public BlackNumber() {
            super();
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getNumber() {
            return number;
        }
    
        public void setNumber(String number) {
            this.number = number;
        }
    
        @Override
        public String toString() {
            return "BlackNumber [id=" + id + ", number=" + number + "]";
        }
    
    }
    BlackNumber.java
    package com.atguigu.app04_sqlite;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.util.Log;
    
    /**
     * 操作black_number表的DAO类
     *
     */
    public class BlackNumberDao {
    
        private DBHelper dbHelper;
        
        public BlackNumberDao(Context context) {
            dbHelper = new DBHelper(context);
        }
        /**
         * 添加一条记录
         * @param blackNumber
         */
        public void add(BlackNumber blackNumber) {
            //1. 得到连接
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            //2. 执行insert insert into black_number (number) values(xxx)
            ContentValues values = new ContentValues();
            values.put("number", blackNumber.getNumber());
            long id = database.insert("black_number", null, values);
            Log.i("TAG", "id="+id);
            
            //设置id
            blackNumber.setId((int) id);
            //3. 关闭
            database.close();
        }
        
        /**
         * 根据id删除一条记录
         */
        public void deleteById(int id) {
            //1. 得到连接
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            //2. 执行delete delete from black_number where _id=id
            int deleteCount = database.delete("black_number", "_id=?", new String[]{id+""});
            Log.i("TAG", "deleteCount="+deleteCount);
            //3. 关闭
            database.close();
        }
        
        /**
         * 更新一条记录
         */
        public void update(BlackNumber blackNumber) {
            //1. 得到连接
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            //2. 执行update update black_number set number=xxx where _id=id
            ContentValues values = new ContentValues();
            values.put("number", blackNumber.getNumber());
            int updateCount = database.update("black_number", values , "_id="+blackNumber.getId(), null);
            Log.i("TAG", "updateCount="+updateCount);
            //3. 关闭
            database.close();
        }
        
        /**
         * 查询所有记录封装成List<BLackNumber>
         */
        public List<BlackNumber> getAll() {
            
            List<BlackNumber> list = new ArrayList<BlackNumber>();
            //1. 得到连接
            SQLiteDatabase database = dbHelper.getReadableDatabase();
            //2. 执行query select * from black_number
            Cursor cursor = database.query("black_number", null, null, null, null, null, "_id desc");
            //3. 从cursor中取出所有数据并封装到List中
            while(cursor.moveToNext()) {
                //id
                int id = cursor.getInt(0);
                //number
                String number = cursor.getString(1);
                list.add(new BlackNumber(id, number));
            }
            //4. 关闭
            cursor.close();
            database.close();
            
            return list;
        }
    }
    BlackNumberDao.java

    配置android测试包

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.atguigu.app04_sqlite"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk
            android:minSdkVersion="18"
            android:targetSdkVersion="18" />
    
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
    
            <!-- 使用android测试包 -->
            <uses-library android:name="android.test.runner" />
    
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
        <!-- android:targetPackage的值应与manifest的package的值一致 -->
        <instrumentation
            android:name="android.test.InstrumentationTestRunner"
            android:targetPackage="com.atguigu.app04_sqlite" />
    
    </manifest>
    AndroidManifest.xml

    测试用例

    package com.atguigu.app04_sqlite.test;
    
    import java.util.List;
    
    import com.atguigu.app04_sqlite.BlackNumber;
    import com.atguigu.app04_sqlite.BlackNumberDao;
    
    import android.test.AndroidTestCase;
    import android.util.Log;
    
    /**
     * BlackNumberDao的单元测试类
     *
     */
    public class BlackNumberDaoTest extends AndroidTestCase {
    
        public void testAdd() {
            // 创建dao对象
            BlackNumberDao dao = new BlackNumberDao(getContext());
            // 调用方法
            dao.add(new BlackNumber(-1, "123"));
        }
    
        public void testGetAll() {
            // 创建dao对象
            BlackNumberDao dao = new BlackNumberDao(getContext());
            // 调用方法
            List<BlackNumber> list = dao.getAll();
            Log.i("TAG", list.toString());
        }
    
        public void testUpdate() {
            // 创建dao对象
            BlackNumberDao dao = new BlackNumberDao(getContext());
            // 调用方法
            dao.update(new BlackNumber(2, "321"));
        }
    
        public void testDeleteById() {
            // 创建dao对象
            BlackNumberDao dao = new BlackNumberDao(getContext());
            // 调用方法
            dao.deleteById(2);
        }
    }
    BlackNumberDaoTest.java
    package com.atguigu.app04_sqlite;
    
    import java.util.List;
    
    import android.app.AlertDialog;
    import android.app.ListActivity;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.view.ContextMenu;
    import android.view.ContextMenu.ContextMenuInfo;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.AdapterView.AdapterContextMenuInfo;
    import android.widget.BaseAdapter;
    import android.widget.EditText;
    import android.widget.ListView;
    import android.widget.TextView;
    
    public class MainActivity extends ListActivity {
    
        private ListView lv_main;
        private BlackNumberAdapter adapter;
        private BlackNumberDao dao;
        private List<BlackNumber> data;
        private int position;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            lv_main = getListView();
            adapter = new BlackNumberAdapter();
            dao = new BlackNumberDao(this);
            data = dao.getAll();
            
            lv_main.setAdapter(adapter);
            
            //给listView设置创建contextMenu的监听
            lv_main.setOnCreateContextMenuListener(this);
        }
        
        @Override
        public void onCreateContextMenu(ContextMenu menu, View v,
                ContextMenuInfo menuInfo) {
            super.onCreateContextMenu(menu, v, menuInfo);
            //添加2个item
            menu.add(0, 1, 0, "更新");
            menu.add(0, 2, 0, "删除");
            
            //得到长按的position
            AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
            position = info.position;
        }
        
        @Override
        public boolean onContextItemSelected(MenuItem item) {
            
            //得到对应的BlackNumber对象
            BlackNumber blackNumber = data.get(position);
            switch (item.getItemId()) {
            case 1://更新
                //1. 显示更新的Dialog
                showUpdateDialog(blackNumber);
                break;
            case 2://删除
                //1). 删除数据表对应的数据
                dao.deleteById(blackNumber.getId());
                //2). 删除List对应的数据
                data.remove(position);
                //3). 通知更新列表
                adapter.notifyDataSetChanged();
                break;
    
            default:
                break;
            }
            
            
            return super.onContextItemSelected(item);
        }
        
        /**
         * 显示更新的Dialog
         * @param blackNumber
         */
        private void showUpdateDialog(final BlackNumber blackNumber) {
            final EditText editText = new EditText(this);
            editText.setHint(blackNumber.getNumber());
            new AlertDialog.Builder(this)
                .setTitle("更新黑名单")
                .setView(editText)
                .setPositiveButton("更新", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //1). 更新List对应的数据
                        String newNumber = editText.getText().toString();
                        blackNumber.setNumber(newNumber);
                        
                        //2). 更新数据表对应的数据
                        dao.update(blackNumber);
                        
                        //3). 通知更新列表 
                        adapter.notifyDataSetChanged();
                    }
                })
                .setNegativeButton("取消", null)
                .show();
        }
    
        public void add(View v) {
            
            //1. 显示添加的dialog(带输入框)
            final EditText editText = new EditText(this);
            editText.setHint("输入黑名单号");
            new AlertDialog.Builder(this)
                .setTitle("添加黑名单")
                .setView(editText)
                .setPositiveButton("添加", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //1). 保存数据表中
                        String number = editText.getText().toString();
                        BlackNumber blackNumber = new BlackNumber(-1, number);
                        dao.add(blackNumber);
                        //2). 保存数据到List
                        //data.add(blackNumber);//已经有id了
                        data.add(0, blackNumber);
                        //3). 通知更新列表
                        adapter.notifyDataSetChanged();
                    }
                })
                .setNegativeButton("取消", null)
                .show();
        }
        
        class BlackNumberAdapter extends BaseAdapter {
    
            @Override
            public int getCount() {
                return data.size();
            }
    
            @Override
            public Object getItem(int position) {
                return data.get(position);
            }
    
            @Override
            public long getItemId(int position) {
                return 0;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                
                if(convertView==null) {
                    convertView = View.inflate(MainActivity.this, android.R.layout.simple_list_item_1, null);
                }
                
                BlackNumber blackNumber = data.get(position);
                TextView textView = (TextView) convertView.findViewById(android.R.id.text1);
                textView.setText(blackNumber.getNumber());
                
                return convertView;
            }
        }
    }
    MainActivity.java
    package com.atguigu.app04_sqlite;
    
    import java.util.List;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.content.DialogInterface;
    import android.os.Bundle;
    import android.view.ContextMenu;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.ContextMenu.ContextMenuInfo;
    import android.widget.BaseAdapter;
    import android.widget.EditText;
    import android.widget.ListView;
    import android.widget.TextView;
    import android.widget.AdapterView.AdapterContextMenuInfo;
    
    public class MainActivity1 extends Activity {
    
        private ListView lv_main;
        private BlackNumberAdapter adapter;
        private BlackNumberDao dao;
        private List<BlackNumber> data;
        private int position;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            //lv_main = (ListView) findViewById(R.id.lv_main);
            adapter = new BlackNumberAdapter();
            dao = new BlackNumberDao(this);
            data = dao.getAll();
            
            lv_main.setAdapter(adapter);
            
            //给listView设置创建contextMenu的监听
            lv_main.setOnCreateContextMenuListener(this);
        }
        
        @Override
        public void onCreateContextMenu(ContextMenu menu, View v,
                ContextMenuInfo menuInfo) {
            super.onCreateContextMenu(menu, v, menuInfo);
            //添加2个item
            menu.add(0, 1, 0, "更新");
            menu.add(0, 2, 0, "删除");
            
            //得到长按的position
            AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
            position = info.position;
        }
        
        @Override
        public boolean onContextItemSelected(MenuItem item) {
            
            //得到对应的BlackNumber对象
            BlackNumber blackNumber = data.get(position);
            switch (item.getItemId()) {
            case 1://更新
                //1. 显示更新的Dialog
                showUpdateDialog(blackNumber);
                break;
            case 2://删除
                //1). 删除数据表对应的数据
                dao.deleteById(blackNumber.getId());
                //2). 删除List对应的数据
                data.remove(position);
                //3). 通知更新列表
                adapter.notifyDataSetChanged();
                break;
    
            default:
                break;
            }
            
            
            return super.onContextItemSelected(item);
        }
        
        /**
         * 显示更新的Dialog
         * @param blackNumber
         */
        private void showUpdateDialog(final BlackNumber blackNumber) {
            final EditText editText = new EditText(this);
            editText.setHint(blackNumber.getNumber());
            new AlertDialog.Builder(this)
                .setTitle("更新黑名单")
                .setView(editText)
                .setPositiveButton("更新", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //1). 更新List对应的数据
                        String newNumber = editText.getText().toString();
                        blackNumber.setNumber(newNumber);
                        
                        //2). 更新数据表对应的数据
                        dao.update(blackNumber);
                        
                        //3). 通知更新列表 
                        adapter.notifyDataSetChanged();
                    }
                })
                .setNegativeButton("取消", null)
                .show();
        }
    
        public void add(View v) {
            
            //1. 显示添加的dialog(带输入框)
            final EditText editText = new EditText(this);
            editText.setHint("输入黑名单号");
            new AlertDialog.Builder(this)
                .setTitle("添加黑名单")
                .setView(editText)
                .setPositiveButton("添加", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        //1). 保存数据表中
                        String number = editText.getText().toString();
                        BlackNumber blackNumber = new BlackNumber(-1, number);
                        dao.add(blackNumber);
                        //2). 保存数据到List
                        //data.add(blackNumber);//已经有id了
                        data.add(0, blackNumber);
                        //3). 通知更新列表
                        adapter.notifyDataSetChanged();
                    }
                })
                .setNegativeButton("取消", null)
                .show();
        }
        
        class BlackNumberAdapter extends BaseAdapter {
    
            @Override
            public int getCount() {
                return data.size();
            }
    
            @Override
            public Object getItem(int position) {
                return data.get(position);
            }
    
            @Override
            public long getItemId(int position) {
                return 0;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                
                if(convertView==null) {
                    convertView = View.inflate(MainActivity1.this, android.R.layout.simple_list_item_1, null);
                }
                
                BlackNumber blackNumber = data.get(position);
                TextView textView = (TextView) convertView.findViewById(android.R.id.text1);
                textView.setText(blackNumber.getNumber());
                
                return convertView;
            }
        }
    }
    MainActivity1.java

    3.3 最新APK下载安装

    • 功能描述:

      1. 下载远程服务器端的APK文件

      2. 同步显示下载进度

      3. 下载完成自动安装

    • 关键技术点:

      1. SD卡文件读写

      2. ProgressDialog的使用

      3. 分线程请求网络

      4. 安装APK

  • 相关阅读:
    一,安装python
    maven搭建ssm
    web优化
    java代码优化29个点
    供参考的 php 学习路线
    javascript-文档结构遍历
    jquery中的cookie使用
    jQuery中的Ajax
    lambda和抽象类
    上传jar包到nexus私服
  • 原文地址:https://www.cnblogs.com/zhaye/p/10911258.html
Copyright © 2011-2022 走看看