Category Android

Realm 在 Android 上的应用

什么是Realm? 在Android平台上,有很多基于SQLite的ORM框架,例如GreenDAO, OrmLite, SugarORM, LitePal等等,对于写习惯了SQL语句的小伙伴们来说,看到SQLite这样的数据库肯定是倍感亲切了,有了这些框架更是如虎添翼。 但是,在我们日常的开发中,数据量并不会特别的大,表的结构也不会特别复杂,用SQL语句有种大(过)材(于)小(繁)用(琐)的感觉,我们需要做的事情可能仅仅是把用户生成的数据对象快速的缓存起来。 这个时候NoSQL就派上了用场,以Mongodb,Redis为代表的NoSQL都引入了一些相对现代化的方式存储数据,比如支持Json,Document的概念,流式api,数据变更通知等等,极大程度的降低了我们学习的成本提高了我们的开发效率。而Realm作为一款移动端的NoSQL框架,官方定位就是替代SQLite等关系型数据库。 Realm是一个由Y Combinator孵化的创业团队开源出来的MVCC(多版本并发控制)的数据库,支持运行在手机,平板和可穿戴设备等嵌入式设备上。 Realm的优点 简单易用 速度快 跨平台 高级功能 可视化 开源 简单易用 上面我们已经说过,Realm并不是基于SQLite上的ORM,它有自己的数据库引擎,使用也非常简单。我们先来看看一段简单的代码。 速度快 我们直接看Realm官方的对比测试数据吧。 每秒能在200K数据记录中进行查询后count的次数: Realm能够达到30.9次,而SQLite只能达到13.6次,Core Data只能达到可怜的一次。 在200K条数据记录进行一次遍历查询,数据和前面的count相似: Realm能够达到每秒遍历200K数据记录31次,SQLite只能达到14次,而Core Data只有可怜的2次。 在一次事物中每秒插入数据量的对比,SQLite可以达到178K条记录,性能最好,Realm可以达到94K,而Core Data再次垫底,只有18K。 我自己也进行一次简单的测试,以JSON格式插入641条记录(好吧,我知道数据量比较小,仅仅只是一个参考,具体的数据可以参考这里)。 207毫秒(Android 7.1.1, Realm 3.0)。 跨平台 Realm目前支持Objective-C(iOS), Swift(iOS), Java(Android), JavaScript, Xamarin等平台。 现在很多应用都需要兼顾iOS和Android两个平台,使用Realm可以使用Realm提供的API,在数据持久化层实现两个平台的无差异化移植,无需对内部数据的架构进行改动。…

如何用一周时间开发一款 Android APP 并在 Google Play 上线

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 目标:实现纸飞机App – 采用MVP架构,集合了知乎日报、果壳精选和豆瓣一刻的综合性阅读客户端。效果图如下所示: 本次教程分为7天,内容分别为: 第一天,准备 功能需求 可行性分析 其他准备 第二天,UI 选择合适的UI 第三天,整体架构 第四天,首页列表 界面编写 实体类 显示数据 缓存内容 第五天,详情页与其他 界面编写 实体类 显示数据 设置与关于 第六天,高级功能 夜间模式 版本适配 第七天,发布与开源 在Google Play上线 在GitHub开源 思考 好了,废话不多说了。现在就开始吧。 DAY 1 俗话说,万事开头难,准备工作做好了,可以起到事半功倍的作用。磨刀不误砍柴工嘛。 Day 1,功能需求 在开始正式编码之前,咱们还是得先把要实现的功能一一列出来,后面实现起来才有方向嘛。我认为咱们需要实现的功能有: 正确获取消息列表并展示…

使用 RenderScript 实现高斯模糊(毛玻璃/磨砂)效果

