android app开发遇到的一些问题(一)

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
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值