需求:打开系统联系人,获取手机号码。
可能会遇到的问题:同一个用户多个手机号码
阅读本文会收获2个技能:
- 学会反编译Apk查看代码实现原理
- 快速定位反编译后的代码
最开始我以为像类似获取系统图片那样去获取手机号码,很快解决,实际上也不是很难,方法对了就很快。
最开始根据Action事件在startActivityForResult中找到官方的文档说明(毕竟我觉得这种应该很容易去实现,一个Action就可以了)
public class MyActivity extends Activity {...static final int PICK_CONTACT_REQUEST = 0;public boolean onKeyDown(int keyCode, KeyEvent event) {if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {// When the user center presses, let them pick a contact.startActivityForResult(new Intent(Intent.ACTION_PICK,new Uri("content://contacts")),PICK_CONTACT_REQUEST);return true;}return false;}protected void onActivityResult(int requestCode, int resultCode,Intent data) {if (requestCode == PICK_CONTACT_REQUEST) {if (resultCode == RESULT_OK) {// A contact was picked. Here we will just display it// to the user.startActivity(new Intent(Intent.ACTION_VIEW, data));}}}}
就是这么简单,然而现实是new Uri(“content://contacts”)报错了,Uri是个抽象类,不能这样实例化,然后我就像以前那样获取image那样处理
图片:Intent intent = new Intent(Intent.ACTION_PICK);intent.setType("image/*");startActivityForResult(intent, requestCode);
联系人:
Intent intent = new Intent(Intent.ACTION_PICK);intent.setType("content://contacts");startActivityForResult(intent,GET_PHONENUM)
还是不行:No Activity found to handle Intent
然后找到网上一种通用的方法(很多人都这样写)
startActivityForResult(
new Intent(Intent.ACTION_PICK,
ContactsContract.Contacts.CONTENT_URI),
GET_PHONENUM);
在onActivityResult中回调
// URI,每个ContentProvider定义一个唯一的公开的URI,用于指定到它的数据集Uri contactData = data.getData();// 查询就是输入URI等参数,其中URI是必须的,其他是可选的,如果系统能找到URI对应的ContentProvider将返回一个Cursor对象.Cursor cursor = managedQuery(contactData, null, null, null, null);cursor.moveToFirst();// 获得DATA表中的名字// username = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));// 条件为联系人IDString contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));// 获得DATA表中的电话号码,条件为联系人ID,因为手机号码可能会有多个Cursor phone = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = "+ contactId, null, null);while (phone.moveToNext()) {
String usernumber = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));etPhone.setText(usernumber.replace(" ", ""));}if (!phone.isClosed()) {phone.close();}
通过CONTACT_ID和cursor语句去获取联系人信息(读写联系人的权限要先有)
然而我的小米手机却只读到了一部分联系人,而且只有最近联系人才能获取到手机号码,系统栏上面显示“请选择联系人”,而饿了么打开之后是显示“请选择电话号码”。
既然这样,那应该是我的发起请求的
ContactsContract.Contacts.CONTENT_URI
不太准确
Contacts下面有很多种URI
在http://www.cnblogs.com/renyuan/archive/2012/08/28/2660297.html( 还有很多uri的集合)
找到了
ContactsContract.CommonDataKinds.Phone.CONTENT_URI
发现用了这个之后可以打开选择电话号码的页面了,但是可能因为联系人和电话号码不是在同个数据库,结果cursor没有找到联系人的号码,也就不会执行
phone.moveToNext()后的代码了。
还有这里managedQuery这个方法过时了,用CursorLoader处理。
然后,我决定还是去看看饿了么是怎么处理的(本来想应该很快解决的,毕竟反编译也要时间),解压apk后用dex2jar反编译dex,解压后有3个,用luyten查看源码,混淆加密是必须的,然后我就用adb命令
adb shell dumpsys activity | grep "mFocusedActivity"
获取当前的Activity(获取当前Activity也有个APP叫“当前Activity”,插一句,我发现酷安有很多好玩的软件,很程序员)
找到对应的包
这里的TYPE就是ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE
然后回调,在aew类中的找到初始化的时候地址有setText,回调就没看到
这里有的类aew$3,在这个dex没找到
在另外的dex找到了,这里的e就是地址
完整代码
protected void onActivityResult(int requestCode, int resultCode,Intent intent) {super.onActivityResult(requestCode,resultCode,intent);if (requestCode == GET_PHONENUM) {if (resultCode == RESULT_OK) {final CursorLoader cursorLoader = new CursorLoader(this, intent.getData(), new String[] { "data1" }, null, null, null);cursorLoader.registerListener(1,new AddressCursorLoader());cursorLoader.startLoading();}}}public class AddressCursorLoader implements Loader.OnLoadCompleteListener<Cursor> {@Overridepublic void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {if (cursor == null) {return;}final int columnIndex = cursor.getColumnIndex("data1");if (cursor.moveToFirst()) {final String string = cursor.getString(columnIndex);if (StringUtil.isValid(string)) {etPhone.setText(string.replace(" ",""));}}cursor.close();}