前言 逛 Instagram 的时候,偶然发现,Instagram 的对话框设计的很有意思,如下图: 它的 dialog 的背景竟然是毛玻璃效果的,看起来很漂亮,恩,我是说对话框和迪丽热巴都漂亮😂。看到这么好的效果,当然就要开始搞事情了,自己动手实现差不多的效果。最终的实现效果如下图: 分别实现了对话框背景的虚化和手动调节虚化程度。 实现方法对比 最开始想要实现毛玻璃效果时,我是一脸懵逼的,不知道如何下手。幸亏,有万能的 Google。搜索之后发现常见的实现方法有4种,分别是: RenderScript Java算法 NDK算法 openGL 处理一整张图片这么大计算量的工作,openGL 的性能最好,而用 java 实现肯定是最差的了。而 RenderScrip t和 NDK 的性能相当,但是你懂得,NDK 和 openGL 我无可奈何,综合考虑,RenderScript 应该是最适合的。 但并不是说 RenderScript 就是完全没有问题的: 模糊半径(radius)越大,性能要求越高,模糊半径不能超过25,所以并不能得到模糊度非常高的图片。 ScriptIntrinsicBlur 在API 17时才被引入,如果需要在 Android 4.2 以下的设备上实现,就需要引入 RenderScript Support…

提升体验 – 支持 Chrome Custom Tabs

前言 文章比较长,先放项目地址: PaperPlane. 实际效果: 什么是Custom Tabs? 所有的APP开发者都面临这样一个选择,当用户点击一个URL时,是应该用浏览器打开还是应该用应用内置的WebView打开呢? 两个选项都面临着一些问题。通过浏览器打开是一个非常重的上下文切换,并且是无法定制的。而WebView不能和浏览器共享数据并且需要需要手动去处理更多的场景。 Chrome Custom Tabs让APP在进行网页浏览时更多的控制权限,在不采用WebView的情况下,这既能保证Native APP和网页之间流畅的切换,又允许APP定制Chrome的外观和操作。可定义的内容如下: toolbar的颜色 进场和退场动画 给Chrome的toolbar、overflow menu和bottom toolbar添加自定义操作 并且,Chrome Custom Tabs允许开发者对Chrome进行预启动和网页内容的预加载,以此提升加载的速度。 Chrome Custom Tabs VS WebView, 我应该什么时候用? 如果页面的内容是由我们自己控制的,可以和Android组件进行交互,那么,WebView是一个好的选择,如果我们的应用需要打开外部的网站,那么推荐使用Chrome Custom Tabs,原因如下: 导入非常简单。不需要编写额外的代码来管理请求,授予权限或者存储cookie 定制UI: Toolbar 颜色 动作按钮 (Action Button) 定制菜单项 定制进场退场动画 Bottom Toolbar…

简洁优雅地实现夜间模式

前言 Android 6.0 Marshmallow 预览版中曾经短暂出现过相关的夜间模式的功能,只是在正式版中被移除了,在 Android 7.0 Nougat 上,用户们再次经历了「得而复失」的遗憾,在开发者预览版中,夜间模式和暗色模式先是开启,然后有再次被移除。而在正式版中,夜间模式也没有出现。但其实相关的代码一直存在于系统中,只是默认没有被开启。如何开启这项功能,可以参考少数派的这一篇文章,帮你找回 Android 7.0 夜间模式的 2 款应用。 不过,今天要介绍的主要内容并不是关于系统的夜间模式,而是如何给我们开发的 APP 添加夜间模式的功能。毫不夸张的说,夜间模式现在已经是阅读类 App 的标配了。事实上,日间模式与夜间模式就是给 APP 定义并应用两套不同颜色的主题。用户可以自动或者手动的开启。我们先看两个我认为实现地很优雅的例子:知乎和 Twitter 。 这两个 APP 在切换的工程中,并没有出现闪现黑屏的情况,切换也比较顺滑。我们的目标就是利用 Support Library 实现同样的效果。 实现 添加依赖 准备资源 1. 让我们自己的主题继承并应用DayNight主题: 如果你使用了 Material Design Components, 那么从 v1.1.0…

