显示标签为“AndroidDev”的博文。显示所有博文
显示标签为“AndroidDev”的博文。显示所有博文

Android首次打开应用介绍界面实现

本文参考ToolbarMenudrawer开源项目实现。他使用的方法是最一般最通用的做法。

基本思想利用sharedpreferences记录用户是否第一次打开,如果第一次打开应用,跳转到介绍Activity,记录sharedpreferences。

SharedPreferences first = PreferenceManager
               .getDefaultSharedPreferences(this);

if (!first.getBoolean("firstTime", false)) {

    Intent intent = new Intent(this, FirstRun.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    startActivity(intent);
    finish();

    //set the flag fot firsttime
    SharedPreferences.Editor editor = first.edit();
    editor.putBoolean("firstTime", true);
    editor.commit();
}  

看到这里或许会有个疑惑,为什么已经调用了finish()方法,下面三句设置SharedPreferences的代码还能生效。经过查询Android中在调用Activity的finish方法,程序会继续执行到finish遇到的一个块结束,也就是“}”括号结束。参考:http://stackoverflow.com/a/6750685/1820217
如果想要强制结束Activity可以在finish之后添加return语句。

Android SDK环境变量配置

Android SDK就是Google提供的Android开发工具包,之前请先配置好JDK

Android SDK下载地址

配置Android SDK环境变量

  1. 下载Android SDK,点击安装,直接默认路径即可! 下载地址:https://developer.android.com/sdk/index.html

  2. 默认路径安装后,安装完成,开始配置环境变量。

  3. 打开计算机属性——高级系统设置——环境变量(如上文)

  4. 新建一个环境变量,变量名:ANDROID_HOME,变量值:D:\Android\android-sdk(以你安装目录为准,确认里面有tools和add-ons等多个文件夹),点击确认。

  5. 在用户变量PATH后面加上变量值;%ANDROID_HOME%\platform-tools;%ANDROID_HOME%\tools;点击确认即可

  6. Android SDK配置完成,接下来验证配置是否成功。

  7. 点击运行——输入cmd——回车——输入adb——回车,如果出现一堆英文,即表示配置成功,输入Android,启动Android SDK Manager。

目录tools和platform-tools的区别

Android sdk目录里,有一些文件夹:

  • tools:该目录存放大量的Android开发,调试工具,该目录下存放大量Android开发工具,例如SDK Manager、androidavd、emulator、ddms等等。
  • platforms-tools:该文件夹存放Android平台和相关工具,存放Android不同平台的相关工具;随着SDK更新版本,这里的工具会有相应更新变化,但是一般都是向后兼容。最常用的是Android Debug Bridge(adb)工具
  • add-ons:该目录存放额外的附件软件。刚解压时该目录为空。
  • platforms:该目录存放不同版本的Android版本。刚解压时该目录为空。
  • SDK Manager.exe:该程序就是Andriod SDK管理器。
  • AVD Manager.exe:该程序就是Andoid虚拟设备。
  • docs:该文件夹存放了Android SDK开发文件和API文档等
  • samples:该文件夹存放不同的Android平台和示例程序。

参考:http://blog.csdn.net/rflyee/article/details/8973529

常用Intent

以下是常用到的Intent的URI及其示例,包含了大部分应用中用到的共用Intent

一、打开一个网页,类别是Intent.ACTION_VIEW

Uri uri = Uri.parse("http://www.android-study.com/");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);

二、打开地图并定位到一个点

Uri uri = Uri.parse("geo:52.76,-79.0342");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);

三、打开拨号界面,类型是Intent.ACTION_DIAL

Uri uri = Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_DIAL, uri);

四、直接拨打电话,与三不同的是,这个直接拨打电话,而不是打开拨号界面

Uri uri = Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_CALL, uri);

五、卸载一个应用,Intent的类别是Intent.ACTION_DELETE

Uri uri = Uri.fromParts("package", "xxx", null);
Intent intent = new Intent(Intent.ACTION_DELETE, uri);

六、安装应用程序,Intent的类别是Intent.ACTION_PACKAGE_ADDED

Uri uri = Uri.fromParts("package", "xxx", null);
Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED, uri);

七、播放音频文件

Uri uri = Uri.parse("file:///sdcard/download/everything.mp3");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setType("audio/mp3");

八、打开发邮件界面

Uri uri= Uri.parse("mailto:admin@android-study.com");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);

九、发邮件,与八不同这里是将邮件发送出去

Intent intent = new Intent(Intent.ACTION_SEND);
String[] tos = { "admin@android-study.com" };
String[] ccs = { "webmaster@android-study.com" };
intent.putExtra(Intent.EXTRA_EMAIL, tos);
intent.putExtra(Intent.EXTRA_CC, ccs);
intent.putExtra(Intent.EXTRA_TEXT, "I come from http://www.android-study.com");
intent.putExtra(Intent.EXTRA_SUBJECT, "http://www.android-study.com");intent.setType("message/rfc882");
Intent.createChooser(intent, "Choose Email Client");

