Android-AsyncTask异步任务(获取手机联系人)
本篇随笔将讲解一下Android的多线程的知识,以及如何通过AsyncTask机制来实现线程之间的通信。
一、Android当中的多线程
在Android当中,当一个应用程序的组件启动的时候,并且没有其他的应用程序组件在运行时,Android系统就会为该应用程序组件开辟一个新的线程来执行。默认的情况下,在一个相同Android应用程序当中,其里面的组件都是运行在同一个线程里面的,这个线程我们称之为Main线程。当我们通过某个组件来启动另一个组件的时候,这个时候默认都是在同一个线程当中完成的。当然,我们可以自己来管理我们的Android应用的线程,我们可以根据我们自己的需要来给应用程序创建额外的线程。
二、Main Thread 和 Worker Thread
在Android当中,通常将线程分为两种,一种叫做Main Thread,除了Main Thread之外的线程都可称为Worker Thread。
当一个应用程序运行的时候,Android操作系统就会给该应用程序启动一个线程,这个线程就是我们的Main Thread,这个线程非常的重要,它主要用来加载我们的UI界面,完成系统和我们用户之间的交互,并将交互后的结果又展示给我们用户,所以Main Thread又被称为UI Thread。
Android系统默认不会给我们的应用程序组件创建一个额外的线程,所有的这些组件默认都是在同一个线程中运行。然而,某些时候当我们的应用程序需要完成一个耗时的操作的时候,例如访问网络或者是对数据库进行查询时,此时我们的UI Thread就会被阻塞。例如,当我们点击一个Button,然后希望其从网络中获取一些数据,如果此操作在UI Thread当中完成的话,当我们点击Button的时候,UI线程就会处于阻塞的状态,此时,我们的系统不会调度任何其它的事件,更糟糕的是,当我们的整个现场如果阻塞时间超过5秒钟(官方是这样说的),这个时候就会出现 ANR (Application Not Responding)的现象,此时,应用程序会弹出一个框,让用户选择是否退出该程序。对于Android开发来说,出现ANR的现象是绝对不能被允许的。
另外,由于我们的Android UI控件是线程不安全的,所以我们不能在UI Thread之外的线程当中对我们的UI控件进行操作。因此在Android的多线程编程当中,我们有两条非常重要的原则必须要遵守:
- 绝对不能在UI Thread当中进行耗时的操作,不能阻塞我们的UI Thread
- 不能在UI Thread之外的线程当中操纵我们的UI元素
三、如何处理UI Thread 和 Worker Thread之间的通信
既然在Android当中有两条重要的原则要遵守,那么我们可能就有疑问了?我们既不能在主线程当中处理耗时的操作,又不能在工作线程中来访问我们的UI控件,那么我们比如从网络中要下载一张图片,又怎么能将其更新到UI控件上呢?这就关系到了我们的主线程和工作线程之间的通信问题了。在Android当中,提供了两种方式来解决线程直接的通信问题,一种是通过Handler的机制(这种方式在后面的随笔中将详细介绍),还有一种就是今天要详细讲解的 AsyncTask 机制。
四、AsyncTask
AsyncTask:异步任务,从字面上来说,就是在我们的UI主线程运行的时候,异步的完成一些操作。AsyncTask允许我们的执行一个异步的任务在后台。我们可以将耗时的操作放在异步任务当中来执行,并随时将任务执行的结果返回给我们的UI线程来更新我们的UI控件。通过AsyncTask我们可以轻松的解决多线程之间的通信问题。
怎么来理解AsyncTask呢?通俗一点来说,AsyncTask就相当于Android给我们提供了一个多线程编程的一个框架,其介于Thread和Handler之间,我们如果要定义一个AsyncTask,就需要定义一个类来继承AsyncTask这个抽象类,并实现其唯一的一个 doInBackgroud 抽象方法。要掌握AsyncTask,我们就必须要一个概念,总结起来就是: 3个泛型,4个步骤。
3个泛型指的是什么呢?我们来看看AsyncTask这个抽象类的定义,当我们定义一个类来继承AsyncTask这个类的时候,我们需要为其指定3个泛型参数:
AsyncTask <Params, Progress, Result>
- Params: 这个泛型指定的是我们传递给异步任务执行时的参数的类型
- Progress: 这个泛型指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型
- Result: 这个泛型指定的异步任务执行完后返回给UI线程的结果的类型
我们在定义一个类继承AsyncTask类的时候,必须要指定好这三个泛型的类型,如果都不指定的话,则都将其写成Void,例如:
AsyncTask <Void, Void, Void>
4个步骤:当我们执行一个异步任务的时候,其需要按照下面的4个步骤分别执行
- onPreExecute(): 这个方法是在执行异步任务之前的时候执行,并且是在UI Thread当中执行的,通常我们在这个方法里做一些UI控件的初始化的操作,例如弹出要给ProgressDialog
- doInBackground(Params... params): 在onPreExecute()方法执行完之后,会马上执行这个方法,这个方法就是来处理异步任务的方法,Android操作系统会在后台的线程池当中开启一个worker thread来执行我们的这个方法,所以这个方法是在worker thread当中执行的,这个方法执行完之后就可以将我们的执行结果发送给我们的最后一个 onPostExecute 方法,在这个方法里,我们可以从网络当中获取数据等一些耗时的操作
(ps:需要注意的是Android为了安全考虑,不允许在主线程即UI线程进行耗时操作。例如HTTP请求等。
如果在UI中使用了耗时操作的话,Android Studio或者eclipse本身是不会报错的。只有在APP执行到相应的耗时操作位置时才会停止运行。手机或模拟器上会出现“很抱歉,XXX已停止运行”同时Android Studio logcat输出“
E/AndroidRuntime: FATAL EXCEPTION: main
Process:.....java.lang.RuntimeException.....
)
- onProgressUpdate(Progess... values): 这个方法也是在UI Thread当中执行的,我们在异步任务执行的时候,有时候需要将执行的进度返回给我们的UI界面,例如下载一张网络图片,我们需要时刻显示其下载的进度,就可以使用这个方法来更新我们的进度。这个方法在调用之前,我们需要在 doInBackground 方法中调用一个 publishProgress(Progress) 的方法来将我们的进度时时刻刻传递给 onProgressUpdate 方法来更新
- onPostExecute(Result... result): 当我们的异步任务执行完之后,就会将结果返回给这个方法,这个方法也是在UI Thread当中调用的,我们可以将返回的结果显示在UI控件上
为什么我们的AsyncTask抽象类只有一个 doInBackground 的抽象方法呢??原因是,我们如果要做一个异步任务,我们必须要为其开辟一个新的Thread,让其完成一些操作,而在完成这个异步任务时,我可能并不需要弹出要给ProgressDialog,我并不需要随时更新我的ProgressDialog的进度条,我也并不需要将结果更新给我们的UI界面,所以除了 doInBackground 方法之外的三个方法,都不是必须有的,因此我们必须要实现的方法是 doInBackground 方法。
下面举个获取手机联系人的栗子:
布局文件如下:
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.contentproviderdemo.MainActivity" > <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_id1"
android:text="id"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_name1"
android:text="姓名"
android:layout_weight="1"
android:layout_marginLeft="5dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_ptype1"
android:text="号码类型"
android:layout_weight="1"
android:layout_marginLeft="5dp"
/> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_phonenum1"
android:text="号码"
android:layout_weight="1"
android:layout_marginLeft="5dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_etype1"
android:text="邮箱类型"
android:layout_weight="1"
android:layout_marginLeft="5dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_email1"
android:text="邮箱"
android:layout_weight="1"
android:layout_marginLeft="5dp"
/> </LinearLayout> <ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
> </ListView>
<Button
android:id="@+id/query_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="获取"
/> </LinearLayout>
activity_main.xml
item_view.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/tx_id"
android:text="id"
android:layout_weight="1"
android:layout_marginLeft="5dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_name"
android:text="姓名"
android:layout_weight="1"
android:layout_marginLeft="5dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_ptype"
android:text="号码类型"
android:layout_weight="1"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_phonenum"
android:text="号码"
android:layout_weight="1"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_etype"
android:text="邮箱类型"
android:layout_weight="1"
android:layout_marginLeft="5dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tx_email"
android:text="邮箱"
android:layout_weight="1"
android:layout_marginLeft="5dp"
/> </LinearLayout>
item_view.xml
新建一个实体类,保存联系人的id,姓名,号码类型和号码,邮箱类型和邮箱
package com.example.contentproviderdemo; public class CallPerson {
private int id;
private String name;
private String type;
private String phoneNum;
private String emailtype;
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
} public String getEmailtype() {
return emailtype;
}
public void setEmailtype(String emailtype) {
this.emailtype = emailtype;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public CallPerson() {
super();
// TODO Auto-generated constructor stub
} }
CallPerson.java
新建一个MyTask类继承AsyncTask
package com.example.contentproviderdemo; import java.util.ArrayList;
import java.util.List; import android.content.ContentResolver;
import android.database.Cursor;
import android.os.AsyncTask;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast; public class MyTask extends AsyncTask { private BaseAdapter adapter;
private MainActivity activity;
private ContentResolver cr;
private List<CallPerson> list;
public MyTask(MainActivity activity){
this.activity = activity;
} //1.所有耗时的代码,写到这里来(数据库、蓝牙、网络服务)
//2.绝对不能碰UI
@Override
protected Object doInBackground(Object... params) {
cr = (ContentResolver) params[0];
int i = 0;
//查询联系人的id和姓名
Cursor c = cr.query(Contacts.CONTENT_URI, new String[]{Contacts._ID,Contacts.DISPLAY_NAME}, null, null, null);
List<CallPerson> l = new ArrayList<CallPerson>();
if(c != null){
String log = "";
while(c.moveToNext()){
CallPerson cp = new CallPerson();
int id = c.getInt(c.getColumnIndex(Contacts._ID));
String name = c.getString(c.getColumnIndex(Contacts.DISPLAY_NAME));
//根据联系人id查询电话号码
Cursor c1 = cr.query(Phone.CONTENT_URI, new String[]{Phone.NUMBER,Phone.TYPE}, Phone.CONTACT_ID + "=" + id, null, null);
if(c1!=null){
while(c1.moveToNext()){
int type = c1.getInt(c1.getColumnIndex(Phone.TYPE));
String typestr = "";
if(type == Phone.TYPE_HOME){
typestr = "家庭电话:";
}else if(type == Phone.TYPE_MOBILE){
typestr = "手机:";
}
String phoneNum = c1.getString(c1.getColumnIndex(Phone.NUMBER));
log += "_id : "+id+" 姓名 :"+ name +" "+typestr+" "+phoneNum;
cp.setId(id);
cp.setName(name);
cp.setPhoneNum(phoneNum);
cp.setType(typestr);
}
c1.close();
}
//根据联系人的ID去查询联系人的邮箱地址
Cursor c2 = cr.query(Email.CONTENT_URI, new String[]{Email.DATA,Email.TYPE}, Phone.CONTACT_ID + "=" + id, null, null);
if(c2!=null){
while(c2.moveToNext()){
String typestr = "邮箱:";
int type = c2.getInt(c2.getColumnIndex(Email.TYPE));
if(type == Email.TYPE_WORK){
typestr = "工作邮箱:";
}
String email = c2.getString(c2.getColumnIndex(Email.DATA));
log += " "+typestr+" "+email;
cp.setEmailtype(typestr);
cp.setEmail(email);
} c2.close();
} l.add(cp);
}
Log.i("info", log);
c.close();
} return l;
} //开始前准备
@Override
protected void onPreExecute() { Toast.makeText(activity, "准备读取...", Toast.LENGTH_SHORT).show(); } //做完后执行
@Override
protected void onPostExecute(Object result) {
list = (List<CallPerson>) result;
//获取listView
ListView lv = (ListView) activity.findViewById(R.id.listView1);
//配置适配器
adapter = new BaseAdapter(){ @Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = activity.getLayoutInflater();
View view;
if (convertView==null){
view = inflater.inflate(R.layout.item_view, null);
}
else{
view = convertView;
}
CallPerson c = list.get(position);
TextView tx_id = (TextView)view.findViewById(R.id.tx_id);
TextView tx_name = (TextView)view.findViewById(R.id.tx_name);
TextView tx_ptype = (TextView)view.findViewById(R.id.tx_ptype);
TextView tx_phonenum = (TextView)view.findViewById(R.id.tx_phonenum);
TextView tx_etype = (TextView)view.findViewById(R.id.tx_etype);
TextView tx_email = (TextView)view.findViewById(R.id.tx_email);
tx_id.setText(c.getId()+"");
tx_name.setText(c.getName());
tx_ptype.setText(c.getType());
tx_phonenum.setText(c.getPhoneNum());
tx_etype.setText(c.getEmailtype());
tx_email.setText(c.getEmail());
return view;
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
} @Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
};
//给ListView设置适配器
lv.setAdapter(adapter);
Toast.makeText(activity, "读取完成...", Toast.LENGTH_SHORT).show(); } }
MyTask
在MainActivity中给button绑定点击事件,调用MyTask;
package com.example.contentproviderdemo; import android.app.Activity;
import android.content.ContentResolver;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.BaseAdapter;
import android.widget.Button; public class MainActivity extends Activity {
private Button btn;
private ContentResolver cr;
private BaseAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button) findViewById(R.id.query_btn);
cr = getContentResolver();
btn.setOnClickListener(new OnClickListener() { @Override
public void onClick(View arg0) { MyTask mt = new MyTask(MainActivity.this);
mt.execute(cr);//里面的参数是传给doInBackground }
}); }
MainActivity
效果图如下:
首先是界面
点击获取之后,会进去子线程读取数据
完成之后效果去下:
不要问我邮箱为什么空白,因为我手机的联系人没有一个存邮箱的
强制关闭的效果:
案例就到此为止,这个案例是我学习四大组件中ContentProvider的简单应用
那么为什么这个案例会拿异步来做呢,原因上面也说了,联网读取数据, 或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,,Android主线程是线程不安全的, 也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。
如果你的手机联系人(也就是你要取的数据)不是很多的话,那不用异步也不会强制关闭,但数据量过大的话,那么他就会进去假死状态。。。。
Android-AsyncTask异步任务(获取手机联系人)的更多相关文章
- android利用ContentResolver访问者获取手机联系人信息
转载自:http://www.jb51.net/article/106379.htm 首先需要在AndroidManifest.xml文件中添加权限: <uses-permission andr ...
- Android软件开发之获取通讯录联系人信息
Android手机的通讯录联系人全部都存在系统的数据库中,如果须要获得通讯里联系人的信息就须要访问系统的数据库,才能将信息拿出来. 这一篇文章我主要带领同学们熟悉Android的通讯录机制. 图中选中 ...
- Expo大作战(三十九)--expo sdk api之 DocumentPicker,Contacts(获取手机联系人信息),Branch
简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...
- Android 获取手机联系人信息
//获取联系人 Uri rawContacts = Uri.parse("content://com.android.contacts/raw_contacts"); Conten ...
- android AsyncTask异步下载并更新进度条
AsyncTask异步下载并更新进度条 //如果不是很明白请看上篇文章的异步下载 AsyncTask<String, Integer, String> 第一个参数:String 传入 ...
- Android AsyncTask异步加载WebAPI
之前做的程序一直存在很多问题,因为需要加载的Activity需要从网络加载数据.并没有完全正确的使用异步的方法去加载! 之前用的虽然是AsyncTask,但是在加载完成的时候还是并没有使用AsyncT ...
- 获取手机联系人项目 PPGetAddressBook
PPGetAddressBook PPGetAddressBook对AddressBook框架(iOS9之前)和Contacts框架(iOS9之后)做了对应的封装处理; 支持获取按联系人姓名首字拼音A ...
- Android AsyncTask异步任务(二)
之前我们讲过了AsyncTask 的生命周期(onPreExecute-->doInBackground-->onProgressUpdate-->onPostExecute),今天 ...
- Android --AsyncTask异步任务(一)
1.为什么要异步任务 Android单线程模式 耗时操作放在非主线程(UI线程)中执行 我们都知道Android是单线程模式,只有主线程才能对UI操作,简称UI线程.当然这样的好处是:保证UI的稳定性 ...
随机推荐
- zk框架中利用map类型传值来创建window,并且传值
@Command @NotifyChange("accList") public void clear(@BindingParam("id") String a ...
- shell 和awk性能对比
time for ((i=0;i<10000;i++)) do ((sum+=i)); done real 0m0.086suser 0m0.079ssys 0m0.007s ...
- java中String byte HexString的转换
原文:http://blog.sina.com.cn/s/blog_62e9ec530101ebv6.html HexString——>byte public static byte[] hex ...
- ruby -- 进阶学习(九)定制错误跳转404和500
在开发阶段,如果发生错误时,都会出现错误提示页面,比如:RecordNotFound之类的,虽然这些错误方便开发进行debug,但是等产品上线时,如果还是出现这些页面,对于用户来说是很不友好的. 所以 ...
- 修复AWS上EC2损坏的sshd_config文件
常识: AWS是没有root用户的,登陆也都是通过SSH KEY完成授权认证. 背景: 正在AWS上搭一个CI (GO),与gitlab,为了将其进行集成,需将gitlab的deploy key设置成 ...
- 美了美了!22款精美的 iOS 应用程序图标模板
22款制作精美的 iOS 应用程序图标设计作品,遵循图形设计的现代潮流,所有图标都非常了不起,给人惊喜.通过学习这些移动应用程序图标,设计人员可以提高他们的创作,使移动用户界面看起来更有趣和吸引人. ...
- IOS开发UI基础UISegment属性
UISegment属性 1.segmentedControlStyle设置segment的显示样式.typedef NS_ENUM(NSInteger, UISegmentedControlStyle ...
- linux中$与()的一点使用疑惑解释
a=$(cat 1.sh)等价于a=`cat 1.sh` 而a=(cat 1.sh) 相当于定义一个a数组,内容为cat 1.sha=(`cat 1.sh`)相当于把1.sh里面的内容当成a的数组,a ...
- .NET 配置文件简单使用
当我们开发系统的时候要把一部分设置提取到外部的时候,那么就要用到.NET的配置文件了.比如我的框架中使用哪个IOC容器需要可以灵活的选择,那我就需要把IOC容器的设置提取到配置文件中去配置.实现有几种 ...
- 重构第29天 去除中间人对象(Remove Middle Man)
理解:本文中的”去除中间人对象”是指把 在中间关联而不起任何其他作用的类移除,让有关系的两个类直接进行交互. 详解:有些时候在我们的代码会存在一些”幽灵类“,设计模式大师Martin Fowler称它 ...