04_數據存儲

1. 理論概述

Android數據存儲方式:java

  • SharedPreferences存儲mysql

  • 手機內部文件存儲android

  • 手機外部文件存儲sql

  • sqlite數據庫存儲shell

  • 遠程服務器存儲數據庫

2. 數據存儲開發apache

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保存到手機內部\n2. 讀取手機內部圖片文件顯示"
        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

相關文章
相關標籤/搜索