//发送带附件的邮件

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_SUBJECT, "The email subject text");
intent.putExtra(Intent.EXTRA_STREAM, "file:///sdcard/mysong.mp3");
intent.setType("audio/mp3");
startActivity(Intent.createChooser(intent, "Choose Email Client"));

十、发短信

Uri uri= Uri.parse("tel:10086");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra("sms_body", "I come from http://www.android-study.com");
intent.setType("vnd.Android-dir/mms-sms");

或者可以
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra("address", "电话号码,这里是电话");
intent.putExtra("sms_body","短信内容,短信");
intent.setType("vnd.android-dir/mms-sms");
startActivity(intent);


十一、直接发短信

Uri uri= Uri.parse("smsto://100861");
Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
intent.putExtra("sms_body", "3g android http://www.android-study.com");

十二、发彩信

ri uri= Uri.parse("content://media/external/images/media/23");
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra("sms_body", "3g android http://www.android-study.com");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setType("image/png");

十三、# Market 相关

1 //寻找某个应用
Uri uri = Uri.parse("market://search?q=pname:pkg_name");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity(it);
//where pkg_name is the full package path for an application
 
2 //显示某个应用的相关信息
Uri uri = Uri.parse("market://details?id=app_id");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity(it);
//where app_id is the application ID, find the ID
//by clicking on your application on Market home
//page, and notice the ID from the address bar

十四、路径规划

Uri uri = Uri.parse("http://maps.google.com/maps?f=d&saddr=startLat%20startLng&daddr=endLat%20endLng&hl=en");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity(it);
//where startLat, startLng, endLat, endLng are a long with 6 decimals like: 50.123456

十五、安装指定apk

public void setupAPK(String apkname){
    String fileName = Environment.getExternalStorageDirectory() + "/" + apkname;
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");
    mService.startActivity(intent);
}

十六、进入联系人页面

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(People.CONTENT_URI);
startActivity(intent);

十七、查看指定联系人

Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, info.id);// info.id联系人ID
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(personUri);
startActivity(intent);

十八、调用相册

public static final String MIME_TYPE_IMAGE_JPEG = "image/*";
public static final int ACTIVITY_GET_IMAGE = 0;
Intent getImage = new Intent(Intent.ACTION_GET_CONTENT);
getImage.addCategory(Intent.CATEGORY_OPENABLE);
getImage.setType(MIME_TYPE_IMAGE_JPEG);
startActivityForResult(getImage, ACTIVITY_GET_IMAGE);

十九、调用系统相机应用程序,并存储拍下来的照片

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
time = Calendar.getInstance().getTimeInMillis();
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment
.getExternalStorageDirectory().getAbsolutePath()+"/tucue", time + ".jpg")));
startActivityForResult(intent, ACTIVITY_GET_CAMERA_IMAGE);

http://www.verydemo.com/demo_c131_i16393.html

Android开发过程中遇到的小问题

Android ProgressBar有一个属性indeterminate
这个属性值用来控制进度条是否为确定性进度条.确定性进度条是指在任务执行过程中,可以数值化当前进度(比如,已经完成50%) 。不确定性进度条是那种除非任务完成,否则不能数值化进度,所以我们会看到一个无限循环的动画。
http://www.lxzhu.net/blogcn/tags/progressbar
通过设置进度条android:indeterminate="true"为不确定.
如果想要设置进度条一直为进度不确定可以通过设置android:indeterminateOnly="true",否则设置为false

http://thinkblog.sinaapp.com/?p=732

[Accessibility] Missing contentDescription attribute on image

在下ADT 16.0 在定义一个ImageVIew的时候 总是提示这个[Accessibility] Missing contentDescription attribute on image警告,虽说可以不理 但总是感觉怪怪的,在网上一搜 发现原来这是ADT 16.0的新特性,在一些没有文本显示的控件里,如imageView和imageButton等,ADT会提示你定义一个android:contentDescription属性,用来描述这个控件的作用。

http://blog.csdn.net/hn307165411/article/details/7179317

安装Android Studio

Google在 #IO13 上面发布了Android Studio,一款全新的Android开发IDE环境,撇开Eclipse+ADT。IO大会上面演示效果非常惊人,现在我来体验一下。

Getting Started with Android Studio

如果不出意外的话,跟随者安装程序一步一步下去就能够完成安装了。而我在安装过程中出现了找不到JDK的错误,打开安装程序,点击下一步,查找Java SE Development Kit的时候会直接程序崩溃,报一个"$(^name)"乱码的错误(我的计算机环境:Windows 7 Ultimate旗舰版SP1 x64位,JDK版本Java\jdk1.7.0_07\)。
经过Google之后StackOverflow的帖子帮助很大,看完我怀疑是我JDK版本的问题,所以我去下载了最新版本的JDK(1.7.0_21)。安装之后也不用修改环境变量,不用做任何修改安装成功。
出现下面截图。
找到JDK路径
安装过程:
安装完成之后会要求你选择配置文件,我又从来没用过这个IDE,所以I do not have
然后就看到了令人欣喜的一幕:
这里你可以选择新建Project,或者你可以按照官方的这篇教程将Eclipse下的工程转移到Android Studio下。
如果你还不知道过程,这里还有一段视频:


