4月 192015
 

一、为什么要使用多线程?

  默认情况下,当一个Android应用启动后,应用内部运行的线程只有一个,就是应用的UI线程,负责界面的展示以后用户和屏幕元素的互动。那么当我们需要进行网络下载、图片加载、复杂运算等比较耗时的操作时,如果也要主线程中进行,UI界面就没有足够的CPU时钟进行处理了,就会造成界面卡死的情况,这就是我们所说的ANR(Application Not Responding)。所以,为了防止出现这种情况,像这些耗时的操作我就需要开一个子线程去进行处理。

二、Runnable接口

  我们在网上看线程的讲解的时候经常会看到Runnable这个接口的使用,其实Runnable只声明了一个接口函数,就是run()函数,它的作用就是给各种的线程类去继承并实现run方法。比如:FutureTask<V>、Thread、TimerTask等都实现了这个接口。我们只要记住它是一个只声明了run()函数的接口就好了。

三、使用Thread类实现多线程

  上面是一个Thread线程的例子,在这里要注意:

  1. Thread启动的线程不能直接访问UI组件,要通过组件的.post方法访问,如上面的textView.post(new Runnable() {})
  2. 要使用start()方法启动线程,不可以使用run()方法

四、使用AsyncTask实现多线程

上面是一个AsyncTask的例子,

  • onPreExecute:doInBackground运行之前的函数,可以操作UI的组件
  • doInBackground: 耗时的操作(网络下载、图像处理等)放到这个函数里
  • onPostExecute:doInBackground运行之后的函数,可以操作UI的组件
  • onProgressUpdate:用于在UI显示运算进度,需要在doInBackground里调用publishProgress

AsyncTask的优点:

  1. 结构清晰,易于理解
  2. 可显示进度
  3. 可方便操作UI组件

AsyncTask的缺点:

  1. 不能多个实例同时运行,每次要创建一个新的实例,比如myTask.execute(“OK”);就会抛异常

五、HandlerThread

  HandlerThread适用于在一个线程中执行多个实时性不高的操作.具体参考下面的链接

  对HandlerThread的理解

六、ExectuorService线程池

七、Handler的post、postDelayed的执行方法并不是在子线程中执行,而是在主线程中执行

八、FutureTask、TimerTask继承自Runnable接口,并不是实现多线程的类,只是实现多线程的辅助类,如new Thread(futureTask).start();这样的。他们与直接使用Runnable接口的差别如下:

  1. FutureTask可以取消,可以返回结果
  2. TimerTask可以定时执行

 

4月 172015
 

一般Android的默认图片编辑器是支持对图片进行剪裁操作的,所以如果我们的应用需要对图片进行剪裁操作,调用系统的图片编辑器即可,代码如下:

如果输出到文件(设置MediaStore.EXTRA_OUTPUT的值):

如果在Activity中处理返回的图片流(return-data的值为true):

 

 

2月 262015
 

  Home键:默认情况下,android中的home键只是切换到系统的home界面,并不会销毁当前的activity,即不会执行activity中的ondestroy方法,从任务栈中恢复那个应用的话,activity中的数据不会丢失

  Back键:而android中的back键,则先销毁当前activity,然后回到之前的应用界面中,而且此时activity中的数据都丢失了。

  如果我们想在按back键的时候,不销毁当前的activity,保留它的数据和状态的话,我们可以改写源码中对back键的处理来达到这个目的,代码如下:

  当然这样做的缺点时,之前打开的应用全部都被关闭掉了,比如用户在用浏览器看新闻,通过通知栏消息打开了你的应用,在你的应用中按back键就回到了home界面,前面的浏览器也被关掉了。

12月 252014
 

  SharedPreferences貌似很简单的一个类,却也存在着陷阱,如下面,我们写了几行简单的保存配置的代码,但却隐含着bug

  那么为什么这段代码不能保存runcount的数据呢,因为prefs.edit()每次返回一个新的Editor对象,所以执行两次prefs.edit()其实调用的是两个Editor对象,如下adnroid源码中的解释

  因此,我们应该先将prefs.edit()赋值给一个Editor对象,然后由该对象进行存值和提交操作,如下:

 

12月 212014
 

  出现此问题的原因是你的项目使用到的android的appcompat支持库,但你目前的开发环境中并没有引用此支持库。下面图文介绍如何下载并引用该类库。

  1、首先需要在android sdk manager中下载Android Support Library,在Eclipse中打开菜单Window->Android SDK Manager,在sdk manager窗口中看你是否已经下载了Android Support Library,在最下面的Extras的节点中有此项,如果没有则选中此项并下载,如下图

Download Android Support Library

  类库将会下载到你的sdk子目录中,路径为<sdk目录>extrasandroidsupport。如果你使用的Android Studio开发环境,则需要下载Android Support Respository。

  2、将类库项目导入到开发环境中。在Eclipse菜单中打开File->New->Project,选择Android Project From Existing Code

Create Project From Existing Code

  3、点击”Browser…”按钮选择appcompat所在的路径,<sdk目录>extrasandroidsupportv7appcompat,勾选Copy projects into workspace,最后点击“Finish”按钮,导入项目成功。

