Android实例剖析笔记(一)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:   开卷语       俗话说,“熟读唐诗三百首,不会作诗也会吟”。最近收集了很多Android的示例代码,从这些代码的阅读和实验中学习到很多知识,从而产生写这个系列的打算,目标就是一步步跟着实例进行动手实作,真正从“做”中体会和学习Android开发。

  开卷语 

      俗话说,熟读唐诗三百首,不会作诗也会吟。最近收集了很多Android的示例代码,从这些代码的阅读和实验中学习到很多知识,从而产生写这个系列的打算,目标就是一步步跟着实例进行动手实作,真正从中体会和学习Android开发。

  本文是这个系列的第一篇,目标是Android自带的一个范例程序:记事本,将分为四篇文章进行详细介绍。

  预备知识

      搭建开发环境,尝试编写”Hello World”,了解Android的基本概念,熟悉AndroidAPI(官方文档中都有,不赘述)

  程序截图

      先来简单了解下程序运行的效果







  程序入口点 

  类似于win32程序里的WinMain函数,Android自然也有它的程序入口点。它通过在AndroidManifest.xml文件中配置来指明,可以看到名为NotesListactivity节点下有这样一个intent-filter,actionandroid.intent.action.MAIN, Category指定为 android.intent.category.LAUNCHER,这就指明了这个activity是作为入口activity,系统查找到它后,就会创建这个activity实例来运行,若未发现就不启动(你可以把MAIN改名字试试)

   < intent-filter >
                
< action  android:name ="android.intent.action.MAIN"   />
                
< category  android:name ="android.intent.category.LAUNCHER"   />
            
</ intent-filter >

  NotesList详解

      就从入口点所在的activity(见图1)开始,可以看到这个activity最重要的功能就是显示日志列表。这个程序的日志都存放在Sqlite数据库中,因此需要读取出所有的日志记录并显示。先来看两个重要的私有数据,第一个PROJECTION字段指明了日志列表所关注的数据库中的字段(即只需要IDTitle就可以了)。

    private   static   final  String[] PROJECTION  =   new  String[] {
            Notes._ID, 
//  0
            Notes.TITLE,  //  1
    };

     第二个字段COLUMN_INDEX_TITLE指明title字段在数据表中的索引。

private   static   final   int  COLUMN_INDEX_TITLE  =   1 ;

然后就进入第一个调用的函数onCreate

        Intent intent  =  getIntent();
        