如果你在新建项目的时候发现下载”http://services.gradle.org/distributions/gradle-1.6-bin.zip“这里东西卡死,设置代理吧。

Android多线程(Thread、Runnable)

Android多线程(Thread、Runnable)以及Timer(Timer部分

认识Thread和Runnable

Java中实现多线程有两种途径:继承Thread类或者实现Runnable接口。Runnable是接口,建议用接口的方式生成线程,因为接口可以实现多继承,况且Runnable只有一个run方法,很适合继承。在使用Thread的时候只需继承Thread,并且new一个实例出来,调用 start()方法即可以启动一个线程。

Thread Test = new Thread();

Test.start();

在使用Runnable的时候需要先new一个实现Runnable的实例,之后启动Thread即可。

Test impelements Runnable;

Test t = new Test();

Thread test = new Thread(t);

test.start();

总结:Thread和Runnable是实现java多线程的2种方式,runable是接口,thread是类,建议使用runable实现 java多线程,不管如何,最终都需要通过thread.start()来使线程处于可运行状态。

认识Thread的start和run

1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的 start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法,这里方法 run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。

2) run:
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。

总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。

线程状态说明

线程状态从大的方面来说,可归结为:初始状态、可运行状态、不可运行状态和消亡状态,具体可细分为上图所示7个状态,说明如下:

1)线程的实现有两种方式,一是继承Thread类,二是实现Runnable接口,但不管怎样,当我们new了thread实例后,线程就进入了初始状态;

2) 当该对象调用了start()方法,就进入可运行状态;

3) 进入可运行状态后,当该对象被操作系统选中,获得CPU时间片就会进入运行状态;

4) 进入运行状态后case就比较多,大致有如下情形:

﹒run()方法或main()方法结束后,线程就进入终止状态;

﹒当线程调用了自身的sleep()方法或其他线程的join()方法,就会进入阻塞状态(该状态既停止当前线程,但并不释放所占有的资源)。当 sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配时间片;

﹒当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被锁牢(synchroniza,lock),将会立即进入锁池状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入可运行状态,等待OS分配 CPU时间片;

﹒当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取锁标记。

﹒当线程调用stop方法,即可使线程进入消亡状态,但是由于stop方法是不安全的,不鼓励使用,大家可以通过run方法里的条件变通实现线程的 stop。

Timer部分见这里

Android联网时显示进度等待

Android中不知道什么时候加入了,联网不能再UI线程中进行的限制,之前调SDK=8的时候一点问题都没有,到了SDK=17就会报错误。Android做这样的限制肯定是好的,只是对于我这种新手就要纠结一点了。

这些天折腾Android联网的东西比较多,总结一些重要的东西,方便以后使。

将联网写在线程中,前台等待
public class Download extends Activity {
        private static final int MESSAGE_OK = 0x0001;
        private ProgressDialog progressDialog = null;

        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                this.setContentView(R.layout.main);        
                
                progressDialog = ProgressDialog.show(HelloXML.this, "下载", "正在下载,请稍候!");                                
                
                new Thread() {
                        public void run() {                        
                                try {
                                    //   连接网络获取数据
                                } catch (Exception e) {
                                        // 显示错误提示
                                        //Log.d("TAG",e.toString());
                                }
                                Message msg_listData = new Message();
                                msg_listData.what = MESSAGE_OK;
                                handler.sendMessage(msg_listData);
                        }
                }.start();
        }

        private Handler handler = new Handler() {     
                public void handleMessage(Message message) {
                        switch (message.what) {
                        case MESSAGE_OK:                                        
                        //刷新UI,显示数据,并关闭进度条                        
                                progressDialog.dismiss(); //关闭进度条
                                break;
                        }
                }
        };
}
Android中如果UI线程等待时间超过5秒钟,系统就会弹出菜单叫你强制关闭,所以一般情况下涉及到联网,就会涉及到线程。(以上参考

之前看过AsyncTask的一些资料,那个东西还是挺有用的,避免了Thread,Handle复杂的操作。

Timer详解

概览

这个小例子可以说明一些用Timer线程实现和计划执行一个任务的基础步骤:
  • 实现自定义的TimerTask的子类,run方法包含要执行的任务代码
  • 实例化Timer类,创建计时器后台线程。
  • 实例化任务对象 (new RemindTask()).
  • 制定执行计划。这里用schedule方法,第一个参数是TimerTask对象,第二个参数表示开始执行前的延时时间(单位是milliseconds,这里定义了5000)。还有一种方法可以指定任务的执行时间,如下例,指定任务在11:01 p.m.执行:
//Get the Date corresponding to 11:01:00 pm today.
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUROFDAY, 23);
calendar.set(Calendar.MINUTE, 1);
calendar.set(Calendar.SECOND, 0);
Date time = calendar.getTime();
timer = new Timer();
timer.schedule(new RemindTask(), time);

终止Timer线程