Import android v7 appcompat

  4、将appcompat库引用到你的项目中。在你的项目上右键选择属性菜单,在左侧的导航列表中选择“Android”,在右侧内容最下的Library区域点击“Add”按钮,将导入的类库项目添加进来就完成了。

Add android v7 appcompat reference

  5、重新Clean你的项目。点击Eclipse的菜单Project->Clean…,在弹出的窗口中选择你要清除的项目,点击ok,清除结束后上述问题应该就可以解决了。

  关于Android支持库的其他特性可以点击这里查询。

 

12月 012014
 

  在有些APP中我们需要实现一个悬浮按钮,比如图片浏览应用左右翻页功能,比如左侧悬浮功能按钮。我们要实现此功能时,最开始想到的就是用FrameLayout来实现,但是如果把按钮简单的放到FrameLayout中,按钮只能在布局的左上角,并不能达到我们的要求。

  那么如何才能让按钮放到布局的左侧中间位置呢,开始时想把按钮高度设置成match_parent,然后图片垂直居中,但是这样整个最左侧的部分都是按钮了,显然会影响其他功能的使用,后来想哪个组件才能自由的让子控件放到想放的位置呢,这个得是RelativeLayout了吧。于是在FrameLayout中放了一个RelavtiveLayout中,然后把按钮放到里面,Layout Parameters中的Align Parent Left设置成true,Center Vertical设置成true,再看一下,一个居中的悬浮按钮就弄好了。

  同样的方式可以设置其他需要的悬浮按钮了。

 

 Posted by on 2014-12-01
11月 142014
 

  做了一个小的app,其中用到了tab形式的actionBar和多个fragment,后来因为需求需要在某两个fragment中加入context menu,加入后添加onContextItemSelected事件响应菜单按下的效果,一开始运行正常,在两个fragment中打开context menu正常,两个fragment中的菜单响应事件也正常,后来改动了一些代码后突然发现菜单响应的不正确了,只有一个fragment中的菜单有响应,另外一个没响应。经过一番修改后,发现是onContextItemSelected的返回值影响到了菜单点击事件的执行。

  我们先来看一下onContextItemSelected的官方描述:

  我们主要看这个返回值部分,返回为了一布尔型变量,当返回false值时由系统去决定此菜单事件的响应流程,返回true则表示菜单响应到此为止,停止后面的流程。

  本人没做深入研究,猜想大概流程是这样的,因为在fragment中调用context menu中通过如下方式

  是通过父activity来填充这个弹出菜单,所以首先是父activity先接收到onContextItemSelected事件,如果父activity中不做处理,则activity会把这个事件分发到各个fragment中去,本人在此次犯的错误就是在子fragment中的onContextItemSelected事件中返回了true值,所以只在一个fragment中响应了事件,另外一个没有响应。

  既然在所有的fragment中都会响应onContextItemSelected这个事件,那么我们在每个fragment中处理该事件的时候就需要做一些判断,以避免多个fragment同时执行这个事件,这个是非常重要的,因为不做判断很可能出现一些莫名奇妙的而且不好测试的问题。如下例子:

 

11月 012014
 

  ContextMenu相当于windows中的右键弹出菜单,在android中一般要长按某个view才会激活这个弹出菜单,但是有些情况下我们想单击就可以打开这个菜单,以提高用户的使用体验。那么如何才能单击就能打开这个菜单呢,其实很简单,我们在view的onClick事件中调用openContextMenu()即可打开这个弹出菜单了。下面是一个代码片断:

  imgcapture是一个ImageView,此代码执行后,单击imgcapture即可触发onCreateContextMenu事件,然后弹出菜单。另外,必须要调用registerForContextMenu注册这个菜单,否则单击和长按都无法弹出这个菜单。

  如果要弹出比较复杂的菜单的话就用使用Dialog来实现了。

10月 292014
 

  对于获取数据库中的记录数量的方法,其实应该是很简单的问题,之所以发文把此方法写出来,是因为有一些需要地方需要注意,避免其他人再在此问题上浪费时间。

方法一:

  这是一个很普通的方法,通过sql语句获取数量并赋给一个sql别名字段,然后通过这个别名来获取记录的数量,但是需要注意的是必须要有cursor.moveToFirst(),这个函数,因为rawQuery执行完成后返回的Cursor不是定位到第一条记录的,而是定位到第一条记录之前,所以必须要moveToFirst让记录定位一下才能读取数据,同样地获取其他的数据的时候也要在其之前调用此函数。

  如果是读取多条记录的时候我们一般会用

方法二:

  此方法需要注意的是,sql语句select的时候最好不要select *这样会把所有字段的数据都读出来,浪费内存,效率低。

10月 102014
 

  从网上看到的代码,在项目的src目录下新建一个widget的package,然后新建一个VerticalSeekbar的类然后把下面的代码复制到里面。在layout设计界面左侧的组件列表的自定义组件中刷新一下即可看到这个组件了。