探索 Android 7.1 Nougat 新特性

What’s new in Android 7.1 Nougat? Android 7.1 Nougat 已经推出有一段时间,相信大多数人和我一样,并没有用上最新的系统,但是,总有一群走在时代的前列线上的Geek们,勇于尝鲜,艰苦奋斗,为刷新版本号贡献自己的力量。好吧,实际上就是我还没有用上7.1,有些眼馋了。那么,和开发者息息相关的有哪些新特性呢? 本次主要介绍3个新特性: App Shortcuts, Round Icon Resource 和 Image Keyboard Support 。所有的新特性可以访问谷歌开发者中文博客的文章 欢迎使用Android 7.1.1 Nougat。 App Shortcuts 作为一个密切关注 Android 发展的伪 Geek ,在7.1正式版未发布之前,通过网上的一些爆料文章,我就了解到了这一新功能。实际上,这个功能刚开始出现时,我还以为 Google Pixel 要上压感屏了呢,事实证明,的确是我想多了。 App Shortcuts 允许用户直接在启动器中显示一些操作,让用户立即执行应用的深层次的功能。触发这一功能的操作就是「长按」。这一功能类似于iOS中的「3D Touch」。 下面通过一张GIF,直观的感受一下 App…

探索 Fragment 的生命周期

Fragment 和 Activity 类似,也有自己的生命周期,并且 fragment 的生命周期和 activity 的生命周期特别相似。 Activity 的生命周期由四种状态,运行、暂停、停止、和销毁,类似的,Fragment 也有这四种状态,只是在一些细小的地方有所不同。 Fragment 生命周期图(来自 Android 官网) 下面直接通过工程来认识 Fragment 的生命周期 首先新建 FragmentDemo 工程。 MainActivity.java 代码很简单,加载相应的布局,通过监听 button 的点击事件,加载 fragment 。 activity_main.xml MyFragment.java MyFragment 中复写了一些方法,每个方法被调用时打印日志。 ok,大功告成,现在就跑到手机上。 观察打印日志,可以看到: 按下按钮时: 此时按下 home 键: 重新回到应用: 按下 back…

实现 RecyclerView Item 的滑动删除

关于ItemTouchHelper 官方文档的解释: This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView. ItemTouchHelper是一个用于在RecyclerView中实现滑动删除和拖拽的工具类。 使用 1. 修改build.gradle文件,添加依赖。 2. 构建object与Adapter 这里假设我已经构建好了实体类CodeLanguage和adapter,在构建adapter时,需要添加一个方法。 3. 创建ItemTouchHelper.SimpleCallback子类 为了处理拖动和滑动事件,需要创建ItemTouchHelper.SimpleCallback的实现类。这里,只对滑动事件感兴趣,这是我们的callback. CodeLanguageItemTouchHelper默认的构造方法需要传入两个参数。 CodeLanguageItemTouchHelper默认需要实现两个方法onMove(),onSwiped(),onMove()是对拖拽的实现,onSwiped()是对滑动的实现。 4. 将ItemTouchHelper添加至RecyclerView 创建好自己的ItemTouchHelper类后,将它附加到RecyclerView就很简单了。在Activity或者Fragment的onCreate()方法中: 现在所有的工作已经完成。我们现在还没有添加动画,默认的动画系统已经添加。现在就可以使用了。

浅析 Android 中 Activity 的四种启动模式