if  (intent.getData()  ==   null
        {
            intent.setData(Notes.CONTENT_URI);
        }

      因为NotesList这个activity是系统调用的,此时的intent是不带数据和操作类型的,系统只是在其中指明了目标组件是Notelist,所以这里把”content:// com.google.provider.NotePad/notes”保存到intent里面,这个URI地址指明了数据库中的数据表名(参见以后的NotePadProvider类),也就是保存日志的数据表notes

        Cursor cursor  =  managedQuery(getIntent().getData(), PROJECTION,  null null , Notes.DEFAULT_SORT_ORDER);

      然后调用managedQuery函数查询出所有的日志信息,这里第一个参数就是上面设置的” content:// com.google.provider.NotePad/notes”这个URI,即notes数据表。PROJECTION 字段指明了结果中所需要的字段,Notes.DEFAULT_SORT_ORDER 指明了结果的排序规则。实际上managedQuery并没有直接去查询数据库,而是通过Content Provider来完成实际的数据库操作,这样就实现了逻辑层和数据库层的分离。

 SimpleCursorAdapter adapter  =   new  SimpleCursorAdapter( this , R.layout.noteslist_item, cursor,
                
new  String[] { Notes.TITLE },  new   int [] { android.R.id.text1 });
        setListAdapter(adapter);

      查询出日志列表后,构造一个CursorAdapter,并将其作为List View的数据源,从而在界面上显示出日志列表。可以看到,第二个参数是R.layout.noteslist_item,打开对应的noteslist_item.xml文件,

< TextView  xmlns:android ="http://schemas.android.com/apk/res/android"
    android:id
="@android:id/text1"
    android:layout_width
="fill_parent"
    android:layout_height
="?android:attr/listPreferredItemHeight"
    android:textAppearance
="?android:attr/textAppearanceLarge"
    android:gravity
="center_vertical"
    android:paddingLeft
="5dip"
    android:singleLine
="true"
/>

  就是用来显示一条日志记录的TextView,最后两个字段指明了实际的字段映射关系,通过这个TextView来显示一条日志记录的title字段。

  处理选择日志事件

      既然有了日志列表,就自然要考虑如何处理某一条日志的单击事件,这通过重载onListItemClick方法来完成,

    @Override
    
protected   void  onListItemClick(ListView l, View v,  int  position,  long  id) {
        Uri uri 
=  ContentUris.withAppendedId(getIntent().getData(), id);
        
        String action 
=  getIntent().getAction();
        
if  (Intent.ACTION_PICK.equals(action)  ||  Intent.ACTION_GET_CONTENT.equals(action)) {
            
//  The caller is waiting for us to return a note selected by
            
//  the user.  The have clicked on one, so return it now.
            setResult(RESULT_OK,  new  Intent().setData(uri));
        } 
else  {
            
//  Launch activity to view/edit the currently selected item
            startActivity( new  Intent(Intent.ACTION_EDIT, uri));
        }
    }

     首先通过”content:// com.google.provider.NotePad/notes”和日志的id 号拼接得到选中日志的真正URI,然后创建一个新的Intent,其操作类型为Intent.ACTION_EDIT,数据域指出待编辑的日志URI(这里只分析else块)。

  Intent深度剖析

那么,上面这句startActivity(new Intent(Intent.ACTION_EDIT, uri))执行后会发生什么事情呢?这时候Android系统就跳出来接管了,它会根据intent中的信息找到对应的activity,在这里找到的是NoteEditor这个activity,然后创建这个activity的实例并运行。

那么,Android又是如何找到NoteEditor这个对应的activity的呢?这就是intent发挥作用的时刻了。

new  Intent(Intent.ACTION_EDIT, uri)

这里的Intent.ACTION_EDIT=” android.intent.action.EDIT”,另外通过设置断点,我们看下这里的uri值:

      可以看到选中的日志条目的URI是:content://com.google.provider.NotePad/notes/1。然后我们再来看下Androidmanfest.xml,其中有这个provider

< provider  android:name ="NotePadProvider"
            android:authorities
="com.google.provider.NotePad"
        
/>

      发现没有?它也有com.google.provider.NotePad,这个是content://com.google.provider.NotePad/notes/1的一部分,同时

     < activity  android:name ="NoteEditor"
            android:theme
="@android:style/Theme.Light"
            android:label
="@string/title_note"
            android:screenOrientation
="sensor"
            android:configChanges
="keyboardHidden|orientation"
        
>
            
<!--  This filter says that we can view or edit the data of
                 a single note 
-->
            
< intent-filter  android:label ="@string/resolve_edit" >
                
< action  android:name ="android.intent.action.VIEW"   />
                
< action  android:name ="android.intent.action.EDIT"   />
                
< action  android:name ="com.android.notepad.action.EDIT_NOTE"   />
                
< category  android:name ="android.intent.category.DEFAULT"   />
                
< data  android:mimeType ="vnd.android.cursor.item/vnd.google.note"   />
            
</ intent-filter >
            
<!--  This filter says that we can create a new note inside
                 of a directory of notes. 
-->
            
< intent-filter >
                
< action  android:name ="android.intent.action.INSERT"   />
                
< category  android:name ="android.intent.category.DEFAULT"   />
                
< data  android:mimeType ="vnd.android.cursor.dir/vnd.google.note"   />
            
</ intent-filter >
        
</ activity >

上面第一个intent-filter中有一个action 名为android.intent.action.EDIT,而前面我们创建的Intent也正好是Intent.ACTION_EDIT=” android.intent.action.EDIT”,想必大家已经明白是怎么回事了吧。

  下面就进入activity选择机制了:

  系统从intent中获取道uri,得到了content://com.google.provider.NotePad/notes/1,去掉开始的content:标识,得到com.google.provider.NotePad/notes/1,然后获取前面的com.google.provider.NotePad,然后就到Androidmanfest.xml中找到authoritiescom.google.provider.NotePadprovider,这个就是后面要讲的contentprovider,然后就加载这个content provider

         < provider  android:name ="NotePadProvider"
            android:authorities
="com.google.provider.NotePad"
        
/>

在这里是NotePadProvider,然后调用NotePadProvidergettype函数,并把上述URI传给这个函数,函数返回URI所对应的类型(这里返回Notes.CONTENT_ITEM_TYPE,代表一条日志记录,而CONTENT_ITEM_TYPE = " vnd.android.cursor.item/vnd.google.note ")。

   @Override
    
public  String getType(Uri uri) {
        
switch  (sUriMatcher.match(uri)) {
        
case  NOTES:
            
return  Notes.CONTENT_TYPE;
        
case  NOTE_ID:
            
return  Notes.CONTENT_ITEM_TYPE;
        
default :
            
throw   new  IllegalArgumentException( " Unknown URI  "   +  uri);
        }
}

     上面的sUriMatcher.match是用来检测uri是否能够被处理,而sUriMatcher.match(uri)返回值其实是由决定的。

        sUriMatcher  =   new  UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(NotePad.AUTHORITY, 
" notes " , NOTES);
        sUriMatcher.addURI(NotePad.AUTHORITY, 
" notes/# " , NOTE_ID);

然后系统使用获得的" vnd.android.cursor.item/vnd.google.note "”android.intent.action.EDIT”androidmanfest.xml中去找匹配的activity.

   < intent-filter  android:label ="@string/resolve_edit" >
                
< action  android:name ="android.intent.action.VIEW"   />
                
< action  android:name ="android.intent.action.EDIT"   />
                
< action  android:name ="com.android.notepad.action.EDIT_NOTE"   />
                
< category  android:name ="android.intent.category.DEFAULT"   />
                
< data  android:mimeType ="vnd.android.cursor.item/vnd.google.note"   />
            
</ intent-filter >

正好NoteEditor这个activityintent-filter满足上述条件,这样就找到了NoteEditor。于是系统加载这个类并实例化,运行,然后就到了NoteEditorOnCreate函数中(见后续文章)。

  小技巧

  1,在命令行中使用”adb shell”命令进入系统中,然后”cd app”进入应用程序所在目录,”rm XXX”就可以删除你指定的apk,从而去掉其在系统顶层界面占据的图标,若两次”cd data”则可以进入应用程序使用的数据目录,你的数据可以保存在这里,例如Notepad就是把其数据库放在它的databases目录下,名为note_pad.db.

  2,第一次启动模拟器会比较慢,但以后就别关闭模拟器了,修改代码,调试都不需要再次启动的,直接修改后rundebug就是。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
前端开发 安全 Android开发
Android实例剖析笔记(三)
  Activity的生命周期 Activity类中有许多onXXX形式的函数可以重载,比如onCreate,onStart,onStop,onPause,那么它们的调用顺序到底是如何的呢?下面就通过一个实验来进行分析。
791 0
|
监控 Android开发
Android实例剖析笔记(二)
  简介   android提供了三种菜单类型,分别为options menu,context menu,sub menu。   options menu就是通过按home键来显示,context menu需要在view上按上2s后显示。
867 0
|
Android开发 数据格式 XML
|
前端开发 安全 Android开发
|
8天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
7天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
20 5
|
5天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
7天前
|
缓存 数据库 Android开发
安卓开发中的性能优化技巧
【10月更文挑战第29天】在移动应用的海洋中,性能是船只能否破浪前行的关键。本文将深入探讨安卓开发中的性能优化策略,从代码层面到系统层面,揭示如何让应用运行得更快、更流畅。我们将以实际案例和最佳实践为灯塔,引领开发者避开性能瓶颈的暗礁。
22 3
|
9天前
|
存储 IDE 开发工具
探索Android开发之旅:从新手到专家
【10月更文挑战第26天】在这篇文章中,我们将一起踏上一段激动人心的旅程,探索如何在Android平台上从零开始,最终成为一名熟练的开发者。通过简单易懂的语言和实际代码示例,本文将引导你了解Android开发的基础知识、关键概念以及如何实现一个基本的应用程序。无论你是编程新手还是希望扩展你的技术栈,这篇文章都将为你提供价值和启发。让我们开始吧!
|
15天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
44 5
下一篇
无影云桌面