默认情况下,只要一个程序的timer线程在运行,那么这个程序就会保持运行。当然,你可以通过以下四种方法终止一个timer线程:
  • 调用timer的cancle方法。你可以从程序的任何地方调用此方法,甚至在一个timer task的run方法里。
  • 让timer线程成为一个daemon线程(可以在创建timer时使用new Timer(true)达到这个目地),这样当程序只有daemon线程的时候,它就会自动终止运行。
  • 当timer相关的所有task执行完毕以后,删除所有此timer对象的引用(置成null),这样timer线程也会终止。
  • 调用System.exit方法,使整个程序(所有线程)终止。
Reminder的例子使用了第一种方式。在这里不能使用第二种方式,因为这里需要程序保持运行直到timer的任务执行完成,如果设成daemon,那么当main线程结束的时候,程序只剩下timer这个daemon线程,于是程序不会等timer线程执行task就终止了。
有些时候,程序的终止与否并不只与timer线程有关。举个例子,如果我们使用AWT来beep,那么AWT会自动创建一个非daemon线程来保持程序的运行。下面的代码我们对Reminder做了修改,加入了beeping功能,于是我们需要加入System.exit的调用来终止程序。
import java.util.Timer;
import java.util.TimerTask;
import java.awt.Toolkit;

/**
* Simple demo that uses java.util.Timer to schedule a task to execute
* once 5 seconds have passed.
*/

public class ReminderBeep {
    Toolkit toolkit;
    Timer timer;

    public ReminderBeep(int seconds) {
        toolkit = Toolkit.getDefaultToolkit();
        timer = new Timer();
        timer.schedule(new RemindTask(), seconds*1000);
    }

    class RemindTask extends TimerTask {
        public void run() {
            System.out.println("Time's up!");
    toolkit.beep();
    //timer.cancel(); //Not necessary because we call System.exit
    System.exit(0);   //Stops the AWT thread (and everything else)
        }
    }

    public static void main(String args[]) {
System.out.println("About to schedule task.");
        new ReminderBeep(5);
System.out.println("Task scheduled.");
    }
}
  • schedule(TimerTask task, long delay, long period)
  • schedule(TimerTask task, Date time, long period)
  • scheduleAtFixedRate(TimerTask task, long delay, long period)
  • scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
当计划反复执行的任务时,如果你注重任务执行的平滑度,那么请使用schedule方法,如果你在乎的是任务的执行频度那么使用scheduleAtFixedRate方法。 例如,这里使用了schedule方法,这就意味着所有beep之间的时间间隔至少为1秒,也就是说,如果有一个beap因为某种原因迟到了(未按计划执行),那么余下的所有beep都要延时执行。如果我们想让这个程序正好在3秒以后终止,无论哪一个beep因为什么原因被延时,那么我们需要使用scheduleAtFixedRate方法,这样当第一个beep迟到时,那么后面的beep就会以最快的速度紧密执行(最大限度的压缩间隔时间)。

进一步分析schedule和scheduleAtFixedRate

(1)2个参数的schedule在制定任务计划时, 如果指定的计划执行时间scheduledExecutionTime<=systemCurrentTime,则task会被立即执行。scheduledExecutionTime不会因为某一个task的过度执行而改变。
(2)3个参数的schedule在制定反复执行一个task的计划时,每一次执行这个task的计划执行时间随着前一次的实际执行时间而变,也就是scheduledExecutionTime(第n+1次)=realExecutionTime(第n次)+periodTime。也就是说如果第n次执行task时,由于某种原因这次执行时间过长,执行完后的systemCurrentTime>=scheduledExecutionTime(第n+1次),则此时不做时隔等待,立即执行第n+1次task,而接下来的第n+2次task的scheduledExecutionTime(第n+2次)就随着变成了realExecutionTime(第n+1次)+periodTime。说白了,这个方法更注重保持间隔时间的稳定。
(3)3个参数的scheduleAtFixedRate在制定反复执行一个task的计划时,每一次执行这个task的计划执行时间在最初就被定下来了,也就是scheduledExecutionTime(第n次)=firstExecuteTime+nperiodTime;如果第n次执行task时,由于某种原因这次执行时间过长,执行完后的systemCurrentTime>=scheduledExecutionTime(第n+1次),则此时不做period间隔等待,立即执行第n+1次task,而接下来的第n+2次的task的scheduledExecutionTime(第n+2次)依然还是firstExecuteTime+(n+2)periodTime这在第一次执行task就定下来了。说白了,这个方法更注重保持执行频率的稳定。
http://blog.csdn.net/gtuu0123/article/details/6040159
  1. schedule方法:“fixed-delay”;如果第一次执行时间被delay了,随后的执行时间按 照 上一次 实际执行完成的时间点 进行计算
  2. scheduleAtFixedRate方法:“fixed-rate”;如果第一次执行时间被delay了,随后的执行时间按照 上一次开始的 时间点 进行计算,并且为了”catch up”会多次执行任务,TimerTask中的执行体需要考虑同步
  3. schedule方法:下一次执行时间相对于 上一次 实际执行完成的时间点 ,因此执行时间会不断延后
  4. scheduleAtFixedRate方法:下一次执行时间相对于上一次开始的 时间点 ,因此执行时间不会延后,存在并发性

