Android 面试常见问题

节约电量的方法

http://hukai.me/android-performance-battery/

• 实时性不高的程序用AlarmManager的定时轮询操作代替推送操作。

android中涉及到将服务器中数据变化信息通知用户一般有两种办法,推送轮询

消息推送是服务端主动发消息给客户端,因为第一时间知道数据发生变化的是服务器自己,所以推送的优势是实时性高。但服务器主动推送需要单独开发一套能让客户端持久连接的服务端程序,不过现在已经有很多开源的代码实现了基于xmmp协议的推送方案,而且还可以使用谷歌的推送方案。但有些情况下并不需要服务端主动推送,而是在一定的时间间隔内客户端主动发起查询。

譬如有这样一个app,实时性要求不高,每天只要能获取10次最新数据就能满足要求了,这种情况显然轮询更适合一些,推送显得太浪费,而且更耗电。

• 无严苛的实时性要求,可延长轮播间隔,如6小时自动请求一次,同时时间隔可通过服务器在线更新。这样既省电,偶尔急需实时推送时也可在线调整时间间隔。
• 对实时性有要求,考虑使用成熟的推送服务,如Google的C2DM(http://code.google.com/android/c2dm/),和亚马逊的AWS SDK (http://aws.amazon.com/sdkforandroid/)。

• 预先获取当前的连接状态,在发送Http请求的时候,是否有网络可以用,如果无网络可用,则不要去获取新的数据,不然必然会浪费电量。

• 检查电量,根据电量的多少采取不同的策略。例如,在充电状态下进行逻辑性较强较复杂的操作。

• WakeLock和其他的硬件资源不要占用时间过多,用完之后及时释放。例如加速度,亮度传感器,蓝牙,GPS等。

为了减少电量的消耗,在蜂窝移动网络下,最好做到批量执行网络请求,尽量避免频繁的间隔网络请求。

另外WiFi连接下,网络传输的电量消耗要比移动网络少很多,应该尽量减少移动网络下的数据传输,多在WiFi环境下传输数据。

那么如何才能够把任务缓存起来,做到批量化执行呢?下面就轮到Job Scheduler出场了。使用Job Scheduler,应用需要做的事情就是判断哪些任务是不紧急的,可以交给Job Scheduler来处理,Job Scheduler集中处理收到的任务,选择合适的时间,合适的网络,再一起进行执行。

解释:

1.获取到手机的充电状态,得到充电状态信息之后,我们可以有针对性的对部分代码做优化。比如我们可以判断只有当前手机为AC充电状态时 才去执行一些非常耗电的操作。

2.一个最简单的唤醒手机的方法是使用PowerManager.WakeLock的API来保持CPU工作并防止屏幕变暗关闭。这使得手机可以被唤醒,执行工作,然后回到睡眠状态。知道如何获取WakeLock是简单的,可是及时释放WakeLock也是非常重要的,不恰当的使用WakeLock会导致严重错误。例如网络请求的数据返回时间不确定,导致本来只需要10s的事情一直等待了1个小时,这样会使得电量白白浪费了。。这也是为何使用带超时参数的wakelock.acquice()方法是很关键的。但是仅仅设置超时并不足够解决问题,例如设置多长的超时比较合适?什么时候进行重试等等?解决上面的问题,正确的方式可能是使用非精准定时器。通常情况下,我们会设定一个时间进行某个操作,但是动态修改这个时间也许会更好。例如,如果有另外一个程序需要比你设定的时间晚5分钟唤醒,最好能够等到那个时候,两个任务捆绑一起同时进行,这就是非精确定时器的核心工作原理。我们可以定制计划的任务,可是系统如果检测到一个更好的时间,它可以推迟你的任务,以节省电量消耗这正是JobScheduler API所做的事情。它会根据当前的情况与任务,组合出理想的唤醒时间,例如等到正在充电或者连接到WiFi的时候,或者集中任务一起执行。我们可以通过这个API实现很多免费的调度算法。

minSdkVersiontargetSdkVersiontargetApiLevel的区别

  在AndroidMenifest.xml中,常常会有下面的语句:
         <uses-sdk android:minSdkVersion="4" 
          android:targetSdkVersion="10"
          android:maxSdkVersion="10" />

  在default.properties中,会看到下面的语句:
   target=android-10
  如果是使用Eclipse的话,还可能会看到这样的警告:
   Attribute minSdkVersion (4) is lower than the project target API level (10)
  那么,这里面的minSdkVersion、targetSdkVersion、maxSdkVersion、target API level四个数值到底有什么区别?
  minSdkVersion与maxSdkVersion比较容易理解,就是在安装程序的时候,如果目标设备的API版本小于minSdkVersion,或者大于maxSdkVersion,程序将无法安装。一般来说没有必要设置maxSdkVersion。

  targetSdkVersion相对复杂一些,如果设置了此属性,那么在程序执行时,如果目标设备的API版本正好等于此数值,他会告诉Android平台:此程序在此版本已经经过充分测,没有问题。不必为此程序开启兼容性检查判断的工作了。也就是说,如果targetSdkVersion与目标设备的API版本相同时,运行效率可能会高一些。

  但是,这个设置仅仅是一个声明、一个通知,不会有太实质的作用,比如说,使用了targetSdkVersion这个SDK版本中的一个特性,但是这个特性在低版本中是不支持的,那么在低版本的API设备上运行程序时,可能会报错:java.lang.VerifyError。也就是说,此属性不会帮你解决兼容性的测试问题。你至少需要在minSdkVersion这个版本上将程序完整的跑一遍来确定兼容性是没有问题的。(这个问题确实让人头疼)

  在default.properties中的target是指在编译的时候使用哪个版本的API进行编译。

  综上,上面的四个值其实是作用于不同的时期:
   target API level是在编译的时候起作用,用于指定使用哪个API版本(SDK版本)进行编译。
   minSdkVersion和maxSdkVersion是在程序安装的时候起作用,用于指定哪些版本的设备可以安装此应用。
   targetSdkVersion是在程序运行的时候起作用,用于提高指定版本的设备上程序运行体验。

  这四个数值在程序编译时也没有严格的检查,比如说,你可以将minSdkVersion设置的比maxSdkVersion还大,他会自动忽略掉错误的maxSdkVersion。

解析XML的几种方式的原理与特点:DOMSAXPULL

java解析xml的几种方式

DOM:消耗内存:先把xml文档都读到内存中,然后再用DOM API来访问树形结构,并获取数据。这个写起来很简单,但是很消耗内存。要是数据过大,手机不够牛逼,可能手机直接死机

SAX:解析效率高,占用内存少,基于事件驱动的:更加简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

PULL:与 SAX 类似,也是基于事件驱动,我们可以调用它的next()方法,来获取下一个解析事件(就是开始文档,结束文档,开始标签,结束标签),当处于某个元素时可以调用XmlPullParser的getAttributte()方法来获取属性的值,也可调用它的nextText()获取本节点的值。

Assets目录与res目录的区别

在Android工程的目录下,/assets是应用系统内部需要使用到的诸如音乐、视频类文件,而/res是应用的资源文件,/res内有一个/raw目录,这个目录和/assets有类似,这里记录一下两者的异同和用法。

assets:用于存放需要打包到应用程序的静态文件,以便部署到设备中。与res/raw不同点在于,ASSETS支持任意深度的子目录。这些文件不会生成任何资源ID,必须使用/assets开始(不包含它)的相对路径名。

res:用于存放应用程序的资源(如图标、GUI布局等),将被打包到编译后的Java中。不支持深度子目录

res/raw:存放通用的文件, 该文件夹内的文件将不会被编译成二进制文件,按原样复制到设备上。 

res/menu:存放基于XML的菜单描述;

res/values:存放字符串、尺寸值。 

res/xml: 存放通用的XML文件

*res/raw和assets的相同点:

1.两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制

*res/raw和assets的不同点:
1.res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类必须使用/assets开始(不包含它)的相对路径名。

2.res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

*读取文件资源:

1.读取res/raw下的文件资源,通过以下方式获取输入流来进行写操作

· InputStream is = getResources().openRawResource(R.id.filename);  

2.读取assets下的文件资源,通过以下方式获取输入流来进行写操作

· AssetManager am = null;  

· am = getAssets();  

· InputStream is = am.open("filename"); 

注意:assets 文件夹是存放不进行编译加工的原生文件,即该文件夹里面的文件不会像 xml, java 文件被预编译,可以存放一些图片,html,js, css 等文件。

由于raw是Resources (res)的子目录,Android会自动的为这目录中的所有资源文件生成一个ID,这个ID会被存储在R类当中,作为一个文件的引用。这意味着这个资源文件可以很容易的被Android的类和方法访问到,甚至在Android XML文件中你也可以`@raw/`的形式引用到它。在Android中,使用ID是访问一个文件最快捷的方式。MP3和Ogg(一种音频格式)文件放在这个目录下是比较合适的。

 - assets目录更像一个附录类型的目录,Android不会为这个目录中的文件生成ID并保存在R类当中,因此它与Android中的一些类和方法兼容度更低。同时,由于你需要一个字符串路径来获取这个目录下的文件描述符,访问的速度会更慢。但是把一些文件放在这个目录下会使一些操作更加方便,比方说拷贝一个数据库文件到系统内存中。要注意的是,你无法在Android XML文件中引用到assets目录下的文件,只能通过AssetManager来访问这些文件。数据库文件和游戏数据等放在这个目录下是比较合适的。 

如何退出Activity

1.Activity.finish();
也可以用killProcess()和System.exit()这样的方法
如何安全退出已调用多个Activity的Application?

方法一:

例如:MainActivity无序随意的打开了其他的Activity:Activity1、Activity、Activity3、Activity4、Activity5等多个Activity,当在MainActivity里面选择退出时,同时将其它已打开的Activity结束的办法:

第1步:在每个Activity里面注册一个广播接收机,从Activity1、Activity、Activity3、Activity4、Activity5都加入以下代码如下:

定义CloseReceiver对象:

Private CloseReceiver closeReceiver;  

在onCreate里面注册广播接收机:

closeReceiver = new CloseReceiver();  
IntentFilter intentFilter = new IntentFilter("org.shuxiang.CloseReceiver");  
registerReceiver(closeReceiver, intentFilter);  

广播接收机的代码:

 /** 
    * 结束Activity的广播接收机 
    * @author shuxiang 
    * 
    */  
   public class CloseReceiver extends BroadcastReceiver  
{   
       @Override  
       public void onReceive(Context context, Intent intent)  
       {  
           finish();  
       }  
   }  

第2步:在MainActivity里面发送广播:
发送结束所有Activity的广播:

Intent intent = new Intent();  
intent.setAction("org.shuxiang.CloseReceiver");  
sendBroadcast(intent);  

方法二: 

例如:

在MainActivity里面按顺序打开了多个Activity:Activity1、Activity、Activity3、Activity4、Activity5,

现在要做的是,从Activity5返回到MainActivity,并同时将Activity5、Activity4、Activity3、Activity2、Activity1这5个Activity都结束掉。

方法:在Activity5.java里面写上:

Intent intent = new Intent();  
intent.setClass(this, MainActivity.class);  
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);  
startActivity(intent);  
finish(); 

