Android 零散记录

Posted by 夏敏的博客 on November 6, 2016
  • Android虚线显示不出来的原因 ``` xml <?xml version=”1.0” encoding=”utf-8”?>
以上虚线显示不出来的解决方法是是:关闭硬件加速

android:background=”@drawable/dashed_line_grey”
android:layerType=”software”


 - ### PopupWindow与AlertDialog的区别
最关键的区别是AlertDialog不能指定显示位置,只能默认显示在屏幕最中间(当然也可以通过设置WindowManager参数来改变位置)。而PopupWindow是可以指定显示位置的,随便哪个位置都可以,更加灵活。
mPopWindow.showAtLocation(rootview, Gravity.BOTTOM, 0, 0);  

 - ### AlertDialog风格不对
主题问题,当主题不对时候,Dialog风格可能会变成很丑的旧API等级的风格。

 - ### 为什么叫Support v4,v7
Android Support v4:  这个包是为了照顾1.6及更高版本而设计的,这个包是使用最广泛的,eclipse新建工程时,都默认带有了。
Android Support v7:  这个包是为了考虑照顾2.1及以上版本而设计的,但不包含更低,故如果不考虑1.6,我们可以采用再加上这个包,另外注意,v7是要依赖v4这个包的,即,两个得同时被包含。
Android Support v13  :这个包的设计是为了android 3.2及更高版本的,一般我们都不常用,平板开发中能用到。

 - ### 未解绑服务使得服务持有一个销毁的activity的context造成内存泄露
MainActivity has leaked ServiceConnection com.skyace.service.MainActivity$1@41cd81f0 that was originally bound here
服务没有解绑,造成内存泄露,onDestroy的回调方法中加入了对服务的解绑操作即 unbindService成功解决

 -  ### handler中的handleMessage返回值
 return true 代表事件被处理了,其他handleMessage不会收到该msg
 return false 事件继续传递,外层的handleMessage() 会继续执行

 -  ###  FC问题从log中快速搜索has died

 11-18 10:10:59.380 V/CommandService(  495): Death received CommandThread:android.os.BinderProxy@41a1b1b8 in pid:1218
随后搜索该pid 快速找到log


 -  ###  Fragment对于onActivityResult捕获不到的情况
被父avtivity的onActivityResult捕获了

 -  ###  软件盘的本质是什么?软键盘其实是一个Dialog!
 InputMethodService为我们的输入法创建了一个Dialog,并且将该Dialog的Window的某些参数(如Gravity)进行了设置,使之能够在底部或者全屏显示。当我们点击输入框时,系统对活动主窗口进行调整,从而为输入法腾出相应的空间,然后将该Dialog显示在底部,或者全屏显示。

 -  ### 如何去掉字符串前后空格,或者说判断字符串是否为空,或者全部为空格
	 TextUtils.isEmpty(mStr.trim()
	 String类自带的trim()方法,能够去掉字符串前后空格

 -  ### 如何使强制控制键盘弹起落下

``` java
   public void showSoftKeyboard() {
        mEditText.setFocusable(true);
        mEditText.setFocusableInTouchMode(true);
        mEditText.requestFocus();
        InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        inputManager.showSoftInput(mEditText, InputMethodManager.SHOW_FORCED);
    }

    public void hideSoftKeyboard() {
        InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        inputManager.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
    }
  • 系统语言改变那点事

    当系统语言改变,当前Activity会进行重新创建,在生命方法中,我们可以在manifest中: android:configChanges=”locale” 语言(国家码)改变

    I/###xiamin( 8571): Setting onPause I/###xiamin( 8571): Setting onStop I/###xiamin( 8571): Setting onDestory I/###xiamin( 8571): Setting onCreate I/###xiamin( 8571): Setting onStart I/###xiamin( 8571): Setting onResume

  • Android模块

    keyguard(锁屏)模块 SystemUI 通知栏和最近应用

  • Android分辨率适配终极方案

    android-support-percent-lib Android基于百分比的布局,谷歌官方推荐 android-support-percent-lib鸿洋博客

  • SurfaceView

    普通的Android控件,例如TextView、Button和CheckBox等,它们都是将自己的UI绘制在宿主窗口的绘图表面之上,这意味着它们的UI是在应用程序的主线程中进行绘制的。由于应用程序的主线程除了要绘制UI之外,还需要及时地响应用户输入,否则的话,系统就会认为应用程序没有响应了,因此就会弹出一个ANR对话框出来。对于一些游戏画面,或者摄像头预览、视频播放来说,它们的UI都比较复杂,而且要求能够进行高效的绘制,因此,它们的UI就不适合在应用程序的主线程中进行绘制。这时候就必须要给那些需要复杂而高效UI的视图生成一个独立的绘图表面,以及使用一个独立的线程来绘制这些视图的UI。

  • android:splitMotionEvents

    定义布局是否传递触摸事件(touch)到子布局,true表示传递给子布局,false表示不传递。

  • 获取当前格式化时间

    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
    Date date = new Date(System.currentTimeMillis());
    String str = format.format(date)
    
  • Android为每个应用程序分配的内存大小

    过去是16M,不过根据机型而言不一样,早期的Android系统G1,就是只有16M

  • Android中内部存储和外部存储的理解和路径获取

    1.data/data/包名/shared_prefs 2.data/data/包名/databases 3.data/data/包名/files 4.data/data/包名/cache