一些注意的问题

每一个Timer仅对应唯一一个线程。 Timer不保证任务执行的十分精确。 Timer类的线程安全的。 参考
void
cancel() Cancels the Timer and all scheduled tasks.
int purge() Removes all canceled tasks from the task queue.
void schedule(TimerTask task, Date when, long period) Schedule a task for repeated fixed-delay execution after a specific time has been reached. 直到when设定的时间开始,间隔period时间执行一次task,间隔时间是从task完成之后开始计时
void schedule(TimerTask task, long delay, long period) Schedule a task for repeated fixed-delay execution after a specific delay. 等待delay时间,每间隔period时间执行task
void
schedule(TimerTask task, Date when) Schedule a task for single execution. 直到when执行一次task
void
schedule(TimerTask task, long delay) Schedule a task for single execution after a specified delay. 等待delay时间执行一次task
void
scheduleAtFixedRate(TimerTask task, long delay, long period) Schedule a task for repeated fixed-rate execution after a specific delay has passed. 等待delay设定时间,间隔period时间执行一次task,间隔时间是从task开始之后计时,可能task完成需要的时间超过period的时间,则有可能同时有多个task在执行。
void
scheduleAtFixedRate(TimerTask task, Date when, long period) Schedule a task for repeated fixed-rate execution after a specific time has been reached. 直到when设定的时间开始,间隔period时间执行一次task,间隔时间是从task开始之后计时
public class TimeThread extends Thread {

 Handler handler = new Handler(){

  @Override
  public void handleMessage(Message msg) {
   // TODO Auto-generated method stub
   super.handleMessage(msg);
   switch (msg.what) {
   case 1:
    long sysTime = System.currentTimeMillis();
    CharSequence sysTimeStr = DateFormat.format("hh:mm:ss", sysTime);
    tv.setText(sysTimeStr);
    break;

   default:
    break;
   }
  }
 };
 
 @Override
 public void run() {
  do {
   try {
    Thread.sleep(1000);
    Message msg = new Message();
    msg.what = 1;
    handler.sendMessage(msg);
    
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   
  } while (true);
 }
}

测试Timer

测试Android中Timer使用MainActivity,以下只是一个简简单单的测试工程,关于Timer的详细讲解在这里
package com.einverne.testtimer;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

 private static String TAG = "EV_DEBUG";
 private Timer timer;
 Integer integer;

 private TextView tv;
 private Button button_start;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  tv = (TextView) findViewById(R.id.textView_time);
  button_start = (Button) findViewById(R.id.button_start);

  integer = new Integer(0);
  new TimeThread().start();

  findViewById(R.id.button_start).setOnClickListener(
    new OnClickListener() {

     @Override
     public void onClick(View v) {
      // TODO Auto-generated method stub
      timer = new Timer();
      integer = 0;
      timer.schedule(new timerTask(), 0, 1000);
      button_start.setEnabled(false);
     }
    });

  findViewById(R.id.button_end).setOnClickListener(new OnClickListener() {

   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    timer.cancel();
    button_start.setEnabled(true);
   }
  });
  
  

 }

 public class timerTask extends TimerTask {

  @Override
  public void run() {
   // TODO Auto-generated method stub
   integer++;
   String time = integer.toString();
   Log.d(TAG, time);
  }
 }

 public class TimeThread extends Thread {
  Handler handler = new Handler() {
   @Override
   public void handleMessage(Message msg) {
    super.handleMessage(msg);
    switch (msg.what) {
    case 1:
     tv.setText(integer.toString());
     break;

    default:
     break;
    }
   }
  };
  @Override
  public void run() {
   do {
    try {
     Thread.sleep(1000);
     Message msg = new Message();
     msg.what = 1;
     handler.sendMessage(msg);

    } catch (InterruptedException e) {
     e.printStackTrace();
    }

   } while (true);
  }
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

}

Android 下载图片

为了学习AsyncTask,网络连接下载图片,我就随便参照教程写了一个例子.代码如下:
MainActivity:
package com.einverne.testdownloadpic;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.widget.ImageView;

public class MainActivity extends Activity {

 ImageView iv;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  iv = (ImageView)findViewById(R.id.imageView1);
  String url = "http://f.hiphotos.baidu.com/album/pic/item/aa64034f78f0f7366a7c4bdf0a55b319eac41369.jpg?psign=6a7c4bdf0a55b319ebc4b74543a98226cefc1e178b823751";
  
