1、Android數據存儲,參考http://www.cnblogs.com/ITtangtang/p/3920916.html。html
①最熟悉的是文件存儲的方式,分兩種:java
一、保存在手機的文件目錄中(也能夠是緩存目錄中,使用了Context對象),mysql
二、保存在sd卡上(使用了Environment對象獲取SD卡信息,參考:http://www.cnblogs.com/mengdd/p/3742623.html)。linux
保存文件時的選擇:不過重要的能夠存在緩存目錄中(能夠直接被刪除);較重要的存在通常目錄中;最重要的能夠存在SD卡上。android
②使用SharedPreferences保存數據一樣是保存文件形式,可是是xml格式的文件。web
Android解析xml文件的方式:Android提供了多種解析器,可是推薦使用pull解析器。參考:http://blog.csdn.net/liuhe688/article/details/6415593.sql
③使用SQlite數據庫存儲數據,和mysql等相比的特色是:在客戶端,不用安裝服務器。Android中經過兩個對象來進行操做:SQLiteOpenHelper,SQLiteDatabase。數據庫
參考:http://www.cnblogs.com/Excellent/archive/2011/11/19/2254888.html。編程
④使用內容提供者:這是應用之間傳遞數據的方式,一個應用做爲提供者,另外一個進行查詢(這也是使用SQLite數據庫,可是是對另外一個用的數據庫進行CURD操做)。緩存
一、提供者將數據操做的類(dao類)繼承ContentProviders便可將其暴露,並經過UriMatcher對象設置訪問路徑且只有uri匹配時才進行相應操做。
注意:此時的dao類須要實現ContentProviders的抽象方法來進行CURD操做;內容提供者能夠設置權限進行控制訪問。
二、在配置文件中進行配置(包含其自己的聲明和權限的配置)
1 import android.content.ContentProvider; 2 import android.content.ContentUris; 3 import android.content.ContentValues; 4 import android.content.UriMatcher; 5 import android.database.Cursor; 6 import android.database.sqlite.SQLiteDatabase; 7 import android.net.Uri; 8 9 public class PersonContentProvider extends ContentProvider { 10 11 private static final String AUTHORITY = "com.itheima28.sqlitedemo.providers.PersonContentProvider"; 12 private static final int PRESON_INSERT_CODE = 0; // 操做person表添加的操做的uri匹配碼 13 private static final int PERSON_DELETE_CODE = 1; 14 private static final int PERSON_UPDATE_CODE = 2; 15 private static final int PERSON_QUERY_ALL_CODE = 3; 16 private static final int PERSON_QUERY_ITEM_CODE = 4; 17 18 private static UriMatcher uriMatcher; 19 private PersonSQLiteOpenHelper mOpenHelper; // person表的數據庫幫助對象 20 21 static { 22 uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 23 // 添加一些uri(相似於分機號) 24 25 // content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/insert 26 uriMatcher.addURI(AUTHORITY, "person/insert", PRESON_INSERT_CODE); 27 28 // content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/delete 29 uriMatcher.addURI(AUTHORITY, "person/delete", PERSON_DELETE_CODE); 30 31 // content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/update 32 uriMatcher.addURI(AUTHORITY, "person/update", PERSON_UPDATE_CODE); 33 34 // content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/queryAll 35 uriMatcher.addURI(AUTHORITY, "person/queryAll", PERSON_QUERY_ALL_CODE); 36 37 // content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/query/# 38 uriMatcher.addURI(AUTHORITY, "person/query/#", PERSON_QUERY_ITEM_CODE); 39 } 40 41 //這裏能夠經過該方法進行初始化,而不用經過構造函數 42 @Override 43 public boolean onCreate() { 44 mOpenHelper = new PersonSQLiteOpenHelper(getContext()); 45 return true; 46 } 47 48 //這個方法是由系統調用的,用於說明返回值的MIME類型 49 @Override 50 public String getType(Uri uri) { 51 switch (uriMatcher.match(uri)) { 52 case PERSON_QUERY_ALL_CODE: // 返回多條的MIME-type 53 return "vnd.android.cursor.dir/person"; 54 case PERSON_QUERY_ITEM_CODE: // 返回單條的MIME-TYPE 55 return "vnd.android.cursor.item/person"; 56 default: 57 break; 58 } 59 return null; 60 } 61 62 @Override 63 public Cursor query(Uri uri, String[] projection, String selection, 64 String[] selectionArgs, String sortOrder) { 65 SQLiteDatabase db = mOpenHelper.getReadableDatabase(); 66 switch (uriMatcher.match(uri)) { 67 case PERSON_QUERY_ALL_CODE: // 查詢全部人的uri 68 if(db.isOpen()) { 69 Cursor cursor = db.query("person", projection, selection, selectionArgs, null, null, sortOrder); 70 return cursor; 71 // db.close(); 返回cursor結果集時, 不能夠關閉數據庫 72 } 73 break; 74 case PERSON_QUERY_ITEM_CODE: // 查詢的是單條數據, uri末尾出有一個id 75 if(db.isOpen()) { 76 77 long id = ContentUris.parseId(uri); 78 79 Cursor cursor = db.query("person", projection, "_id = ?", new String[]{id + ""}, null, null, sortOrder); 80 81 return cursor; 82 } 83 break; 84 default: 85 throw new IllegalArgumentException("uri不匹配: " + uri); 86 } 87 return null; 88 } 89 90 @Override 91 public Uri insert(Uri uri, ContentValues values) { 92 93 switch (uriMatcher.match(uri)) { 94 case PRESON_INSERT_CODE: // 添加人到person表中 95 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 96 97 if(db.isOpen()) { 98 99 long id = db.insert("person", null, values); 100 101 db.close(); 102 103 return ContentUris.withAppendedId(uri, id); 104 } 105 break; 106 default: 107 throw new IllegalArgumentException("uri不匹配: " + uri); 108 } 109 return null; 110 } 111 112 @Override 113 public int delete(Uri uri, String selection, String[] selectionArgs) { 114 switch (uriMatcher.match(uri)) { 115 case PERSON_DELETE_CODE: // 在person表中刪除數據的操做 116 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 117 if(db.isOpen()) { 118 int count = db.delete("person", selection, selectionArgs); 119 db.close(); 120 return count; 121 } 122 break; 123 default: 124 throw new IllegalArgumentException("uri不匹配: " + uri); 125 } 126 return 0; 127 } 128 129 @Override 130 public int update(Uri uri, ContentValues values, String selection, 131 String[] selectionArgs) { 132 switch (uriMatcher.match(uri)) { 133 case PERSON_UPDATE_CODE: // 更新person表的操做 134 SQLiteDatabase db = mOpenHelper.getWritableDatabase(); 135 if(db.isOpen()) { 136 int count = db.update("person", values, selection, selectionArgs); 137 db.close(); 138 return count; 139 } 140 break; 141 default: 142 throw new IllegalArgumentException("uri不匹配: " + uri); 143 } 144 return 0; 145 } 146 147 }
1 import android.content.ContentResolver; 2 import android.content.ContentUris; 3 import android.content.ContentValues; 4 import android.database.Cursor; 5 import android.net.Uri; 6 import android.test.AndroidTestCase; 7 import android.util.Log; 8 9 public class TextCase extends AndroidTestCase { 10 11 private static final String TAG = "TextCase"; 12 13 public void testInsert() { 14 Uri uri = Uri.parse("content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/insert"); 15 16 // 內容提供者訪問對象 17 ContentResolver resolver = getContext().getContentResolver(); 18 19 ContentValues values = new ContentValues(); 20 values.put("name", "fengjie"); 21 values.put("age", 90); 22 23 uri = resolver.insert(uri, values); 24 Log.i(TAG, "uri: " + uri); 25 long id = ContentUris.parseId(uri); 26 Log.i(TAG, "添加到: " + id); 27 } 28 29 public void testDelete() { 30 Uri uri = Uri.parse("content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/delete"); 31 32 // 內容提供者訪問對象 33 ContentResolver resolver = getContext().getContentResolver(); 34 35 String where = "_id = ?"; 36 String[] selectionArgs = {"21"}; 37 int count = resolver.delete(uri, where, selectionArgs); 38 Log.i(TAG, "刪除行: " + count); 39 } 40 41 public void testUpdate() { 42 Uri uri = Uri.parse("content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/update"); 43 44 // 內容提供者訪問對象 45 ContentResolver resolver = getContext().getContentResolver(); 46 47 ContentValues values = new ContentValues(); 48 values.put("name", "lisi"); 49 50 int count = resolver.update(uri, values, "_id = ?", new String[]{"20"}); 51 Log.i(TAG, "更新行: " + count); 52 } 53 54 public void testQueryAll() { 55 Uri uri = Uri.parse("content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/queryAll"); 56 57 // 內容提供者訪問對象 58 ContentResolver resolver = getContext().getContentResolver(); 59 60 Cursor cursor = resolver.query(uri, new String[]{"_id", "name", "age"}, null, null, "_id desc"); 61 62 if(cursor != null && cursor.getCount() > 0) { 63 64 int id; 65 String name; 66 int age; 67 while(cursor.moveToNext()) { 68 id = cursor.getInt(0); 69 name = cursor.getString(1); 70 age = cursor.getInt(2); 71 Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age); 72 } 73 cursor.close(); 74 } 75 } 76 77 public void testQuerySingleItem() { 78 Uri uri = Uri.parse("content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/query/#"); 79 80 // 在uri的末尾添加一個id content://com.itheima28.sqlitedemo.providers.PersonContentProvider/person/query/20 81 uri = ContentUris.withAppendedId(uri, 20); 82 83 // 內容提供者訪問對象 84 ContentResolver resolver = getContext().getContentResolver(); 85 86 Cursor cursor = resolver.query(uri, new String[]{"_id", "name", "age"}, null, null, null); 87 88 if(cursor != null && cursor.moveToFirst()) { 89 int id = cursor.getInt(0); 90 String name = cursor.getString(1); 91 int age = cursor.getInt(2); 92 cursor.close(); 93 Log.i(TAG, "id: " + id + ", name: " + name + ", age: " + age); 94 } 95 } 96 }
內容提供者是Android四大組件之一,是系統數據向外提供的方式,例如短信,聯繫人信息等,參考:http://www.2cto.com/kf/201312/261478.html。
和內容提供者相關的是內容觀察者,指的是監聽一個uri路徑上的數據庫變化並做出相應操做的類,參考:http://blog.csdn.net/qinjuning/article/details/7047607,注意這篇文章中的其餘連接也有些不錯的好文。
參考:http://www.it165.net/pro/html/201402/9053.html.
⑤網絡數據:Android是一個操做系統,裏面各類應用進行網絡操做,因此須要多種網絡訪問方式,而不像web中只有http便可,這就須要所謂的網絡編程。參考:http://www.jb51.net/article/44859.htm。
可是移動端網絡性能對用戶體驗影響大,因此一般須要多線程處理網絡訪問或者其餘比較複雜的問題,這就涉及到多線程編程,而在多個線程之間進行消息的傳遞也是一個必須處理的問題,參考:http://blog.csdn.net/beiminglei/article/details/8474026。
1 import java.io.InputStream; 2 import java.net.HttpURLConnection; 3 import java.net.MalformedURLException; 4 import java.net.URL; 5 6 import javax.net.ssl.HttpsURLConnection; 7 8 import android.os.Bundle; 9 import android.os.Handler; 10 import android.os.Message; 11 import android.app.Activity; 12 import android.graphics.Bitmap; 13 import android.graphics.BitmapFactory; 14 import android.util.Log; 15 import android.view.Menu; 16 import android.view.View; 17 import android.view.View.OnClickListener; 18 import android.widget.EditText; 19 import android.widget.ImageView; 20 import android.widget.Toast; 21 22 public class MainActivity extends Activity implements OnClickListener { 23 24 private static final String TAG = "MainActivity"; 25 protected static final int ERROR = 1; 26 private EditText etUrl; 27 private ImageView ivIcon; 28 private final int SUCCESS = 0; 29 30 private Handler handler = new Handler() { 31 32 /** 33 * 接收消息 34 */ 35 @Override 36 public void handleMessage(Message msg) { 37 super.handleMessage(msg); 38 39 Log.i(TAG, "what = " + msg.what); 40 if(msg.what == SUCCESS) { // 當前是訪問網絡, 去顯示圖片 41 ivIcon.setImageBitmap((Bitmap) msg.obj); // 設置imageView顯示的圖片 42 } else if(msg.what == ERROR) { 43 Toast.makeText(MainActivity.this, "抓去失敗", 0).show(); 44 } 45 } 46 }; 47 48 @Override 49 protected void onCreate(Bundle savedInstanceState) { 50 super.onCreate(savedInstanceState); 51 setContentView(R.layout.activity_main); 52 53 ivIcon = (ImageView) findViewById(R.id.iv_icon); 54 etUrl = (EditText) findViewById(R.id.et_url); 55 56 findViewById(R.id.btn_submit).setOnClickListener(this); 57 } 58 59 @Override 60 public void onClick(View v) { 61 final String url = etUrl.getText().toString(); 62 63 new Thread(new Runnable() { 64 65 @Override 66 public void run() { 67 Bitmap bitmap = getImageFromNet(url); 68 69 // ivIcon.setImageBitmap(bitmap); // 設置imageView顯示的圖片 70 if(bitmap != null) { 71 Message msg = new Message(); 72 msg.what = SUCCESS; 73 msg.obj = bitmap; 74 handler.sendMessage(msg); 75 } else { 76 Message msg = new Message(); 77 msg.what = ERROR; 78 handler.sendMessage(msg); 79 } 80 }}).start(); 81 82 } 83 84 /** 85 * 根據url鏈接取網絡抓去圖片返回 86 * @param url 87 * @return url對應的圖片 88 */ 89 private Bitmap getImageFromNet(String url) { 90 HttpURLConnection conn = null; 91 try { 92 URL mURL = new URL(url); // 建立一個url對象 93 94 // 獲得http的鏈接對象 95 conn = (HttpURLConnection) mURL.openConnection(); 96 97 conn.setRequestMethod("GET"); // 設置請求方法爲Get 98 conn.setConnectTimeout(10000); // 設置鏈接服務器的超時時間, 若是超過10秒鐘, 沒有鏈接成功, 會拋異常 99 conn.setReadTimeout(5000); // 設置讀取數據時超時時間, 若是超過5秒, 拋異常 100 101 conn.connect(); // 開始連接 102 103 int responseCode = conn.getResponseCode(); // 獲得服務器的響應碼 104 if(responseCode == 200) { 105 // 訪問成功 106 InputStream is = conn.getInputStream(); // 得到服務器返回的流數據 107 108 Bitmap bitmap = BitmapFactory.decodeStream(is); // 根據 流數據 建立一個bitmap位圖對象 109 110 return bitmap; 111 } else { 112 Log.i(TAG, "訪問失敗: responseCode = " + responseCode); 113 } 114 } catch (Exception e) { 115 e.printStackTrace(); 116 } finally { 117 if(conn != null) { 118 conn.disconnect(); // 斷開鏈接 119 } 120 } 121 return null; 122 } 123 }
Android下進行http鏈接可使用HttpClient類,可是在Android 6.0(即sdk23)以後移除了相關包,因此不推薦使用,可是好像有的框架依然在使用,能夠做爲了解,對於Android下使用參考http://liangruijun.blog.51cto.com/3061169/803097/:關於其餘的使用參考:http://www.ibm.com/developerworks/cn/opensource/os-httpclient/。注意其中引用的文章列表。
!!!網絡訪問在模擬器下測試時,訪問本機的路徑不是127.0.0.1,而是10.0.2.2,或者是局域網中的路徑。參考:http://www.cnblogs.com/csulennon/p/3709032.html.
2、android權限
①Android文件權限是linux風格的
②安卓的權限很是細,並且應用須要申請,而用戶能夠對其進行設定(在模擬器中直接賦予),
具體權限參考:http://www.cnblogs.com/classic/archive/2011/06/20/2085055.html。
3、多線程下載:
上圖的要點是:
①RandomAccessFile類,這是一個很是適合用於多線程下載的類,由於它帶有一個文件指針,能夠方便的針對文件的指定位置進行操做。因此多個線程能夠分別在不一樣的位置操做同一個文件而不出線程問題。參考:http://www.2cto.com/kf/201208/149816.html。注意:rwd是直接將讀取的文件寫入到硬盤上,而rw是先寫到內存的緩衝區中,當達到必定量以後在寫入硬盤。二者各有優勢:一個防止特殊狀況丟失下載數據(尚未保存就斷網等狀況),一個減小io操做。可是下載時使用rwd更好。
②上面提到了多線程常常應用在網絡等比較耗時的方面,對於網絡下載更是如此。可是在Android下的下載使用的依然是j2se中的API,也就是HttpURLConnection類中的方法。具體實現的原理就是HTTP協議中的HTTP頭 Range和Content-Range字段。參考:。
所謂的端點下載就是在用一個臨時文件同步存儲下載數據,只有在下載完成時才刪除,因此這個文件就用於判斷是否下載完成和記錄下載到了那裏,便於以後繼續下載。
1 import java.io.BufferedReader; 2 import java.io.File; 3 import java.io.FileInputStream; 4 import java.io.InputStream; 5 import java.io.InputStreamReader; 6 import java.io.RandomAccessFile; 7 import java.net.HttpURLConnection; 8 import java.net.URL; 9 10 public class MutileThreadDownload { 11 /** 12 * 線程的數量 13 */ 14 private static int threadCount = 3; 15 16 /** 17 * 每一個下載區塊的大小 18 */ 19 private static long blocksize; 20 21 /** 22 * 正在運行的線程的數量 23 */ 24 private static int runningThreadCount; 25 26 /** 27 * @param args 28 * @throws Exception 29 */ 30 public static void main(String[] args) throws Exception { 31 // 服務器文件的路徑 32 String path = "http://192.168.1.100:8080/ff.exe"; 33 URL url = new URL(path); 34 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 35 conn.setRequestMethod("GET"); 36 conn.setConnectTimeout(5000); 37 int code = conn.getResponseCode(); 38 if (code == 200) { 39 long size = conn.getContentLength();// 獲得服務端返回的文件的大小 40 System.out.println("服務器文件的大小:" + size); 41 blocksize = size / threadCount; 42 // 1.首先在本地建立一個大小跟服務器如出一轍的空白文件。 43 File file = new File("temp.exe"); 44 RandomAccessFile raf = new RandomAccessFile(file, "rw"); 45 raf.setLength(size); 46 // 2.開啓若干個子線程分別去下載對應的資源。 47 runningThreadCount = threadCount; 48 for (int i = 1; i <= threadCount; i++) { 49 long startIndex = (i - 1) * blocksize; 50 long endIndex = i * blocksize - 1; 51 if (i == threadCount) { 52 // 最後一個線程 53 endIndex = size - 1; 54 } 55 System.out.println("開啓線程:" + i + "下載的位置:" + startIndex + "~" 56 + endIndex); 57 new DownloadThread(path, i, startIndex, endIndex).start(); 58 } 59 } 60 conn.disconnect(); 61 } 62 63 private static class DownloadThread extends Thread { 64 private int threadId; 65 private long startIndex; 66 private long endIndex; 67 private String path; 68 69 public DownloadThread(String path, int threadId, long startIndex, 70 long endIndex) { 71 this.path = path; 72 this.threadId = threadId; 73 this.startIndex = startIndex; 74 this.endIndex = endIndex; 75 } 76 77 @Override 78 public void run() { 79 try { 80 // 當前線程下載的總大小 81 int total = 0; 82 File positionFile = new File(threadId + ".txt"); 83 URL url = new URL(path); 84 HttpURLConnection conn = (HttpURLConnection) url 85 .openConnection(); 86 conn.setRequestMethod("GET"); 87 // 接着從上一次的位置繼續下載數據 88 if (positionFile.exists() && positionFile.length() > 0) {// 判斷是否有記錄 89 FileInputStream fis = new FileInputStream(positionFile); 90 BufferedReader br = new BufferedReader( 91 new InputStreamReader(fis)); 92 // 獲取當前線程上次下載的總大小是多少 93 String lasttotalstr = br.readLine(); 94 int lastTotal = Integer.valueOf(lasttotalstr); 95 System.out.println("上次線程" + threadId + "下載的總大小:" 96 + lastTotal); 97 startIndex += lastTotal; 98 total += lastTotal;// 加上上次下載的總大小。 99 fis.close(); 100 } 101 102 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" 103 + endIndex); 104 conn.setConnectTimeout(5000); 105 int code = conn.getResponseCode(); 106 System.out.println("code=" + code); 107 InputStream is = conn.getInputStream(); 108 File file = new File("temp.exe"); 109 RandomAccessFile raf = new RandomAccessFile(file, "rw"); 110 // 指定文件開始寫的位置。 111 raf.seek(startIndex); 112 System.out.println("第" + threadId + "個線程:寫文件的開始位置:" 113 + String.valueOf(startIndex)); 114 int len = 0; 115 byte[] buffer = new byte[512]; 116 while ((len = is.read(buffer)) != -1) { 117 RandomAccessFile rf = new RandomAccessFile(positionFile, 118 "rwd"); 119 raf.write(buffer, 0, len); 120 total += len; 121 rf.write(String.valueOf(total).getBytes()); 122 rf.close(); 123 } 124 is.close(); 125 raf.close(); 126 127 } catch (Exception e) { 128 e.printStackTrace(); 129 } finally { 130 // 只有全部的線程都下載完畢後 才能夠刪除記錄文件。 131 synchronized (MutileThreadDownload.class) { 132 System.out.println("線程" + threadId + "下載完畢了"); 133 runningThreadCount--; 134 if (runningThreadCount < 1) { 135 System.out.println("全部的線程都工做完畢了。刪除臨時記錄的文件"); 136 for (int i = 1; i <= threadCount; i++) { 137 File f = new File(i + ".txt"); 138 System.out.println(f.delete()); 139 } 140 } 141 } 142 143 } 144 } 145 } 146 }
在Android上實現多線程下載的基本原理同樣,只是有一些問題須要注意,
①下載文件的保存位置,Android項目須要保存在指定的位置:sd卡/Android項目文件路徑
②多線程之間的信息傳遞
③對用戶的提示