外部存储 外部存储才是我们平时操作最多的,外部存储一般就是我们上面看到的storage文件夹,当然也有可能是mnt文件夹,这个不同厂家有可能不一样。

一般来说,在storage文件夹中有一个sdcard文件夹,这个文件夹中的文件又分为两类,一类是公有目录,还有一类是私有目录,其中的公有目录有九大类,比如DCIM、DOWNLOAD等这种系统为我们创建的文件夹,私有目录就是Android这个文件夹,这个文件夹打开之后里边有一个data文件夹,打开这个data文件夹,里边有许多包名组成的文件夹。

所以外部存储的路径是:

storage/sdcard/Android/data/包名/files storage/sdcard/Android/data/包名/cache

  • SharedPreferences也可以设置监听器

    mSharedPreferences.registerOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener);

  • Android获取唯一机器码的代码

    String mDeviceId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);

  • 将内容复制到粘贴板

    ClipboardManager copy = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    ClipData myClip;
    String text = "" + mIdcode;
    myClip = ClipData.newPlainText("text", text);
    copy.setPrimaryClip(myClip);
    Toast.makeText(WelcomeActivity.this, "复制成功", Toast.LENGTH_SHORT).show();
  • 监听屏幕唤醒和关闭的广播

	private void registSreenStatusReceiver() {  
		mScreenStatusReceiver = new ScreenStatusReceiver();    
		IntentFilter screenStatusIF = new IntentFilter();    
		screenStatusIF.addAction(Intent.ACTION_SCREEN_ON);    
		screenStatusIF.addAction(Intent.ACTION_SCREEN_OFF);    
		registerReceiver(mScreenStatusReceiver, screenStatusIF);    
	}  

unregisterReceiver(mScreenStatusReceiver);  

class ScreenStatusReceiver extends BroadcastReceiver {  
    String SCREEN_ON = "android.intent.action.SCREEN_ON";  
    String SCREEN_OFF = "android.intent.action.SCREEN_OFF";  

    @Override  
    public void onReceive(Context context, Intent intent) {  
        if (SCREEN_ON.equals(intent.getAction())) {  

        }  
        else if (SCREEN_OFF.equals(intent.getAction())) {  
        }  
    }  
}
  • Android应用的persistent属性

    android:persistent=”true” 在Android系统中,有一种永久性应用。它们对应的AndroidManifest.xml文件里,会将persistent属性设为true 在系统启动之时,AMS的systemReady()会加载所有persistent为true的应用。

  • 服务的前台运行(现在没什么用了)

    服务前台运行

  • 使用AIDL作为项目之间的接口可能存在一定的风险。

如何规避这个风险,网上有文章说,在IBinder里面的onTransact函数中调用Binder.getCallingUid()和Binder.getCallingPid()来判断外来方的身份。 但这个方法,只能是被调用方检测调用方的身份。 最近我的服务作为系统级服务存在的,但是其中的一个标志位出了问题,就是通过该方法找到是哪个进程改了的.

  • 如何导入外部数据库

    把原数据库包括在项目源码的 res/raw

android系统下数据库应该存放在 /data/data/com..(package name)/ 目录下,所以我们需要做的是把已有的数据库传入那个目录下.操作方法是用FileInputStream读取原数据库,再用FileOutputStream把读取到的东西写入到那个目录.

  • LaunchMode应用场景

standard,创建一个新的Activity。

singleTop,栈顶不是该类型的Activity,创建一个新的Activity。否则,onNewIntent。

singleTask,回退栈中没有该类型的Activity,创建Activity,否则,onNewIntent+ClearTop。

注意:

设置了”singleTask”启动模式的Activity,它在启动的时候,会先在系统中查找属性值affinity等于它的属性值taskAffinity的Task存在; 如果存在这样的Task,它就会在这个Task中启动,否则就会在新的任务栈中启动。因此, 如果我们想要设置了”singleTask”启动模式的Activity在新的任务中启动,就要为它设置一个独立的taskAffinity属性值。 如果设置了”singleTask”启动模式的Activity不是在新的任务中启动时,它会在已有的任务中查看是否已经存在相应的Activity实例, 如果存在,就会把位于这个Activity实例上面的Activity全部结束掉,即最终这个Activity 实例会位于任务的Stack顶端中。 在一个任务栈中只有一个”singleTask”启动模式的Activity存在。他的上面可以有其他的Activity。这点与singleInstance是有区别的。 singleInstance,回退栈中,只有这一个Activity,没有其他Activity。 singleTop适合接收通知启动的内容显示页面。

例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。

singleTask适合作为程序入口点。

例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。

singleInstance应用场景:

闹铃的响铃界面。 你以前设置了一个闹铃:上午6点。在上午5点58分,你启动了闹铃设置界面,并按 Home 键回桌面;在上午5点59分时,你在微信和朋友聊天;在6点时,闹铃响了,并且弹出了一个对话框形式的 Activity(名为 AlarmAlertActivity) 提示你到6点了(这个 Activity 就是以 SingleInstance 加载模式打开的),你按返回键,回到的是微信的聊天界面,这是因为 AlarmAlertActivity 所在的 Task 的栈只有他一个元素, 因此退出之后这个 Task 的栈空了。如果是以 SingleTask 打开 AlarmAlertActivity,那么当闹铃响了的时候,按返回键应该进入闹铃设置界面。

  • invalidate()和postInvalidate()的区别

