來(lái)源: lizhengwei1989 發(fā)布時(shí)間:2018-11-30 17:48:25 閱讀量:1327
數(shù)據(jù)庫(kù)作為Android數(shù)據(jù)存儲(chǔ)重要的一部分,相信很多應(yīng)用中都會(huì)用到,面試也會(huì)遇到很多關(guān)于數(shù)據(jù)庫(kù)的問(wèn)題。實(shí)際開發(fā)中我沒(méi)遇到過(guò)特別復(fù)雜的數(shù)據(jù)庫(kù)使用,所以對(duì)這一塊的優(yōu)化沒(méi)怎么研究過(guò)。
以前面試的時(shí)候被問(wèn)到過(guò)這么一個(gè)問(wèn)題:
面試官:數(shù)據(jù)庫(kù)并發(fā)訪問(wèn)怎么處理?
我:給增刪改查方法加鎖。
面試官:那樣會(huì)有什么問(wèn)題?
我:效率低。
面試官:怎么解決?
我:不太清楚啊。。。
我一直認(rèn)為Android開發(fā)不會(huì)涉及到數(shù)據(jù)庫(kù)大量線程訪問(wèn)數(shù)據(jù)庫(kù)的問(wèn)題,所以方法加鎖的效率問(wèn)題是可以容忍的,搜索這個(gè)問(wèn)題的解決方式,發(fā)現(xiàn)大家也都是粗暴的用synchronize來(lái)解決。
我覺(jué)得即使優(yōu)化也是在加鎖的方式上優(yōu)化,關(guān)于加鎖的高級(jí)用法我沒(méi)有研究過(guò),所以一直沒(méi)有太在意過(guò)這個(gè)問(wèn)題(不要罵我不思進(jìn)取不去學(xué)習(xí)高級(jí)用法。。。)。
后來(lái)不知道在哪看了什么東東受到了啟發(fā),我們可以用隊(duì)列來(lái)解決這個(gè)問(wèn)題?。?!
一個(gè)思路就是:創(chuàng)建一個(gè)單線程的線程池,所有的數(shù)據(jù)庫(kù)操作都用這個(gè)線程池來(lái)操作
下面是我的demo,非常簡(jiǎn)單:
public class DBHelper extends SQLiteOpenHelper{
private static String DBName = "TestDB";
private static int DBVersion = 1;
private SQLiteDatabase db;
private static DBHelper INSTANCE;
// 不用的時(shí)候需要關(guān)閉線程池
private ExecutorService pool;
private DBHelper(Context context) {
super(context, DBName, null, DBVersion);
db = getReadableDatabase();
// 創(chuàng)建一個(gè)單線程池
pool = Executors.newSingleThreadExecutor();
}
// 單例
public static DBHelper getInstance(Context context){
if(INSTANCE == null){
synchronized (DBHelper.class){
if (INSTANCE == null){
INSTANCE = new DBHelper(context.getApplicationContext());
}
}
}
return INSTANCE;
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
// 模擬一個(gè)插入,參數(shù)隨便寫了,返回值和SQLiteDatabase的插入方法一樣都是long類型,表示插入的id
public long insert(final String param){
Future<Long> submit = pool.submit(new Callable<Long>() {
@Override
public Long call() throws Exception {
// 休眠1秒,模擬耗時(shí)
Thread.sleep(1000);
Log.e("lzw","插入 param: " + param);
return 100L;
}
});
long id = -1;
try {
// 拿操作結(jié)果,這里會(huì)阻塞
id = submit.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return id;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
整個(gè)DBHelper是一個(gè)單例,在構(gòu)造里實(shí)例化了一個(gè)單線程池,insert方法是表示插入,參數(shù)隨便寫了一個(gè),我們通過(guò)pool.submit方法把任務(wù)提交到了線程池進(jìn)行執(zhí)行,返回了一個(gè)Future類型的變量,
后面調(diào)用Future的get方法可以拿到返回值,get方法會(huì)阻塞,直到執(zhí)行完call方法里的內(nèi)容并拿到返回值。關(guān)于ExecutorService和Future的使用不是本文重點(diǎn),大家自行學(xué)習(xí)。
下面是測(cè)試Activity:
public class MainActivity extends AppCompatActivity {
private TextView tv;
private DBHelper helper;
private int param = 0;
static Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.content);
helper = DBHelper.getInstance(this);
findViewById(R.id.insert).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
param++;
final int param2 = param;
final long id = helper.insert("----> " + param2);
handler.post(new Runnable() {
@Override
public void run() {
tv.append(id + ":" + param2 + "\n");
}
});
}
}).start();
}
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
所有的操作都會(huì)逐條執(zhí)行。
效果圖如下:
以上就是所有的代碼,只是講了一下思路,用的時(shí)候大家根據(jù)自己的情況去封裝就好了。
其實(shí)擴(kuò)展一下,大家可以自己寫一個(gè)生產(chǎn)者消費(fèi)者模型也可以解決這個(gè)問(wèn)題,核心思路和上面的方法都一樣,就是一個(gè)阻塞隊(duì)列,需要修改數(shù)據(jù)庫(kù)的,把需求添加進(jìn)隊(duì)列,DBHelper從隊(duì)列中不斷取并操作數(shù)據(jù)庫(kù)。
其實(shí)Handler的思路也是這么個(gè)套路。
不多說(shuō)了,下面是代碼地址:
https://github.com/dreamlizhengwei/DatabaseDemo
---------------------
在線
客服
服務(wù)時(shí)間:周一至周日 08:30-18:00
選擇下列產(chǎn)品馬上在線溝通:
客服
熱線
7*24小時(shí)客服服務(wù)熱線
關(guān)注
微信
關(guān)注官方微信