【安卓实现手机通讯录】开发总结

总结遇到的问题

一、 权限

安卓的权限主要分为

普通权限:
涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。这些权限在应用安装时授予,运行时不再询问用户。例如: 网络访问、WIFI状态、音量设置等。

危险权限:
涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如: 读取通讯录、读写存储器数据、获取用户位置等。如果应用声明需要这些危险权限,则必须在运行时明确告诉用户,让用户手动授予。

特殊权限:
SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS 这两个权限比较特殊,不能通过代码申请方式获取,必须得用户打开软件设置页手动打开,才能授权。
注意:
当获取危险权限时
在运行程序时,除了在Manifest文件中定义,还需要模拟器在运行时,手动添加权限原来Android虚拟机权限需要手动添加:
在 所有应用 中,找到你的程序,点开应用信息,然后手动添加 “权限”。
常用的危险权限如下:
请添加图片描述

问题

当编译完app之后,在我自己手机上运行时,闪退不能运行,原来是因为我没有自己手动设置应用权限
解决
在MainActivity界面添加代码,即实现在app运行时,询问用户开启权限,这样用户就不会在第一次使用时无法打开app,一头雾水了

 /**
     * 权限处理
     * //处理权限请求
     * //首先定义一个变量来记录处理权限了几次
     * 该代码实现了一次同时询问多个权限。
     */
    private int times = 0;
    //在处理权限时的回调
    private final int REQUEST_PHONE_PERMISSIONS = 0;

    //检查全新的核心方法
    private void checkPermission() {
        times++;
        final List<String> permissionsList = new ArrayList<>();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //获取权限,将需要获取的权限添加入list;
            if ((checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED))
                permissionsList.add(Manifest.permission.READ_CONTACTS);
            if ((checkSelfPermission(Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED))
                permissionsList.add(Manifest.permission.WRITE_CONTACTS);
            if ((checkSelfPermission(Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED))
                permissionsList.add(Manifest.permission.READ_CALL_LOG);
            if ((checkSelfPermission(Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED))
                permissionsList.add(Manifest.permission.SEND_SMS);
            if ((checkSelfPermission(Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED))
                permissionsList.add(Manifest.permission.READ_SMS);
            if ((checkSelfPermission(Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED))
                permissionsList.add(Manifest.permission.CALL_PHONE);

            if (permissionsList.size() != 0) {
                if (times==1) {
                //申请结果的主要部分就为下面这一句
                    requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                            REQUEST_PHONE_PERMISSIONS);
                } else {
                    new AlertDialog.Builder(this)
                            .setCancelable(true)
                            .setTitle("提示")
                            .setMessage("获取不到授权,APP将无法正常使用,请在设置中允许APP获取权限!")
                            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface arg0, int arg1) {
                                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                        requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                                                REQUEST_PHONE_PERMISSIONS);
                                    }
                                }
                            }).setNegativeButton("取消", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface arg0, int arg1) {
                            finish();//这里对该界面直接进行销毁,让用户从新进入该界面
                        }
                    }).show();
                }
            } else {
                //initData();//初始化数据
            }
        } else {
            //initData();//初始化数据
        }
    }

    //权限处理的回调,当用户提出权限访问时,系统会返回结果,该方法中处理结果。
    @Override
    public void onRequestPermissionsResult(int requestCode, final String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        checkPermission();
    }

二、Android studio 数据库可视化操作

操作步骤:

  1. 需要下载插件,但该插件不是免费下载,需要破解
    安装数据库插件SQLScout,操作目录为:File->setting->plugings,搜索SQLcount 点击install 即可获得一天的使用,成功后Android studio右边栏会出现“SQLite Explrer”

  2. 连上手机或模拟器,打开“Device File Explorer”,通过右边栏“Device File Explorer”直接打开,或者“View”—>“Tool Windows”—>“Device File Explorer”

  3. 进入目录data/data/app包名/databases/,双击数据库文件,然后点击"SQLite Explorer"即可对数据查看和操作,如果发生Error downloading contents of device file “xx.db”: open failed: Permission,命令行执行“adb root”即可

  4. 除了下载在AndroidStudio 下载 SQL Count 插件外,也可自己下载一个工具软件
    在这里插入图片描述
    百度搜索即可免费下载,但使用过程有点麻烦,需要导出数据库中的”xx.db“ ,找到需要查看的db文件,右键save as 即可保存,再使用该软件即可打开查看数据库中的内容。