可以把UI线程理解为主线程。其余的线程可以理解为工作者线程。 invalidate()得在UI线程中被调动,在工作者线程中可以通过Handler来通知UI线程进行界面更新。

而postInvalidate()在工作者线程中被调用

  • Android动画框架实现原理

Animation框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个View,实现原理是每次绘制视图时View所在的ViewGroup中的drawChild函数获取该View的Animation的Transformation值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧,如果动画没有完成,继续调用invalidate()函数,启动下次绘制来驱动动画,动画过程中的帧之间间隙时间是绘制函数所消耗的时间,可能会导致动画消耗比较多的CPU资源,最重要的是,动画改变的只是显示,并不能相应事件。

  • View刷新机制

由ViewRoot对象的performTraversals()方法调用draw()方法发起绘制该View树,值得注意的是每次发起绘图时,并不会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内部变量包含了一个标志位DRAWN,当该视图需要重绘时,就会为该View添加该标志位。

  • View 的关键生命周期

View 的关键生命周期为 [改变可见性] –> 构造View –> onFinishInflate –> onAttachedToWindow –> onMeasure –> onSizeChanged –> onLayout –> onDraw –> onDetackedFromWindow

  • Android中图片占用内存的计算

ARGB_4444 每个像素2字节 RGB_565 每个像素2字节 ARGB_8888 每个像素4字节。

那么一张720p的图片,就要占用72012804 大概3.5兆的大小,所以图片处理很重要,不然分分钟OOM

  • ART和Dalvik区别

Art上应用启动快,运行快,但是耗费更多存储空间,安装时间长,总的来说ART的功效就是”空间换时间”。

ART: Ahead of Time Dalvik: Just in Time

什么是Dalvik:Dalvik是Google公司自己设计用于Android平台的Java虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一,它可以支持已转换为.dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

什么是ART:Android操作系统已经成熟,Google的Android团队开始将注意力转向一些底层组件,其中之一是负责应用程序运行的Dalvik运行时。Google开发者已经花了两年时间开发更快执行效率更高更省电的替代ART运行时。ART代表Android Runtime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。ART则完全改变了这套做法,在应用安装的时候就预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。

ART优点:

  1. 系统性能的显著提升
  2. 应用启动更快、运行更快、体验更流畅、触感反馈更及时
  3. 更长的电池续航能力
  4. 支持更低的硬件

ART缺点:

  1. 更大的存储空间占用,可能会增加10%-20%
  2. 更长的应用安装时间
  • 关于include

如果我们要在标签中覆写layout属性,必须要将layout_width和layout_height这两个属性也进行覆写,否则覆写xiaoguo将不会生效。

  • Android几种进程

  1. 前台进程:即与用户正在交互的Activity或者Activity用到的Service等,如果系统内存不足时前台进程是最后被杀死的
  2. 可见进程:可以是处于暂停状态(onPause)的Activity或者绑定在其上的Service,即被用户可见,但由于失去了焦点而不能与用户交互
  3. 服务进程:其中运行着使用startService方法启动的Service,虽然不被用户可见,但是却是用户关心的,例如用户正在非音乐界面听的音乐或者正在非下载页面自己下载的文件等;当系统要空间运行前两者进程时才会被终止
  4. 后台进程:其中运行着执行onStop方法而停止的程序,但是却不是用户当前关心的,例如后台挂着的QQ,这样的进程系统一旦没了有内存就首先被杀死
  5. 空进程:不包含任何应用程序的程序组件的进程,这样的进程系统是一般不会让他存在的

如何避免后台进程被杀死:

  1. 调用startForegound,让你的Service所在的线程成为前台进程
  2. Service的onStartCommond返回START_STICKY或START_REDELIVER_INTENT
  3. Service的onDestroy里面重新启动自己
  • 65K问题

在Android系统中,一个App的所有代码都在一个Dex文件里面。Dex是一个类似Jar的存储了许多Java编译字节码的归档文件。因为Android系统使用Dalvik虚拟机,所以需要把使用Java Compiler编译之后的class文件转换成Dalvik能够执行的class文件。这里需要强调的是,Dex和Jar一样是一个归档文件,里面仍然是Java代码对应的字节码文件。当Android系统启动一个应用的时候,有一步是对Dex进行优化,这个过程有一个专门的工具来处理,叫DexOpt。DexOpt的执行过程是在第一次加载Dex文件的时候执行的。这个过程会生成一个ODEX文件,即Optimised Dex。执行ODex的效率会比直接执行Dex文件的效率要高很多。但是在早期的Android系统中,DexOpt有一个问题,也就是这篇文章想要说明并解决的问题。DexOpt会把每一个类的方法id检索起来,存在一个链表结构里面。但是这个链表的长度是用一个short类型来保存的,导致了方法id的数目不能够超过65536个。当一个项目足够大的时候,显然这个方法数的上限是不够的。尽管在新版本的Android系统中,DexOpt修复了这个问题,但是我们仍然需要对低版本的Android系统做兼容.

对Android方法数不能超过65K的限制应该有所耳闻,随着应用程序功能不断的丰富,总有一天你会遇到一个异常:

Conversion to Dalvik format failed:Unable toexecute dex: method ID not in [0, 0xffff]: 65536

Android系统中,一个Dex文件中存储方法id用的是short类型数据,所以导致你的dex中方法不能超过65k

解决方法是dex分包

dex.force.jumbo=true 是的,加入了这句话,确实可以让你的应用通过编译,但是在一些2.3系统的机器上很容易出现 INSTALL_FAILED_DEXOPT异常