Android为什么要设计出Bundle而不是直接使用HashMap来进行数据传递?

·Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保证更快的速度和更少的内存占用。

·另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输。

缓存文件可以放在哪里?它们各自的特点是什么?

Android app的文件缓存目录可以是app内置私有的目录,当然也可以选择外置sdcard目录

内置私有目录

1) /data/data/[packagename]/files 文件缓存目录,一般存小的文件缓存,如果是图片,不建议放这里,一般放到外置卡。

File file = getFilesDir(); 返回该目录 
写文件到该目录下可以像这样

penFileOutput(name, Context.MODE_PRIVATE);
        fos.write(content.getBytes());
    } catch (Exception e) {
    }finally{
        try {
            if(null != fos){
                fos.close();
            } 
        }catch (Exception e) {
        }
    }

2) /data/data/[packagename]/cache目录,存放一些其他缓存 File cache = getCacheDir();

3) /data/data/[packagename]/databases,存放数据库

4) /data/data/[packagename]/lib,应用的so目录

5) /data/data/[packagename]/shared_prefs 应用的SharedPreferences保存

可以自己创建其他目录吗? 可以的 
使用 File ownDataPath = getDir(“service”,Context.MODE_PRIVATE); 
使用它可以创建app_service目录,放什么自己定义

你的app的internal storage 目录是以你的app的包名作为标识存放在Android文件系统的特定目录下[data/data/com.example.xx]。 从技术上讲,如果你设置文件为可读的,那么其他app就可以读取你的internal文件。然而,其他app需要知道你的包名与文件名。若是你没有设置为可读或者可写,其他app是没有办法读写的。因此只要你使用MODE_PRIVATE ,那么这些文件就不可能被其他app所访问。