  new setImageViewData().execute(url);
 }

 /**
  * 
  * 异步加载图片
  *
  */
 public class setImageViewData extends AsyncTask{

  @Override
  protected Bitmap doInBackground(String... params) {
   Log.d("EV_DEBUG", "setImageViewData doInBackground");
   return getBitmap(params[0]);
  }

  @Override
  protected void onPostExecute(Bitmap bitmap) {
   Log.d("EV_DEBUG", "setImageViewData onPostExecute");
   iv.setImageBitmap(bitmap);
  }

  @Override
  protected void onPreExecute() {
   Log.d("EV_DEBUG", "setImageViewData onPreExecute");
  }
 }
 
 /**
  * get url and return Bitmap
  * @param url
  * @return Bitmap
  */
 public Bitmap getBitmap(String url){
  URL myFileurl = null;
  Bitmap bitmap = null;
  try {
   myFileurl = new URL(url);
  } catch (MalformedURLException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   return null;
  }
  try {
   HttpURLConnection  httpURLConnection = (HttpURLConnection) myFileurl.openConnection();
   httpURLConnection.setDoInput(true);
   httpURLConnection.connect();
   InputStream inputStream = httpURLConnection.getInputStream();
   bitmap = BitmapFactory.decodeStream(inputStream);
   inputStream.close();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return bitmap;
 }
 
 
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

}

最后别忘了在Android权限中添加Internet权限.很重要的,不然会出错.

Android Context几种获取方法区别

在Android开发中常常遇到Context(上下文)这个参数,调用很多函数的时候经常要传递这个参数过去.而默认Android中有好几个函数返回值是Context变量,而这些函数也不知道原理是啥.所以Google搜了一下在stackoverflow上看到有人回答,就顺便贴过来,备忘吧.


getApplicationContext() Application context is associated with the Application and will always be the same throughout the life cycle.
这个函数返回的这个Application的上下文,所以是与app挂钩的,所以在整个生命周期里面都是不变的

getBasecontext() should not be used just use Context instead of it which is associated with the activity and could possible be destroyed when the activity is destroyed.
stackoverflow上面写的是,这个函数不应该被使用,用Context代替,而Context是与activity相关连,所以当activity死亡后可能会被destroyed

getApplication() is available to Activity and Services only. Although in current Android Activity and Service implementations, getApplication() and getApplicationContext() return the same object, there is no guarantee that this will always be the case (for example, in a specific vendor implementation). So if you want the Application class you registered in the Manifest, you should never call getApplicationContext() and cast it to your application, because it may not be the application instance (which you obviously experienced with the test framework).
getApplication只能被Activity和Services使用,虽然在现在的android的实现中,getApplication和getApplicationContext返回一样的对象,但也不能保证这两个函数一样(例如在特殊的提供者来说),所以如果你想得到你在Manifest文件里面注册的App class,你不要去调用getApplicationContext,以为你可能得不到你所要的app实例(你显然有测试框架的经验)。。。。

getParent() returns object of the activity if the current view is a child..In other words returns the activity object hosting the child view when called within the child.
返回activity的上下文,如果这个子视图的话,换句话说,就是当在子视图里面调用的话就返回一个带有子视图的activity对象

在语句 AlertDialog.Builder builder = new AlertDialog.Builder(this); 中,要求传递的 参数就是一个context,在这里我们传入的是this,那么这个this究竟指的是什么东东呢? 这里的this指的是Activity.this,是这个语句所在的Activity的this,是这个Activity 的上下文。网上有很多朋友在这里传入getApplicationContext(),这是不对的。 AlertDialog对象是依赖于一个View的,而View是和一个Activity对应的。 于是,这里涉及到一个生命周期的问题,this.getApplicationContext()取的是这个应 用程序的Context,Activity.this取的是这个Activity的Context,这两者的生命周期是不同 的,前者的生命周期是整个应用,后者的生命周期只是它所在的Activity。而AlertDialog应 该是属于一个Activity的,在Activity销毁的时候它也就销毁了,不会再存在;但是,如果传 入getApplicationContext(),就表示它的生命周期是整个应用程序,这显然超过了它 的生命周期了。 所以,在这里我们只能使用Activity的this。

在找以上四个区别的时候又发现360DOC上面的Context的介绍文
Android上的Context

不过查一遍我还是没找到区别,我可能还是不知道区别在哪里....
update:2013/12/24
今天重新回顾一下,貌似有点明白了,和生命周期有关系,Context上下文,依赖关系。

Android:资源id及资源id的动态获取