详细方法见:彻底解决Android 应用方法数不能超过65K的问题

  • Android使用static静态代码块最多的地方

ContentProvider中,用来初始化UriMatcher

    private static final UriMatcher
            sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        sURLMatcher.addURI("mms", null,         MMS_ALL);
        sURLMatcher.addURI("mms", "#",          MMS_ALL_ID);
    }

    private SQLiteOpenHelper mOpenHelper;
  • SurfaceTexture 的好处

可以在不使用SurfaceView的情况下,获取到摄像头的数据,并且自己做相应处理.

  • 摄像头数据那点事

摄像头数据在

public void onPreviewFrame(byte[] data, Camera camera)

回调中拿到手,到手的为YUV420SP数据,需要转成bitmap处理,最好是Bitmap.Config.RGB_565.

  • 查看每个应用程序最高可用内存:

    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
    Log.d("TAG", "Max memory is " + maxMemory + "KB");
  • java.lang.NoClassDefFoundError: Failed resolution of: Landroid/support/v7

原因是:

compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.0.1'

问题是v7和design版本不一致 改成一样的搞版本就好

  • Intent Filter

  android的3个核心组件——Activity、services、广播接收器——是通过intent传递消息的。intent消息用于在运行时绑定不同的组件。   在 Android 的 AndroidManifest.xml 配置文件中可以通过 intent-filter 节点为一个 Activity 指定其 Intent Filter,以便告诉系统该 Activity 可以响应什么类型的 Intent。

intent-filter 的三大属性

  1. Action

  一个 Intent Filter 可以包含多个 Action,Action 列表用于标示 Activity 所能接受的“动作”,它是一个用户自定义的字符串。

  

<intent-filter >
 <action android:name="android.intent.action.MAIN" />
 <action android:name="com.scu.amazing7Action" />
……
 </intent-filter>

在代码中使用以下语句便可以启动该Intent 对象:

Intent i=new Intent();
i.setAction("com.scu.amazing7Action");

Action 列表中包含了“com.scu.amazing7Action”的 Activity 都将会匹配成功

  1. URL

  在 intent-filter 节点中,通过 data节点匹配外部数据,也就是通过 URI 携带外部数据给目标组件。

<data android:mimeType="mimeType"
    android:scheme="scheme"
     android:host="host"
     android:port="port"
     android:path="path"/>

注意:只有data的所有的属性都匹配成功时 URI 数据匹配才会成功

  1. Category

  为组件定义一个 类别列表,当 Intent 中包含这个类别列表的所有项目时才会匹配成功。

<intent-filter . . . >
   <action android:name="code android.intent.action.MAIN" />
   <category android:name="code android.intent.category.LAUNCHER" />
</intent-filter>
  • Activity 中 Intent Filter 的匹配过程

  ①加载所有的Intent Filter列表
  ②去掉action匹配失败的Intent Filter
  ③去掉url匹配失败的Intent Filter
  ④去掉Category匹配失败的Intent Filter
  ⑤判断剩下的Intent Filter数目是否为0。如果为0查找失败返回异常;如果大于0,就按优先级排序,返回最高优先级的Intent Filter

  • 开发中Activity的一些问题

一般设置Activity为非公开的  

<activity  
......
android:exported="false" />

注意:非公开的Activity不能设置intent-filter,以免被其他activity唤醒(如果拥有相同的intent-filter)。

  • 不要指定activity的taskAffinity属性

  • 不要设置activity的LaunchMode(保持默认)

  注意Activity的intent最好也不要设定为FLAG_ACTIVITY_NEW_TASK

  • 在匿名内部类中使用this时加上activity类名(类名.this,不一定是当前activity)

  • 设置activity全屏

  在其 onCreate()方法中加入:

// 设置全屏模式
 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
 // 去除标题栏
 requestWindowFeature(Window.FEATURE_NO_TITLE);
  • Android去除系统自带动画的两种方法

方法一:

在startActivity()或者finish()后紧跟调用:

((Activity) mContext).overridePendingTransition(0, 0);

方法二: 在一些特殊情况下方法一是不能实现的. 比如给Intent设置了属性:

intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

此时可以这么做: 1.在styles.xml下添加:

    <style name="Theme" parent="android:Theme">
        <item name="android:windowAnimationStyle">@style/noAnimation</item>
        <item name="android:windowNoTitle">true</item>
    </style>

    <style name="noAnimation">
        <item name="android:activityOpenEnterAnimation">@null</item>
        <item name="android:activityOpenExitAnimation">@null</item>
        <item name="android:activityCloseEnterAnimation">@null</item>
        <item name="android:activityCloseExitAnimation">@null</item>
        <item name="android:taskOpenEnterAnimation">@null</item>
        <item name="android:taskOpenExitAnimation">@null</item>
        <item name="android:taskCloseEnterAnimation">@null</item>
        <item name="android:taskCloseExitAnimation">@null</item>
        <item name="android:taskToFrontEnterAnimation">@null</item>
        <item name="android:taskToFrontExitAnimation">@null</item>
        <item name="android:taskToBackEnterAnimation">@null</item>
        <item name="android:taskToBackExitAnimation">@null</item>
    </style>

2.在AndroidManifest.xml中为跳出和跳入的Activity设置:

android:theme=”@style/Theme”

  • 说说AsyncTask的源码设计

