最新 文章连接,本文不再同步

Android9月1日题: 请解释下 Android 程序运行时权限与文件系统权限的区别?

参考答案:

apk 程序是运行在虚拟机上的,对应的是 Android 独特的权限机制,只有体现到文件系统上时才使用 linux 的权限设置。

(一)linux 文件系统上的权限

-rwxr-x--x system system 4156 2010-04-30 16:13 test.apk

代表的是相应的用户/用户组及其他人对此文件的访问权限,与此文件运行

起来具有的权限完全不相关。比如上面的例子只能说明 system 用户拥有对此文件的读写执行权限;system 组的用户对此文件拥有读、执行权限;其他人对此文件只具有执行权限。而 test.apk 运行起来后可以干哪些事情,跟这个就不相关了。千万不要看 apk 文件系统上属于 system/system 用户及用户组,或者

root/root 用户及用户组,就认为 apk 具有 system 或 root 权限

(二)Android 的权限规则

(1)Android 中的 apk 必须签名

(2)基于 UserID 的进程级别的安全机制

(3)默认 apk 生成的数据对外是不可见的

(4)AndroidManifest.xml 中的显式权限声明


Android9月2日题: 属性动画,例如一个 button 从 A 移动到 B 点,B 点还是可以响应点击事件,这个原理是什么?

参考答案:

补间动画只是显示的位置变动,View 的实际位置未改变,表现为 View 移动到其他地方,点击事件仍在原处才能响应。而属性动画控件移动后事件相应就在控件移动后本身进行处理


Android9月3日题: 内存泄漏的场景和解决办法

参考答案:

1.非静态内部类的静态实例

非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,就会长期的维持着外部类的引用,组织被系统回收,解决办法是使用静态内部类

2.多线程相关的匿名内部类和非静态内部类

匿名内部类同样会持有外部类的引用,如果在线程中执行耗时操作就有可能发生内存泄漏,导致外部类无法被回收,直到耗时任务结束,解决办法是在页面退出时结束线程中的任务

3.Handler 内存泄漏

Handler 导致的内存泄漏也可以被归纳为非静态内部类导致的,Handler 内部

message 是被存储在 MessageQueue 中的,有些 message 不能马上被处理,存在的时间会很长,导致 handler 无法被回收,如果 handler 是非静态的,就会导致它的外部类无法被回收,解决办法是1.使用静态 handler,外部类引用使用弱引用处理 2.在退出页面时移除消息队列中的消息

4.Context 导致内存泄漏

根据场景确定使用 Activity 的 Context 还是 Application 的 Context,因为二者生命周期不同,对于不必须使用 Activity 的 Context 的场景(Dialog),一律采用Application 的 Context,单例模式是最常见的发生此泄漏的场景,比如传入一个

Activity 的 Context 被静态类引用,导致无法回收

5.静态 View 导致泄漏

使用静态View 可以避免每次启动Activity 都去读取并渲染View,但是静态 View 会持有 Activity 的引用,导致无法回收,解决办法是在 Activity 销毁的时候将静态 View 设置为 null(View 一旦被加载到界面中将会持有一个 Context 对象的引用,在这个例子中,这个 context 对象是我们的 Activity,声明一个静态变量引用这个 View,也就引用了 activity)

6.WebView 导致的内存泄漏

WebView 只要使用一次,内存就不会被释放,所以 WebView 都存在内存泄漏的问题,通常的解决办法是为 WebView 单开一个进程,使用 AIDL 进行通信,根据业务需求在合适的时机释放掉

7.资源对象未关闭导致

如 Cursor,File 等,内部往往都使用了缓冲,会造成内存泄漏,一定要确保关闭它并将引用置为 null

8.集合中的对象未清理

集合用于保存对象,如果集合越来越大,不进行合理的清理,尤其是入股集合是静态的

9.Bitmap 导致内存泄漏

bitmap 是比较占内存的,所以一定要在不使用的时候及时进行清理,避免静态变量持有大的 bitmap 对象

10.监听器未关闭

很多需要 register 和 unregister 的系统服务要在合适的时候进行 unregister,手动添加的 listener 也需要及时移除


Android9月4日题: 静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?

参考答案:

可继承 不可重写 而是被隐藏

如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为

“隐藏”。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成。


Android9月7日题: 如何修改Activity 进入和退出动画

参考答案:
https://medium.com/mobile-app-development-publication/animate-android-activities-transition-bf7f89a74b35
一是通过定义 Activity 的主题
动画属性文件enter_activity.xml如下

<set android:shareInterpolator="false"xmlns:android="http://schemas.android.com/apk/res/android"><scaleandroid:interpolator="@android:anim/accelerate_decelerate_interpolator"android:fromXScale="0.0"android:toXScale="1.0"android:fromYScale="0.0"android:toYScale="1.0"android:pivotX="50%"android:pivotY="50%"android:fillAfter="false"android:duration="2000" />
</set>

在 styles.xml 中编辑如下代码:

<resources><style name="AppTheme"  ... ><item name="android:windowAnimationStyle">@style/WindowAnimations</item></style><style name="WindowAnimations"><item name="android:activityOpenEnterAnimation">@anim/enter_activity</item><item name="android:activityOpenExitAnimation">@anim/exit_activity</item><item name="android:activityCloseEnterAnimation">@anim/enter_activity</item><item name="android:activityCloseExitAnimation">@anim/exit_activity</item></style>
</resources>

在 AndroidManifest.xml 中给指定的 Activity 指定 theme。

二是通过覆写 Activity的overridePendingTransition 方法。

class Main2Activity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main2)overridePendingTransition(R.anim.enter_activity, R.anim.exit_activity)}override fun finish() {super.finish()overridePendingTransition(R.anim.enter_activity, R.anim.exit_activity)}
}

Android9月8日题: android 应用对内存是如何限制的?我们应该如何合理使用内存?

参考答案:

Android 应用的开发语言为 Java,每个应用最大可使用的堆内存受到