另外记住一点,内部存储在你的APP卸载的时候,会一块被删除,因此,我们可以在cache目录里面放置我们的图片缓存,而且cache与files的差别在于,如果手机的内部存储空间不够了,会自行选择cache目录进行删除,因此,不要把重要的文件放在cache文件里面,可以放置在files里面,因为这个文件只有在APP被卸载的时候才会被删除。还有要注意的一点是,如果应用程序是更新操作,内部存储不会被删除,区别于被用户手动卸载。

外置SDCARD目录

1)外置缓存目录(File sdcache = getExternalCacheDir();) 
/storage/emulated/0/Android/data/[packagename]/cache 
一些重要性不高的cache或者大文件放到这里,比如图片缓存

2)外置文件缓存目录(File sdfile = getExternalFilesDir(null);), 
/storage/emulated/0/Android/data/[packagename]/files 
一些重要性不高的file cache或者大文件放到这里 
注意: /storage/emulated/0/Android/data/[packagename] 在android2.2之后,在应用卸载后也会一并卸载。所以不需要用什么清理缓存的软件清理的。

应用内切换主题有哪些方案可以实现

这里讨论的只是白天、夜晚主题切换这种场景,不涉及外部资源加载。

现在要给App添加夜晚主题,所以就需要选择一种应用内部更换主题的实现方案,目前来说,比较常见的几种方式如下:

Theme

设置Theme来切换不同主题。

优点:利用系统自带的机制实现,根据标志位setTheme()即可。

缺点:在主题切换界面不重启的情况下,不能自动完成界面主题的刷新。

遍历View

对主题的更换,使用遍历View,然后单独设置更改后的属性即可。

优点:可以即时更新界面,不需要重启Activity

缺点:需要单独添加标志位,来标记需要更换主题的View,需要增加额外工作,另外就是标记的添加,有可能影响原来的代码逻辑。

开源项目

关于Theme的解决方案就不说了,就是在style文件中定义不同的主题即可。

目前开源的几个应用内换肤项目,基本采用的都是遍历View,然后更换属性来完成,下面我们简单分析一下实现机制。

·MultipleTheme

·Colorful

·AndroidChangeSkin

·NightOw

有什么便捷的方式实现activity变暗的效果

·不要新开启Activity的方式

·也不要使用Dialog

·让背景跟Dialog出现一样,变暗,带动画。

private void dimBackground(final float from, final float to) {
        final Window window = getWindow();
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(from, to);
        valueAnimator.setDuration(500);
        valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                WindowManager.LayoutParams params = window.getAttributes();
                params.alpha = (Float) animation.getAnimatedValue();
                window.setAttributes(params);
            }
        });

        valueAnimator.start();
    }

亲测可用

 /** 窗口背景变暗*/
    dimBackground(1.0f,0.5f);

    /** 窗口背景变亮*/
    dimBackground(0.5f,1.0f);

SharedPreference在使用过程中有什么注意点?commit()apply()的区别

返回值

apply()没有返回值,而commit()返回boolean表明修改是否提交成功。

操作效率

apply()是将修改数据提交到内存, 而后异步真正提交到硬件磁盘。

而commit()其过程是先把数据更新到内存,然后在当前线程中写文件操作同步的提交到硬件磁盘,提交完成返回提交状态 

建议

如果对提交的结果不关心的话,建议使用apply(),如果需要确保提交成功且有后续操作的话,还是需要用commit()。

多进程表现

一句话:在多进程中,如果要交换数据,不要使用SharedPreference,因为在不同版本表现不稳定,推荐使用ContentProvider替代。

使用细节

·ContextImpl中有一个静态的ArrayMap变量sSharedPrefs,无论有多少个ContextImpl对象实例,系统都共享这一个sSharedPrefs的Map,应用启动以后首次使用SharePreference时创建,系统结束时才可能会被垃圾回收器回收,所以如果我们一个App中频繁的使用不同文件名的SharedPreferences很多时这个Map就会很大,也即会占用移动设备宝贵的内存空间。所以我们应用中应该尽可能少的使用不同文件名的SharedPreferences,取而代之的是合并他们,减小内存使用

·SharedPreferences在实例化时首先会从sdcard异步读文件,然后缓存在内存中;接下来的读操作都是内存缓存操作而不是文件操作。

·在SharedPreferences的Editor中如果用commit()方法提交数据,其过程是先把数据更新到内存,然后在当前线程中写文件操作,提交完成返回提交状态;如果用的是apply()方法提交数据,首先也是写到内存,接着在一个新线程中异步写文件,然后没有返回值。