当我们调用execute的时候,其实是去调executeOnExecutor,传入我们的默认执行器的.从代码中,我们能看到,默认的执行器是一个串行的执行器.当然,这个串行执行器会再将runnable给一个ThreadPoolExecutor来执行, 这个ThreadPoolExecutor 默认大小是处理器的核心数的2倍+1, 比如我们是双核,也就是默认大小为5个线程.

     private static final BlockingQueue<Runnable> sPoolWorkQueue =
         new LinkedBlockingQueue<Runnable>(128);

     /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
     public static final Executor THREAD_POOL_EXECUTOR
         = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                 TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);  

     public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
     private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

     @MainThread
     public final AsyncTask<Params, Progress, Result> execute(Params... params) {
         return executeOnExecutor(sDefaultExecutor, params);
     }

     @MainThread
     public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
             Params... params) {
         if (mStatus != Status.PENDING) {
             switch (mStatus) {
                 case RUNNING:
                     throw new IllegalStateException("Cannot execute task:"
                             + " the task is already running.");
                 case FINISHED:
                     throw new IllegalStateException("Cannot execute task:"
                             + " the task has already been executed "
                             + "(a task can be executed only once)");
             }
         }

         mStatus = Status.RUNNING;

         onPreExecute();

         mWorker.mParams = params;
         exec.execute(mFuture);

         return this;
     }
  • 子线程更新UI那点事情

在onCreate或者onResume中, 直接开一个线程更新UI是可以的, 在Activity刚刚起来的时候, ViewRootImpl不会走checkTHread 方法, 因为mParent为空.

但是当Activity存活了一会儿,比如我们设置个button点击事件, 然后点击触发使用线程修改UI, 便会报错

  • Glide是如何解决ListView Item图片加载错乱的问题

因为在Glide into(ImageView)时, 绑定了控件, 若into的是SimpleTarget 然后自己手动设置,若我们自己没有设置tag, 便会错乱.

  • 使用lambda表达式AS错误

     Error:Jack is required to support java 8 language features. Either enable Jack or remove sourceCompatibility JavaVersion.VERSION_1_8.
    

    解决:配置Gradle.

在模块特定的 build.gradle 文件中输入以下内容:

android { …….  
  defaultConfig {    
        jackOptions {        
          enabled true    
        }  
      }
    }  
  • 防止点击多个控件

在一个手机界面中,用户经常会同时点击多个控件,经常会在短时间内对某一个控件点击多下,这样就会造成各种各样的bug。

只要在xml文件中的父容器总加入这样一行代码即可:

android:splitMotionEvents=”false”

如在下面的LinearLayout中加入这行话,表示每一次只能点击LinearLayout其中的一个元素,点击了其他的元素是无效的。

android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dp10"
android:splitMotionEvents="false"
android:background="@drawable/shape_frame_black"
android:orientation="vertical" >
  • ServiceConnection中的onServiceDisconnected()调用问题

    ServiceConnection中的onServiceDisconnected()方法在正常情况下是不被调用的,它的调用时机是当Service服务被异外销毁时,例如内存的资源不足时这个方法才被自动调用。

  • 灭屏的生命周期

    灭屏也是走onStop的

  • PowerManager.WakeLock日志查看

    查看有灭屏操作的相关bug时候,

    D/PowerManagerNotifier( 508): onWakeLockReleased: flags=1,

flag为1的log是不会影响休眠时间的

  • LayoutInflaterFactory那点事

在Activity中,设置自定义的LayoutInflaterFactory, 可以通过下列这种方式.

       getLayoutInflater().setFactory(new LayoutInflater.Factory() {
            @Override
            public View onCreateView(String name, Context context, AttributeSet attrs) {
                                if (name.equals("TextView")) {
                    Button button = new Button(context, attrs);
                    return button;
                }

                View view = null;
                try {
                    if (-1 == name.indexOf('.')){
                        if ("View".equals(name)) {
                            view = LayoutInflater.from(context).createView(name, "android.view.", attrs);
                        }
                        if (view == null) {
                            view = LayoutInflater.from(context).createView(name, "android.widget.", attrs);
                        }
                        if (view == null) {
                            view = LayoutInflater.from(context).createView(name, "android.webkit.", attrs);
                        }
                    }else {
                        view = LayoutInflater.from(context).createView(name, null, attrs);
                    }

                    Log.i("xxx","about to create " + name);

                } catch (Exception e) {
                    Log.i("xxx","error while create 【" + name + "】 : " + e.getMessage());
                    view = null;
                }
                return view;

            }
        });

我们也可以直接复写 Activity的 onCreateView方法,

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        return super.onCreateView(name, context, attrs);
    }

AppCompatActivity中,我们可以直接LayoutInflaterCompat.setFactory的方式来设置.

    LayoutInflaterCompat.setFactory(LayoutInflater.from(this), new LayoutInflaterFactory() {
        @Override
        public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {

            if (name.equals("TextView")) {
                Button button = new Button(context, attrs);
                return button;
            }

            AppCompatDelegate delegate = getDelegate();
            View view = delegate.createView(parent, name, context, attrs);

            return view;

        }
    });

  • Android在初始化的线池时使用处理器的核心数

     int processors = Runtime.getRuntime().availableProcessors();
    
  • 当我们只负责传递一个Listener,但有需求在传递过程中进行部分数据处理的时候, 可以使用装饰模式.

    使用ListenerWrapper一下就好了.

  • android:clipChildren属性

    是否允许子View超出父View的返回,有两个值true 、false ,默认true