Android 系统的限制

  • Android 每一个应用的堆内存大小有限

  • 通常的情况为 16M-48M

  • 通过 ActivityManager 的 getMemoryClass()来查询可用堆内存限制

ActivityManager manager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
manager.getMemoryClass();
  • 3.0(HoneyComb)以上的版本可以通过 largeHeap=“true”来申请更多的堆内存

  • NexueHeap 512

  • 如果试图申请的内存大于当前余下的堆内存就会引发OutOfMemoryError()

  • 应用程序由于各方面的限制,需要注意减少内存占用,避免出现内存泄漏。

如何合理使用内存?

  1. 注意资源回收,像数据库,输入输出流,定位操作这样的对象,要在使用完及时关闭流。

  2. 少使用静态变量,因为系统将静态变量的优先级设定的很高,会最后回收。所以可能因为静态变量导致该回收的没有回收。而回收了不该回收的内存。

  3. 注意大图片的缩放,如果载入的图片很大,要先经过自己程序的处理, 降低分辨率等。最好设置多种分辨率格式的图片,以减少内存消耗。

  4. 动态注册监听,把一些只有显示的时候才使用到的监听放进程序内部, 而不是放在 manifesat 中去。

  5. 减少使用动画,或者适当减少动画的帧数。

  6. 注意自己的程序逻辑,在该关闭自己程序的控件的时候,主动关闭,不要交给系统去决定。(这个要自己把握好,也不是说都自己搞定,只有那些自己确定需要关闭的对象,自己将其关闭。)


Android9月9日题: 如何避免OOM?

参考答案:

  1. 使用更加轻量的数据结构:如使用 ArrayMap/SparseArray 替代HashMap,HashMap 更耗内存,因为它需要额外的实例对象来记录 Mapping 操作,SparseArray 更加高效,因为它避免了 Key Value 的自动装箱,和装箱后的解箱操作

  2. 便面枚举的使用,可以用静态常量或者注解@IntDef 替代

  3. Bitmap 优化:
    a. 尺寸压缩:通过 InSampleSize 设置合适的缩放
    b. 颜色质量:设置合适的format,ARGB_6666/RBG_545/ARGB_4444/ALPHA_6, 存在很大差异
    c. inBitmap:使用 inBitmap 属性可以告知 Bitmap 解码器去尝试使用已经存在的内存区域,新解码的Bitmap 会尝试去使用之前那张Bitmap 在Heap 中所占据的pixel data 内存区域,而不是去问内存重新申请一块区域来存放 Bitmap。利用这种特性,即使是上千张的图片,也只会仅仅只需要占用屏幕所能够显示的图片数量的内存大小, 但复用存在一些限制,具体体现在:在Android 4.4 之前只能重用相同大小的 Bitmap 的内存,而 Android 4.4 及以后版本则只要后来的 Bitmap 比之前的小即可。使用 inBitmap 参数前,每创建一个 Bitmap 对象都会分配一块内存供其使用,而使用了 inBitmap 参数后,多个 Bitmap 可以复用一块内存,这样可以提高性能

  4. StringBuilder 替代 String: 在有些时候,代码中会需要使用到大量的字符串拼接的操作,这种时候有必要考虑使用 StringBuilder 来替代频繁的“+”

  5. 避免在类似 onDraw 这样的方法中创建对象,因为它会迅速占用大量内存,引起频繁的 GC 甚至内存抖动

  6. 减少内存泄漏也是一种避免 OOM 的方法


Android9月10日题: 什么导致线程阻塞?线程如何关闭?

参考答案:

阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket 的accept()方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。

一种是调用它里面的 stop()方法

另一种就是你自己设置一个停止线程的标记 (推荐这种)


Android9月11日题: 哪些情况下的对象会被垃圾回收机制处理掉?

参考答案:

  1. 所有实例都没有活动线程访问。
  2. 没有被其他任何实例访问的循环引用实例。
    Java 中有不同的引用类型。判断实例是否符合垃圾收集的条件都依赖于它的引用类型。

要判断怎样的对象是没用的对象。这里有 2 种方法:

  1. 采用标记计数的方法:
    给内存中的对象给打上标记,对象被引用一次,计数就加 1,引用被释放了,计数就减一,当这个计数为 0 的时候,这个对象就可以被回收了。当然,这也就引发了一个问题:循环引用的对象是无法被识别出来并且被回收的。所以就有了第二种方法:
  2. 采用根搜索算法:
    从一个根出发,搜索所有的可达对象,这样剩下的那些对象就是需要被回收的

Android9月14日题: 说下Activity 的启动模式,生命周期,两个 Activity 跳转的生命周期,如果一个 Activity 跳转另一个 Activity 再按下Home 键在回到Activity 的生命周期是什么样的

参考答案:

启动模式

Standard 模式:Activity 可以有多个实例,每次启动 Activity,无论任务栈中是否已经有这个 Activity 的实例,系统都会创建一个新的 Activity 实例

SingleTop 模式:当一个 singleTop 模式的 Activity 已经位于任务栈的栈顶,再去启动它时,不会再创建新的实例,如果不位于栈顶,就会创建新的实例

SingleTask 模式:如果 Activity 已经位于栈顶,系统不会创建新的 Activity 实例, 和 singleTop 模式一样。但 Activity 已经存在但不位于栈顶时,系统就会把该Activity 移到栈顶,并把它上面的 activity 出栈

SingleInstance 模式:singleInstance 模式也是单例的,但和 singleTask 不同,singleTask 只是任务栈内单例,系统里是可以有多个 singleTask Activity 实例的, 而 singleInstance Activity 在整个系统里只有一个实例,启动一singleInstanceActivity 时,系统会创建一个新的任务栈,并且这个任务栈只有他一个Activity

Standard Activity生命周期 onCreate onStart onResume onPause onStop onDestroy