standard, singleTop, singleTask, singleInstance. 启动模式一共有上述的四种,可以在AndroidManifest.xml文件中通过给标签指定android:launchMode属性来选择启动模式。 standard standard是Activity默认的启动模式,在不进行显示指定的情况下,所有Activity都会自动的使用这种模式。Android使用返回栈来管理Activity,在standard模式下,每当启动一个新的Activity,它就会在返回栈中入栈,并处于栈顶的位置。对于使用standard模式的Activity,系统不会在乎这个Activity是否已经在返回栈中,每次启动都会创建一个新的Activity实例。 现在通过实践来体会一下standard模式,首先是新建一个ActivityTest项目,修改MainActivity的onCreate()中代码,如下所示: 代码很简单,在MainActivity的基础上启动MainActivity,从逻辑上讲没什么意义,不过我们在于研究standard模式,因此我们也就不去追究实际的用途了。另外我们还在onCreate()中添加了一句打印日志信息,打印当前Activity的实例。 现在运行程序,然后再MainActivity界面连续点击两次按钮,观察LogCat中打印的日志信息。 从打印信息中可以很清楚的看到,每次点击按钮就会创建一个新的MainActivity的实例,此时返回栈中也会存在三个MainActivity的实例,因此想要完全退出应用,必须点击三次返回键。 singleTop 在某些情况下,你可能会觉得standard模式不太合理。activity明明已经在栈顶了,为什么再次启动时还要创建一个新的Activity实例呢?这时singleTop模式就派上用场了。当Activity的启动模式被指定为singleTop时,在启动Activity时如果发现返回栈的栈顶已经是该Activity,则可以认为直接使用它,不创建新的实例。 下面看实例,修改AndroidManifest.xml中的MainActivity的启动模式为singleTop: 重新运行程序,通过LogCat查看日志信息,我们可以看到已经创建了一个新的Activity实例。 之后不管点击多少次按钮,都不会有新的打印信息出现,因为目前Activity已经处于返回栈的栈顶,每当想要再启动一个MainActivity时都会直接使用栈顶的Activity,因此MainActivity也只会有一个实例,仅按一次返回键即可退出程序。 不过当MainActivity不在栈顶时,这是再启动MainActivity,还是会创建新的实例,下面来实验一下。修改MainActivity中的代码,如下所示: 这次我们点击按钮启动的是SecondActivity。然后修改SecondActivity中onCreate()方法中的代码,如下所示: 我们在SecondActivity中的按钮的点击事件中又加入了启动MainActivity的代码。现在重新运行程序,在MainActivity界面点击按钮进入SecondActivity,然后在SecondActivity界面点击按钮,又会重新进入到MainActivity。查看LogCat中的信息: 可以看到系统创建了两个不同的MainActivity,这是由于在SecondActivity中再次启动Activity时,栈顶的Activity已经变成了SecondActivity,因此会新建一个新的MainActivity。现在按下back键就会返回到SecondActivity,再次按下back键,又会回到MainActivity,再按一次back键才会退出程序。 singleTask 使用singleTask模式可以很好的解决重复创建栈顶Activity的问题,但是正如你看到的,如果该Activity并没有处于栈顶的位置,还是可能创建多个Activity实例的。那么有没有方法可以让Activity在整个应用程序的上下文中指存在一个实例呢? 这就要借助singleTask模式来实现了。当Activity的启动模式指定为singleTask,每次启动该Activity时系统首先会在返回栈中检查是否存在该Activity的实例,如果发现已经存在则直接使用该实例,并把在这个Activity之上的所有Activity统统出栈,如果没有发现就会创建一个新的实例。 下面通过代码来直观的理解一下。修改AndroidManifest.xml文件中MainActivity的启动模式: 然后在MainActivity中添加onRestart()方法,并打印日志: 最后在SecondActivity中添加onDestroy()方法,并打印日志: 现在重新运行程序,在MainActivity界面点击按钮进入SecondActivity,然后在SecondActivity界面点按钮,又会重新进入到MainActivity。查看LogCat中的日志信息: 从打印信息中可以看出,在SecondActivity中启动MainActivity时,会发现返回栈里已经存在一个MainActivity的实例,并且是在SecondActivity的下面,于是SecondActivity会从返回栈中出栈,而MainActivity重新成为了栈顶Activity,因此MainActivity的onRestart()方法和SecondActivity的onDestroy()方法会得到执行。现在返回栈中只剩下一个MainActivity的实例了,按一下back键就可以退出程序。 singleInstance singleInstance应该是四种启动模式中最特殊也最复杂的一种了。不同于以上三种启动模式,指定为singleInstance的Activity会启用一个新的返回栈来管理这个Activity(其实如果singleTask模式指定了不同的taskAffinity,也会启动一个新的返回栈)。那么这么做有什么意义呢?假设我们的程序中有一个Activity是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个Activity的实例,应该如何实现呢?使用前面三种启动模式肯定是无法实现的,因为每个程序都会有自己的返回栈,同一个Activity在不同的返回栈中入栈时必然是创建新的实例。而使用singleInstance模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个Activity,不管是那个应用程序来访问这个Activity,都共用的同一个返回栈,也就解决了共享Activity实例的问题。 OK,talk is cheap,show me the code,我们还是来实践一下。修改AndroidManifest.xml中SecondActivity的启动模式: 我们先将SecondActivity的启动模式指定为singleInstance,然后修改MainActivity中onCreate()方法的代码: 在onCreate()方法中打印了当前返回栈的id。然后修改SecondActivity中onCreate()方法的代码: 同样在onCreate()方法中打印了当前返回栈的id,然后又修改了按钮点击事件的代码,用于启动ThirdActivity。最后修改ThirdActivity中onCreate()方法的代码: 仍然是在onCreate()方法中打印了当前返回栈的id。现在重新运行程序,在MainActivity界面中点击按钮进入到SecondActivity,然后在SecondActivity中点击按钮进入到ThirdActivity。查看LogCat中的打印信息:…