使用的时候给子View和根节点View控件都设置android:clipChildren=”false”,那么这个子View就不会限制在父View当中

  • WebView 缓存处理

    LOAD_CACHE_ONLY:  不使用网络,只读取本地缓存数据
    LOAD_DEFAULT:  根据cache-control决定是否从网络上取数据。
    LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
    LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
    LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
    

    建议缓存策略为,判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK。


  • 自定义View时的枚举

当我们需要限定某个属性只能是哪些值时,使用下面这种方法.

         <attr name="anitation_translation">
            <flag name="left" value="0x01" />
            <flag name="top" value="0x02" />
            <flag name="right" value="0x04" />
            <flag name="bottom" value="0x08" />
        </attr>
  • PackageMonitor是什么?

PackageMonitor类继承了 BroadcastReceiver 的类,PackageMonitor是一个内部API,并且它实际上是一个BroadcastReceiver。主要用来监听package的安装,卸载,重启等动作

  • 关于EmptyView

    在使用 GridView、ListView 时经常需要处理无数据的情况,给用户一些必要的提示。而 GridView 和 ListView 可以使用 setEmptyView() 方法来设置无数据时展示的 View 。

  • 面试的时候大家经常会被问IntentService跟Thread有什么区别吧?

比如App升级的时候,去服务端下载apk包的时候为什么要开一个IntertService而不是Thread. Answer:我们要从Android系统的5大进程级别来看待这个事情,分别为“前台进程”,“可视进程”,“服务进程”,“后台进程”,“空进程”.当系统的内存不足时,会根据进程的级别来回收这些进程.即回收的顺序为:“空进程”->…->“前台进程”.很明显,如果用户按下home键之后,我们的进程会变成一个后台进程,此时很容易被系统回收掉,那咱们在Thread中所做的操作就白扯了,而如果我们开一个IntentService的话,我们的进程会变成一个Service进程,被回收的几率就大大降低了。

当然,这种问题我们不应该只停留在理论阶段,应付面试是一方面,我们还是要多看系统的文档,这也是我等菜鸟通往高级工程师必不可少的一环。

  • IntentService与Service的区别

Service主要用于后台服务 当应用程序被挂到后台的时候,为了保证应用某些组件仍然可以工作而引入了Service这个概念,那么这里面要强调的是Service不是独立的进程,也不是独立的线程,它是依赖于应用程序的主线程的,在更多时候不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。这时需要引入IntentService,IntentService是继承Service的,那么它包含了Service的全部特性,当然也包含service的生命周期,那么与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去执行你的耗时操作。

IntentService:异步处理服务,新开一个线程:handlerThread在线程中发消息,然后接受处理完成后,会清理线程,并且关掉服务。 IntentService有以下特点:

(1) 它创建了一个独立的工作线程来处理所有的通过onStartCommand()传递给服务的intents。
(2) 创建了一个工作队列,来逐个发送intent给onHandleIntent()。
(3) 不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。
(4) 默认实现的onBind()返回null
(5) 默认实现的onStartCommand()的目的是将intent插入到工作队列中

  • Activity防止截屏

    this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);

  • startLockTask()

    这个方法是锁住当前Activity的Task,这种模式下,其他页面不能弹出来挡住你的Activity, 甚至打电话来了都不行,闹钟也不行. 属奇葩需求奇葩API.(安全需求)

  • ListView/RecyclerView Item 布局宽高以及一些属性无效问题

    因为LayoutInflater用法出错:

    LayoutInflater.from(parent.getContext()).inflate(R.layout.inflate_test_item,null)
    

上面这种用法就会使得跟布局的一些属性无效

LayoutInflater.from(parent.getContext()).inflate(R.layout.inflate_test_item,parent,false)

这种会有效

因为父布局的参数传空,就会使得子布局没有父布局的属性,那么’match_parent’属性也就没用了

第三个参数是代表是不是要自动添加到parent上,默认为true,会执行 parent.addView, 在ListView这种模式下,我们只需要传false.

  • 测试打开链接:

    打开KeepGank界面

  • 对于webView打不开Arouter页面问题

    net::ERR_CACHE_MISS

mWebView.setWebViewClient(new WebViewClient(){  
        @Override  
        public boolean shouldOverrideUrlLoading(WebView view, String url) {  
            if( url.startsWith("http:") || url.startsWith("https:") ) {  
                return false;  
            }  
   try{
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));  
            startActivity( intent );  
   }catch(Exception e){}
            return true;  
        }  
    });  
  • Android录音对于蓝牙耳机的录音适配

蓝牙一般有两种语音相关的模式是A2DP和SCO,前者是高质量音乐播放(俗称:只进不出),后者是语音通话(俗称:有进有出)。要实现语音从蓝牙进,那么它一字得处于SCO模式下,也是通话模式下。

        /**
         * if the user use Bluetooth mic
         */
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mAudioManager.setBluetoothScoOn(true);
        mAudioManager.startBluetoothSco();

用完记得

        mAudioManager.setBluetoothScoOn(false);
        mAudioManager.stopBluetoothSco();
  • Android数据库事务

一.什么是事务
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。
事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。
二.事务的 ACID
事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。

  • 原子性 :事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
  • 一致性 :事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
  • 隔离性 :一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持续性 :也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。