两个Standard Activity 跳转的生命周期

  1. 启动 A onCreate - onStart - onResume

  2. 在 A 中 启 动 B
    ActivityA onPause ActivityB onCreate ActivityB onStart ActivityB onResumeActivityA onStop

  3. 从 B 中返回 A(按物理硬件返回键)
    ActivityB onPause ActivityA onRestart ActivityA onStartActivityA onResume ActivityB onStop ActivityB onDestroy

  4. 继续返回
    ActivityA onPause ActivityA onStop ActivityA onDestroy


Android9月15日题: Framework 工作方式及原理,Activity 是如何生成一个 view 的,机制是什么?

参考答案:

所有的框架都是基于反射 和 配置文件(manifest)的。

普通的情况:

Activity 创建一个 view 是通过 ondraw 画出来的, 画这个 view 之前呢,还会调用 onmeasure 方法来计算显示的大小.

特殊情况:

Surfaceview 是直接操作硬件的, 因为 或者视频播放对帧数有要求,

onDraw 效率太低,不够使,Surfaceview 直接把数据写到显存。

来自热心市民邓先生的参考答案:


Android9月16日题: 一个应用程序有几个Context?为什么Activity、Service、Application都继承自Context,Context的作用是什么呢?

参考答案:

Context个数 = Activity个数 + Service个数 + 进程个数

Context的作用总结为如下几个方面:

四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等

获取系统/应用资源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等

文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等

数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等

其它辅助功能,比如设置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生

Activity就像是一个”皇帝“,Activity可以做很多很多的事情,但是Context就像是他手中的权利,如果没有Context,Activity其实只是一个”普通人“(普通java类)而已.


Android9月17日题: Java 中堆和栈有什么不同?

参考答案:

为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己的栈,如果多个线程使用该变量就可能引发问题,这时 volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值


Android9月18日题: Lint是什么?它对我们开发Android应用有什么帮助

参****考答案:

1,功能强大,Lint支持Java和Kt源文件、class文件、资源文件、Gradle等文件的检查。

2,扩展性强,支持开发自定义Lint规则。

3,配套工具完善,Android Studio、Android Gradle插件原生支持Lint工具。

4,Lint专为Android设计,原生提供了几百个实用的Android相关检查规则。

5,有Google官方的支持,会和Android开发工具一起升级完善。

详情链接地址如下

https://juejin.im/post/6861562664582119432


Android9月21日题: 数据库的四大特征,数据库的隔离级别?

参考答案:

事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列, 这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。例如,银 行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要 么都不执行。所以,应该把它们看成一个事务。事务是数据库维护数据一致性的 单位,在每个事务结束时,都能保持数据一致性。

事务具有以下 4 个基本特征:数据库的四大特征:

(1)原子性(Atomicity)

原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。

(2)一致性(Consistency)

一个事务执行之前和执行之后都必须处于一致性状态。

(3)隔离性(Isolation)

隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个 用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

(4)持久性(Durability)

持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性 的。

数据库的隔离级别:

1)Serializable(串行化):可避免脏读、不可重复读、幻读的发生。

2)Repeatable read (可重复读):可避免脏读、不可重复读的发生。

3)Read committed (读已提交):可避免脏读的发生。

4)Read uncommitted (读未提交):最低级别,任何情况都无法保证。


Android9月22日题: 通过静态内部类实现单例模式有哪些优点?

参考答案:

  1. 不用 synchronized ,节省时间。

  2. 调用 getInstance() 的时候才会创建对象,不调用不创建,节省空间,这有点像传说中的懒汉式。


Android9月23日题: Hashmap 底层为什么是线程不安全的?

参考答案:
并发场景下使用时容易出现死循环,在 HashMap 扩容的时候会调用 resize() 方法,就是这里的并发操作容易在一个桶上形成环形链表;这样 当获取一个不存在的 key 时,计算出的 index 正好是环形链表的下标就 会出现死循环;· 在 1.7 中 hash 冲突采用的头插法形成的链表,在并发条件下会形成循环链表,一旦有查询落到了这个链表上,当获取不到值时就会死循环。


Android9月24日题: 为什么属性动画移动后仍可点击?

参考答案:

播放补间动画的时候,我们所看到的变化,都只是临时的。而属性动画呢,它所 改变的东西,却会更新到这个 View 所对应的矩阵中,所以当 ViewGroup 分派事 件的时候,会正确的将当前触摸坐标,转换成矩阵变化后的坐标,这就是为什么 播放补间动画不会改变触摸区域的原因了。


Android9月25日题: Android 怎么加速启动 Activity?

参考答案:

  1. onCreate() 中不执行耗时操作 把页面显示的 View 细分一下,放在 AsyncTask 里逐步显示,用 Handler 更好。这样用户的看到的就是有层 次有步骤的一个个的 View 的展示,不会是先看到一个黑屏,然后一下显 示所有 View。最好做成动画,效果更自然。
  2. 利用多线程的目的就是尽可能的减少 onCreate() 和 onReume() 的时 间,使得用户能尽快看到页面,操作页面。
  3. 减少主线程阻塞时间。
  4. 提高 Adapter 和 AdapterView 的效率。
  5. 优化布局文件。

Android9月27日题: 多进程场景遇见过么?

参考答案:

  1. 在新的进程中,启动前台 Service,播放音乐。
  2. 一个成熟的应用一定是多 模块化的。首先多进程开发能为应用解决了 OOM 问题,因为 Android 对内存的 限制是针对于进程的,所以,当我们需要加载大图之类的操作,可以在新的进程 中去执行,避免主进程 OOM。而且假如图片浏览进程打开了一个过大的图片, java heap 申请内存失败,该进程崩溃并不影响我主进程的使用。

Android9月28日题: Message 实例的最佳方式?

参考答案:

为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候 尽量复用 Message ,减少内存消耗:
通过 Message 的静态方法 Message.obtain();
通过 Handler 的公有方法 handler.obtainMessage()。