·在写操作commit时有三级锁操作,效率很低,所以当我们一次有多个修改写操作时等都批量put完了再一次提交确认,这样可以提高效率。

ART、AOT、JIT、Dalvik之间有什么关系?

JIT与Dalvik

  JIT是"Just In Time Compiler"的缩写,就是"即时编译技术",与Dalvik虚拟机相关。

怎么理解这句话呢?这要从Android的一些特性说起。

  JIT是在2.2版本提出的,目的是为了提高Android的运行速度,一直存活到4.4版本,因为在4.4之后的ROM中,就不存在Dalvik虚拟机了。

  我们使用Java开发android,在编译打包APK文件时,会经过以下流程

   ·Java编译器将应用中所有Java文件编译为class文件

   ·dx工具将应用编译输出的类文件转换为Dalvik字节码,即dex文件

  之后经过签名、对齐等操作变为APK文件。

  Dalvik虚拟机可以看做是一个Java VM,他负责解释dex文件为机器码,如果我们不做处理的话,每次执行代码,都需要Dalvik将dex代码翻译为微处理器指令,然后交给系统处理,这样效率不高。

  为了解决这个问题,Google在2.2版本添加了JIT编译器,当App运行时,每当遇到一个新类,JIT编译器就会对这个类进行编译,经过编译后的代码,会被优化成相当精简的原生型指令码(即native code),这样在下次执行到相同逻辑的时候,速度就会更快。

  当然使用JIT也不一定加快执行速度,如果大部分代码的执行次数很少,那么编译花费的时间不一定少于执行dex的时间。Google当然也知道这一点,所以JIT不对所有dex代码进行编译,而是只编译执行次数较多的dex为本地机器码。

  有一点需要注意,那就是dex字节码翻译成本地机器码是发生在应用程序的运行过程中的,并且应用程序每一次重新运行的时候,都要做重做这个翻译工作,所以这个工作并不是一劳永逸,每次重新打开App,都需要JIT编译。

  另外,Dalvik虚拟机从Android一出生一直活到4.4版本,而JIT在Android刚发布的时候并不存在,在2.2之后才被添加到Dalvik中。

ART与AOT

  AOT是"Ahead Of Time"的缩写,指的就是ART(Anroid RunTime)这种运行方式。

  前面介绍过,JIT是运行时编译,这样可以对执行次数频繁的dex代码进行编译和优化,减少以后使用时的翻译时间,虽然可以加快Dalvik运行速度,但是还是有弊病,那就是将dex翻译为本地机器码也要占用时间,所以Google在4.4之后推出了ART,用来替换Dalvik。

  在4.4版本上,两种运行时环境共存,可以相互切换,但是在5.0+,Dalvik虚拟机则被彻底的丢弃,全部采用ART。

  ART的策略与Dalvik不同,在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。之后打开App的时候,不需要额外的翻译工作,直接使用本地机器码运行,因此运行速度提高。

  当然ART与Dalvik相比,还是有缺点的。

   ·ART需要应用程序在安装时,就把程序代码转换成机器语言,所以这会消耗掉更多的存储空间,但消耗掉空间的增幅通常不会超过应用代码包大小的20%

   ·由于有了一个转码的过程,所以应用安装时间难免会延长

  但是这些与更流畅的Android体验相比而言,不值一提。

  总结

  通过前面背景知识的介绍,我终于可以更简单的介绍这四个名词之间的关系了:

   ·JIT代表运行时编译策略,也可以理解成一种运行时编译器,是为了加快Dalvik虚拟机解释dex速度提出的一种技术方案,来缓存频繁使用的本地机器码

   ·ART和Dalvik都算是一种Android运行时环境,或者叫做虚拟机,用来解释dex类型文件。但是ART是安装时解释,Dalvik是运行时解释

   ·AOT可以理解为一种编译策略,即运行前编译,ART虚拟机的主要特征就是AOT