在Android中,SQLiteDatabase也有事务功能,

    //开启事务  
    db.beginTransaction();  
    //设置事务标志为成功,当结束事务时就会提交事务,若不成功endTransaction就会回滚事务
    db.setTransactionSuccessful();  
    db.endTransaction();  
  • 调用Android相机部分代码

    拍照:

                  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                  intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(getCaptureFile()));
                  startActivityForResult(intent, RESULT_CAPTURE_IMAGE);
    

    选择:

                  Intent intent2 = new Intent(Intent.ACTION_PICK,
                          MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                  startActivityForResult(intent2, RESULT_PICK_IMAGE);
    

    裁剪:

          Intent intent = new Intent("com.android.camera.action.CROP");
          intent.setType("image/*");
          intent.setDataAndType(uri, "image/jpeg");
          intent.putExtra("crop", "true");
          intent.putExtra("aspectX", width);
          intent.putExtra("aspectY", height);
          intent.putExtra("outputX", width);
          intent.putExtra("outputY", height);
          intent.putExtra("return-data", false);
          intent.putExtra("outputFormat", "JPEG");
          intent.putExtra("output", Uri.fromFile(getCropFile()));
          startActivityForResult(intent, RESULT_CROP_IMAGE);
    

在部分机型上,在选择照片的部分有可能会返回错误的Uri。应该以file开头的路径变成了content开头的,此时会报没有权限的错误。

解决办法:
在进行头像选择的时候,传入的Uri要进行检查.

  • GridLayoutManager妙用

    setSpanSizeLookup里面可以通过位置判断

          final GridLayoutManager layoutManager = new GridLayoutManager(this, SPAN_COUNT);
          layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
              @Override
              public int getSpanSize(int position) {
                  return (items.get(position) instanceof Category) ? SPAN_COUNT : 1;
              }
          });
    
  • LinearLayout和RelativeLayout性能对比

    RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,也会调用子View2次onMeasure RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题,当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin。

复杂的View嵌套对性能的影响会更大一些

  • Scrollview (NestedScrollView) 嵌套 RecyclerView 的时候RecyclerView 抢焦点问题

    Scrollview (NestedScrollView) 嵌套 RecyclerView 的时候RecyclerView 抢焦点,跳转到这个Activity 页面的时候,Scrollview 自动滑动到RecyclerView 的地方而不是本页面的最上方的View, 这时候是因为RecyclerView抢了焦点 ,自动滑动

在顶层布局上

android:focusableInTouchMode="true"  
        android:focusable="true"  

Toolbar的title问题

有时候直接设置 setTitle(“”) 是不会让其变空的,使用下面方式

getSupportActionBar().setDisplayShowTitleEnabled(false);

获取系统当前语言,并判断是哪种语言

有时候需要对特殊的语言进行UI调整

        Locale locale = mContext.getResources().getConfiguration().locale;
        LogUtils.i("current language---locale: " + locale);
        if (locale.equals(Locale.SIMPLIFIED_CHINESE)) {
            LogUtils.i("current language Chinese");
        }

dex与odex

我们平时写的类都会被转换成class然后打包成dex, 该文件存在与apk中,解压apk即可看见

ODEX是安卓上的应用程序apk中提取出来的可运行文件,即将APK中的classes.dex文件通过dex优化过程将其优化生成一个dex文件单独存放. 在有些rom中,system/app中的apk就会生成odex.因此我们光push apk是没用的,还要删除掉odex文件.

View转bitmap

        view.setDrawingCacheEnabled(true);
        view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        view.buildDrawingCache();
        Bitmap bitmap = view.getDrawingCache();

TextView设置字体

Typeface typeFace =Typeface.createFromAsset(getAssets(),"fonts/HandmadeTypewriter.ttf");
textView.setTypeface(typeface);

Android如何设置虚线

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="line">
    <!-- 显示虚线,破折线的宽度为dashWith,空隙的宽度为dashGap, darkgray -->
    <stroke
        android:width="1dp"
        android:color="#A9A9A9"
        android:dashGap="2dp"
        android:dashWidth="2dp"/>
    <size android:height="1dp"/>
</shape>

JNI中jstring转C++的string,以及jboolean与bool的转换

ScopedUtfChars是将matchStr转化成UTF8格式字符串

    jboolean removeMediaInfo(JNIEnv * env, const jstring jmedia) {
        ScopedUtfChars media(env, jmedia); 或者 const char* szStr = (env)->GetStringUTFChars(jmedia, 0 );
        return _removeMediaInfo(media.c_str()) ? JNI_TRUE : JNI_FALSE;
    }

Java类型和本地类型对应  
   在如下情况下,需要在本地方法中应用java对象的引用,就会用到类型之间的转换:
   1)java方法里面将参数传入本地方法;
   2)在本地方法里面创建java对象;
   3)在本地方法里面return结果给java程序。
   分为如下两种情况:
   Java原始类型
   像booleans、integers、floats等从Java程序中传到本地方法中的原始类型可以直接使用,下面是java中的原始类型和本地方法中的类型的对应:
 