Android9月29日题: 服务启动一般有几种,服务和 activty 之间怎么通信,服务和服务之间怎么通信

参考答案:

1、startService:onCreate()—>onStartCommand() —> onDestory() 如果服务已经开启,不会重复的执行 onCreate(), 而是会调用 onStartCommand()。一旦服务开启跟调用者(开启者)就没有任何关系了。开启 者退出了,开启者挂了,服务还在后台长期的运行。开启者不能调用服务里面 的方法。

2、bindService:onCreate() —>onBind()—>onunbind()—>onDestory() bind 的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。绑定者可 以调用服务里面的方法。

3、通信:

  • 通过 Binder 对象。
  • 通过 broadcast(广播)。

Android9月30日题: Handler、Thread 和 HandlerThread 的差别

参考答案:

  1. Handler:在 android 中负责发送和处理消息,通过它可以实现其他支线线程与主线程之间的消息通讯。
  2. Thread:Java 进程中执行运算的最小单位,亦即执行处理机调度的基本单位。某一进程中一路单独运行的程序。
  3. HandlerThread:一个继承自 Thread 的类 HandlerThread,Android 中没有 对 Java 中的 Thread 进行任何封装,而是提供了一个继承自 Thread 的类 HandlerThread 类,这个类对 Java 的 Thread 做了很多便利的封装。HandlerThread 继承于 Thread,所以它本质就是个 Thread。与普通 Thread 的差 别就在于,它在内部直接实现了 Looper 的实现,这是 Handler 消息机制必不可 少的。有了自己的 looper,可以让我们在自己的线程中分发和处理消息。如果 不用 HandlerThread 的话,需要手动去调用 Looper.prepare()和 Looper.loop() 这些方法。

Android10月09日题: 简单说下接入支付的流程,是否自己接入过支付功能?

参考答案:

Alipay 支付功能:

  1. 首先登录支付宝开放平台创建应用,并给应用添加 App 支付功能, 由于 App 支付功能需要签约,因此需要上传公司信息和证件等资料进行签约。
  2. 签约成功后,需要配置秘钥。使用支付宝提供的工具生成 RSA 公钥和私钥,公钥需要设置到管理后台。
  3. android studio 集成
  • copy jar包;
  • 发起支付请求,处理支付请求。

Android10月12日题: 简单说一下Service种类划分

参考答案:

按运行地点分类:

类别 区别 优点 缺点 应用
本地服务(Local Service) 该服务依附在主进程中 服务依附于主进程而不是独立的进程,这样在一定程度上节约了资源,因为Local服务是在同一个进程,所以不需要IPC和AIDL。相应bindService会方便很多。 主进程被kill后,服务便会终止 如:音乐播放等不需要常驻的服务
远程服务(Remote Service) 服务为独立的进程,对应进程名格式为所在包名加上你指定的 android:process 字符串。由于是独立的进程,因此在Activity所在进程被kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。 该服务是独立的进程,会占用一定的资源,并且使用AIDL进行IPC稍麻烦 一些提供系统服务的Service,这种Service是常住的

按运行类型分类:

类别 区别 应用
前台服务 会在通知栏显示onGoing的Notification 当服务被终止的时候,通知栏的Notification也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务
后台服务 默认的服务即为后台服务,即不会在通知一栏显示onGoing的Notification 当服务被终止的时候,用户是看不到效果的。某些不需要运行或者终止提供的服务,如天气更新、日期同步等等

按使用方式分类:

类别 区别
startService启动的服务 主要用于启动一个服务执行后台任务,不进行通信,停止服务使用stopService
bindService启动的服务 启动的服务要进行通信,停止服务使用unbindService
同时使用startService、bindService启动的服务 停止服务应同时使用stopService与unbindService

Android10月13日题: 你知道Android消息机制模型吗?

参考答案:

消息机制主要包含:MessageQueue、Handler、Looper和Message这四大部分。

  • Message:需要传递的消息,可以传递数据
  • MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能是向消息池传递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)
  • Handler:消息辅助类,主要功能是向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage)
  • Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

Android10月14日题: 如何生动形象的理解Context?

参考答案:

一个Android程序可以理解为一部电影,Activity、Service、BroadcastReceiver和ContentProvider这四大组件就好比戏了的四个主角,它们是剧组(系统)一开始定好的,主角并不是大街上随便拉个人(new 一个对象)都能演的。有了演员当然也得有摄像机拍摄啊,它们必须通过镜头(Context)才能将戏传给观众,这也就正对应说四大组件必须工作在Context环境下。那么Button、TextView等等控件就相当于群演,显然没那么重用,随便一个路人甲都能演(可以new一个对象),但是它们也必须在面对镜头(工作在Context环境下),所以Button mButtom = new Button(context) 是可以的。


Android10月15日题: Service生命周期

参考答案:

startService() --> onCreate() --> onStartCommand() --> Service running --> onDestory()
bindService() --> onCreate() --> onBind() --> Service running --> onUnbind() --> onDestory()

onCreate():
系统在Service第一次创建时执行此方法,来执行只运行一次的初始化工作,如果service已经运行,这个方法不会调用。
onStartCommand():
每次客户端调用startService()方法启动该Service都会回调该方法(多次调用),一旦这个方法执行,service就启动并且在后台长期运行,通过调用stopSelf()或stopService()来停止服务。
onBind():
当组件调用bindService()想要绑定到service时,系统调用此方法(一次调用),一旦绑定后,下次在调用bindService()不会回调该方法。在你的实现中,你必须提供一个返回一个IBinder来使客户端能够使用它与service通讯,你必须总是实现这个方法,但是如果你不允许绑定,那么你应返回null
onUnbind():
当前组件调用unbindService(),想要解除与service的绑定时系统调用此方法(一次调用,一旦解除绑定后,下次再调用unbindService()会抛异常)
onDestory():
系统在service不在被使用并且要销毁的时候调用此方法(一次调用)。service应在此方法中释放资源,比如线程,已注册的监听器、接收器等等。