Intent可以传递哪些类型的数据

  1、8种基本数据类型及其数组 
  2、String(String实现了 Serializable )/CharSequence实例类型的数据及其数组 
  3、实现了Parcelable的对象及其数组 
  4、实现了 Serializable 的对象及其数组 

  Parcelable主要用在内存序列化上,Serializable 主要用于将对象序列化到本地存储设备中或者将对象序列化后通过网络传输。

  在JDK1.4中,引入了CharSequence接口,实现了这个接口的类有:CharBuffer、String、StringBuffer、StringBuilder这个四个类。

  CharBuffer为nio里面用的一个类,String实现这个接口理所当然,StringBuffer也是一个CharSequence,StringBuilder是Java抄袭C#的一个类,基本和StringBuffer类一样,效率高,但是不保证线程安全,在不需要多线程的环境下可以考虑。

  提供这么一个接口,有些处理String或者StringBuffer的类就不用重载了。但是这个接口提供的方法有限,只有下面几个:charat、length、subSequence、toString这几个方法,感觉如果有必要,还是重载的比较好,避免用instaneof这个操作符。

显示Intent和 隐式 Intent

intent就是意图的意思。Intent分两种:显式(Explicit intent)和隐式(Implicit intent)

一、显式(设置Component

显式,即直接指定需要打开的activity对应的类
以下多种方式都是一样的,实际上都是设置Component直接指定Activity类的显式Intent,由MainActivity跳转到SecondActivity:
1、构造方法传入Component,最常用的方式

Intent intent = new Intent(this, SecondActivity.class);  
startActivity(intent);  

2setComponent方法

ComponentName componentName = new ComponentName(this, SecondActivity.class);  
// 或者ComponentName componentName = new ComponentName(this, "com.example.app016.SecondActivity");  
// 或者ComponentName componentName = new ComponentName(this.getPackageName(), "com.example.app016.SecondActivity");  
  
Intent intent = new Intent();  
intent.setComponent(componentName);  
startActivity(intent); 

3setClass/setClassName方法

Intent intent = new Intent();  
  
intent.setClass(this, SecondActivity.class);  
// 或者intent.setClassName(this, "com.example.app016.SecondActivity");  
// 或者intent.setClassName(this.getPackageName(), "com.example.app016.SecondActivity");  
          
startActivity(intent);  

显式Intent通过Component可以直接设置需要调用的Activity类,可以唯一确定一个Activity,意图特别明确,所以是显式的。设置这个类的方式可以是Class对象(如SecondActivity.class),也可以是包名加类名的字符串(如"com.example.app016.SecondActivity")。这个很好理解,在应用程序内部跳转界面常用这种方式。

二、隐式

Android理解:显式和隐式Intent

隐式,即不是像显式的那样直接指定需要调用的Activity,隐式不明确指定启动哪个Activity,而是设置ActionDataCategory,让系统来筛选出合适的Activity。筛选是根据所有的<intent-filter>来筛选。

下面以Action为例:

AndroidManifest.xml文件中,首先被调用的Activity要有一个带有<intent-filter>并且包含<action>的Activity,设定它能处理的Intent,并且category设为"android.intent.category.DEFAULT"。action的name是一个字符串,可以自定义,例如我在这里设成"abcdefg":

<activity  
    android:name="com.example.app016.SecondActivity">  
    <intent-filter>  
        <action android:name="abcdefg"/>  
        <category android:name="android.intent.category.DEFAULT"/>  
    </intent-filter>  
</activity>  

然后,在MainActivity,才可以通过这个action name找到上面的Activity。下面两种方式分别通过setAction和构造方法方法设置Action,两种方式效果相同。

1setAction方法

Intent intent = new Intent();  
intent.setAction("abcdefg");  
startActivity(intent); 

2、构造方法直接设置Action

Intent intent = new Intent("abcdefg");  
startActivity(intent);  

通过设置Action字符串,表明自己的意图,即我想干嘛,需要由系统解析,找到能够处理这个IntentActivity并启动。比如我想打电话,则可以设置Action"android.intent.action.DIAL"字符串,表示打电话的意图,系统会找到能处理这个意图的Activity,例如调出拨号面板。
有几点需要注意:
1、这个Activity其他应用程序也可以调用,只要使用这个Action字符串。这样应用程序之间交互就很容易了,例如手机QQ可以调用QQ空间,可以调用腾讯微博等。

因为如此,为了防止应用程序之间互相影响,一般命名方式是包名+Action名,例如这里命名"abcdefg"就很不合理了,就应该改成"com.example.app016.MyTest"
2、当然,你可以在自己的程序中调用其他程序的Action
例如可以在自己的应用程序中调用拨号面板:

Intent intent = new Intent(Intent.ACTION_DIAL);  
// 或者Intent intent = new Intent("android.intent.action.DIAL");  
// Intent.ACTION_DIAL是内置常量,值为"android.intent.action.DIAL"  
startActivity(intent); 

3、一个Activity可以处理多种Action
只要你的应用程序够牛逼,一个Activity可以看网页,打电话,发短信,发邮件。。。当然可以。
IntentAction只要是其中之一,就可以打开这个Activity

<activity  
    android:name="com.example.app016.SecondActivity">  
    <intent-filter>  
        <!-- 可以处理下面三种Intent -->  
        <action android:name="com.example.app016.SEND_EMAIL"/>  
        <action android:name="com.example.app016.SEND_MESSAGE"/>  
        <action android:name="com.example.app016.DAIL"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</activity>  

对于一个Action字符串,系统有可能会找到一个Activity能处理这个Action,也有可能找到多个Activity,也可能一个都找不到。
1、找到一个Activity
很简单,直接打开这个Activity。这个不需要解释。
2、找到多个Acyivity
系统会提示从多个activity中选择一个打开。
例如我们自己开发一个拨号面板应用程序,可以设置activity<intent-filter>Action name"android.intent.action.DIAL",这样别的程序调用拨号器时,用户可以从Android自带的拨号器和我们自己开发的拨号器中选择。


<activity  
    android:name="com.example.app016.SecondActivity">  
    <intent-filter>  
        <action android:name="android.intent.action.DIAL"/>  
        <category android:name="android.intent.category.DEFAULT" />  
    </intent-filter>  
</activity>  

这也就是当Android手机装上UC浏览器后,打开网页时会弹出选择Android自带浏览器还是UC浏览器,可能都会遇到过。
3、一个Activity都没找到
一个都没找到的话,程序就会出错,会抛出ActivityNotFoundException。比如随便写一个action字符串:

Intent intent = new Intent("asasasas");  
startActivity(intent);  

所以应该注意try catch异常。

Intent intent = new Intent("asasasas");  
try  
{  
    startActivity(intent);  
}  
catch(ActivityNotFoundException e)  
{  
    Toast.makeText(this, "找不到对应的Activity", Toast.LENGTH_SHORT).show();  
}  

或者也可以使用IntentresolveActivity方法判断这个Intent是否能找到合适的Activity,如果没有,则不再startActivity,或者可以直接禁用用户操作的控件。

Intent intent = new Intent(Intent.ACTION_DIAL);  
if(intent.resolveActivity(getPackageManager()) == null)  
{  
    // 设置控件不可用  
}  

注意resolveActivity方法返回值就是显式Intent上面讲到的ComponentName对象,一般情况下也就是系统找到的那个Activity。但是如果有多个Activity可供选择的话,则返回的Componentcom.android.internal.app.ResolverActivity,也就是用户选择Activity的那个界面对应的Activity,这里不再深究。

Intent intent = new Intent(Intent.ACTION_DIAL);  
ComponentName componentName = intent.resolveActivity(getPackageManager());  
if(componentName != null)  
{  
    String className = componentName.getClassName();  
    Toast.makeText(this, className, Toast.LENGTH_SHORT).show();  
}  

什么是嵌入式实时操作系统, Android操作系统属于实时操作系统吗?

  嵌入式实时操作系统是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统作出快速响应,并控制所有实时任务协调一致运行的嵌入式操作系统。主要用于工业控制、军事设备、航空航天等领域对系统的响应时间有苛刻的要求,这就需要使用实时系统。又可分为软实时和硬实时两种,而android是基于linux内核的,因此属于软实时。

Android中的IOC框架

Android中的IOC框架,完全注解方式就可以进行UI绑定和事件绑定

http://www.jianshu.com/p/3968ffabdf9d#

Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)

