|
一、异步任务加载网络数据: 在Android中提供了一个异步任务的类AsyncTask,简单来说,这个类中的任务是运行在后台线程中的,并可以将结果放到UI线程中进行处理,它定义了三种泛型,分别是Params、Progress和Result,分别表示请求的参数、任务的进度和获得的结果数据。 1、使用原因: 1)是其中使用了线程池技术,而且其中的方法很容易实现调用 2)可以调用相关的方法,在开启子线程前和后,进行界面的更新 3)一旦任务多了,不用每次都new新的线程,可以直接使用 2、执行的顺序: onPreExecute()【执行前开启】--- > doInBackground() --- > onProgressUpdate() --- > onPostExecute() 3、执行过程: 当一个异步任务开启后,执行过程如下: 1)、onPreExecute(): 这个方法是执行在主线程中的。这步操作是用于准备好任务的,作为任务加载的准备工作。建议在这个方法中弹出一个提示框。 2)、doInBackground(): 这个方法是执行在子线程中的。在onPreExecute()执行完后,会立即开启这个方法,在方法中可以执行耗时的操作。需要将请求的参数传递进来,发送给服务器,并将获取到的数据返回,数据会传给最后一步中;这些值都将被放到主线程中,也可以不断的传递给下一步的onProgressUpdate()中进行更新。可以通过不断调用publishProgress(),将数据(或进度)不断传递给onProgressUpdate()方法,进行不断更新界面。 3)、onProgressUpdate(): 这个方法是执行在主线程中的。publishProgress()在doInBackground()中被调用后,才开启的这个方法,它在何时被开启是不确定的,执行这个方法的过程中,doInBackground()是仍在执行的,即子线程还在运行着。 4)、onPostExecute(): 这个方法是执行在主线程中的。当后台的子线程执行完毕后才调用此方法。doInBackground()返回的结果会作为参数被传递过来。可以在这个方法中进行更新界面的操作。 5)、execute(): 最后创建AsyncTask自定义的类,开启异步任务。 3、实现原理: 1)、线程池的创建: 在创建了AsyncTask的时候,会默认创建一个线程池ThreadPoolExecutor,并默认创建出5个线程放入到线程池中,最多可防128个线程;且这个线程池是公共的唯一一份。 2)、任务的执行: 在execute中,会执行run方法,当执行完run方法后,会调用scheduleNext()不断的从双端队列中轮询,获取下一个任务并继续放到一个子线程中执行,直到异步任务执行完毕。 3)、消息的处理: 在执行完onPreExecute()方法之后,执行了doInBackground()方法,然后就不断的发送请求获取数据;在这个AsyncTask中维护了一个InternalHandler的类,这个类是继承Handler的,获取的数据是通过handler进行处理和发送的。在其handleMessage方法中,将消息传递给onProgressUpdate()进行进度的更新,也就可以将结果发送到主线程中,进行界面的更新了。 4、需要注意的是: ①、这个AsyncTask类必须由子类调用 ②、虽然是放在子线程中执行的操作,但是不建议做特别耗时的操作,如果操作过于耗时,建议使用线程池ThreadPoolExecutor和FutureTask 示例代码:private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } } new DownloadFilesTask().execute(url1, url2, url3); 二、ListView优化: ListView的工作原理 首先来了解一下ListView的工作原理(可参见http://mobile.51cto.com/abased-410889.htm),如图: 1、如果你有几千几万甚至更多的选项(item)时,其中只有可见的项目存在内存(内存内存哦,说的优化就是说在内存中的优化!!!)中,其他的在Recycler中 2、ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的 3、当item1滚出屏幕,并且一个新的项目从屏幕低端上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新创建一个视图
一、复用convertView,减少findViewById的次数 1、优化一:复用convertView Android系统本身为我们考虑了ListView的优化问题,在复写的Adapter的类中,比较重要的两个方法是getCount()和getView()。界面上有多少个条显示,就会调用多少次的getView()方法;因此如果在每次调用的时候,如果不进行优化,每次都会使用View.inflate(….)的方法,都要将xml文件解析,并显示到界面上,这是非常消耗资源的:因为有新的内容产生就会有旧的内容销毁,所以,可以复用旧的内容。 优化: 在getView()方法中,系统就为我们提供了一个复用view的历史缓存对象convertView,当显示第一屏的时候,每一个item都会新创建一个view对象,这些view都是可以被复用的;如果每次显示一个view都要创建一个,是非常耗费内存的;所以为了节约内存,可以在convertView不为null的时候,对其进行复用 2、优化二:缓存item条目的引用——ViewHolder findViewById()这个方法是比较耗性能的操作,因为这个方法要找到指定的布局文件,进行不断地解析每个节点:从最顶端的节点进行一层一层的解析查询,找到后在一层一层的返回,如果在左边没找到,就会接着解析右边,并进行相应的查询,直到找到位置(如图)。因此可以对findViewById进行优化处理,需要注意的是: 》》》》特点:xml文件被解析的时候,只要被创建出来了,其孩子的id就不会改变了。根据这个特点,可以将孩子id存入到指定的集合中,每次就可以直接取出集合中对应的元素就可以了。
优化: 在创建view对象的时候,减少布局文件转化成view对象的次数;即在创建view对象的时候,把所有孩子全部找到,并把孩子的引用给存起来 ①定义存储控件引用的类ViewHolder 这里的ViewHolder类需要不需要定义成static,根据实际情况而定,如果item不是很多的话,可以使用,这样在初始化的时候,只加载一次,可以稍微得到一些优化 不过,如果item过多的话,建议不要使用。因为static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(比如Context的情况最多),这时就要尽量避免使用了。 class ViewHolder{ //定义item中相应的控件 } ②创建自定义的类:ViewHolder holder = null; ③将子view添加到holder中: 在创建新的listView的时候,创建新的ViewHolder,把所有孩子全部找到,并把孩子的引用给存起来 通过view.setTag(holder)将引用设置到view中 通过holder,将孩子view设置到此holder中,从而减少以后查询的次数 ④在复用listView中的条目的时候,通过view.getTag(),将view对象转化为holder,即转化成相应的引用,方便在下次使用的时候存入集合。 通过view.getTag(holder)获取引用(需要强转) 示例代码: public class ActivityDemo extends Activity { private ListView listview1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); listview1 = (ListView) findViewById(R.id.listview1); MyAdapter adapter = new MyAdapter(); listview1.setAdapter(adapter); } private class MyAdapter extends BaseAdapter{ @Override public int getCount() { return 40; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if(convertView!=null && convertView instanceof RelativeLayout){ //注意:这里不一定用RelativeLayout,根据XML文件中的根节点来确定 holder = (ViewHolder) convertView.getTag(); }else{ //1、复用历史缓存view对象,检索布局问转化成view对象的次数 convertView = View.inflate(ActivityDemo.this, R.layout.item, null); //2、在创建view对象的时候,把所有的子view找到,把子view的引用存起来 holder = new ViewHolder(); holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon); holder.tvContent = (TextView) convertView.findViewById(R.id.tv_content); convertView.setTag(holder); /* 实现存储子view引用的另一种方式: convertView.setTag(holder.ivIcon); convertView.setTag(holder.tvContent); */ } //直接复用系统提供的历史缓存对象convertView return convertView; } } class ViewHolder{ public ImageView ivIcon; public TextView tvContent; } }
|