Android10月16日题: Window、Activity、DecorView以及ViewRoot之间的关系

参考答案:

Activity
Activity并不负者视图控制,它只是控制生命周期和处理事件。真正控制视图的是Window。一个Activity包含了一个Window,Window才是真正代表一个窗口。Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与Window以及View进行交互。
Window
Window是视图的承载器,内部持有一个DecorView,而这个DecorView才是view的跟布局。Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局。Window通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。
DecorView
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。
DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,**在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面是内容栏。**在Activity中通过setContentView所设置的布局文件其实就是被加到内容栏之中的,成为其唯一子View。
ViewRoot
ViewRoot可能比较陌生,但是其作用非常重大。所有View的绘制以及事件分发等交互都是通过它来执行或传递的。
ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量、布局、绘制)均通过ViewRoot来完成。
ViewRoot并不属于View树的一份子。从源码实现上来看,它既是非View的子类,也是非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。ViewRoot继承了Handler类,可以接收事件并分发,Android的所有触屏事件,按键事件、界面刷新等事件都是通过ViewRoot来进行分发的。

总结
Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图。DecorView就是个顶级视图,是所有View的最外层布局。ViewRoot像个连接器,负者沟通,通过硬件感知来通知视图,进行用户之间的交互。


Android10月19日题: 什么是Dalvik虚拟机?

参考答案:

Dalvik是Google公司自己设计用于Android平台的Java虚拟机,它是Android平台的重要组成部分,支持dex格式的Java应用程序的运行。dex格式是专门为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。Google对其进行了特定的优化,Dalvik具有高效、简洁、节省资源的特点。从Android系统架构图知,Dalvik虚拟机运行在Android的运行时库层。

Dalvik作为面向Linux、为嵌入式操作系统设计的虚拟机,主要负责完成对象生命周期、堆栈管理、线程管理、安全和异常管理,以及垃圾回收等。另外,Dalvik早期并没有JIT编译器,直到Android2.2才加入了对JIT的技术支持。


Android10月21日题: ART虚拟机与Dalvik虚拟机的区别

参考答案:

什么是ART?
ART代表Android Runtime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。ART则完全改变了这套做法,在应用安装时就预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)编译。在移除解释代码这一过程后,应用程序执行将更加效率。启动更快。
ART优点:

  1. 系统性能的显著提升。
  2. 应用启动更快、运行更快、体验更流畅、触摸反馈更及时。
  3. 更长的电池续航能力
  4. 支持更低的硬件。
    ART缺点:
  5. 更大的存储空间占用,可能会增加10%-20%
  6. 更长的应用安装时间

Android10月22日题: Android事件分发机制

参考答案:

简述:
Activity --> ViewGroup --> View 责任链模式

  • dispatchTouchEvent 和 onTouchEvent 一旦 return true,事件就停止传递了(到达终点,没有谁再能收到这个事件),对于 return true 我们经常说事件被消费了,消费了的意思就是事件走到这里就是终点,不会往下传,没有谁能在收到这个事件了。

  • dispatchTouchEvent 和 onTouchEvent return false 的时候事件都会回传给父控件的 onTouchEvent处理。对于dispatchTouchEvent返回false的含义应该是:事件停止往子View传递和分发同时开始往父控件回溯(父控件的onTouchEvent开始从下往上回传直到某个onTouchEvent return true),事件分发机制就像递归,return false 的意义就是递归停止然后开始回溯。

  • 对于onTouchEvent return false就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。

  • oninterceptTouchEvent,用于事件拦截,只存在于ViewGroup中,如果返回true就会交给自己的onTouchEvent处理,如果不拦截就是往子控件往下传递。默认是不会去拦截的,因为子View也需要这个事件,所以onInterceptTouchEvent拦截器return super和false是一样的,事件往子View的dispatchTouchEvent传递。

  • 对于ViewGroup,dispatchTouchEvent,之前说的return true就是终结传递,return false就是回溯到父View的onTouchEvent。那么ViewGroup怎样通过dispatchTouchEvent方法能把事件分发到自己的onTouchEvent处理呢?return false 和 true 都不行,那么只能通过 onInterceptTouchEvent把事件拦截下来给自己的onTouchEvent,所以ViewGroup的dispatchTouchEvent方法的super默认实现就是去调用onInterceptTouchEvent,记住这一点。
    总结:

  • 对于dispatchTouchEvent,onTouchEvent,return true 是终结事件传递,return false是回溯父View的onTouchEvent方法

  • ViewGroup想把事件分发给自己的onTouchEvent处理,需要拦截器onInterceptTouchEvent方法return true把事件拦截下来

  • ViewGroup的拦截器onInterceptTouchEvent默认是不拦截的,所以return super和return false是一样的

  • View没有拦截器,为了让View可以把事件分发给自己的onTouchEvent处理,View的dispatchTouchEvent默认实现(super)就是把事件分发给自己的onTouchEvent


Android10月23日题: RelativeLayout和LinearLayout在实现效果同等的情况下使用哪个?为什么?

参考答案:

选择LinearLayout,因为RelativeLayout在measure过程需要两次


//RelativeLayout源码
View[] views = mSortedHorizontalChildren;
int count = views.length;
for (int i = 0; i < count; i++) {/**/measureChildHorizontal(child, params, myWidth, myHeight);
}
/**/
for (int i = 0; i < count; i++){/***/measureChild(child, params, myWidth, myHeight);
}
//LinearLayout
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (mOrientation == VERTICAL) {measureVertical(widthMeasureSpec, heightMeasureSpec);} else {measureHorizontal(widthMeasureSpec, heightMeasureSpec);}
}