Inversion of Control,英文缩写为IOC,字面翻译:控制反转。

一种设计思想。使调用者和被调用者解耦和分离,便于更改和代码重用,便于移植。

许多应用都是由多个类通过彼此合作来实现业务逻辑,每个对象之间都相互依赖,这将导致代码高度耦合并且难以测试、难以修改难以重用。

IoC很好的解决了该问题,它将实现组件间关系从程序内部提到外部容器来管理。也就是说由容器在运行期将组件间的某种依赖关系动态的注入组件中。控制程序间关系的实现交给了外部容器来完成。

什么意思呢?就是一个类里面需要用到很多个成员变量,传统的写法,你要用这些成员变量,那么你就new 出来用呗!IOC的原则是:NO,我们不要new,这样耦合度太高,你配置个xml文件,里面标明哪个类,里面用了哪些成员变量,等待加载这个类的时候,我帮你注入(new)进去;当然了,你又会觉得,写个配置文件,卧槽,这多麻烦。于是乎,又出现了另一种方案,得~你嫌配置文件麻烦,那用注解呗~在你需要注入的成员变量上面加个注解,例如:@Inject,这样就行了,你总不能说这么个单词麻烦吧。当然了,有了配置文件和注解,那么怎么注入呢?其实就是把字符串类路径变成类么,这个时候需要用到反射。