三、数据库查找工作

1、查找联系人信息


 private void getPhoneContacts() {
        ContentResolver resolver = this.getContentResolver();
// 获取手机联系人
        Cursor phoneCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PHONES_PROJECTION, null,
                null, null);
        if (phoneCursor != null) {
            while (phoneCursor.moveToNext()) {
                //得到手机号码
                String phoneNumber = phoneCursor.getString(PHONES_NUMBER_INDEX);
                //当手机号码为空的或者为空字段 跳过当前循环
                if (TextUtils.isEmpty(phoneNumber))
                    continue;
                //得到联系人名称
                String contactName = phoneCursor.getString(PHONES_DISPLAY_NAME_INDEX);
                //得到联系人 ID
                Long contactid = phoneCursor.getLong(PHONES_CONTACT_ID_INDEX);
                //得到联系人头像 ID
                Long photoid = phoneCursor.getLong(PHONES_PHOTO_ID_INDEX);

                //得到联系人头像 Bitamp
                Bitmap contactPhoto = null;
                //photoid 大于 0 表示联系人有头像 如果没有给此人设置头像则给他一个默认的
                if (photoid > 0) {
                    Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactid);
                    InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(resolver, uri);
                    contactPhoto = BitmapFactory.decodeStream(input);
                } else {
                    //设置默认头像
                    contactPhoto = BitmapFactory.decodeResource(getResources(), R.drawable.touxiang1);
                }
                myPhone mp = new myPhone(contactid, phoneNumber, contactName, null, null, null, null, null);
                myPhoneuserList.add(mp);
//                mContactsPhonto.add(contactPhoto);
                Log.d("获取", "getPhoneContacts: " + contactName + " phoneNumber " + phoneNumber + " photoid " + photoid);
            }
            phoneCursor.close();
            Log.d("获取", "获取联系人列表完成");
        }
    }

2、如何异步查询短信记录

(自行理解吧。。。)

private ListView talkView;
	private List<MessageBean> messages = null;
	private AsyncQueryHandler asyncQuery;
	private String address;
	private SimpleDateFormat sdf;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.message_list_view);
		sdf = new SimpleDateFormat("MM-dd HH:mm");
		
		String threadid = getIntent().getStringExtra("threadid");
		init(threadid);
		Log.v("messageBoxList",messages.size()+"");
	}
//定义init() 方法,实现查询
      private void init(String thread) {
		asyncQuery = new MessageAsynQueryHandler(getContentResolver());
		talkView = (ListView) findViewById(R.id.message_list);
		messages = new ArrayList<MessageBean>();
		Uri uri = Uri.parse("content://sms");
		String[] projection = new String[] { "date", "address", "person", "body", "type" }; // 查询的列
		asyncQuery.startQuery(0, null, uri, projection, "thread_id = " + thread, null, "date asc");

	}
/**
	 * 异步查询数据库的类
	 * 查找对应用户的消息对话,
	 */
	private class MessageAsynQueryHandler extends AsyncQueryHandler {
 
		public MessageAsynQueryHandler(ContentResolver cr) {
			super(cr);
		}
		@SuppressLint("Range")//cursor 会报出value > 0 的错误 ,对于cursor查找应该对应的index必须大于等于0,但为了规范,是通过getColumIndex查找,报出错误,所以加上警告提示
		@Override
		protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
			if (cursor != null && cursor.getCount() > 0) {
				cursor.moveToFirst();
				for (int i = 0; i < cursor.getCount(); i++) {
					cursor.moveToPosition(i);
					String date = sdf.format(new Date(cursor.getLong(cursor.getColumnIndex("date"))));
					if (cursor.getInt(cursor.getColumnIndex("type")) == 1) {// 他说的信息
						MessageBean d = new MessageBean(
								cursor.getString(cursor.getColumnIndex("address")),
								date,
								cursor.getString(cursor.getColumnIndex("body")),R.layout.message_list_say_he_item);
						messages.add(d);
					} else { // MessageBean为自定义的短信实体类,用于保存每一条短信的相关内容,如短信内容,发送日期,发送地址等
						MessageBean d = new MessageBean(cursor.getString(cursor.getColumnIndex("address")),date,cursor.getString(cursor.getColumnIndex("body")), R.layout.message_list_say_me_item);
						messages.add(d);
					}
				}
				if (messages.size() > 0) {
				//talkView为listview 用于展示短信内容
					talkView.setAdapter(new MessageBoxListAdapter(
							MessageBoxList.this, messages));
					talkView.setDivider(null);
					talkView.setSelection(messages.size());
				} else {
					Log.v("messageBoxList","没有数据显示");
					Toast.makeText(MessageBoxList.this, "没有短信进行操作",Toast.LENGTH_SHORT).show();
				}

			}
			Log.d("messageBoxList",cursor.getCount()+"");
			super.onQueryComplete(token, cookie, cursor);
		}
	}