从源码我们发现RelativeLayout会对子View做两次measure。这是为什么呢?首先RelativeLayout中子View的排列方式是基于彼此的依赖关系,而这个依赖关系可能和布局中View的顺序并不相同,在确定每个子View的位置的时候,就需要先给所有的子View排序一下。又因为RelativeLayout允许A B两个子View,横向上B依赖于A,纵向上A依赖于B,所以需要横向纵向分别进行一次排序测量。
RelativeLayout另外一个性能问题:
View的measure方法里对绘制过程做了一个优化,如果我们的子View没有要求强制刷新,而父View给子View的传入值也没有变化,也就说子View的位置没有变化,就不会做无谓的measure。但是上面已经说了RelativeLayout要做两次measure,而在做横向测量时,纵向的测量结果尚未完成,只好暂时使用myHeight传入子View系统,假如子View的Height不等于(设置了margin)myHeight的高度,那么measure中优化则不起作用,这一过程将进一步影响RelativeLayout的绘制性能。而LinearLayout则无这方面的担忧,解决这个问题也很好办,如果可以,尽量使用padding代替margin。
结论

  • RelativeLayout会让子View调用两次onMeasure,LinearLayout再有weight时,也会调用子View两次onMeasure
  • RelativeLayout的子View如果高度和RelativeLayout不同,则会引发效率问题。当子View很复杂时,这个问题会更加严重。如果可以,尽量使用padding代替margin
  • 在不影响层级深度的情况下,使用LinaerLayout和FrameLayout而不是RelativeLayout

Android10月26日题: 简单的说一下布局相关的 、 控件作用及实现原理

参考答案:

  • ViewStub本身是一个视图,会被添加到界面上,之所以看不到是因为其设置了隐藏与不绘制
  • 当调用infalte或者ViewStub.setVisibilty(View.VISIBLE)时(两个都使用infalte逻辑),先从父视图上把当前ViewStub删除,再把加载的android:layout视图添加上
  • 把ViewStub LayoutParams 添加到加载的android:layout视图上,而其根节点的LayoutParams设置无效
  • ViewStub是指用来占位的视图,通过删除自己并添加android:layout视图达到懒加载效果

Android10月27日题: 说一下Assets目录与res目录的区别

参考答案:

assets目录与res下的raw、drawable目录一样,也可用来存放资源文件,但它们三者区别如下:

assets res/raw res/drawable
获取资源方式 文件路径+文件名 R.raw.xxx R.drawable.xxx
是否被压缩 false false true(失真压缩)
能否获取子目录下的资源 true false false

res/raw和assets的区别:

res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即可,assets文件夹下的文件不会被映射到R文件中,访问的时候需要AssetManager类。

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

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

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

读取assets下的文件资源,通过以下方式获取输入流进行写操作,使用AssetManager。

注意:

  1. Android 2.2 以下,AssertManager中不能处理单个超过1M的文件,而raw没有这个限制
  2. assets文件夹是存放不进行编译加工的原生文件,即该文件夹里面的文件不会像xml、java文件被预编译,可以存放一些图片、html、js等等

Android10月28日题: 知道View视图绘制过程原理吗?

参考答案:

View视图绘制需要搞清楚两个问题,一个是从哪里开始绘制,一个是怎么绘制?
从哪里开始绘制?我们平常使用Activity的时候,都会调用setContentView来设置布局文件,没错,视图绘制就是从这个方法开始。
怎么绘制?
在我们的Activity中调用了setContentView之后,会转而执行PhoneWindow的setContentView,在这个方法里面会判断我们存放内容的ViewGroup(这个ViewGroup可以是DecorView也可以是DecorView的子View)是否存在。不存在的话,则会创建一个DecorView处理,并且会创建出相应的窗体风格,存在的话则会删除原先的ViewGroup上面已有的View,接着会调用LayoutInflater的inflate方法以pull解析的方式将当前布局文件中存在的View通过addView的方式添加到ViewGroup上面来,接着在addView方法里面就会执行我们常见的invalidate方法了,这个方法不只是在View视图绘制的过程中经常用到,其实动画的实现原理也是不断的调用这个方法来实现视图不断重绘的,执行这个方法的时候会调用父View的invalidateChild方法,这个方法是属于ViewParent的,ViewGroup以及ViewRootImpl中都会他进行了实现,invalidateChild里面主要做的是就是通过do while循环一层一层计算出当前View的四个点所对应的矩阵在ViewRoot中所对应的位置,那么有了这个矩阵的位置之后最终都会执行ViewRootImpl的invalidateChildInParent方法,执行这个方法的时候首先会检查当前线程是不是主线程,因为我们要开始准备更新UI了,不是主线程的话是不允许更新UI的,接着就会执行scheduleTraversals方法了,这个方法会通过handler来执行doTraversal方法,在这个方法里面就见到了我们平常所熟悉的View视图绘制的起点方法performTraversals了。
那么接下来就是真正的视图绘制流程了,大体上讲View的绘制流程经历了Measure测量、Layout布局以及Draw绘制的三个过程,具体来讲是从ViewRootImpl的performTraversals方法开始,首先执行的将是performMeasure方法,这个方法里面会传入两个MeasureSpec类型的参数,它在很大程度上决定了View的尺寸规格,对于DecorView来说宽高的MeasureSpec值的获取与窗口尺寸以及自身的LayoutParams有关,对于普通View来说其宽高的MeasureSpec值获取由父容器以及自身的LayoutParams属性共同决定,在performMeasure里面会执行measure方法,在measure方法里面会执行onMeasure方法,到这里Measure测量过程对View与ViewGroup来说是没有区别的,但是从onMeasure开始两者有差别了,因为View本身已经不存在子View了,所以他onMeasure方法将执行setMeasuredDimension方法,该方法会设置View的测量值,但是对于ViewGroup来说,因为它里面还存在着子View,那么我们就需要继续测量它里面的子View了,调用的方法是measureChild方法,该方法内部又会执行measure方法,而measure方法转而又会执行onMeasure方法,这样不断的递归进行下去,直到整个View树测量结束,这样performMeasure方法执行结束了。
接着便是执行performLayout方法了,performMeasure只是测量出了View树中View的大小了,但是还不知道View的位置,所以也就出现了performLayout方法了,performLayout方法首先会执行layout方法,以确定View自身的位置,如果当前View是ViewGroup的话,则会执行onLayout方法。在onLayout方法里面又会递归的执行layout方法,直到当前遍历到的View不再是ViewGroup为止,这样整个layout布局过程就结束了。
在View树中View的大小以及位置都确定之后,接下来就是真正的绘制View显示在界面的过程了,该过程首先从performDraw方法开始,performDraw首先会执行draw方法,在draw方法中首先绘制背景,接着调用onDraw方法绘制自己,如果当前View是ViewGroup的话,还要调用dispatchDraw方法绘制当前ViewGroup的子View,而dispatchDraw方法里面实际上是通过drawChild方法间接调用draw方法形成递归绘制整个View树,直到当前View不再是ViewGroup为止,这样整个View的绘制过程就结束了。
总结:

  • ViewRootImpl会调用performTraversals(),其内部会调用performMeasure()、performLayout、performDraw
  • performMeasure会调用最外层的ViewGroup的measure() --> onMeasure() ,ViewGroup的onMeasure()是抽象方法,但其提供了measureChildren(),这之中会遍历子View然后循环调用measureChild(),传入MeasureSpec参数,然后调用子View的measure()到View的onMeasure() -->setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize()默然返回measureSpec的测量数值,所以继承View进行自定义的wrap_content需要重写。
  • performLayout()会调用最外层的ViewGroup的layout(l,t,r,b),本View在其中使用setFrame()设置本View的四个顶点位置。在onLayout(抽象方法)中确定子View的位置,如LinearLayout会遍历子View,循环调用setChildFrame() --> 子View.layout()
  • performDraw()会调用最外层的ViewGroup的draw()方法,其中会先后调用background.draw()绘制背景,onDraw(绘制自己),dispatchDraw(绘制子View)、onDrawScrollBars(绘制装饰)
  • MeasureSpec由两位SpecMode(UNSPECIFIED、EXACTLY(对于精确值和match_parent)、AL_MOST(对应warp_content))和三十位SpecSize组成一个int,DecorView的MeasureSpec由窗口大小和其LayoutParams决定,其他View有父View的MeasureSpec和本View的LayoutParams决定。ViewGroup中有getChildMeasureSpec()来获取子View的MeasureSpec。

