Database closed, 还有线程在操作数据库, 导致抛出异常
- 抛出异常如下:
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/com.seaice.safephone/databases/safe_phone.db
- 可以加个静态同步锁,保证操作的时候的原子性。
private static final byte[] lock = new byte[0];
...
synchronized (lock){
//打开数据库
//操作数据库
//关闭数据库
}
- 多线程操作数据库,各种error; 还需要保证只有一个SqliteDatabase
- 必须保证操作数据库的时候,db不应该是closed状态。
public class HomeCallDbMgr {
private static final String TAG = "HomeCallDbUtil";
private static final String NUMBER = "number";
private static final String MODE = "mode";
//AtomicInteger,提供原子操作的Integer类,AtomicInteger是一种线程安全的加减操作接口。
private AtomicInteger mOpenCount = new AtomicInteger();
//静态变量
private static HomeCallDbMgr instance = null;
private static SqliteDbHelper dbHelper = null;
//持有自身的一个私有变量,就是用它来操作数据库
private SQLiteDatabase database;
//使用之前,必须初始化数据库
public static synchronized void initDataBase(Context ctx) {
if (instance == null) {
synchronized (HomeCallDbMgr.class){
if(instance == null){
instance = new HomeCallDbMgr();
dbHelper = new SqliteDbHelper(ctx, GlobalConstant.DB_NAME);//加载数据库
}
}
}
}
//获取管理实例
public static synchronized HomeCallDbMgr getInstance() {
if (instance == null) {
throw new IllegalStateException(HomeCallDbMgr.class.getSimpleName()+"is not create," +
"call initDataBase() method first");
}
return instance;
}
//打开数据库连接
private synchronized SQLiteDatabase openWritableDataBase(){
if(mOpenCount.incrementAndGet() == 1){//自增+1 是线程安全的,这也是用它的原因
database = dbHelper.getWritableDatabase();
}
return database;
}
//关闭数据库
public synchronized void closeDataBase(){
if(mOpenCount.decrementAndGet() == 0){
database.close();
}
}
//操作例子
public synchronized void update(...){
getInstance().openWritableDataBase().update(...);
}
fragment commit 抛出异常
- activity进入onStop时,或者调用onSavedInstanceState(),fragment commit 就会抛出此类异常。
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1489)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1507)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:634)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:613)
- 解决的办法如下,判断activty的状态或者强制commit
FragemntTransaction.commit()->
FragmentTransaction.commitAllowingStateLoss()
Fragment Transaction commit when activity destroy, 需要判断activity是否销毁了。
activity.isFinishing() || activity.isDestroy()
ListView 的Adapter 一直get到的view Item是为空
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.cache_listview_item, null, false);
holder.iv_icon = (ImageView) findViewById(R.id.iv_cache_icon);
其中,应当指定从哪个view get,改为
holder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_cache_icon);
HttpURLConnection connect() 后 Block,抛出超时异常
URL url = new URL(updateVirusUrl);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");//设置请求方法
conn.setConnectTimeout(5000);//设置连接超时
conn.setReadTimeout(5000);//设置响应超时,连接上了但是服务器迟迟不给响应
conn.connect();//连接服务器
Log.e(TAG, "CODE=" + conn.getResponseCode());//这代码一直不跑,因为连接上不上超时了
- 此时我想要根据这个返回值是否正常做不同的操作,但是始终无法跑到这部分代码。是因为这个URL是invalide, 所以一直连不上,timeout了,conn没有返回码。
- 解决方法如下,加个catch exception 关于timeout的 –》ConnectException,并在这里做操作。
//实际上,第一个和第二个Exception都是IOException的子类,其实可以删去这两个,直接在IOException操作
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ConnectException e) {
e.printStackTrace();
Log.e(TAG, "time out");
handler.sendEmptyMessage(UPDATE_VERSION_FAILED);//time out
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
} finally {
conn.disconnect();
}
- 后面测试时,发现有时候还是不行,是另一个异常-> SocketTimeoutException。实际上,这两个异常都是IOException的子类,其实可以直接这么做:
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "check version IOException");
handler.sendEmptyMessage(UPDATE_VERSION_FAILED);
}
加载listview 的data item的时候,抛出异常
我在listview滑动的时候启动线程去加载,然后通过handler去notifyDataSetChanged();这种情况就很可能发生adapter的数据和通知不同时匹配,而出现这个异常。
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131624034, class android.widget.ListView) with Adapter(class android.widget.HeaderViewListAdapter)]
- 你的adapter的内容变化了,但是你的ListView并不知情。请保证你adapter的数据在主线程中进行更改!
- listview的adapter数据更新和dapter.notifyDataSetChanged()最好放到单独一个线程里
- 避免多线程去更新adapter的数据,把更新adapter数据和notifyDataSetChanged放到统一个线程中。
- http://www.cnblogs.com/tonybright/p/4733734.html 因此确保修改adapter数据和notifyDataSetChanged是coupled出现的,否则在特定时序下很容易出现上述异常。
监听来电获取不到电话号码
- 通过TelephonyManager注册监听器,获取的号码为空
private class MyListener extends PhoneStateListener {
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
- 看到stackoverflow也有人提问,说要用receiver去监听,才可行。实际上也不可行。
//动态注册广播
mCallSmsReceiver = new CallSmsStateReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");//过滤器
filter.addAction("android.intent.action.PHONE_STATE");
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(mCallSmsReceiver, filter);
--------------------------------------------------------------------
//监听电话和短信的广播
private class CallSmsStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG, "ONrECEIVE");
Log.e(TAG, intent.getAction().toString());
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
Object[] objs = (Object[]) intent.getExtras().get("pdus");
for (Object obj : objs) {
SmsMessage sms = SmsMessage.createFromPdu((byte[]) obj);
String sender = sms.getOriginatingAddress();
String mode = HomeCallDbMgr.getInstance().findModeOfNum(sender);
if ("拦截短信".equals(mode) && "拦截短信和拦截电话".equals(mode)) {
abortBroadcast();
}
}
} else if (intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)){
String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.e(TAG, number);
}
}
}
}
- 在setting->应用程序->找到对应的app,打开对应的phone权限,才能够显示。android 6.0