精华内容
下载资源
问答
  • 随着安卓智能手机不停的更新换代。安卓手机系统越来越完美,屏幕尺寸也越来越大啦!比如最近小米的miui 6的发布和魅族手机系统的更新...根据目前流行的安卓手机的系统体验来完成我们的安卓APP设计规范。应该说这是...

    随着安卓智能手机不停的更新换代。安卓手机系统越来越完美,屏幕尺寸也越来越大啦!比如最近小米的miui 6的发布和魅族手机系统的更新等等。

    以小米MIUI6的安卓手机来说,MIUI6进行了全新设计,坚持“内容才是本质”的设计哲学,重新提炼内容,简化图标设计。

    所以,我们在进行安卓APP设计时,需要好好调整之前的设计规范和设计细节。根据目前流行的安卓手机的系统体验来完成我们的安卓APP设计规范。应该说这是整理出最全面的安卓app设计规范。

    25学堂站在不断更新和完善安卓app设计规范为宗旨!利用周末的时间整理了2014年Android APP设计规范教程。

    1、安卓app设计规范之尺寸或分辨率

    目前最新安卓手机的屏幕尺寸都是5.5英寸啦。我们都知道安卓机的尺寸很多很杂。而且不同的分辨率对应不同的dpi模式。

    Android也支持多种不同的dpi模式:ldpi   mdpi   hdpi     xhdpi   xxhdpi    xxxhdpi(4K分辨率)

    关于安卓APP设计的dpi详细解读请查看:

    目前主流的安卓手机分辨率有以下3种:

    hdpi,对应800*480的手机。主流机型,很多。如小米1 ,1s 三星 htc 等

    xdpi,对应1280*720的手机。三星Galaxy系列和华为p6.

    xxdpi,对应1080*1920的手机。小米手机,华为荣耀手机系列为主加上 htc one。

    下面是当面流行的安卓手机的屏幕尺寸和分辨率:

    小米 3和小米4 屏幕尺寸和分辨率:        5英寸 1920x1080像素

    魅族MX2 屏幕尺寸和分辨率:                 4.4英寸 1280x800像

    魅族MX3 屏幕尺寸和分辨率:                 5.1英寸 1800x1080像素

    HTC  one屏幕尺寸和分辨率:               4.7英寸 1920x1080像素

    华为荣耀6屏幕尺寸和分辨率:             5英寸 1920x1080像素

    华为p6屏幕尺寸和分辨率:                 4.7英寸 1280x720像素

    华为p7屏幕尺寸和分辨率:                  5英寸 1920x1080像素

    在目前我们的安卓APP设计项目当中,我们并不会去为每一种分辨率去设计一套UI界面。这是一种追求完美和理想的状态。小公司肯定是耗不起这样的。

    所以,这个时候我们需要学会变通。为了适应多分辨率,

    1:在标准基础(xdpi:1280*720)上开始,然后放大或缩小,以适应到其他尺寸。

    2:从设备的最大尺寸(xxdpi:1920x1080)开始,然后缩小,并适应到所需的最小屏幕尺寸。

    有些时候我们也会在实际开发过程中,Android和IOS的设计稿若无太大差异,也可从IOS的分辨率(960*640)开始,再调整设计稿的比例,适应其他分辨率。但是这种方法在切图的时候 需要做一些图片的调整。如果不是矢量图的元件需要重新按照1280*720的尺寸设计下。

    2、安卓app设计规范之字体和字体大小

    我们必须知道的安卓设计常识:安卓4.0之后用的字体是Roboto。中文字体:方正兰亭黑体

    今天跟大家讲解的是在720*1280的基础上的字体设计大小。

    注释最小字体:               12sp  ==  24px

    文本字体:                       14sp  ==  28px

    文章标题或图标名称:   16sp  ==   32px

    导航标题:                       18sp  ==   36px

    SP的详细介绍如下:

    sp和dp一样,是android开发里特有的单位,设计师在做UI设计的时候通常最初是建立320*480这个尺寸的画布开始的,这个尺寸的画布在android分辨率的分类中称为mdpi,在这个尺寸下,ps里的1px就等于android中的1dp,同样,这个时候1点的字就等于android中1sp,举个栗子:你建立画布的尺寸是320-480,里面的文字是30点,那么它就是30sp。

    一般android设置长度和宽度多用dip,设置字体大小多用sp. 在屏幕密度为160,1dp=1px=1dip, 1pt = 160/72 sp  1pt = 1/72 英寸.当屏幕密度为240时,1dp=1dip=1.5px.

    设计时候,我们还需要遵循48dp定律。

    48dp作为安卓可触摸的UI元件的标准。

    一般来说,48dp转化为一个物理尺寸约9毫米。建议的目标大小为7-10毫米的范围,这是一个用户手指能准确并且舒适触摸的区域。

    如果你设计的元素高和宽至少48dp,你就可以保证:

    (1). 触摸目标绝不会比建议的最低目标(7mm)小,无论在什么屏幕上显示。

    (2). 在整体信息密度和触摸目标大小之间取得了一个很好的平衡。

    而每个UI元素之间的空白通常是8dp.

    下面是某个安卓APP设计师对android设计做出的一个设计信息图总结。

    3、安卓app设计规范之切图

    这块需要按照设计按照下面4篇文章来了解安卓app设计标注和切图的一些规范。这里不做详细解说了。

    4、安卓app设计规范之适配和设计图测试预览

    (1)尺寸标注工具 MarkMan(马克鳗)

    (2)APP快速切图工具:Cutterman

    (3)一个可视化的Android UI界面设计工具:DroidDraw

    (5)设计图完成之后,预览工具和在线预览方法介绍:Ps play

    (6)在线生成自定义APP图标字体利器:IconVault

    展开全文
  • 开发一款Android App,从零开始详细讲解

    万次阅读 多人点赞 2021-03-04 17:16:38
    入门篇:第一篇:开发环境篇第二篇:材料设计篇第三篇:规范开发篇第四篇:从项目开发到上架篇...功能很简单,利用豆瓣电影Api实现一款基于Android的电影集合的App。star me on GitHub! 该项目的数据源来自于豆瓣;

    入门篇:
    第一篇:开发环境篇
    第二篇:材料设计篇
    第三篇:规范开发篇
    第四篇:从项目开发到上架篇(篇章调整,最后更新)

    进阶篇:
    第五篇:设计模式篇
    第六篇:网络请求篇(上)
        网络请求篇(下)
    第七篇:图片处理篇
    第八篇:数据库篇
    第九篇:开源资源篇

    高阶篇:
    第十篇:自定义控件篇
    第十一篇:跨进程篇

    关于Demo

    • 写这个系列的文章的同时,也在做一个小Demo。功能很简单,利用豆瓣电影Api实现一款基于Android的电影集合的App。star me on GitHub!

    看看目前的效果:

    Demo.png

     

    入门篇:
    第一篇:开发环境篇

     

    Android作为Google的亲儿子,其开发工具Android Studio在2014年底发布1.0版本,目前更新到2.1.1。这是一款很赞的IDE,毕竟power by Intellij Platform。本篇主要介绍Android Studio的一些简单的配置和使用。帮助初学者解决前期使用Android Studio的一些“坑”。

    Android Studio

    • 使用Android Studio的时候希望大家能够学会科学上网,否则体验会非常差。

    • Android Studio需要在官网下载的,这给身处墙内的我们造成很大的困扰。这里给出国内下载镜像(同时里面还有一些Android Studio的SDK在线更新的镜像服务器的配置),可供参考。
      *Android Studio2.0以后无论从编译速度还是模拟器的性能和速度都有了很大的提升,大家可以尝试下载最新版本AS。

    • 下载Android Studio完成后,第一次启动又会卡住,弹出 "Fetching android sdk component information" 对话框,这是Android Studio在检查sdk的更新,我们会被墙;
      解决方法:在Android Studio的安装目录的bin文件夹下找到idea.properties文件,用sublime或者其他文本编辑器打开,在文件的末尾加上disable.android.first.run=true,重启AS,即可顺利进行进入界面;

      Android Studio.png

    SDK Manager中的东西哪些需要下载?
    打开SDK Manager,很多待下载文件,可是哪些需要下载呢?因为即使有代理,从SDK Manager中下载东西也是很慢的,全部下载会花费很长时间,而且也没有必要。所以我总结了一下,SDK Manager中只需要下载以下几点:

    • Tools文件夹下的内容:这里面是不同版本的SDK编译工具。
    • 各个SDK版本:这里SDK版本需要全部下载。但不是让你把每个API下的所有内容都下载,只需要下载SDK Platform即可,其他内容按需下载。

    SDKManagerSample.png

    我们不知道从网上clone下来的工程的compileSdkVersion、buildToolVersion、minSdkVersion和targetSdkVersion的版本,所以,提前全部下载下来等着自动适配有很大的好处。

    *tips: 建议将Android Studio、gradle和Sdk版本更新到最新

    Android Studio插件

    选择一些好的Android Studio插件会使得我们的开发事半功倍,下面介绍一些我正在使用的不错的插件,欢迎大家补充:

    1. Android ButterKnife Zelezny:这个插件要配合Jake Wharton大神的ButterKnife使用,后面我会简单介绍到;
    2. Android Paracelable Code generator:序列化代码生成工具,当你的entities里面有一大堆元素需要Paracelable的时候就会发现这个插件的好处。
    3. .gitignore: 设置git 上传的ignore文件
    4. Lifecycle Sorter:根据Activity和fragment的生命周期对生命周期方法的位置进行排序

    插件下载的方法:Android Studio -> Preferences -> Plugins ->(Search)
    这里还有其他的优秀插件,开发人员可以自行选择。

    代码托管

    Android Studio中设置git和Github是非常容易的(如果是windows的话需要先下载git),这里不详述。Github上免费的代码托管是public的,所有人都可以看到。如果你不想公开你的项目,或是需要合作开发一个private项目,就找一个国内的免费git托管吧,Coding是一个不错的选择。

    开发前准备

    Android Studio配置完成后就可以开始开发了,这个项目托管于Github,fork me on Github

    数据源:豆瓣电影Api

    Android project相关参数:

    • 开发环境 Android Studio 2.11
    • compileSdkVersion 23
    • buildToolVersion 23.0.3
    • minSdkVersion 15
    • targetSdkVersion 23
    • JDK Version 1.8

     

     


    第二篇:材料设计篇

     

    Google在I/O 2014上推出Material Design(材料设计)规范后,被外界称赞:“Google 第一次在设计方面超越了Apple。”,随着Android5.0的市场占有率增加,Material Design越来越被开发者和用户所接受。本文内容旨在介绍Material Design在具体开发中的使用;如果有对Material Design不了解的朋友,请先参考官网介绍

    “Material Design”推出很多的新控件,如RecyclerView、CardView、Drawable、FloatingActionButton等等。本篇不讨论这些控件的使用方法,简书的“Android材料设计”专题里面的大神们已经写了很多,如果有控件使用方面的疑虑,可以参考该专题;本篇将讨论一些实际性的问题:

    玩转“Material Design”

    先教大家一个小技巧:如果项目中想要使用“Material Design”相关东西,新建项目时在"Add an Activity to Mobile"这个选择框时选择"Navigation Drawer Activity",而不是选择“Empty Activity”,这时,新建的项目就会有材料设计的框架。在这个基础上开发,会避免一些麻烦。否则,如果对材料设计的各种style和各类包不熟悉的话,在编译的时候会出很多问题;

    Navigation_Drawer.png

    新建工程的build.gradle文件中,看到AS已经自动导入了你当前SDK最新的“材料设计”的v7包:

     
    1. compile 'com.android.support:appcompat-v7:23.3.0'

    2. compile 'com.android.support:design:23.3.0'

    运行新建的工程文件,一个带侧滑和沉浸式状态栏的的空项目完成。此时我们就来研究一下如何实现沉浸式状态栏:

    沉浸式状态栏

    先看看效果图:

    沉浸式状态栏.png

    沉浸式状态栏:即 将状态栏的颜色改成ToolBar颜色的深色版本。使得状态栏也成为App的一部分,而不是像5.0以下版本那样一成不变的灰色或黑色。

    方法一:
    在AndroidManifest.xml可以看到Application使用的Theme是自定义的style “AppTheme”。于是,我们打开在values文件夹下看到AppTheme:

     
    1. <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

    2. <!-- Customize your theme here. -->

    3. <item name="colorPrimary">@color/colorPrimary</item>

    4. <item name="colorPrimaryDark">@color/colorPrimaryDark</item>

    5. </style>

    这里的colorParimary是Toolbar的颜色,而colorParimaryDark是状态栏的颜色,两个颜色请自行设定;

    方法二
    同时我们注意到:MainActivity由于需要添加NavigationView,所以要自己写ToolBar。因此这里的Theme可以自定义AppTheme.NoActionBar(写好的Toolbar后会默认使用AppTheme的沉浸式状态栏的配色)。但由于是自定义主题,并不是V7包的内容,则需要在values和values-v21两个文件夹下都写一个同名的style以适配5.0以下版本的机型:
    values下的AppTheme.NoActionBar:

     
    1. <style name="AppTheme.NoActionBar">

    2. <item name="windowActionBar">false</item>

    3. <item name="windowNoTitle">true</item>

    4. </style>

    values-v21下的AppTheme.NoActionBar:

     
    1. <style name="AppTheme.NoActionBar">

    2. <item name="windowActionBar">false</item>

    3. <item name="windowNoTitle">true</item>

    4. <item name="android:windowDrawsSystemBarBackgrounds">true</item>

    5. <item name="android:statusBarColor">@android:color/transparent</item>

    6. </style>

    此时两种设置沉浸式状态栏的方法完成,如果你对沉浸式状态栏的配色不满意,请将这个网站推荐给你的设计师;

    ToolBar跟随滑动至消失

    效果图:

    Toolbar跟随滑动.gif

    Toolbar跟随滑动至消失这个效果是一个很容易但是交互性比较强的小动画;

     
    1. <android.support.design.widget.AppBarLayout

    2. android:layout_width="match_parent"

    3. android:layout_height="wrap_content"

    4. android:fitsSystemWindows="true"

    5. android:theme="@style/AppTheme.AppBarOverlay">

    6.  
    7. <android.support.v7.widget.Toolbar

    8. android:id="@+id/toolbar"

    9. android:layout_width="match_parent"

    10. android:layout_height="?attr/actionBarSize"

    11. app:layout_scrollFlags="scroll|enterAlways|snap"

    12. app:popupTheme="@style/AppTheme.PopupOverlay" />

    13.  
    14. <android.support.design.widget.TabLayout

    15. android:id="@+id/tab_top"

    16. android:layout_width="match_parent"

    17. android:layout_height="wrap_content">

    18. </android.support.design.widget.TabLayout>

    19. </android.support.design.widget.AppBarLayout>

    从这里看到,我们写Toolbar的时候需要嵌套在AppBarLayout里面,AppBarLayout的好处就是可以让Toolbar配合TabLayout一同使用;要实现Toolbar跟随滑动,只需要在AppBarLayout中添加:

    android:fitsSystemWindows="true"

    在ToolBar中添加:

    app:layout_scrollFlags="scroll|enterAlways|snap"

    但是要注意的是:AppBarLayout应该配合CoordinatorLayout使用,同时,ToolBar下的主体部分必须是,RecyclerView、NestedScrollView等实现NestedScrollView接口的可滑动部件;ListView可以在外面嵌套一层NestedScrollView来触发CoordingatorLayout的滑动;布局代码

    本篇结束语

    本篇仅仅写了“Material Design”的九牛一毛,但是我认为这是上面的两个东西是项目中最常用的,希望我的文章已经解释清楚了。接下来,我的Demo中还会陆续写到RecyclerView、Drawer等控件的使用,虽然不会再另写一篇博客,但是还是希望大家可以关注我的系列文章:从零开始开发一款Android App

    刚开始接触“材料设计”这个概念的时候感觉很难,但是在用过一些控件之后,感觉材料设计的人机交互性很强,希望各位能够真正将“材料设计”当成一种工具来实现。



    作者:Torang
    链接:http://www.jianshu.com/p/47b81f3a0b31
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

     

    第三篇:规范开发篇

     

    我从一个新建的Material Design工程介绍了“沉浸式状态栏”和“ToolBar跟随滑动的”的效果的实现,本篇会结合实际开发写一些Android项目工程下的res文件夹的资源规范问题:

    图片资源规范

    在Android工程中,drawable和mipmap都可以存放图片资源,但是这两个文件夹有什么区别呢? mipmap下有mipmap-hdpi, mipmap-xhdpi, mipmap-xxhdpi, mipmap-xxxhdpi,这些文件夹下存放什么分辨率的图片资源呢?

    drawable 和 mipmap

    来看看官方介绍

    drawable/
    For bitmap files (PNG, JPEG, or GIF), 9-Patch image files, and XML files that describe Drawable shapes or Drawable objects that contain multiple states (normal, pressed, or focused).
    mipmap/
    For app launcher icons. The Android system retains the resources in this folder (and density-specific folders such as mipmap-xxxhdpi) regardless of the screen resolution of the device where your app is installed. This behavior allows launcher apps to pick the best resolution icon for your app to display on the home screen.

    上面的官方的解释大意是:mipmap文件夹下只是存放不同分辨率的ic_launcher的图标,同时mipmap会在底层对icon进行优化。而drawable文件夹下存放应用中用到的各类图像资源和自定义的xml资源。但是,很多开发者有不同的见解,认为mipmap可以完全代替drawable存放图片资源,看这里

    当出现争议的时候,需要以官方的标准为准!但是官方的标准代码中,这些图片资源是放在那个文件夹下呢?在上一篇文章中提到,我的Demo新建的Navigation Drawer Activity的工程,在这个Demo中,drawer内置了几个图标,我们来看看drawer的几个图标的位置:

    drawer.png

    drawer图标的位置.png


    看到这两个图片,一切都明白了,项目中用到的图片资源都是存放的drawable里面的,而ic_launcher放在mipmap中的,具体原因可以看看这篇文章;好了,项目开发中就接受官方的建议吧。

    UI需要做几套图来适配不同分辨率的手机呢?

    我们都知道,在xml写布局的时候长度单位是dp,但是UI给的图片的单位是px,我们如何将dp转化为px呢?不同的屏幕分辨率转化转换不同。具体的转化公式和概念本文不给出,如果不清楚可自行Google:
    下面直接给出不同的屏幕密度下的px与dp的转化表:

    分辨率ldpimdpihdpixhdpixxhdpixxxhdpi
    px:dp0.7511.5234

    ic_launcher: 在一个新建的项目中可以看到,有5个mipmap文件夹,分别存放不同分辨率的ic_launcher:mipmap-hdpi(72*72px), mipmap-mdpi(48*48px), mipmap-xhdpi(96*96px), mipmap-xxhdpi(144*144px), mipmap-xxxhdpi(196*196px)。所以,根据上个问题的介绍,ic_launcher需要做5种不同的分辨率,

    图片资源:首先我们先理解一下,在drawable文件夹下为了适配不同的分辨率也应该分为drawable-hdpi, drawable-ldpi, drawable-mdpi, drawable-xhdpi, drawable-xxhdpi如上图 drawer图标位置.png 。为所有的图片资源都做五种规格显然不现实,那最少需要做几套呢?做一套图行吗?

    • 一般开发会做三套图,对应drawable-hdpi, drawable-xhdpi, drawable-xxhdpi;

      • 同一张图片,放在不同目录下,会生成不同大小的Bitmap。Bitmap的长度和宽度越大,占用的内存就越大。
      • 同一张图片,放在不同的drawable目录下(从drawable-lpdi到drawable-xxhpdi)在同一手机上占用的内存越来越小。
      • 图片在硬盘上占用的大小,与在内存中占用的大小完全不一样。
    • 对App要求不是很高的话,做一套图也是可以的,放在drawable-xxhdpi即可;原因就是为了省内存

    结论来自于这篇博客,分析很到位,大家可以结合具体的测试理解。

    其他资源

    在res文件夹下,除了图片资源,还有values文件夹,该目录下有string, colors, dimens, arrays, styles等资源文件,所有的资源文件都是以xml的格式存储的,这里强烈建议在xml和java代码中来引用资源文件中的资源,而不是直接将字符串、颜色等资源直接写出来,因为这样能方便统一管理,统一修改。

    举一个栗子:如果当你做出来一款中文的App以后,你的App想要随着手机系统语言改变App的语言该怎么做呢?如果你所有的string都没有写在strings.xml里,那你就麻烦了!但是如果你规范地写在了strings.xml,恭喜你,只需要翻译就好了;

    • 在res目录下新建一个values-zh-rCN文件夹,将我们的中文资源的strings.xml和arrays.xml复制在该文件夹下;
    • 在原values文件夹下,将strings.xml和arrays.xml中的中文翻译成你自己想要的English就可以了。

    完成上面步骤后,你的App就可以跟随系统语言切换不同语言了。

    除了字符资源以外,colors、dimens等资源写在xml资源文件中对以后App的版本更新迭代有很大的帮助。所以,在初次开发的时候请不要省去这个步骤。



    第四篇:从项目开发到上架篇(篇章调整,最后更新)

     

     

    进阶篇:
    第五篇:设计模式篇

     

    介绍了Android工程res目录下资源的规范。本篇将简单介绍Android开发中MVP模式的包结构和Java设计模式在Android中的应用。

    MVP

    最近MVP模式很火,可能是因为面向接口编程这种思想逐渐深入人心的缘故。其实MVP的核心就是:将所有的底层接口的功能实现都封装起来,而不让调用者了解到任何实现细节,最终实现用户界面与数据层的高度解耦的一种设计方法。本篇将结合实际的操作来写一写Android MVP模式的实现。

    基本概念

    M V P 分别代表Model、View、Presenter

    1. View层负责Android界面用户事件和数据展示:一般多为Activity和Fragment;
    2. Model层负责访问数据,如从Server加载的Java Bean、本地的Database、Sharedpreference、文件数据等;
    3. Presenter层是View层与Model层之间数据传输的桥梁;

    下图为MVC和MVP模式之间的区别:(其实我们传统的开发模式并不是真正的MVC模式,我认为是一种阉割版的。因为在我们传统编码中Android SDK提供了基于XML的界面框架与存储数据的ContentProvider组件,而开发者则更加专注于业务逻辑的编写。)

    MVC-vs-MVP.png

    所以我们以往开发模式(MVC)中,如果需要频繁更改显示数据,Activity总是即充当View又充当Controller,那么你的Activity.java文件会出现大量代码,不易于维护和版本迭代。但是当View层和Model层解耦后,就不会出现这种情况。从上图中我们可以看到MVP模式的View层和Model层不发生任何联系,而数据交换与用户操作事件依赖于Presenter层来实现,所以在这一点上,MVP优于传统的编码模式。那么MVP将如何实现呢?

    MVP的实现

    举一个栗子:网络请求数据,然后显示在用户界面上:
    下面的代码的MVP的结构参考这个开源项目,代码结构很清晰。

    View层

    MvpView

     
    1. public interface MvpView {

    2. void startLoading();

    3. void hideLoading();

    4. void showError(String msg, View.OnClickListener onClickListener);

    5. }

    DataView

     
    1. public interface DataView extends MvpView {

    2. void loadData(UsBoxEntity usBoxEntity);

    3. }

    HotMovieFragment

     
    1. public class HotMovieFragment extends BaseFragment implements DataView {

    2. @Override

    3. public void onCreate(@Nullable Bundle savedInstanceState) {

    4. super.onCreate(savedInstanceState);

    5. presenter = new HotMoviePresenterImpl(getActivity());

    6. presenter.attachView(this);

    7. presenter.loadData();

    8. }

    9. /**

    10. * Presenter 的回调接口,用于更新UI

    11. */

    12. @Override

    13. public void loadData(UsBoxEntity usBoxEntity) {

    14. log.d(usBoxEntity.getTitle());

    15. mAdapter = new OutRecyclerAdapter(getActivity(),usBoxEntity);

    16. recyclerMovie.setAdapter(mAdapter);

    17. //网络请求到的数据在这里更新UI

    18. }

    19. }

    BaseFragment

     
    1. public class BaseFragment extends Fragment implements MvpView {

    2. @Override

    3. public void startLoading() { }

    4. @Override

    5. public void hideLoading() { }

    6. @Override

    7. public void showError(String msg) { }

    8. }

    Presenter层

    Presenter

     
    1. public interface Presenter<V extends MvpView> {

    2. void attachView(V mvpView);

    3. void detachView();

    4. }

    BasePresenter

     
    1. public class BasePresenter <T extends MvpView> implements Presenter<T> {

    2. private T mMvpView;

    3. @Override

    4. public void attachView(T mvpView) {

    5. mMvpView = mvpView;

    6. }

    7. @Override

    8. public void detachView() {

    9. mMvpView = null;

    10. }

    11. public boolean isViewAttached() {

    12. return mMvpView != null;

    13. }

    14. public T getMvpView() {

    15. return mMvpView;

    16. }

    17. public void checkViewAttached() {

    18. if (!isViewAttached()) throw new MvpViewNotAttachedException();

    19. }

    20. public static class MvpViewNotAttachedException extends RuntimeException{

    21. public MvpViewNotAttachedException(){

    22. super("Please call Presenter.attachView(MvpView) before requesting data to the Presenter");

    23. }

    24. }

    25. }

    HotMoviePresenterImpl

     
    1. public class HotMoviePresenterImpl extends BasePresenter<DataView>{

    2. Context context; UsBoxEntity usBoxentity;

    3. public HotMoviePresenterImpl(Context context){

    4. this.context = context;

    5. }

    6. @Override

    7. public void attachView(DataView mvpView) {

    8. super.attachView(mvpView);

    9. }

    10. @Override

    11. public void detachView() {

    12. super.detachView();

    13. }

    14. public void loadData(){

    15. //在这里写网络请求的代码,从Server中请求到JavaBena UsBoxEntity

    16. //调用MvpView中的loadData方法,将请求到的数据传回View层,让View层更新数据;

    17. getMvpView().loadData(usBoxentity);

    18. }

    Model层

    model层即为网络请求的一些配置(也可以是数据库的请求等),例如:OkHttp、Retrofit等网络请求的配置,由于下一篇将着重讲述Retrofit配合Okhttp的应用,所以这里不详细写。

    给出我的Demo的包结构截图供大家参考:

    MVP_包结构.png

    以上说了很多MVP模式的好,但是并不是说所有的工程都适合MVP。MVP类似于面向接口编程,所以在一个大型项目中会出现很多接口文件,这也是不利于维护的。所以,具体开发还需要根据实际情况选择。

    Java设计模式在Android中应用

    如果对Java设计模式还不熟悉的朋友,可以先看看《设计模式之禅》这本书。这本书通过对6大设计模式原则进行了全新的解读,对面向接口编程和Java的23种设计模式都做了详细讲述。本篇不会涉及概念性的东西,只是简单说一说隐藏在Android开发中的一些Java设计模式。

    适配器模式:对于Android开发人员来说,适配器模式应用非常广泛,例如ListView、RecyclerView、Viewpager等控件的实现,都是需要写一个adapter从而使原本不匹配而无法在一起工作的两个类能够在一起工作;

    单例模式:当应用启动后会create一个Application,这个Application就是一个单例模式,从应用启动到关闭都会维持这个Application不会改变。还有,otto、RxBus等开源框架在使用中都会维护一个单例从而保证数据发送的唯一性;

    观察者模式:点击事件用到的Listener就是一种订阅,订阅点击事件。还有最近比较火的Rxjava的使用,也是一种观察者模式,它有发布者和观察者,观察者通过消息的订阅来获取发布者发布的消息;

    动态代理模式:Retrofit网络请求就是用到了动态网络代理模式,通过代理模式,插入不同的功能框架,来达到定制的网络请求的效果;

    响应链模式:Android的事件分发机制;

    Java设计模式在Android中的应用很多,这里只写出我认为比较常用的几种,其它的不一一列举,如果想要了解,可以查看这个专题

    本篇如果有不正确的地方,希望大家指出!



    作者:Torang
    链接:http://www.jianshu.com/p/fa92ca51bdb0
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    第六篇:网络请求篇(上)

     

     

    简单地写了一些关于Android开发模式MVP的实际操作应用,最后还说了关于Java设计模式在Android中的应用。本篇主要写一些在Android开发中使用RxJava配合Retrofit和OkHttp进行网络请求的操作。

    开发前准备

    Gradle:最新包的下载地址:RxJava&RxAndroid ,Retrofit,OkHttpVolley

     
    1. dependencies {

    2. compile fileTree(dir: 'libs', include: ['*.jar'])

    3. compile 'io.reactivex:rxandroid:1.1.0'

    4. compile 'io.reactivex:rxjava:1.1.3'

    5. compile 'com.squareup.retrofit2:retrofit:2.0.2'

    6. compile 'com.squareup.retrofit2:converter-gson:2.0.2'

    7. compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'

    8. compile 'com.squareup.okhttp3:okhttp:3.2.0'

    9. compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'

    10. compile 'com.mcxiaoke.volley:library:1.0.19'

    11. }

    网络权限:

    <uses-permission android:name="android.permission.INTERNET"/>

    OkHttp & Volley

    Volley(项目暂停维护)

    说OkHttp之前不得不说一下Volley,作为曾经比较强大的一个网络请求库它被Google官方接受并于2013年Google I/O上推出,其实Volley内部封装使用的是HttpURLConnection和HttpClient。如果想要深入了解一下Volley可以参考CSDN郭霖大神的四篇文章
    由于Volley不是本篇的重点,这里只是给出使用Volley Http Get请求的简单例子:

     
    1. public void loadDataWithVolley(){

    2. RequestQueue mQueue = Volley.newRequestQueue(context);

    3. JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(URL, null,

    4. new Response.Listener<JSONObject>() {

    5. @Override

    6. public void onResponse(JSONObject response) {

    7. log.d(response.toString());

    8. }

    9. }, new Response.ErrorListener() {

    10. @Override

    11. public void onErrorResponse(VolleyError error) {

    12. log.e(error);

    13. }

    14. });

    15. mQueue.add(jsonObjectRequest);

    16. }

    通过上面的的代码可以看出:Volley通过维护一个请求队列来实现网络请求。它的底层有一个缓存线程和一个线程池,这样一来Volley的开发效率就比较低了,在上传大文件方面处理也不好,同时在功能拓展性方面也有欠缺。于是,Okhttp逐渐深入人心:

    OkHttp3

    对于基本的OkHttp3的使用参考官网
    OkHttp是一个高效的Http客户端,其特点:

    1. 支持HTTP2/SPDY黑科技
    2. socket自动选择最好路线,并支持自动重连
    3. 拥有自动维护的socket连接池,减少握手次数
    4. 拥有队列线程池,轻松写并发
    5. 拥有Interceptors轻松处理请求与响应(比如透明GZIP压缩,LOGGING)
    6. 基于Headers的缓存策略

    以上内容参考系列文章:OkHttp3源码分析[综述],如果想要深入了解OkHttp的可以参考源码查看文章。

    RxJava配合OkHttp3下载 文件&图片

    如果你按照我上面的建议,看了OkHttp官网上的代码,并把它用到了项目中,同时没有做其他的操作,编译的时候你会发现问题:

     
    1. java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.

    2. Caused by: android.os.NetworkOnMainThreadException

    这是因为,官网上只是给出了OkHttp同步请求的操作,并没有给出异步操作!!!(OkHttp2.x的时候官网上是有异步操作的例子)OkHttp3和OkHttp2.x的异步请求还是有一些区别的:如果想要了解OkHttp3的异步操作,可以参考这篇文章
    官网上没有给出OkHttp3的异步操作,也许是想要让开发者配合RxJava的使用。如果还不了解RxJava的用法,请先Google一下,这几篇是国内最初翻译的Rxjava文献,可供参考。

    下载文件(实时返回下载进度)

    RxJava中的事件源(被观察者):这里配合OkHttp进行进行网络操作,

     
    1. File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

    2. File file = new File(path,"/okhttpdownload");

    3. /**

    4. * download file with OkHttp and RxJava (rate)

    5. */

    6. Observable<String> downloadObservable = Observable.create(new Observable.OnSubscribe<String>() {

    7. @Override

    8. public void call(Subscriber<? super String> subscriber) {

    9. InputStream inputStream = null;

    10. OutputStream outputStream = null;

    11. OkHttpClient client = new OkHttpClient();

    12. Request request = new Request.Builder()

    13. .url("Https://xxxx.txt")

    14. .build();

    15. try{

    16. Response response = client.newCall(request).execute();

    17. if (response.isSuccessful()){

    18. inputStream = response.body().byteStream();

    19. long length = response.body().contentLength();

    20. //将文件下载到file路径下

    21. outputStream = new FileOutputStream(file);

    22. byte data[] = new byte[1024];

    23. subscriber.onNext("0%");

    24. long total = 0;

    25. int count;

    26. while ((count = inputStream.read(data)) != -1){

    27. total += count;

    28. // 返回当前实时进度

    29. subscriber.onNext(String.valueOf(total*100/length) + "%");

    30. outputStream.write(data,0,count);

    31. }

    32. outputStream.flush();

    33. outputStream.close();

    34. inputStream.close();

    35. }

    36. }catch (IOException e){

    37. //告诉订阅者错误信息

    38. subscriber.onError(e);

    39. }finally {

    40. if (inputStream != null){

    41. try{

    42. inputStream.close();

    43. }catch (IOException e){}

    44. }

    45. if (outputStream != null){

    46. try {

    47. outputStream.close();

    48. }catch (IOException e){}

    49. }

    50. }

    51. //告诉订阅者请求数据结束

    52. subscriber.onCompleted();

    53. });

    事件源写好后,就要开始使用RxJava订阅事件了;

     
    1. downloadobservable.subscribeOn(Schedulers.io())

    2. .onBackpressureBuffer()

    3. .observeOn(AndroidSchedulers.mainThread())

    4. .subscribe(new Subscriber<String>() {

    5. @Override

    6. public void onCompleted() {

    7. //接收到事件源的onCompleted后的操作

    8. //一般是取消downloading dialog的操作

    9. }

    10. @Override

    11. public void onError(Throwable e) {

    12. //接收事件源的错误信息操作

    13. log.e(e.toString());

    14. }

    15. @Override

    16. public void onNext(String s) {

    17. // 接受实时的下载进度

    18. log.d(s);

    19. }

    20. });

    我认为RxJava中,设计最精妙的就是 .subscribeOn() 和 .observeOn()两个操作了,它可以轻而易举的实现Android中主线程和IO线程的切换,从而让我们可以完全抛弃Handler和AsyncTask。

    下载Bitmap图片

    虽然Square专门出了一款图片处理的开源库Picasso(下一篇会介绍到),但是如果想要用OkHttp配合RxJava下载图片并现实该怎么做呢?

    还是通过RxJava的框架来实现:

     
    1. Observable<Bitmap> downloadDrawble = Observable.create(new Observable.OnSubscribe<Bitmap>() {

    2. @Override

    3. public void call(Subscriber<? super Bitmap> subscriber) {

    4. OkHttpClient client = new OkHttpClient();

    5. Request request = new Request.Builder()

    6. .url(url)

    7. .build();

    8. try {

    9. Response response = client.newCall(request).execute();

    10. if (response.isSuccessful()){

    11. InputStream input = response.body().byteStream();

    12. Bitmap bitmap = BitmapFactory.decodeStream(input);

    13. subscriber.onNext(bitmap);

    14. }

    15. }catch (IOException e){

    16. subscriber.onError(e);

    17. }

    18. subscriber.onCompleted();

    19. }

    20. });

    其实下载图片这里和上面的文件下载大致无二,只是将inputstream转换成bitmap的格式。这里没有将文件下载到本地,而是下载到内存中,应用关闭后会清除数据。事件的订阅和上文一样,可以在onNext()中更新UI,这里会回调到主线程。



         网络请求篇(下)

     

     

    我写了一些OkHttp和Volley的对比、RxJava配合OkHttp3实现文件&&图片的下载。本篇我将结合实际写一些RxJava配合Retrofit2.0+OkHttp3的网络请求库的操作;

    OkHttp3和Retrofit2.0这些都是Square的东西,所以相互支持。Retrofit2.0的底层网络请求就是OkHttp实现的;

    Retrofit2.0+RxJava+OkHttp

    Retrofit2.0相对于Retrofit1.x来说最值得期待的功能就是可以配合RxJava使用,自从去年九月份发布以后,Retrofit2.0配合RxJava的使用的教程举不胜举,例如:这一篇,下面贴上我的代码,不做过多解释。因为本篇的重点不在Retrofit2.0与RxJava的使用上,而是Retrofit配合OkHttp的拓展功能。

    Service:Retrofit用注解方式进行数据的请求,以下给出使用post上传map表单和get请求数据两种操作,Delete、Put等其它操作,请看官网详解;

     
    1. public interface CourierInfoService {

    2. @FormUrlEncoded

    3. @POST("/api/v3/login")

    4. Observable<GetData> login(@FieldMap Map<String, String> detail);

    5.  
    6. @GET("/api/v3/login")

    7. Observable<GetData> courierLogout(@Query("man_id") String courierId);

    8. }

    Retrofit2.0+OkHttp3进行配置;

     
    1. private static OkHttpClient client = new OkHttpClient

    2. .Builder()

    3. //拓展功能:网络请求的log,compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'

    4. .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))

    5. //拓展功能:数据请求的压缩,下面会解析自定义:

    6. .addInterceptor(new GzipRequsetInterceptor())

    7. .build();

    8.  
    9. private static Retrofit retrofit = new Retrofit.Builder()

    10. .baseUrl("Https://xxxxx.xxx")

    11. .client(client)

    12. //拓展添加RxJava的功能,导入的库:compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'

    13. .addCallAdapterFactory(RxJavaCallAdapterFactory.create())

    14. //使用Gson对Json进行解析,导入的库:compile 'com.squareup.retrofit2:converter-gson:2.0.2'

    15. .addConverterFactory(GsonConverterFactory.create())

    16. .build();

    使用RxJava进行订阅

     
    1. retrofit.create(Service.class)

    2. .login(detailmap)

    3. .subscribeOn(Schedulers.io())

    4. .observeOn(AndroidSchedulers.mainThread())

    5. .subscribe(new Observer<GetData>() {

    6. @Override

    7. public void onCompleted() {}

    8. @Override

    9. public void onError(Throwable e) {}

    10. @Override

    11. public void onNext(GetData getData) {}

    12. });

    Retrofit网络请求的代码就告一段落了,是不是很容易?尤其是配合RxJava的链式操作,很容易就实现了线程的切换和回调。开发者可以自行尝试一下!

    当网络请求数据量非常大、返回的Json数据比较复杂时,将Json数据解析成为JavaBean是一件非常痛苦的事情。分享一个便捷的方法:打开这个网站,将你的Json数据扔进去,然后在右边选择,Source type:JSON,Annotation style:GSON,点击Generate。网站会自动将Json数据自动解析成Java Bean文件(zip格式,解压),最后将所有的文件放进你项目合适的地方就可以使用(如果有报错的注解,删除)。

    OkHttp功能拓展

    OkHttp的实例的构建应该和Retrofit一样,使用了动态代理的模式,可以随意的定制你自己想要的功能,其是通过 .addInterceptor() 的拓展实现的。

    添加网络请求header

    在Retrofit1.x版本的时候,添加网络请求的头部需要实例化一个RequestInterceptor,然后在RequestInterceptor中addHeader(xxx,xxx),然后在插入RestAdapter实例中,实现网络请求的header添加;而在Retrofit2.x版本没有RestAdapter的配置了,所以我们要将其写在OkHttp的 .networkInterceptor()中;(官网上讲的添加@Header注解只能对某一个请求有效,下面的方法对所有请求有效。)

     
    1. client.addNetWorkInterceptor(new HeaderInterceptor())

    2. .....

    3. static class HeaderInterceptor implements Interceptor{

    4. @Override

    5. public Response intercept(Chain chain) throws IOException {

    6. Request originalRequest = chain.request();

    7. Request compressedRequest = originalRequest.newBuilder()

    8. //根据服务器的要求,自行添加IP报文的头部

    9. .addHeader("User-Agent", "SampleDemo/"+ " (android;" + android.os.Build.VERSION.RELEASE + ";" + android.os.Build.MODEL + ")")

    10. .addHeader("Content-Type", "application/x-www-form-urlencoded")

    11. .addHeader("Accept", "*/*")

    12. .build();

    13. return chain.proceed(compressedRequest);

    14. }

    15. }

    网络请求log:

    以前是看官方wiki自己写的打log的方法,但是现在有了新的选择,使用OkHttp自带的库,比官网上那个log清晰很多;
    官方wiki上给出的打log的方法:

     
    1. client.addIntercepter(new LoggingIntercepter());

    2. .......

    3. static class LoggingIntercepter implements Interceptor {

    4. @Override

    5. public Response intercept(Chain chain) throws IOException {

    6. Request request = chain.request();

    7. long t1 = System.nanoTime();

    8. log.d(String.format("Sending request %s on %s%n%s",

    9. request.url(),chain.connection(),request.headers()));

    10. Response response = chain.proceed(request);

    11. long t2 = System.nanoTime();

    12. log.d(String.format("Received response for %s in %.1fms%n%s",

    13. response.request().url(),(t2-t1)/1e6d, response.headers()));

    14. return response;

    15. }

    16. }

    但是如果你导入' com.squareup.okhttp3:logging-interceptor:3.2.0'包后,就可以直接使用log;

      client.addIntercepter(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));

    这个工具就像一个抓包工具一样,将你网络请求和接收的数据都以log的形式写打印出来;

    Gzip压缩

    如果你的服务器数据支持Gzip压缩,请使用下面的Interceptor;了解更多,请看官方wiki

     
    1. client.addInterceptor(new GzipRequestInterceptor());

    2. .......

    3. static class GzipRequsetInterceptor implements Interceptor{

    4. @Override

    5. public Response intercept(Chain chain) throws IOException {

    6. Request originalRequest = chain.request();

    7. if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {

    8. return chain.proceed(originalRequest);

    9. }

    10. Request compressedRequest = originalRequest.newBuilder()

    11. .header("Content-Encoding","gzip")

    12. .method(originalRequest.method(),gzip(originalRequest.body()))

    13. .build();

    14. return chain.proceed(compressedRequest);

    15. }

    16. private RequestBody gzip(final RequestBody body){

    17. return new RequestBody() {

    18. @Override

    19. public MediaType contentType() {

    20. log.d("gzip!");

    21. return body.contentType();

    22. }

    23. @Override

    24. public long contentLength() throws IOException {

    25. return -1;

    26. }

    27. @Override

    28. public void writeTo(BufferedSink sink) throws IOException {

    29. BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));

    30. body.writeTo(gzipSink);

    31. gzipSink.close();

    32. }

    33. };

    34. }

    35. }

    通过两篇的分析,我基本上将最近比较流行的Android中的网络请求框架写完了;OkHttp是一个很强大的网络请求的客户端,有兴趣的朋友可以深入研究一下;


    第七篇:图片处理篇

     

     

    我分为两部分基本上把最近Android网络请求最火的开源框架说完了。本篇就来讲一讲Android的图片的处理,在Android App上加载图片是很常见的,同时也是很容易造成OOM。

    如果你对加载大图、图片缓存、OOM异常等问题不太了解,请先看看郭大神这篇文章,分析的很详细;当然,本篇主要是让你学会使用Picasso和Fresco这两个图片处理库,这两个库的使用方法在网上都很多,而且都非常简单,但是今天我会说点不一样的!

    Picasso2 && OkHttp3

    我在 第六篇 网络请求篇(上) 的时候为了说OkHttp3,就示例使用OkHttp3加载图片,其实那不是很好的加载图片的方法,Square有一款专门的图片加载工具Picasso:官网地址

    Picasso的常规使用

    在官网上可以看到,Picasso的使用方法很简单:

     Picasso.with(context).load("path").into(imageView);

    通过官网上面的解析,我们还可以了解到Picasso的其它的一些用法,比如:placeholder()、resize()、centerCrop()、error()等等;都可以很简单的实现。

    同时,Picasso的内部会默认帮你实现了内存缓存,其大小为内存1/7左右,所以不做详述。因为今天我们要说的是本地缓存,如何用OkHttp3配合Picasso实现本地缓存呢?

    Picasso的本地缓存

    由于Picasso和OkHttp同属Square公司,所以,他们互相支持调用;
    我在写这篇博客之前在google了很久,stackOverflow上面的用OkHttp写缓存都比较简单,也没有比较权威的说法。于是在github上面找到了Jake Wharton大神写的关于Picasso使用OkHttp3的配置,瞬间觉得这就是权威!看源码

    这里不把全部代码贴出来,部分解析:大家需要明确一点Downloader已经实现了DiskLruCache,我们现在需要的是配合OkHttp的网络请求来重写缓存。

     
    1. public final class OkHttp3Downloader implements Downloader {

    2. private static final String PICASSO_CACHE = "picasso-cache";

    3. private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB

    4. private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB

    5.  
    6. //新建一个默认的缓存文件夹

    7. //可以在你手机中/android/data/yourapp/picasso-cache文件夹中可以找到

    8. private static File createDefaultCacheDir(Context context) {

    9. File cache = new

    10. File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);

    11. if (!cache.exists()) {

    12. //noinspection ResultOfMethodCallIgnored

    13. cache.mkdirs();

    14. }

    15. return cache;

    16. }

    17.  
    18. //计算缓存文件的大小,

    19. private static long calculateDiskCacheSize(File dir) {

    20. long size = MIN_DISK_CACHE_SIZE;

    21. try {

    22. StatFs statFs = new StatFs(dir.getAbsolutePath());

    23. long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize();

    24. // Target 2% of the total space.

    25. size = available / 50;

    26. } catch (IllegalArgumentException ignored) {}

    27.  
    28. // Bound inside min/max size for disk cache.

    29. return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);

    30.  
    31. }

    32.  
    33. ......

    34.  
    35. //网络请求时的本地缓存处理,如何没有网络且有缓存,则从缓存中读取;

    36. @Override public Response load(Uri uri, int networkPolicy) throws IOException {

    37. CacheControl cacheControl = null;

    38. if (networkPolicy != 0) {

    39. if (NetworkPolicy.isOfflineOnly(networkPolicy)) {

    40. cacheControl = CacheControl.FORCE_CACHE;

    41. } else {

    42. CacheControl.Builder builder = new CacheControl.Builder();

    43. if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {

    44. builder.noCache();

    45. }

    46. if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {

    47. builder.noStore();

    48. }

    49. cacheControl = builder.build();

    50. }

    51. }

    52.  
    53. Request.Builder builder = new Request.Builder().url(uri.toString());

    54. if (cacheControl != null) {

    55. builder.cacheControl(cacheControl);

    56. }

    57. okhttp3.Response response = client.newCall(builder.build()).execute();

    58. int responseCode = response.code();

    59. if (responseCode >= 300) {

    60. response.body().close();

    61. throw new ResponseException(responseCode + " " + response.message(), networkPolicy,responseCode);

    62. }

    63. boolean fromCache = response.cacheResponse() != null;

    64. ResponseBody responseBody = response.body();

    65. return new Response(responseBody.byteStream(), fromCache, responseBody.contentLength());

    66. }

    67. //关闭App时,如果有缓存,关闭DiskLruCache;

    68. @Override public void shutdown() {

    69. if (cache != null) {

    70. try {

    71. cache.close();

    72. } catch (IOException ignored) {}

    73. }

    74. }

    75. }

    通过上面的代码已经构建出来一个OkHttp3downloader了,现在只需要将它加载到Picasso中去执行就好了;(注意:构建的picasso实例为一个单例,所以需要在Application中去创建这个实例,然后在调用的地方获取picasso单例即可);我是这样写的:

     
    1. Picasso picasso = new Picasso.Builder(this)

    2. .downloader(new OkHttp3Downloader(new OkHttpClient()))

    3. .build();

    4. Picasso.setSingletonInstance(picasso);

    调用的时候这样写:

    picasso.with(context).load("Path").into(imageView);

    好了,Picasso的本地缓存说完了,接下来说一说Picasso的其它的使用方法:
    说之前推荐一篇文章:使用Picasso加载带饼状进度条的图片

    Picasso的使用技巧

    添加listener:当你的使用picasso加载图片失败的时候,可以通过listener把失败原因打印

     
    1. Picasso picasso = new Picasso.Builder(this)

    2. .listener(new Picasso.Listener() {

    3. @Override

    4. public void onImageLoadFailed(Picasso picasso, Uri uri, Exception exception) {

    5. //图片加载失败时的操作

    6. }

    7. })

    8. .loggingEnabled(true)// 打log

    9. .indicatorsEnabled(true)// 分辨不同的路径的价值的图片;

    10. .build();

    回调:当图片加载成功和失败时的回调;

     
    1. picasso.with(this)

    2. .load("path")

    3. .into(imageView, new Callback() {

    4. @Override

    5. public void onSuccess() { }

    6. @Override

    7. public void onError() { }

    8. });

    Picasso的相关内容就说完了,如果大家有问题请在评论中指出;

    Fresco

    Fresco是Facebook开发的一款强大的开源图片处理库,好处不多说,看官方文档,Facebook还是很贴心的,中文版也很清晰;
    使用方法(官网代码):

     
    1. //使用Facebook自定义的ImageView

    2. <com.facebook.drawee.view.SimpleDraweeView

    3. android:id="@+id/my_image_view"

    4. android:layout_width="20dp"

    5. android:layout_height="20dp"

    6. fresco:placeholderImage="@drawable/my_drawable" />

    7.  
    8. //图片加载

    9. Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/fresco-logo.png");

    10. SimpleDraweeView draweeView = (SimpleDraweeView)findViewById(R.id.my_image_view);

    11. draweeView.setImageURI(uri);

    当然我们最关心还是缓存技术,在Fresco中实现了三层缓存技术来最大限度地节省空间和CPU时间:两级内存(内存缓存和本地缓存)和一级文件(Bitmap缓存),这里不需要像上面Picasso一样来自己使用OkHttp来做本地缓存了。

    关于网络请求方面,Fresco也是支持自定义网络请求的,可以使用OkHttp,官网文档有介绍,不过我认为这里的OkHttp已经不是我们熟悉的Square的OkHttp了,Facebook应该为适配Fresco而做了一些改进。

    使用Fresco在提升性能的同时牺牲了不少空间的,可以看看这篇文章:Fresco调研与性能测试。里面结合实际例子展现出来:Fresco占用的Native Heap很大,但是占用Java Heap很小:意思就是说Fresco更多的是使用了OS的资源,而不是使用我们程序所在进程中的资源;

    Fresco要说的不多,但是它的使用方法非常多,我们以后想要将这个库应用到实际开发中,还是需要多上手实践一下;官网是最好的教程。

    最后看一篇文章,带你看看Fresco有哪些不足之处:Fresco之强大之余的痛楚

    Picasso? Fresco?

    看了上面的介绍,你是否有疑惑?两个框架同样优秀,到底该选择哪一个使用呢?两者的区别可以在stackoverflow上面找;

    下图是我分别使用Picasso和Fresco的例子,如果有兴趣的可以在Github上面把代码Clone下来看看效果:

    Picasso&&Fresco.png

    (我为什么使用Picasso?因为对Square的信仰!!!)

    总结一下:当你的App有大量的图片出现、图片的分辨率非常大的时候,请使用Fresco,因为使用其它框架很容易造成OOM;为了性能而牺牲一些空间资源是值得的;而当App中图片较少、图片的质量不是很高的时候可以使用Picasso:Fresco资源占用太多就不值得了,这时Picasso就会有很大的优势;

    当然还有其他的图片加载库,如Glide,使用方法和Picasso几乎一样,有兴趣可以了解一下。

    以上就是本篇的全部内容,如果有问题,请在评论中指出!



    作者:Torang
    链接:http://www.jianshu.com/p/9b93737bfa88
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    第八篇:数据库篇

     

     

    讲了Picssco和Fresco的图片缓存处理的相关例子,本篇将来说一说Android数据库的相关操作:SqLite和Realm

    Android的数据存储的几种方式

    • SharePreferences:轻量级的数据存储,以键值对的形式存储数据,数据存储的格式为xml
    • SQLite:Sql数据库
    • Content Provider:Android提供的数据共享的一种功能,可以获取其他App的数据
    • File文件存储:I/O文件存储;
    • 网络存储:

    上面五种方式都能够存储数据,使用情境不同,本篇介绍的是SQLite,然后再介绍一款开源数据库Realm的使用:

    SQLite

    SQLite使用的是标准的Sql语句,增、删、改、查等操作与其他数据库无异,只要熟悉sql语句,再了解一下格式SQLite操作就能正确使用SQLite;

    SQLite使用请参考Android开发的文档;文档中有很好的例子,下面解释部分;

     
    1. public class FeedReaderDbHelper extends SQLiteOpenHelper {

    2. // If you change the database schema, you must increment the database version.

    3. public static final int DATABASE_VERSION = 1;

    4. public static final String DATABASE_NAME = "FeedReader.db";

    5. public FeedReaderDbHelper(Context context) {

    6. super(context, DATABASE_NAME, null, DATABASE_VERSION);

    7. }

    8. public void onCreate(SQLiteDatabase db) {

    9. //新建表操作

    10. db.execSQL(SQL_CREATE_ENTRIES);

    11. }

    12. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    13. // 表的更新操作

    14. db.execSQL(SQL_DELETE_ENTRIES);

    15. onCreate(db);

    16. }

    17. public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    18. onUpgrade(db, oldVersion, newVersion);

    19. }

    20. }

    存入数据库

     
    1. SQLiteDatabase db = mDbHelper.getWritableDatabase();

    2. // 通过一个新建的ContentValues以键值对的方式传值

    3. ContentValues values = new ContentValues();

    4. values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);

    5. values.put(FeedEntry.COLUMN_NAME_TITLE, title);

    6. values.put(FeedEntry.COLUMN_NAME_CONTENT, content);

    7. // 插入新的一个行,返回该item的主键

    8. long newRowId;

    9. newRowId = db.insert(

    10. FeedEntry.TABLE_NAME,

    11. FeedEntry.COLUMN_NAME_NULLABLE,

    12. values);

    读取数据库

     
    1. SQLiteDatabase db = mDbHelper.getReadableDatabase();

    2. // 定义一个 projection[] ,projection是你想获取数据库的列数据

    3. String[] projection = {

    4. FeedEntry._ID,

    5. FeedEntry.COLUMN_NAME_TITLE,

    6. FeedEntry.COLUMN_NAME_UPDATED,

    7. ... };

    8.  
    9. // 数据以某种方式排序

    10. CursorString sortOrder = FeedEntry.COLUMN_NAME_UPDATED + " DESC";

    11.  
    12. Cursor c = db.query(

    13. FeedEntry.TABLE_NAME, // 查询的表名

    14. projection, // 返回的列数据

    15. selection, // 查询列要求

    16. selectionArgs, // 查询值的要求

    17. null, // 分组语句

    18. null, // 分组过滤操作

    19. sortOrder // 排序规则 );

    通过Cursor的值获取方式读取数据

     
    1. cursor.moveToFirst();

    2. long itemId = cursor.getLong(

    3. cursor.getColumnIndexOrThrow(FeedEntry._ID));

    Sqlite需要注意的

    • 一个Application维护一个数据库文件(.db文件),每个数据库文件中可以有多张表;
    • 在数据库增删改查操作用到的FeedReaderDbHelper(extends SQLiteOpenHelper)实例mDbHelper需要在Application中的OnCreat()中创建,而且全局只需要一个实例;(和前面介绍的picasso实例构造相同)
    • 数据库操作属于耗时操作,不能在主线程调用;

    Realm && RxJava

    虽然Google官方使用Sqlite来作为Android的数据库,但是看到上面的简介和代码后感觉还是觉得庞然大物,如果编程基础比较少的童鞋一定不会感觉到容易,所以现在就来说一说另外一种开源数据库Realm:官方文档

    官方文档介绍的Realm的用法非常详细,并且相对于SQLite来说创建数据表是非常简单的,所以本篇的重点不在这里。本篇主要讲一讲Realm如何配合Retrofit+RxJava使用:

    先对照看看Realm给出的例子:从Person数据表中读取出"githubUserName",然后使用retrofit做网络请求,得到GithubUser,将GithubUser的数据传入到UserViewModel中,最后通过UI显示出来;(这里只是截取RxJava的操作部分)

    看代码:

     
    1. subscription = realm.where(Person.class).isNotNull("githubUserName").findAllSortedAsync("name").asObservable()

    2. .filter(new Func1<RealmResults<Person>, Boolean>() {

    3. @Override

    4. public Boolean call(RealmResults<Person> persons) {

    5. // 过滤没有被出的数据行

    6. return persons.isLoaded();

    7. }

    8. })

    9. .flatMap(new Func1<RealmResults<Person>, Observable<Person>>() {

    10. @Override

    11. public Observable<Person> call(RealmResults<Person> persons) {

    12. // 一个一个将RealmResults<Person> 传入Observable

    13. return Observable.from(persons);

    14. }

    15. })

    16. .flatMap(new Func1<Person, Observable<GitHubUser>>() {

    17. @Override

    18. public Observable<GitHubUser> call(Person person) {

    19. // 使用Retrofit进行网络请求,得到GitHubUser类型的数据

    20. return api.user(person.getGithubUserName());

    21. }

    22. })

    23. .map(new Func1<GitHubUser, UserViewModel>() {

    24. @Override

    25. public UserViewModel call(GitHubUser gitHubUser) {

    26. //使用Map将GitHubUser数据转换成UserViewModel类型

    27. return new UserViewModel(gitHubUser.name, gitHubUser.public_repos, gitHubUser.public_gists);

    28. }

    29. })

    30. .observeOn(AndroidSchedulers.mainThread())

    31. // Retrofit put us on a worker thread. Move back to UI

    32. .subscribe(new Action1<UserViewModel>() {

    33. @Override

    34. public void call(UserViewModel user) {

    35. // 打印UI的数据

    36. TextView userView = new TextView(RetrofitExample.this);

    37. userView.setText(String.format(Locale.US, "%s : %d/%d",user.getUsername(), user.getPublicRepos(), user.getPublicGists()));

    38. container.addView(userView);

    39. }

    40. }, new Action1<Throwable>() {

    41. @Override

    42. public void call(Throwable throwable) {

    43. throwable.printStackTrace();

    44. }

    45. });

    好吧,我承认RxJava让上面代码的可读性变差了,但是这里的RxJava链式操作写的非常漂亮,对filter、flatmap、map等几个操作符的使用的也非常到位(膜拜ing!!!),如果你了解这些操作符,那么你就会喜欢上RxJava这种编程方式。如果不了解RxJava操作符的童鞋可以先看看这篇文章

    Android数据库是比较轻量级的,数据存储量不宜过大、创建的数据表不宜太复杂。我们常用的操作在官网上全部可以找得到,这里算是给大家一个引子。当然Square公司的sqlbrite也是一个不错的选择;

    至于上面说的两个数据库,大家可以自行选择使用:

    • SQLite是官方推荐的数据库,而且里面很多“坑”也已经被开发者们踩过了,开发过程中遇到的任何问题应该都可以找到解决方法;但是SQLite的使用的方法非常麻烦!
    • 虽然目前Realm还是beta版本,但是使用简单,功能也越发强大,并且基本上完全满足我们需求,同时它还能够结合很多流行的开源库来使用,写起来还是很“酷”的;

    如果上面有不正确的地方,请大家指正!



    第九篇:开源资源篇

     

     

    简单讲了SQLite的使用方法和开源数据库Realm配合RxJava的使用方法;本篇我主要总结上面用到的开源库,让大家避免重复造轮子;

    开源项目:阅读别人的代码是一种更好学习的方式

    • gank.io:每天提供技术干货的App;
    • coding:Coding官方客户端;
    • u2020:Jake Whart大神写的App,必须支持!

    这里查看更多的Github 上的Android优秀开源项目;

    开源库:避免重复造轮子

    • RxJava:RxJava 是一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库, 简单来说它就是一个实现异步操作的库, RxJava 的优点在于一个词 "简洁",配合RxJava的操作符使用,让开发更加高效;
    • Retrofit:目前最流行的HTTP请求库
    • OkHttp:Square的另一网络请求库
    • PicassoFresco/glide:图片加载库(square/facebook/google)
    • Realm-Java:专为移动平台打造的数据库
    • json在线解析:将json快速解析成为Java Bean
    • ButterKnife:强大的View注入框架,免去很多findviewbyid的操作,配合Android Studio的插件使用效果更好;
    • EventBus/Otto/RxBus:三者都是事件管理总线,让消息传递过程中逻辑代码与View层高度解耦;建议使用RxBus,毕竟是RxJava拓展;
    • leakcanary:内存泄漏检测;

    几个关于UI的网站:个人开发者的福利

    我认为较好的资源已经全部放在上面了,欢迎大家补充!


    star me on Github
    本篇结束以后,我们就基本上可以独自开发一些Android的小型的App了,接下来的两篇我会介绍一些比较进阶性的开发,如Android自定义控件和跨进程通信;


     

     

    高阶篇:
    第十篇:自定义控件篇

     

    我整理了一些开源的资源供大家开发参考,至此,也就完成了Android App开发的基本流程;本篇我就结合实际来谈一谈Android的自定义控件的流程;

    自定义控件是每个Android开发人员都需要经历的一个过程,在实际开发过程中,需要自定义控件的地方很多,很多自定义控件也往往伴随着动画的使用,今天就和大家一起做一个loading的自定义控件,如下图;

    screenshot.gif

    好吧,这个loading的控件是我在github上找:看源代码

    先说说概念

     为什么要自定义控件?

    • 提升用户交互体验;
    • 搭配App的整体风格;
    • 优化布局;
    • ......

     自定义控件分为两种:

    1. 继承自View;
    2. 继承自TextView、ImageView等现成的控件;

    第一种需要我们从无到有来实现我们想要的控件,第二种只需要在原有的控件功能的基础上来拓展一些功能;

    再说说流程

    自定义控价的基本流程:

    1. 自定义属性的声明(类似于xml中的android:width)
      • 在res/values/attrs.xml定义声明
      • 在xml文件中使用或者在view构造函数中获取
    2. 实现onMeasure(),控件的测量
      • MeasureSpec.getMode(xxx)和MeasureSpec.getSize(xxx),获取用户从xml中设置的控件的大小;(Mode的三种模式,EXACTLY, AT_MOST, UNSPECIFIED)
      • setMeasuredDimension
      • requestLayout()
    3. 实现onLayout(),控件的布局的位置(只在ViewGroup中使用)
      • 布局子View的位置
    4. 实现onDraw()方法,控件的绘制
      • 绘制内容
      • 使用Canvas api中的一些方法
      • 当发生改变时,重新绘制invalidate() postInvalidate()
    5. onTachEvent()/onInterceptTouchEvent(ViewGroup)
      • 用户交互实现
      • 触摸事件、点击事件、多指触控等
      • viewGroup中拦截子View事件拦截

    Code Time

    按照上面的基本流程,我们就来实现一下loading的自定义。
    通过分析:我们发现要实现上面的loading需要分为两个步骤:

    1. 自定义小球,实现一个loading的小球
    2. 取5个第一步中自定义出来的小球,水平排列起来,然后再进行小球的动画渲染

    自定义小球:就是我们自定义View的第一种,extends View
    1、自定义属性:
    分析:我们的小球只需要改变颜色,则在attrs.xml中将颜色属性定义出来;

     
    1. <resources>

    2. <declare-styleable name="SingleBall">

    3. <attr name="single_ball_color" format="color"/>

    4. </declare-styleable>

    5. </resources>

    2、自定义View:

     
    1. public class SingleBall extends View {

    2.  
    3. private int loadingColor = Color.BLUE;

    4. ......

    5. // 初始化View,应用自定义属性

    6. private void initView(AttributeSet attrs){

    7. if (null != attrs){

    8. // 在这里获取我们自定义的各个属性

    9. TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SingleBall);

    10. loadingColor = typedArray.getColor(R.styleable.SingleBall_single_ball_color,Color.BLUE);

    11. typedArray.recycle();

    12. }

    13. //初始化画笔,设置颜色,类型等

    14. paint = new Paint();

    15. paint.setColor(loadingColor);

    16. paint.setStyle(Paint.Style.FILL);

    17. //这个函数是为了防止边缘锯齿

    18. paint.setAntiAlias(true);

    19. }

    20.  
    21. // 由于只要关注小球的大小,而且在xml中指定就可以,

    22. // 所以不需要重写onMeasure方法

    23. @Override

    24. protected void onSizeChanged(int w, int h, int oldw, int oldh) {

    25. super.onSizeChanged(w, h, oldw, oldh);

    26. width = w;

    27. height = h;

    28. }

    29. // 用canvas画一个圆

    30. @Override

    31. protected void onDraw(Canvas canvas) {

    32. super.onDraw(canvas);

    33. canvas.drawCircle(width/2, height/2, width/2, paint);

    34. }

    35. //设置小球的颜色

    36. public void setLoadingColor(int color){

    37. loadingColor = color;

    38. paint.setColor(color);

    39. postInvalidate();

    40. }

    41. public int getLoadingColor(){

    42. return loadingColor;

    43. }

    44. }

    实现loading
    1、xml布局:将五个小球布局到LinearLayout中;

     
    1. <LinearLayout

    2. xmlns:android="http://schemas.android.com/apk/res/android"

    3. xmlns:app="http://schemas.android.com/apk/res-auto"

    4. android:orientation="horizontal"

    5. android:layout_width="match_parent"

    6. android:layout_height="match_parent">

    7. <com.toryang.sampledemo.loading.SingleBall

    8. android:id="@+id/ball_one"

    9. android:layout_width="20dp"

    10. android:layout_height="20dp"

    11. android:layout_gravity="center"

    12. android:layout_marginLeft="50dp"

    13. //我们自定义的属性

    14. app:single_ball_color="#333"/>

    15. ......

    16. ......

    17. <com.toryang.sampledemo.loading.SingleBall

    18. android:id="@+id/ball_five"

    19. android:layout_width="20dp"

    20. android:layout_height="20dp"

    21. android:layout_gravity="center"

    22. android:layout_marginRight="50dp"/>

    23. </LinearLayout>

    2、自定义LinearLayout:在LinearLayout的基础上拓展了小球的动画效果

     
    1. public class MyLoading extends LinearLayout {

    2.  
    3. private boolean isStart = false;

    4. public MyLoading(Context context) {

    5. super(context);

    6. initView(context);

    7. }

    8. ......

    9. private void initView(Context context) {

    10. LayoutInflater.from(context).inflate(R.layout.single_ball_loading, this, true);

    11. }

    12. // inflate方法执行结束后执行这个方法

    13. @Override

    14. protected void onFinishInflate() {

    15. super.onFinishInflate();

    16. cradleBallOne = (SingleBall) findViewById(R.id.ball_one);

    17. cradleBallTwo = (SingleBall) findViewById(R.id.ball_two);

    18. cradleBallThree = (SingleBall) findViewById(R.id.ball_three);

    19. cradleBallFour = (SingleBall) findViewById(R.id.ball_four);

    20. cradleBallFive = (SingleBall) findViewById(R.id.ball_five);

    21. initAnim();

    22. }

    23. RotateAnimation rotateLeftAnimation;//旋转动画效果

    24. RotateAnimation rotateRightAnimation;

    25. TranslateAnimation shakeLeftAnimation;// 位移动画效果

    26. TranslateAnimation shakeRightAnimation;

    27.  
    28. //部分代码:初始化动画效果

    29. //动画:左右两边的小球会向上旋转30度,中间的小球会有小幅度的水平位移震动

    30. private void initAnim() {

    31. rotateRightAnimation = new RotateAnimation(0, -DEGREE, RotateAnimation.RELATIVE_TO_SELF, PIVOT_X, RotateAnimation.RELATIVE_TO_SELF, PIVOT_Y);

    32. rotateRightAnimation.setRepeatCount(1);

    33. rotateRightAnimation.setRepeatMode(Animation.REVERSE);

    34. rotateRightAnimation.setDuration(DURATION);

    35. rotateRightAnimation.setInterpolator(new LinearInterpolator());

    36. rotateRightAnimation.setAnimationListener(new Animation.AnimationListener() {

    37. @Override

    38. public void onAnimationStart(Animation animation) {

    39. }

    40. @Override

    41. public void onAnimationEnd(Animation animation) {

    42. if (isStart)

    43. startRightAnim();

    44. }

    45. @Override

    46. public void onAnimationRepeat(Animation animation) {

    47. }

    48. });

    49. shakeLeftAnimation = new TranslateAnimation(0, SHAKE_DISTANCE, 0, 0);

    50. shakeLeftAnimation.setDuration(DURATION);

    51. shakeLeftAnimation.setInterpolator(new CycleInterpolator(2));

    52. ......

    53. ......

    54.  
    55. }

    56. private void startRightAnim() {

    57. cradleBallTwo.startAnimation(shakeRightAnimation);

    58. cradleBallThree.startAnimation(shakeRightAnimation);

    59. cradleBallFour.startAnimation(shakeRightAnimation);

    60. }

    61. //动画开始

    62. public void start() {

    63. if (!isStart) {

    64. isStart = true;

    65. startLeftAnim();

    66. }

    67. }

    68. //动画停止

    69. public void stop() {

    70. isStart = false;

    71. cradleBallOne.clearAnimation();

    72. cradleBallTwo.clearAnimation();

    73. cradleBallThree.clearAnimation();

    74. cradleBallFour.clearAnimation();

    75. cradleBallFive.clearAnimation();

    76. public boolean isStart() {

    77. return isStart;

    78. }

    79. public void setLoadingColor(int color) {

    80. cradleBallOne.setLoadingColor(color);

    81. cradleBallTwo.setLoadingColor(color);

    82. cradleBallThree.setLoadingColor(color);

    83. cradleBallFour.setLoadingColor(color);

    84. cradleBallFive.setLoadingColor(color);

    85. }

    86. }

    Android SDK为我们提供了四种动画效果:

    • AlphaAnimation 透明度动画效果
    • ScaleAnimation 缩放动画效果
    • TranslateAnimation 位移动画效果
    • RotateAnimation 旋转动画效果

    我们实现loading的时候用到了位移动画效果和旋转动画效果两种,由于动画不是本篇的重点,所以这里就不深入介绍了。如果有兴趣可以自行google。

    使用Myloading
    Xml:

     
    1. <com.toryang.sampledemo.loading.MyLoading

    2. android:id="@+id/loading"

    3. android:layout_width="wrap_content"

    4. android:layout_height="40dp"

    5. android:layout_centerInParent="true"/>

    Java:

      (MyLoading)findViewById(R.id.loading).start()/.stop()

    至此,自定义loading控件结束!


    其实自定义控件并不是我们所认为的那么难,只是我们不了解而已!如果多练习几次,自定义控件也就变的很容易了。大家也可以看看其他的自定义控件的文章如:自定义view之仿QQ健康ArcProgressbar

    如果发现有错误的地方,大家可以在评论中指出!



    第十一篇:跨进程篇

     

    我为大家介绍了Android开发中自定义控件,同时也讲解了Github 上的一个开源loading的实现,本篇就来说一下Android的跨进程通信;

    说起Android的跨进程通信,我们通常会听到IPC、Binder和AIDL这些名词,但是这些都代表什么意思呢?今天我们就主要来说一说跨进程的概念。

    IPC(进程间通信)

    大家都知道,Android系统是Linux内核,在Linux系统中为了维护系统的稳定,采用了进程间隔离的原则,即各个进程之间是互相独立,互不影响的。在Android系统中,也充分利用了这一原则,不允许将某个App应用程序的进程与系统的进程绑定,从而提高了Android系统的稳定性;而不同进程之间相互配合、相互支持、相互通信,就要依赖于IPC(进程间通信);

    Linux 系统为进程间通信提供了很多方法,具体的看这里,常用的有:

    • 管道(Pipe)及命名管道(named pipe)
    • 信号(Signal)
    • 消息队列(Message)
    • 共享内存
    • 信号量(semaphore)
    • 套接字(Socket)
    • ......

    但是Android系统作为嵌入式移动操作系统,其通信相对于Linux还有一定的特殊性:如内存受限、权限问题等。针对Android平台的特殊性,上面Linux IPC方法在这里并不适用,即不能完全复用。所以,在Android平台推出其特有的IPC--Binder。

    Binder

    上面提到Binder是Android平台特有的IPC机制,其实Binder就是对IPC的具体实现和具体实行;

    Binder通信采用的是client/server通信结构,Binder的架构分为:client、server、Service Manager和Binder驱动程序。

    看一张我盗来的图:

    Binder架构.png

    解释一下上图:处于用户态的client和server之间的通信是由Binder驱动程序和Service Manager两个组件协助,这两个组件Android系统已经实现好并运行在系统内核中,开发者只需要按照框架规范实现client和server接口来进行Android 跨进程;

    IPC的过程:Client通过Binder驱动程序向Service Manager发送请求,在Service Manager查找系统中已经注册了的Service,如果和Client的请求相匹配,给Client返回Binder对象,请求结束。通信开始建立;

    AIDL

    上面提到,开发者需要按照规范实现client和server接口来进行跨进程通信,这里实现的接口就要用到我们AIDL(接口定义语言):它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能;
    举一个栗子,在Service中写一个加法的方法,然后在Client中调用方法。Service和Client不在同一进程中;看一下代码;

    写一个.aidl接口文件,MyAidl.aidl

     
    1. interface MyAidl {

    2. int add(int a, int b);

    3. }

    保存之后,Android Studio会在 generated/source/aidl/debug/<包名> 的目录下生成对应的java文件:

     
    1. public interface MyAidl extends android.os.IInterface{

    2. public int add(int a, int b);

    3. public static abstract class Stub extends android.os.Binder implements <包名>.MyAidl

    4. }

    接下来在Service实现接口并重写方法,Client中实现方法的调用即可;由于这个过程比较复杂,所以这里推荐几篇blog,写更加详细,大家可以参考一下:

    Android Service完全解析,关于服务你所需知道的一切(下)
    AIDL与Binder框架浅谈
    Android Binder 完全解析


    Android跨进程的基本概念就是这样,希望我将IPC、Binder和AIDL三个概念给大家讲解清楚了。由于以前没有实践过,所以我在本篇没有添加写代码,希望大家谅解!各位如果发现有错误的地方,欢迎在评论指出。

    展开全文
  • 安卓app图标

    2021-05-28 09:28:13
    Android开发中,通常会有这样的需求,逢年过节UI的小伙伴们都会做出节日或活动相关的APP图标让我们更换,可是每次更换都要发版实现。那么,如何在不发版的情况下动态地更换我们的图标呢?本篇文章就来讲讲动态更换...

    在Android开发中,通常会有这样的需求,逢年过节UI的小伙伴们都会做出节日或活动相关的APP图标让我们更换,可是每次更换都要发版实现。那么,如何在不发版的情况下动态地更换我们的图标呢?本篇文章就来讲讲动态更换的方法。

    原理:在Manifest文件中,使用标签为我们的启动Activity准备多个别名,拥有标签的activity指向启动Activity,每个拥有标签的Activity都可以单独设置一个icon,在程序中我们动态设置和的enabled属性来实现替换图标效果。

    步骤:

    在AndroidManifest.xml中添加标签,代码如下所示:

    android:allowBackup="true"

    android:icon="@mipmap/ic_launcher"

    android:label="@string/app_name"

    android:supportsRtl="true"

    android:theme="@style/AppTheme">

    android:name="RoundActivity"

    android:enabled="false"

    android:icon="@mipmap/ic_launcher_round"

    android:targetActivity=".MainActivity">

    这个标签需要注意的地方如下:

    (1)android:name属性可以随意起。

    (2)android:enabled属性要设为false,否则桌面会存在多个APP图标。

    (3)android:icon属性设置为不同的图标。

    (4)android:targetActivity属性要设为启动的Activity。

    (5)添加那部分来使其作为启动的Activity。

    我们在布局文件中创建两个按钮,用来切换不同的图标,代码如下:

    xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

    android:id="@+id/btn_round_icon"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:text="切换圆形图标"/>

    android:id="@+id/btn_primitive_icon"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:text="切换原始图标"/>

    在Java代码中切换和的使能状态,代码如下:

    private void setRoundIcon() {

    PackageManager packageManager = getPackageManager();

    packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() +

    ".MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager

    .DONT_KILL_APP);

    packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() +

    ".RoundActivity"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager

    .DONT_KILL_APP);

    }

    private void setPrimitiveIcon() {

    PackageManager packageManager = getPackageManager();

    packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() +

    ".RoundActivity"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED,

    PackageManager.DONT_KILL_APP);

    packageManager.setComponentEnabledSetting(new ComponentName(this, getPackageName() +

    ".MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager

    .DONT_KILL_APP);

    }

    注意setComponentEnabledSetting()方法的第3个参数有两个值供选择:1(也就是PackageManager.DONT_KILL_APP)和0。这两种参数对应两种效果:当设为1时,当切换APP图标时,会有几秒钟的延迟,并且在延迟期间不能点击图标进入APP;当设为0时,当切换APP图标时,会立刻更换,但是应用会被强制退出并被清理掉。

    存在的问题:

    当启动的的enabled属性设为disabled时,Android Studio再次编译运行会出现如下错误:

    错误提示

    需要将的enabled属性设为enabled或者将APP卸载后才能编译运行。

    在应用管理和应用详情页里App的图标一直是原来的,不会动态修改

    展开全文
  • 转:https://blog.csdn.net/luoguopeng/article/details/72832567android app目录: SDCard/Android/data/你的应用的包名/原来,只要把文件写在这里的话,可以不用动态申请权限,只要在manifest里面声明权限就ok了;...

    转:https://blog.csdn.net/luoguopeng/article/details/72832567

    android app目录: SDCard/Android/data/你的应用的包名/

    原来,只要把文件写在这里的话,可以不用动态申请权限,只要在manifest里面声明权限就ok了;

    应用程序在运行的过程中如果需要向手机上保存数据,一般是把数据保存在SDcard中的。

    大部分应用是直接在SDCard的根目录下创建一个文件夹,然后把数据保存在该文件夹中。

    这样当该应用被卸载后,这些数据还保留在SDCard中,留下了垃圾数据。

    并且在API

    6.0之后,根目录文件存储是需要用户授权的,就算你在AndroidManifest.xml中配置了存储权限,用户不授权也是写不进去了。

    SD卡读写权限:

    '''

    如果你想让你的应用被卸载后,与该应用相关的数据也清除掉,该怎么办呢?

    通过

    context.getExternalFilesDir()

    可以得到

    SDCard/Android/data/你的应用的包名/files/

    这个目录里面,一般放一些长时间保存的数据

    方法可以获取到 SDCard/Android/data/你的应用的包名/files/ 目录,一般放一些长时间保存的数据

    通过

    context.getExternalCacheDir()

    方法可以获取到

    SDCard/Android/data/你的应用包名/cache/

    这个目录里面,一般存放临时缓存数据

    如果使用上面的方法,当你的应用在被用户卸载后,SDCard/Android/data/你的应用的包名/ 这个目录下的所有文件都会被删除,不会留下垃圾信息。并且

    这个方法获得的文件存储路径适用于6.0以后系统,只要AndroidManifest.xml配置读写权限了,就不需要用户再授权了。

    正常开发中获取存储路径的方法是:

    public String getDiskCacheDir(Context context) {

    String cachePath = null;

    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())

    || !Environment.isExternalStorageRemovable()) {

    cachePath = context.getExternalCacheDir().getPath();

    } else {

    cachePath = context.getCacheDir().getPath();

    }

    return cachePath;

    }

    可以看到,当SD卡存在或者SD卡不可被移除的时候,就调用getExternalCacheDir()方法来获取缓存路径,否则就调用getCacheDir()方法来获取缓存路径。前者获取到的就是 /sdcard/Android/data//cache 这个路径,而后者获取到的是 /data/data//cache 这个路径。

    设置->应用->应用详情里面的”清除数据“与”清除缓存“选项分别对应的是data/data//和data/data//cache

    external storage (外部存储):

    Environment.getExternalStorageDirectory()SD根目录:/mnt/sdcard/ (6.0后写入需要用户授权)

    context.getExternalFilesDir(dir)路径为:/mnt/sdcard/Android/data/< package name >/files/…

    context.getExternalCacheDir()路径为:/mnt/sdcard//Android/data/< package name >/cache/…

    internal storage(内部存储):

    context.getFilesDir()路径是:/data/data/< package name >/files/…

    context.getCacheDir()路径是:/data/data/< package name >/cach/…

    展开全文
  • Android app更新适配安卓10、11版本

    千次阅读 2021-03-25 17:59:15
    Android app内部更新适配安卓10、11版本 前言 ​ App内部更新现在基本每个app中都有,由于安卓各大应用市场不统一,不像Ios那样只有一个应用商城。并且现在安卓已经更新到11版本了,中间有几个版本还需对App内部更新...
  • Android App 代码架构

    2021-06-06 08:13:46
    安卓APP代码架构搭建 搞安卓开发两年了,一直都想整理一份自己平时开发用得到的代码作为其他项目开发的基础架构,之前也整理过一份,但是碍于自己当时技术的短板,那份代码已经有点落后了,所以今天还是重新整理一份...
  • Android中,为APP设置全屏模式,主要有如下几种方式:在manifest中设置在项目中找到AndroidManifest.xml配置文件,找到Activity所在的节点,添加theme。package=...
  • javaSE以及Android和Unity3D引擎哪个能开发出安卓系统的游戏?先说一下,Android系统的开发是底层C,中间层C++/C,上层应用是JAVA,应该说,系统是C和C++开发,只有运行在其上的应用app用的java,2d的cocos2dx,3d的...
  • 独立开发一款简单的安卓app

    千次阅读 2021-01-26 21:41:27
    功能十分简单,不需要数据库连接,特别适合初学者练练手。 登入界面: 主界面: 功能界面: 代码如下 xml代码: 布局一代码: ...xml version="1.0" encoding="utf-8"?... android:layout_height="m
  • 安卓app开发基础入门

    千次阅读 2021-03-04 10:06:05
    开发安卓APP游戏,需要用哪种开发软件?之前我朋友用的eclipse +ADT,安卓毕竟是基于JAVA的,可以到google下载安卓的框架包,怎么部署,网上有教程可以自己看。不过现在比较流行的是android studio,是google自己...
  • 1. 配置文件的功能使用Android提供的Properties类可以很方便的实现(1) Properties的配置mProperty.setProperty("IP", strIP);mProperty.setProperty("Port", strPort);mIP.setText("");mPort.setText("");(2)...
  • Android Studio开发实战:从零基础到App上线》自面世以来,承蒙众多朋友的抬爱,该书一直保持不错的销量,其中第一版的出货量突破一万,第二版的出货量即将突破两万。对于一个程序员来说,这是对其劳动成果的极大...
  • Android App自动更新基本上是每个App都需具备的功能,参考网上各种资料,自己整理了下,先来看看大致的界面: 一、实现思路: 1.发布Android App时,都会生成output-metadata.json文件和对应的apk文件。(不...
  • Android的每个应用程序,都有自己的可控的目录。在Setting/Application info里面,可以看到每个应用程序,都有Clear data和Clear cache选项。具体这些目录在哪里呢?用adb连接上设备。如果是连接真实设备,需要有...
  • Android APP 渗透测试方法大全by backlion一、Android APP 渗透测试方法1.测试环境SDK : J a JDK , Android SDK。工具: 7zip, dex2jar ,jd -gui, apktool, IDA pro (6.1), ApkAnalyser , Eclipse,dexopt-...
  • Delphi 安卓App自动升级

    2021-06-07 15:17:05
    Androidapi.JNI.Support引用这个单元procedure _InstallApk(Apk: string);varLFile: JFile;LIntent: JIntent;beginLFile := TJFile.JavaClass.init(StringToJString(ExtractFilePath(Apk)), StringToJstring...
  • 最近看到一个在零基础自学Android最后能否找到工作,这个问题下的高票回答,讲述了她从开始选择Android,经过非常努力的学习和挣扎,然而最后面对当前的环境却不得不放弃。看完以后真的非常替她感觉惋惜,如果早两...
  • Android app开发小白的开发android app时需要使用各种图标,下面介绍一些常用的Android app图标的网站,可免费下载图标使用,希望对大家有所帮助 1.Google Fonts Google Fonts是谷歌提供给Android开发者的图标网站...
  • Android App设计架构

    2021-10-12 17:30:31
    Android App程序进行架构设计的原因,归根到底是为了提高生产力。通过架构设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合。这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点,提高...
  • [干货]手把手教你写一个安卓app

    万次阅读 多人点赞 2021-04-27 21:06:49
    我想大家是想写一个手机app吧,前面已经分享了在QT上如何写一个安卓蓝牙app,虽然qt可以做app但是比起Android Studio还是差很多了!这里我们介绍一种快速入门的方法来制作一款app,就算你是零基础小白没有学习过java...
  • 首先是.gradle和.idea,这两个是Android Studio生成的文件,我们暂时不需要太关心其内容。 app文件夹包含我们项目的各种资源,是我们工作的核心目录。 gradle文件夹,是gradle构建工具 gradle目录 其中...
  • Android APP开发入门教程

    千次阅读 2021-03-08 14:44:38
    这篇文章主要介绍了Android APP开发入门教程,从SDK下载、开发环境搭建、代码编写、APP打包等步骤一一讲解,非常简明的一个Android APP开发入门教程,需要的朋友可以参考下。 工作中有做过手机App项目,前端和...
  • 初次看到这个东西,可能会误人为是移动硬盘或某人的午餐盒,但实际上,这是一台PoGo3*4打印机,既然是打印机那就是用来打印的喽,可是...在以前这还是一个虽然很多手机都有但从来没人用的内置功能,相信Android App...
  • 找了半天,没有现成的在安卓上安装访问redis数据库的app,那就动手写一个试一下咯。 下载这里
  • 学习用VS2019开发安卓app(一)

    千次阅读 2021-08-18 08:48:45
    Xamarin 是一个开放源代码平台,用于通过 .NET 构建适用于 iOS、Android 和 Windows 的新式高性能应用程序。 Xamarin 是一个抽象层,可管理共享代码与基础平台代码的通信。 Xamarin 在提供便利(如内存分配和垃圾...
  • 安卓应用中,如果是偶发性bug导致APP崩溃,APP可能会直接闪退或者白屏,用户体验不太好,我们可以在application中捕获到全局异常,然后再进行处理。首先创建一个MainApplication(项目中已有自定义application的不...
  • android app运行脚本

    2021-06-03 17:14:38
    首先,您将使用Eclipse创建一个简单的Android helloworld应用程序.并在布局中添加一个按钮.这是Android开发的一个非常好的做法,你可以从http://d.android.com挖掘更多请在按钮的onclick回拨功能中尝试此代码:Button ...
  • 如何实现Android app开机自启动

    千次阅读 2021-01-18 16:24:20
    上一篇文章如何实现无界面Android app介绍了如何使app仅在后台运行服务,而这样的app常常需要同时具备开机自启动的功能,接下来讲解一下实现app开机自启动的方法。 代码实现 AndroidManifest.xml 为了实现开机自启动...
  • Android APP LOGO尺寸

    千次阅读 2021-06-02 17:19:14
    android 启动图标的尺寸大小和圆角半径大小规范720*1280 px的安卓设计界面 对应的启动图标尺寸是 96px*96px 圆角约等于18px1080*1920px的安卓界面设计 对应的启动图标尺寸是144px 144px 圆角约等于25px=============...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,095,752
精华内容 438,300
关键字:

安卓app