我们平时获取资源是通过 findViewById 方法进行的,比如我们常在onCreate方法中使用这样的语句:
btnChecked=(ImageView)findViewById(R.id.imgCheck);
findViewById是我们获取layout中各种View 对象比如按钮、标签、ListView和ImageView的便利方法。顾名思义,它需要一个int参数:资源id。
资源id非常有用。Android回自动为每个位于res目录下的资源分配id,包括各种图片文件、xml文中的”@+id”对象。res的子目录几乎总是固定的,比如每次都能见到的:drawable-xxxx、layout、values,以及不常见的:anim、xml、row、color。
Android教科书上告诉你:
res/drawable/用来存放图片文件;
res/layout/用来存放布局定义文件;
res/values/用来存放一些变量、参数等文件。
这都是我们已经知道的。此外,Android会为res目录下的所有资源分配id,其主要的分配原则是:
drawable中的图片文件总是每个文件一个资源id。
Xml文件中每个使用android:id=”@+id/xxx”的view都会被分配一个未用的资源id。
其他更复杂的规则也许每个人都会补充一些。
在 ADK 的API 中,有许多方法其实都会使用资源id作为参数,比如 getDrawable 方法 :
getResources().getDrawable(R.drawable.sendsms_bk));
从字面上看,getResouces方法返回一个android.content.res.Resources对象。
getDrawalbe方法返回一个Drawable对象,我们知道这就是一张图片。
getDrawable方法使用的参数就是资源id。
但是,这些int型的资源id到底是些什么数字?或者它们都放在哪里。如果你够细心,你可以在gen目录的R.java文件中找到它们。每个16进制整型id都有一个很O-O 的属性名字,它们都是public static final的,建议你不要手动去修改它们。这些资源id位于不同类层次结构中,你仔细看一看,会发现其中的一些规律,比如,drawable类中包含资源id其实代表了res/drawable目录中的图片文件;layout类中包含的资源id其实是代表了在res/layout目录中的xml文件 ;id类中包含的资源id其实是代表了布局xml文件中使用了 android:id=”@+id/xxx”的widget对象——当然如果你使用的是”@+Tyre/xxx”,则Android编译时会在R.java 中创建一个叫做 Tyre 的类。
通过上面的了解,我们发现资源id就是以“R.”开头的变量或属性——因为它都定义在R类中——这个类跟android.R不是同一个R,它们不在同一个包层次中。前者是定义了android框架自己的资源,比如:android.R.layout.simple_expandable_list_item_1、android.R.layout.simple_expandable_list_item_2。这些资源你使用过吗?有时间不妨试试。而另一个R就是我们自己的R了,定义了我们自己的资源id。我们可以用面向对象的R.xxx.yyy的方式使用资源id,当然也可以直接用16进制数来使用它们。
资源id在Android中是如此重要,因此我们只要获得了资源id,即可获得资源。因为某些情况下,我们不能使用某个常量作为资源id(因为我们想用一个变量代替它),所以只能采用两种方式动态获取资源id:

1、反射

java的反射是如此强大,以至于我们在走投无路时总是想起它。我们来看一段代码:
try{
  Field field=R.drawable.class.getField(type);
  int i= field.getInt(new R.drawable());
  Log.d(tag,i+"");
  return i;
}catch(Exception e){
  Log.e(tag,e.toString());
  return R.drawable.unknow;
}

type是一个变量,他的可能值会是“close”、“edit”、“icon”等。我们通过java的反射类Field 去访问 R.drawable 类下面的某个字段。实际上,我们知道在R.drawable 下的字段就是 res.drawable-xxxx 目录下的图片文件的资源id。因此运行上面的这段代码,其实就是通过一个字符串去获取对应图片文件的资源id。比如,当 type 的值为“icon”时,上面的代码会获得res.drawable-xxxx目录下的 icon.png 图片文件的资源id并返回给我们。我们通过资源 id 来把图片显示在ImageView中就是顺理成章的事了。

2、使用Resources 类的 getIdentifier方法

使用反射毕竟还是麻烦。如果同样的事情用 android.content.res.Resources 类来干,就是两句话而已:
Resources res=getResources();
return res.getIdentifier(type,"drawable",getPackageName());
getResources方法是来自于contenxt(也就是Activity类),它直接可以返回一个Resouces对象。而Resouces的getIdentifier方法可以返回R.java中的任何资源id,当然,你必须指定3个参数:字段名,类名,包名。包名指定了的 全限定名的包名部分,如果R 的全限定名为 android.R 或者 com.company.R,则包名在这里就是“android”或“com.company”。getPackageName其实是this.getPackageName(),它直接返回本类的包名。
类名则是资源所属的类。比如我们知道的,在R.java 类中的几个固定的类:drawable、id、string、layout等,在它们下边又定义了许多资源id。
字段名则是资源id的名字。比如这个资源id定义: public static final int del=0x7f020002;
del就是一个资源id的名字,0x7f020002则是它的16进制值。
通过3个参数,getIdentifier 方法就可以通过比较动态的方式获得资源id。

Android本机localhost调试

习惯了调试Web项目的127.0.0.1和Localhost,在android项目调试时也理所当然的打上127.0.0.1,结果发现果断连接失败了!发现android把127.0.0.1当做手机自身的ip地址,而要在android模拟器上访问本地电脑是用的ip是10.0.2.2  ;

Android 3D立方体

简单的入门程序,实现一个不断绕x轴,y轴旋转的彩色立方体
在Android中我们使用GLSurfaceView来显示OpenGL视图,GLSurfaceView:是其中很重要的一个类,此类位于android.opengl包下,用于管理是一块可以是复合视图机器人系统的内存的特殊的曲面。管理一个使表面呈现 OpenGL 的 EGL 显示。接受一个用户提供输入Render对象进行显示。从 UI 线程实现一个专用线程渲染界面实现3D性能。支持按需要和连续的呈现。 包装、 跟踪,和检查 OpenGL 渲染器调用的错误。所以首先我们需要创建一个GLSurfaceView。
##ReadMore##
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Window;