Android10月29日题: Android如何利用scheme协议进行跳转

参考答案:

scheme是一种页面跳转协议。
通过定义自己的scheme协议,可以非常方便的跳转App中的各个页面;
通过scheme协议,服务器可以定制化告诉App跳转到App内部页面。
Scheme协议在Android中的使用场景

  • H5跳转到native页面
  • 客户端获取push消息中后,点击消息跳转到App内部页面
  • App根据URL跳转到另外一个App指定页面

示例
注册 SchemeActivity:

<activity android:name=".SchemeActivity"><intent-filter>//配置协议<data android:scheme="scheme" android:host="mtime" android:path="/goodsDetail"/><category android:name="android.intent.category.DEFAULT"/><action android:name="android.intent.action.VIEW"/><category android:name="android.intent.category.BROWSABLE"/></intent-filter>
</activity>

在MainActivity里通过以下代码跳转到SchemeActivty:

String url = "scheme://mtime/goodsDetail?goodsId=2333";
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);

然后我们可以在SchemeActivity获取到请求的参数等等信息:

Uri uri = getIntent().getData();
assert uri != null;
String host = uri.getHost();
String path = uri.getPath();
String query = uri.getQuery();
String param = uri.getQueryParameter("goodsId");
Toast.makeText(this, host + "  " + path + "  " + query + "  " + param, Toast.LENGTH_SHORT).show()

Android10月30日题: 谈谈你对Application类的理解

参考答案:

  • Application 在一个 Dalvik 虚拟机里面只会存在一个实例。为什么强调说是一个Dalvik虚拟机,而不是一个APP呢?那是因为,一个App有可能有多个Dalvik虚拟机,也就是传说中的多进程模式。在这种模式下,每一个Dalvik都会存在一个Application实例,它们之间没有关系,在A进程Application里面保存的数据不能在B进程的Application获取,因为它们根本不是一个对象。而且被隔离在两个进程里面,所以这里强调的是一个Dalvik虚拟机,而不是一个App。
  • Application有两个子类,一个是MultiDexApplication,如果你遇到了65535问题,可以选择继承至它,完成相关的工作。另外一个是在TDD(测试用例驱动)开发模式中使用Moke进行测试的时候用到的,可以来替代Application的Moke类MokeApplication。
  • 在应用启动的时候,会首先调用Application.attach(),当然,这个方法是隐藏的,开发者能接触到的第一个被调用的方法其实是Application.onCreate(),我们通常会在这个方法里面完成各种初始化,比如图片加载库、网络请求库等初始化操作。但是最好别在这个方法里面进行太多的耗时操作,因为这会影响App的启动速度,所以对于不必要的操作可以使用异步操作、懒加载、延时加载等策略来减少对UI线程的影响。
  • 由于在Context中可以通过getApplicationContext()获取到Application对象,或者是通过Activity.getApplication()、Service.getApplication()获取到Application,所以可以在Application保存全局的数据,供所有的Activity或者Service使用。
  • 使用上面的三种方法获取到的都是同一个Application对象,getApplicationContext()是在Context的实现类ContextImpl中具体实现的,而getApplication()则是在Activity和Service中单独实现的,所以他们的作用域不同,但是获取到的都是同一个Application对象,因为一个Dalvik虚拟机只有一个Application对象。
  • 在低内存情况下,Application有可能被销毁,从而导致保存在Application里面的数据被销毁,最后程序错乱,甚至Crash。所以当你想在Application保存数据的时候,请做好判null,或者选择其他方式保存你的数据,比如存储在硬盘上等等。
  • 在Application中存在几个有用的方法,比如onLowMemory()和onTrimMemory()(Activity里面也存在这两个方法),在这两个方法里面,我们可以实现自己的内存回收逻辑,比如关闭数据库链接、移除图片内存缓存等操作来降低内存消耗,从而降低被系统回收的风险。
  • 要注意Application的生命周期,他和Dalvik虚拟机生命周期一样长,所以在进行单例或者静态变量的初始化操作时,一定要用Application作为Context进行初始化,否则会造成内存泄漏的发生。使用Dialog的时候一般使用Activity的Context。但是也可以使用Application作为上下文,前提是你必须设置Window类型为TYPE_SYSTME_DAILOG,并且申请相关权限。这个时候弹出的Dialog是属于整个Application的,弹出这个Dialog的Activty销毁时也不会回收Dialog,只有在Application销毁时,这个Dialog才会自动消失。