四、Intent 传送数据

通过Intent 调用,可以实现页面的跳转,进行数据传送

1.使用方式一:

通过putExtra() 方法

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
	   intent.putExtra("key", "value");
		startActivity(intent);
		// 或者可一定义bundle
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
       Bundle bundle = new Bundle();
       bundle.putLong("contactid", myPhoneuserList.get(position).getContactId());
       intent.putExtras(bundle);
       startActivity(intent);         

2.使用方法二:

通过Serializable接口
Serializable是序列化的意思,表示将一个对象转换为可存储或者可传输的状态,序列化后的对象可以在网络上进行传输,也可以存储到本地

//自定义序列化
public class MyMap implements Serializable {
    private Map<String, Object> map;
    public Map<String, Object> getMap() {
        return map;
    }
    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
}

//调用序列化存入数据
MyMap myMap = new MyMap();
myMap.setMap(your_map);//把你自己的map集合放进去
Bundle bundle = new Bundle();
Intent intent = new Intent(mActivity, OtherActivity.class);
bundle.putSerializable("map", myMap);
intent.putExtras(bundle);
startActivity(intent);
//获取数据
Bundle bundle = getIntent().getExtras();
MyMap my_map= (MyMap) bundle.get("map");
Map your_map= my_map.getMap();

五、运行时的错误

1.空指针异常

在这里插入图片描述

解决:
错误原因:因为关于Async QueryHander的调用异常 ,因为我没有在init() 方法 中
Uri uri = android.provider.CallLog.Calls.CONTENT_URI;
asynccall_contact_Query.startQuery(0, null, uri, contact_projection, null, null, CallLog.Calls.DEFAULT_SORT_ORDER);
忘记在在这之前添加
asynccall_contact_Query = new MyAsyncQueryHandler(getContentResolver());
导致空指针异常

在这里插入图片描述

这里空指针异常,是因为在适配器中的getview方法中,没有正确的获取view,没有写上view

2.内存溢出

Java 内存溢出(java.lang.OutOfMemoryError)
原因:将cursor.moveToNext() 写成 cursor.MoveToFirst() 所以在运行之后一直没有反应,知道报出内存溢出的问题,这么粗心的问题,以后要避免再犯!!!
在这里插入图片描述

总结
Java 内存溢出(java.lang.OutOfMemoryError)的常见情况和处理方式总结
java.lang.OutOfMemoryError这个错误我相信大部分开发人员都有遇到过,产生该错误的原因大都出于以下原因:JVM内存过小、程序不严密,产生了过多的垃圾。
导致OutOfMemoryError异常的常见原因有以下几种:

  1. 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  2. 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
  3. 代码中存在死循环或循环产生过多重复的对象实体;
  4. 使用的第三方软件中的BUG;
  5. 启动参数内存值设定的过小;
    此错误常见的错误提示:
  6. tomcat:java.lang.OutOfMemoryError: PermGen space
  7. tomcat:java.lang.OutOfMemoryError: Java heap space
  8. weblogic:Root cause of ServletException java.lang.OutOfMemoryError
  9. resin:java.lang.OutOfMemoryError java:java.lang.OutOfMemoryError

其他错误
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值