public class mainActivity extends Activity {

 CubeRenderer mCubeRenderer;
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  requestWindowFeature(Window.FEATURE_NO_TITLE); // 去掉标题
  GLSurfaceView GLView = new GLSurfaceView(this); //创建一个GLSurfaceView
     mCubeRenderer = new CubeRenderer();
  GLView.setRenderer(mCubeRenderer);
  setContentView(GLView);
 }
}
接下来我们的主要工作就是去创建一个继承Renderer接口的CubeRenderer。Renderer是一个专门用来渲染3D的接口。继承它,我们需要重载以下方法:
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLU;
import android.opengl.GLSurfaceView.Renderer;

public class CubeRenderer implements Renderer {

 float box[] = new float[] {
   // FRONT
   -0.5f, -0.5f,  0.5f,
    0.5f, -0.5f,  0.5f,
   -0.5f,  0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
   // BACK
   -0.5f, -0.5f, -0.5f,
   -0.5f,  0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
   // LEFT
   -0.5f, -0.5f,  0.5f,
   -0.5f,  0.5f,  0.5f,
   -0.5f, -0.5f, -0.5f,
   -0.5f,  0.5f, -0.5f,
   // RIGHT
    0.5f, -0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f, -0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
   // TOP
   -0.5f,  0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
   // BOTTOM
   -0.5f, -0.5f,  0.5f,
   -0.5f, -0.5f, -0.5f,
    0.5f, -0.5f,  0.5f,
    0.5f, -0.5f, -0.5f,
  };

 FloatBuffer cubeBuff;
 
 float xrot = 0.0f;
 float yrot = 0.0f;
 
 /**
  * 将float数组转换存储在字节缓冲数组
  * @param arr
  * @return
  */
 public FloatBuffer makeFloatBuffer(float[] arr) {
  ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);//分配缓冲空间,一个float占4个字节
  bb.order(ByteOrder.nativeOrder()); //设置字节顺序, 其中ByteOrder.nativeOrder()是获取本机字节顺序
  FloatBuffer fb = bb.asFloatBuffer(); //转换为float型
  fb.put(arr);        //添加数据
  fb.position(0);      //设置数组的起始位置
  return fb;
 }
 
 public CubeRenderer() {
  // TODO Auto-generated constructor stub
  cubeBuff = makeFloatBuffer(box);//转换float数组
 }
 
 
 protected void init(GL10 gl) {
  gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//设置清屏时背景的颜色,R,G,B,A
  
  gl.glEnable(GL10.GL_DEPTH_TEST); //启用深度缓存
  gl.glEnable(GL10.GL_CULL_FACE);  //启用背面剪裁
  gl.glClearDepthf(1.0f);    // 设置深度缓存值
  gl.glDepthFunc(GL10.GL_LEQUAL);  // 设置深度缓存比较函数,GL_LEQUAL表示新的像素的深度缓存值小于等于当前像素的深度缓存值(通过gl.glClearDepthf(1.0f)设置)时通过深度测试 
  gl.glShadeModel(GL10.GL_SMOOTH);// 设置阴影模式GL_SMOOTH
 }
 
 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  // TODO Auto-generated method stub
  init(gl);
 }
 
 public void onSurfaceChanged(GL10 gl, int w, int h) {
  // TODO Auto-generated method stub
  gl.glViewport(0, 0, w, h); //设置视窗
  gl.glMatrixMode(GL10.GL_PROJECTION); // 设置投影矩阵
  gl.glLoadIdentity();  //设置矩阵为单位矩阵,相当于重置矩阵  
  GLU.gluPerspective(gl, 45.0f, ((float) w) / h, 0.1f, 10f);//设置透视范围 
 }
 
 public void onDrawFrame(GL10 gl) {
  // TODO Auto-generated method stub
  gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);// 清除屏幕和深度缓存
  
  gl.glMatrixMode(GL10.GL_MODELVIEW);   //切换至模型观察矩阵
  gl.glLoadIdentity();// 重置当前的模型观察矩阵
  GLU.gluLookAt(gl, 0, 0, 3, 0, 0, 0, 0, 1, 0);//设置视点和模型中心位置
 
  gl.glVertexPointer(3, GL10.GL_FLOAT, 0, cubeBuff);//设置顶点数据
  gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
 
  gl.glRotatef(xrot, 1, 0, 0);  //绕着(0,0,0)与(1,0,0)即x轴旋转
  gl.glRotatef(yrot, 0, 1, 0);
  
  gl.glColor4f(1.0f, 0, 0, 1.0f);   //设置颜色,红色
  gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);  //绘制正方型FRONT面
  gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);
 
  gl.glColor4f(0, 1.0f, 0, 1.0f);
  gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);
  gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);
  
  gl.glColor4f(0, 0, 1.0f, 1.0f);
  gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);
  gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
 
  xrot += 1.0f;
  yrot += 0.5f;
 }

}



参考:http://www.linuxidc.com/Linux/2011-09/43751.htm

Quote Of The Day