相信很多使用过Afinal和Xutils的朋友会发现框架中自带View控件注解及事件绑定功能,我们无需使用findViewById和setOnClickListener即可完成view初始化和监听事件,使用注解在很大程度上使我们的代码看起来更加简洁,让我们的代码看起来不是那么冗余,那我们今天就来一探究竟,看看其中原理是如何来实现的。

Java注解相当于一种标记,标记可以加在包,类,字段,方法,方法的参数以及局部变量上。在程序中加入注解可以起到说明和配置的功能,Javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,根据你的标记,去做相应的事。

实现原理,首先主要是获取当前类对象的所有属性包括public、private、protected,然后循环判断属性是否采用注解并且是否是使用我们自定义的注解类,如果两种情况都满足则获取属性所指定的注解值,如果注解值不是默认的就表示指定了明确的值,这里就可以根据获取到的ID值来findViewById()来获取对象,并且设置给当前属性来完成初始化。

指定控件的点击事件就比较麻烦,首先需要先将属性转成Object对象,然后判断是否是View的实例,如果是则设置点击事件,在OnClickListener的onClick方法中获取当前类中以根据注解值来命名的方法,然后通过Method的invoke来调用执行,注意,这里传递了事件对象本身,所以我们方法中必须要加入参数。

Intent可以传递哪些类型的数据

1. 8种基本数据类型及其数组 
2. String(String实现了 Serializable )/CharSequence实例类型的数据及其数组 
3. 实现了Parcelable的对象及其数组 
4. 实现了 Serializable 的对象及其数组 

charsequence  :

在JDK1.4中,引入了CharSequence接口,实现了这个接口的类有:CharBuffer、String、StringBuffer、StringBuilder这个四个类。

CharBuffer为nio里面用的一个类,String实现这个接口理所当然,StringBuffer也是一个CharSequence,StringBuilder是Java抄袭C#的一个类,基本和StringBuffer类一样,效率高,但是不保证线程安全,在不需要多线程的环境下可以考虑。

提供这么一个接口,有些处理String或者StringBuffer的类就不用重载了。但是这个接口提供的方法有限,只有下面几个:charat、length、subSequence、toString这几个方法,感觉如果有必要,还是重载的比较好,避免用instaneof这个操作符。

在android中使用Menu时可能需要重写的方法有?

上下文菜单(通过在某元素上长按,来呼出菜单) 
选项菜单(通过按手机上的菜单按钮,来呼出菜单)   

重写 onCreateContextMenu 用以创建上下文菜单 
重写 onContextItemSelected 用以响应上下文菜单  

重写 onCreateOptionsMenu 用以创建选项菜单 
重写 onOptionsItemSelected 用以响应选项菜单 

当每次Menu显示时,会调用方法onPrepareOptionsMenu,也可以在菜单每次被调用时,对菜单中的项重新生成,通过重载onPrepareOptionsMenu来实现,由于每次调用时都要重新生成,对于那些不经常变化的菜单,效率就会比较低。 

调用Menu.addSubMenu()方法,为某个菜单项添加子菜单

GLSurFaceView特性

一个GLSurfaceView类 , 具有以下特点 :

1.管理一个Surface, 这个Surface是一个特殊的内存块 , 它可以和 android 视图系统混合 .

2.管理一个EGL 显示 , 它能够让 OpenGL 渲染到一个平面 .

3.接受一个用户提供的实际显示的Renderer 对象 .

4.使用一个专用线程去渲染从而和UI 线程解耦 .

5.支持on-demand  和连续的渲染.

6.可选的包, 追踪 和 / 或者错误检查这个渲染器的 OpenGL 调用 .

Android 显示原理简介:

http://djt.qq.com/article/view/987

微信即时通信实现 微信手机休眠状态即时接受消息:

android设备休眠

微信、陌陌等著名IM软件设计架构详解(内容扩展)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值