Java类型 | 本地类型 | 字节(bit)   :—: | :—: | :—:  boolean | jboolean | 8, unsigned  byte | jbyte | 8  char | jchar | 16, unsigned  short | jshort | 16  int | jint | 32  long | jlong | 64  float | jfloat | 32  double | jdouble | 64 void | void | n/a   也就是说如果我在方法中传进去了一个boolean的参数的话,那么我在本地方法中就有jboolean类型与之对应。同理,如果在本地方法中return一个jint的话,那么在java中就返回一个int类型。
  

  • 如何加载网络视频预览图

    public static Bitmap createVideoThumbnail(String url, int width, int height) {
      Bitmap bitmap = null;
      MediaMetadataRetriever retriever = new MediaMetadataRetriever();
      int kind = MediaStore.Video.Thumbnails.MINI_KIND;
      try {
          if (Build.VERSION.SDK_INT >= 14) {
              retriever.setDataSource(url, new HashMap<String, String>());
          } else {
              retriever.setDataSource(url);
          }
          bitmap = retriever.getFrameAtTime();
      } catch (IllegalArgumentException ex) {
          // Assume this is a corrupt video file
      } catch (RuntimeException ex) {
          // Assume this is a corrupt video file.
      } finally {
          try {
              retriever.release();
          } catch (RuntimeException ex) {
              // Ignore failures while cleaning up.
          }
      }
      if (kind == MediaStore.Images.Thumbnails.MICRO_KIND && bitmap != null) {
          bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,
                                                   ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
      }
      return bitmap;
    }
    
  • System.nanoTime与System.currentTimeMillis的区别

    System.currentTimeMillis的结果,但是在执行一些循环中使用了System.currentTimeMillis,那么每次的结果将会差别很小,甚至一样,因为现代的计算机运行速度很快.

System.nanoTime提供相对精确的计时
System.currentTimeMillis返回的是从1970.1.1 UTC 零点开始到现在的时间,精确到毫秒,平时我们可以根据System.currentTimeMillis来计算当前日期,星期几等,可以方便的与Date进行转换

  • Handler的hasCallback等方法

    通过查看Handler源码, 对于hasMessage hasCallback等方法都是通过内部MessageQueue来判断的. 若不想一个runnable多次被触发, 在调用前remove一下即可.

  • SettingsProvider以键值对的形式存在

    目录为: /data/system/users/0/ 可以在root情况下修改

在adb shell里写SettingsProvider : settings put system alarm_alert void

  • 为什么Android在onResume的时候,视图才可见

    Window是在ActivityThread中的performLaunchActivity方法里面的activity.attach方法中创建的,但是这个时候DecorView并没有被WindowManager识别,因此此时Window也无法提供功能, 在Activity的handleResumeActivity中会调用r.activity.makeVisible(); 此时才可见

    void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }
  • 什么是DirectBoot以及FallbackHome

    DirectBoot Android 7.0 中引入了一项称为直接启动的新功能。该功能处于启用状态时,已加密设备在启动后将直接进入锁定屏幕。之前,在使用全盘加密 (FDE) 的已加密设备上,(这种全盘加密的效果就是, 比如设置了第三方的输入法, 但是在锁屏界面上是没法用的,只有解锁手机才能使用, 因此锁屏输入密码界面都不是第三方的输入法. 短信等也是.. 第三方短信app在开机后不解锁的情况下是无法工作的.) 用户在访问任何数据之前都需要先提供凭据,从而导致手机只能执行最基本的操作. 我们设置应用的android:directBootAware为true即可直接启动. 那么在Launcher不是directBoot的, 因此会去启动一个能够直接的启动的”假Home”-即FallbackHome. 这个FallbackHome是定义在设置里面的, 因为设置是能够直接启动的. 随后通过FallbackHome的逻辑进行是否解锁等判断决定是否启动Home组件.

全盘加密(FDE):Full-Disk Encryption 文件加密(FBE):File-Based Encryption ro.crypto.type: block 加密方式为:FDE 返回值为:ro.crypto.type: file加密方式为:FBE

  • Android 在Service中启动Activity需要加FLAG_ACTIVITY_NEW_TASK

    frameworks/base/core/java/android/app/ContextImpl.java 的startActivity方法里就是有这个判断

    if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
          && (targetSdkVersion < Build.VERSION_CODES.N
                  || targetSdkVersion >= Build.VERSION_CODES.P)
          && (options == null
                  || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
      throw new AndroidRuntimeException(
              "Calling startActivity() from outside of an Activity "
                      + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                      + " Is this really what you want?");
    }
    

    因此在N以下,以及P以上的机型上,都需要这个flag标志位

  • Android静态注册广播, 应用未打开过收不到问题

    静态注册的广播, 应用未打开过的时候,是stop状态, 需要加标志位

我们可以通过Intent的Flag,指定广播是否可以被停止状态的应用所接收。

FLAG_INCLUDE_STOPPED_PACKAGES
Include intent filters of stopped applications in the list of potential targets to resolve against.

FLAG_EXCLUDE_STOPPED_PACKAGES
Exclude intent filters of stopped applications from the list of potential targets.

从名字可以看出来二者的区别,添加Intent Flag:FLAG_INCLUDE_STOPPED_PACKAGES 的广播可以被停止状态的应用接收到。不添加这两个Flag的话默认是FLAG_EXCLUDE_STOPPED_PACKAGES

  • 给Dialog单独设置主题的方法

    AlertDialog.Builder builder = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.AlertDialogCustom));
    
  • 查看单个进程的各线程占用

    adb shell top -Hp pid


本文作者:Anderson/Jerey_Jobs

博客地址 : http://jerey.cn/
简书地址 : Anderson大码渣
github地址 : https://github.com/Jerey-Jobs

作者:Anderson大码渣,欢迎关注我的简书: Anderson大码渣