进程与线程之间的关系

什么是进程? 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的独立单位。进程拥有一个完整的虚拟空间地址,并不依赖线程而独立存在。 什么是线程? 线程是进程的一个实体,是cpu调度和分配的基本单位。它是进程更小的能独立运行的基本单位。线程本身并不拥有系统资源,运行是只是暂用一些计数器、寄存器和栈。 进程与线程之间的关系 简单来说,一个程序至少拥有一个进程,而一个进程至少拥有一个线程。 一个线程可以创建和撤销另外一个线程,同一个进程中可以有多个线程并发的执行。 线程更加接近于执行体的概念,它可以同进程中的其他线程共享数据,但是拥有独立的栈空间,拥有独立的执行序列。 进程与线程的区别 进程与线程的主要区别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃之后,在保护模式对其他进程没有影响,线程只是进程的不同执行路径。线程有自己独立的堆栈和局部变量,但是没有独立的地址空间,一个线程死掉那么整个进程也就死掉了,所以多进程的应用要比多线程的应用更加健壮。但是进程在切换时耗费的资源也比较多,效率也要更差,所以在并发性要求高又要共享变量数据,只能靠线程,而不是进程。 在执行过程中,每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多线程执行控制。 从逻辑上看,多线程的意义在于在一个应用程序当中,有多个执行部分可以同时执行。但是操作系统并没有将多个线程看作多个独立的应用,来实现进程的调度和管理以及资源分配。

浅谈 Android 中的 ANR 现象

什么是ANR ANR,Application Not Responding,即程序未响应。发生ANR时,系统会显示一个dialog,提示用户出现了ANR,用户可以选择“等待”,或者是“强制关闭”,结束应用。ANR同崩溃一样,是在开发过程中应该避免的。 什么会触发ANR 主线程被IO操作阻塞。(文档中特别强调:you should not perform the work on the UI thread(不要在UI线程中进行IO操作))。 主线程中存在耗时的操作。 主线程中错误操作。如Thread.wait()或Thread.sleep()等。 在Android中,应用程序的响应是受到Activity Manager 和 Window Manageer系统服务的。一旦出现以下情况,Android系统就会展示dialog: 5秒内没有相应触摸操作。(No response to an input event (such as key press or screen touch events) within 5 seconds.) 广播接收器在10内没有执行完成。(A…