转载自 安卓2020年9月每日一题
转载自 安卓2020年10月每日一题

安卓 每日一题 2020年9-10月问题及答案相关推荐

  1. 安卓 每日一题 2020年3月问题及答案

    Android 3月2日题: onMeasure是干什么的? 答案: onMeasure方法主要是用于度量ViewGroup的子view的大小同时确定和保存自己ViewGroup的大小,将xml中的布 ...

  2. 安卓 每日一题 2020年5-6月问题及答案

    最新 文章连接,本文不再同步 Android5月7日题: 在使用 HashMap 的时候,用 String 做 key 有什么好处? 参考答案: HashMap 内部实现是通过 key 的 hashc ...

  3. 安卓 每日一题 2020年4月问题及答案

    最新 文章连接,本文不再同步 Android4月1日题: 什么是代理模式 答:为其他对象提供一种代理以控制对这个对象的访问,它主要解决在直接访问对象时带来的问题, 比如说:要访问的对象在远程的机器上. ...

  4. SQL每日一题 牛客17 10月的新客户单价和获客成本

    试题链接10月的新户客单价和获客成本_牛客题霸_牛客网 分析排行榜第一名的解法 用窗口函数与join连接两个表格来做(要把大象装冰箱,拢共分五步) 第一步:用rank()over()窗口函数按照时间排 ...

  5. 安卓 每日一题 2019年9-12月问题及答案

    最新 文章连接,本文不再同步 安卓 每日一题 2019年9-12月问题及答案 文章目录 安卓 每日一题 2019年9-12月问题及答案 安卓2019年09月每日一题 安卓2019年10月每日一题 安卓 ...

  6. 实验室每日一题 2020.11.30

    实验室每日一题 2020.11.30 先打开没有加密的文本文档,里面有一串密文,根据结尾的+推测应该是XXencode,直接找个在线网站解密,又得到一串密文:fwilvyhublqwhuhvwlqj, ...

  7. 02384计算机原理2019真题,全国2002年10月自考02384计算机原理试卷

    全国2002年10月高等教育自学考试 计算机原理试题 课程代码:02384 第一部分 选择题 一.单项选择题(本大题共25小题,每小题1分,共25分)在每小题列出的四个选项中只有一个选项是符合题目要求 ...

  8. 03137计算机网络原理真题,全国2003年10月自考计算机网络基本原理试题 (清晰word版) - 自考历年真题 - 91考试网...

    全国2003年10月高等教育自学考试计算机网络基本原理试题 课程代码:03137 一.单项选择题(在每小题的四个备选答案中,选出一个正确答案,并将正确答案的序号填在题干的括号内.每小题1分,共20分) ...

  9. 【每日微信新闻早报简报】10月11日 星期五

    [今日新闻早报]   10月11日    星期五    农历九月十三 1.11日零时起,全国铁路实施新的列车运行图,增开旅客列车160列: 2.2019胡润百富榜:马云家族第三次成为中国首富,马化腾以 ...

最新文章

  1. VM 下装ubuntu系统
  2. 家用计算机先驱逝世:Linux之父曾受他启发,马斯克悼念
  3. Edgware.RC1中ZuulFallbackProvider的改进
  4. 简述python程序的基本构成_(一)Python入门-2编程基本概念:01程序的构成
  5. vsco使用教程_VSCO如何使用 vsco新手教程
  6. Java界面排号系统_【前端系统】javaweb技术的医院门诊在线预约及排号管理系统的实现...
  7. PHP中三元运算符的用法_php 三元运算符实例详细介绍
  8. 阿里云物联网平台物模型SDK试用
  9. 解决office2010每次打开出现配置进度的方法
  10. 纯真数据库 解析 php,PHP解析纯真IP数据库(qqwry.dat)
  11. 【原创】nbsp;Window7nbsp;vs201…
  12. 萤石云开放平台java_萤石云控制代码
  13. 列宽一字符等于多少厘米_Excel中行高多少等于1厘米?列宽多少等...
  14. 苹果手机连接Wifi认证机制
  15. android 轮播图 github,Android轮播图控件的实现详解(附GitHub开源链接)
  16. [会员积分运营了解]各大主流电商平台会员及积分体系概况集合!
  17. linux硬盘自动停转,求助硬盘异常系统自动挂起导致宕机
  18. Microsoft SQL Server Download
  19. ICAP: 互换客户端地址协议 1
  20. 面向Android的开发基于Tensorflow Lite框架深度学习的应用(一)

热门文章

  1. 轮播组件/瀑布流/组合搜索/KindEditor插件
  2. SDDCN架构下的DC内部负载均衡策略-ECMP
  3. 嵌入式ARM交叉编译器安装
  4. 亲测全套支付可用(微信支付宝)复制粘贴即可跑
  5. 操作系统—进程调度—HRRN,RR 高响应比和时间片轮转算法
  6. [K/3Cloud]K3Cloud的移动审批方面
  7. 批量写入tidb提高写入效率
  8. 百度网盘突然大调整,网友炸了..
  9. FineUI 关闭当前Tab
  10. python中文字体下载_解决Linux系统下python matplotlib中文字体显示问题