一.android四大组件

(一)android四大组件详解
Android四大组件分别为activity、service、content provider、broadcast receiver。
1、activity
(1)一个Activity通常就是一个单独的屏幕(窗口)。
(2)Activity之间通过Intent进行通信。
(3)android应用中每一个Activity都必须要在AndroidManifest.xml配置文件中声明,否则系统将不识别也不执行该Activity。
2、service
(1)service用于在后台完成用户指定的操作。service分为两种:
(a)started(启动):当应用程序组件(如activity)调用startService()方法启动服务时,服务处于started状态。
(b)bound(绑定):当应用程序组件调用bindService()方法绑定到服务时,服务处于bound状态。
(2)startService()与bindService()区别:
(a)started service(启动服务)是由其他组件调用startService()方法启动的,这导致服务的onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。因此,服务需要在完成任务后调用stopSelf()方法停止,或者由其他组件调用stopService()方法停止。
(b)使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
(3)开发人员需要在应用程序配置文件中声明全部的service,使用标签。
(4)Service通常位于后台运行,它一般不需要与用户交互,因此Service组件没有图形用户界面。Service组件需要继承Service基类。Service组件通常用于为其他组件提供后台服务或监控其他组件的运行状态。
3、content provider
(1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。
(2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。
(3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。
(4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。
(5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。
4、broadcast receiver
(1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。
(2)广播接收者的注册有两种方法,分别是程序动态注册和AndroidManifest文件中进行静态注册。
(3)动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。

(二)android四大组件总结:

(1)4大组件的注册
4大基本组件都需要注册才能使用,每个Activity、service、Content Provider都需要在AndroidManifest文件中进行配置。AndroidManifest文件中未进行声明的activity、服务以及内容提供者将不为系统所见,从而也就不可用。而broadcast receiver广播接收者的注册分静态注册(在AndroidManifest文件中进行配置)和通过代码动态创建并以调用Context.registerReceiver()的方式注册至系统。需要注意的是在AndroidManifest文件中进行配置的广播接收者会随系统的启动而一直处于活跃状态,只要接收到感兴趣的广播就会触发(即使程序未运行)。
(2)4大组件的激活
内容提供者的激活:当接收到ContentResolver发出的请求后,内容提供者被激活。而其它三种组件activity、服务和广播接收器被一种叫做intent的异步消息所激活。
(3)4大组件的关闭
内容提供者仅在响应ContentResolver提出请求的时候激活。而一个广播接收器仅在响应广播信息的时候激活。所以,没有必要去显式的关闭这些组件。Activity关闭:可以通过调用它的finish()方法来关闭一个activity。服务关闭:对于通过startService()方法启动的服务要调用Context.stopService()方法关闭服务,使用bindService()方法启动的服务要调用Contex.unbindService()方法关闭服务。
(4)android中的任务(activity栈)
(a)任务其实就是activity的栈,它由一个或多个Activity组成,共同完成一个完整的用户体验。栈底的是启动整个任务的Activity,栈顶的是当前运行的用户可以交互的Activity,当一个activity启动另外一个的时候,新的activity就被压入栈,并成为当前运行的activity。而前一个activity仍保持在栈之中。当用户按下BACK键的时候,当前activity出栈,而前一个恢复为当前运行的activity。栈中保存的其实是对象,栈中的Activity永远不会重排,只会压入或弹出。
(b)任务中的所有activity是作为一个整体进行移动的。整个的任务(即activity栈)可以移到前台,或退至后台。
(c)Android系统是一个多任务(Multi-Task)的操作系统,可以在用手机听音乐的同时,也执行其他多个程序。每多执行一个应用程序,就会多耗费一些系统内存,当同时执行的程序过多,或是关闭的程序没有正确释放掉内存,系统就会觉得越来越慢,甚至不稳定。为了解决这个问题,Android引入了一个新的机制,即生命周期(Life Cycle)。

Android基础

  1. 请描述一下Activity 生命周期。
    答: 如下图所示。共有七个周期函数,按顺序分别是: onCreate(), onStart(), onRestart(), onResume(), onPause(),onStop(), onDestroy()。
    onCreate(): 创建Activity时调用,设置在该方法中,还以Bundle的形式提供对以前存储的任何状态的访问。
    onStart(): Activity变为在屏幕上对用户可见时调用。
    onResume(): Activity开始与用户交互时调用(无论是启动还是重新启动一个活动,该方法总是被调用。
    onPause(): Activity被暂停或收回cpu和其他资源时调用,该方法用户保护活动状态的,也是保护现场。
    onStop(): Activity被停止并转为不可见阶段及后续的生命周期事件时调用。
    onRestart(): Activity被重新启动时调用。该活动仍然在栈中,而不是启动新的Activity。
    1、完整生命周期: 即从一个Activity从出现到消失,对应的周期方法是从onCreate()到onDestroy()。
    2、可见生命周期: 当Activity处于可以用户看见的状态,但不一定能与用户交互时,将多次执行从onStart()到onStop()。
    3、前景生命周期: 当Activity处于Activity栈最顶端,能够与其他用户进行交互时,将多次执行从onResume()到onPause()。

  2. 两个Activity之间跳转时必然会执行的是哪几个方法。
    答: 两个Activity之间跳转必然会执行的是下面几个方法。
    onCreate()//在Activity生命周期开始时调用。
    onRestoreInstanceState()//用来恢复UI状态。
    onRestart()//当Activity重新启动时调用。
    onStart()//当Activity对用户即将可见时调用。
    onResume()//当Activity与用户交互时,绘制界面。
    onSaveInstanceState()//即将移出栈顶保留UI状态时调用。
    onPause()//暂停当前活动Activity,提交持久数据的改变,停止动画或其他占用GPU资源的东西,由于下一个Activity在这个方法返回之前不会resume,所以这个方法的代码执行要快。
    onStop()//Activity不再可见时调用。
    onDestroy()//Activity销毁栈时被调用的最后一个方法。

  3. 横竖屏切换时候Activity的生命周期。
    答: 1、不设置Activity的android: configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次。
    2、设置Activity的android: configChanges=“orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次。
    3、设置Activity的android: configChanges=“orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfiguration方法

  4. 如何将一个Activity设置成窗口的样式。
    答: 第一种方法,在styles.xml文件中,可以新建如下的类似Dialog的style。
    name=“Theme.FloatActivity” parent=“android:style/Theme.Dialog”
    第二种方法,在AndroidManifest.xml中在需要显示为窗口的Activity中添加如下属性:
    android: theme=“@style/Theme.FloatActivity”即可。
    也可以直接添加对应需要展示为Dialog style的Activity的属性为android: theme=“@ android: style/Theme.Dialog”。

  5. 两个Activity之间怎么传递数据?
    答: 可以在Intent对象中利用Extra来传递存储数据。
    在Intent的对象请求中,使用putExtra(“键值对的名字”,”键值对的值”);在另外一个Activity中将Intent中的请求数据取出来:
    Intent intent = getIntent();
    String value = intent.getStringExtra(“testIntent”);

  6. 怎么让在启动一个Activity是就启动一个service?
    答: 首先定义好一个service,然后在Activity的onCreate里面进行连接并bindservice或者直接startService。

  7. Activity怎么和service绑定,怎么在activity中启动自己对应的service?
    答: 1、activity能进行绑定得益于Serviece的接口。为了支持Service的绑定,实现onBind方法。
    2、Service和Activity的连接可以用ServiceConnection来实现。需要实现一个新的ServiceConnection,重现onServiceConnected和OnServiceDisconnected方法,一旦连接建立,就能得到Service实例的引用。
    3、执行绑定,调用bindService方法,传入一个选择了要绑定的Service的Intent(显示或隐式)和一个你实现了的ServiceConnection的实例

  8. 什么是Service以及描述下它的生命周期。Service有哪些启动方法,有什么区别,怎样停用Service?
    答: Android Service是运行在后台的代码,不能与用户交互,可以运行在自己的进程,也可以运行在其他应用程序进程的上下文里。需要通过某一个Activity或者Context对象来调用。Service有两个启动方法,分别是Context.startService()和Context.bindService()。如果在Service执行耗时的操作需要启动一个新线程来执行。
    Android Service只继承了onCreate(), onStart(),onDestroy()三个方法,当我们第一次启动Service时,先后调用onCreate(), onStart()这两个方法,当停止Service时,则执行onDestroy()方法时。如果Service已经启动了,当我们再次启动Service时,不会再执行onCreate()方法,而是直接执行onStart()方法。

  9. 什么时候使用Service?
    答: 比如播放多媒体的时候,用户启动了其他Activity,这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你的地理信息位置的改变等等。

  10. 请描述一下Intent 和 Intent Filter。
    答: Intent在Android中被翻译为”意图”,他是三种应用程序基本组件-Activity,Service和broadcast receiver之间相互激活的手段。在调用Intent名称时使用ComponentName也就是类的全名时为显示调用。这种方式一般用于应用程序的内部调用,因为你不一定会知道别人写的类的全名。而Intent Filter是指意图过滤,不出现在代码中,而是出现在android Manifest文件中,以的形式。(有一个例外是broadcast receiver的intent
    filter是使用Context.registerReceiver()来动态设定的,其中intent filter也是在代码中创建的)
    一个intent有action,data,category等字段。一个隐式intent为了能够被某个intent filter接收,必须通过3个测试,一个intent为了被某个组件接收,则必须通过它所有的intent filter中的一个。

  11. Intent传递数据时,可以传递哪些类型数据?
    答: intent间传送数据一般有两种常用的方法: 1、extra 2、data。
    extra可以用Intent.putExtra放入数据。新启动的Activity可用Intent.getExtras取出Bundle,然后用Bundles.getLong,getInt,getBoolean,getString等函数来取放进去的值。
    Data则是传输url。url可以是指我们熟悉的http,ftp等网络地址,也可以指content来指向ContentProvider提供的资源。Intent.setData可以放入数据,Intent.getData可以取出数据。

  12. 说说Activity,Intent,Service是什么关系 ?
    答: 一个Activity通常是一个单独的屏幕,每一个Activity都被实现为一个单独的类,这些类都是从Activity基类中继承而来的。Activity类会显示由视图控件组成的用户接口,并对视图控件的事件做出响应。
    Intent的调用是用来进行屏幕之间的切换。Intent描述应用想要做什么。Intent数据结构中两个最重要的部分是动作和动作对应的数据,一个动作对应一个动作数据。
    Service是运行在后台的代码,不能与用户交互,可以运行在自己的进程里,也可以运行在其他应用程序进程的上下文里。需要一个Activity或者其他Context对象来调用。
    Activity跳转Activity,Activity启动Service,Service打开Activity都需要Intent表明意图,以及传递参数,Intent是这些组件间信号传递的承载着。

  13. 请描述一下BroadcastReceiver。
    答: Broadcast Receiver用于接收并处理广播通知(broadcast announcements)。多数的广播是系统发起的,如地域变换、电量不足、来电短信等。程序也可以播放一个广播。程序可以有任意数量的broadcast receivers来响应它觉得重要的通知。Broadcast receiver可以通过多种方式通知用户: 启动activity、使用NotificationManager、开启背景灯、振动设备、播放声音等,最典型的是在状态栏显示一个图标,这样用户就可以点它打开看通知内容。通常我们的某个应用或系统本身在某些事件(电池电量不足、来电短信)来临时会广播一个Intent出去,我们利用注册一个broadcast
    receiver来监听这些Intent并获取Intent中的数据。

  14. 在manifest和代码中如何注册和使用 broadcast receiver 。
    答: 在android的manifest中注册

    <receiver android: name =“Receiver1”>
             <intent-filter>
                        <!----和Intent中的action对应--->
                        <actionandroid: name=“com.forrest.action.mybroadcast”/>
            </intent-filter>
     </receiver>
    

在代码中注册
1、 IntentFilter filter = new IntentFilter(“com.forrest.action.mybroadcast”);//和广播中Intent的action对应;
2、 MyBroadcastReceiver br= new MyBroadcastReceiver();
3、 registerReceiver(br, filter);

二.六大布局:

Android六大界面布局方式:
声明Android程序布局有两种方式:

  1. 使用XML文件描述界面布局;
  2. 在Java代码中通过调用方法进行控制。
    我们既可以使用任何一种声明界面布局的方式,也可以同时使用两种方式。
    使用XML文件声明有以下3个特点:
  3. 将程序的表现层和控制层分离;
  4. 在后期修改用户界面时,无须更改程序的源程序;
  5. 可通过WYSIWYG可视化工具直接看到所设计的用户界面,有利于加快界面设计的过程。
    建议尽量采用XML文件声明界面元素布局。在程序运行时动态添加界面布局会大大降低应用响应速度,但依然可以在必要时动态改变屏幕内容。
    六大界面布局方式包括: 线性布局(LinearLayout)、框架布局(FrameLayout)、表格布局(TableLayout)、相对布局(RelativeLayout)、绝对布局(AbsoluteLayout)和网格布局(GridLayout) 。
    1. LinearLayout线性布局
    LinearLayout容器中的组件一个挨一个排列,通过控制android:orientation属性,可控制各组件是横向排列还是纵向排列。
    LinearLayout的常用XML属性及相关方法
    Android基础笔记(一)-LMLPHP
    其中,gravity属性支持top, left, right, center_vertical, fill_vertical, center_horizontal, fill_horizontal, center, fill, clip_vertical, clip_horizontal。也可以同时指定多种对齐方式的组合。
    LinearLayout子元素支持的常用XML属性及方法
    Android基础笔记(一)-LMLPHP

2. TableLayout表格布局
TableLayout继承自Linearout,本质上仍然是线性布局管理器。表格布局采用行、列的形式来管理UI组件,并不需要明确地声明包含多少行、多少列,而是通过添加TableRow、其他组件来控制表格的行数和列数。

-每向TableLayout中添加一个TableRow就代表一行;

  • 每向TableRow中添加一个一个子组件就表示一列;
  • 如果直接向TableLayout添加组件,那么该组件将直接占用一行;
  • 在表格布局中,可以为单元格设置如下三种行为方式:
    Shrinkable:该列的所有单元格的宽度可以被收缩,以保证该表格能适应父容器的宽度;
    Strentchable:该列所有单元格的宽度可以被拉伸,以保证组件能完全填满表格空余空间;
    Collapsed:如果该列被设置为Collapsed,那么该列的所有单元格会被隐藏;
    TableLayout的常用XML属性及方法
    Android基础笔记(一)-LMLPHP
    3. FrameLayout帧布局
    FrameLayout直接继承自ViewGroup组件。帧布局为每个加入其中的组件创建一个空白的区域(称为一帧),每个子组件占据一帧,这些帧会根据gravity属性执行自动对齐。
    FrameLayout的常用XM了属性及方法
    Android基础笔记(一)-LMLPHP

4. RelativeLayout相对布局
RelativeLayout的XML属性及相关方法说明
Android基础笔记(一)-LMLPHP
为了控制该布局容器的各子组件的布局分布,RelativeLayout提供了一个内部类:RelativeLayout.LayoutParams。
RelativeLayout.LayoutParams里只能设为boolean的XML属性
Android基础笔记(一)-LMLPHP
RelativeLayout.LayoutParams里属性值为其他UI组件ID的XML属性*

Android基础笔记(一)-LMLPHP

5. Android 4.0新增的网格布局GridLayout
GridLayout是Android4.0增加的网格布局控件,与之前的TableLayout有些相似,它把整个容器划分为rows × columns个网格,每个网格可以放置一个组件。性能及功能都要比tablelayout好,比如GridLayout布局中的单元格可以跨越多行,而tablelayout则不行,此外,其渲染速度也比tablelayout要快。GridLayout提供了setRowCount(int)和setColumnCount(int)方法来控制该网格的行和列的数量。

GridLayout常用的XML属性和方法说明
Android基础笔记(一)-LMLPHP

为了控制GridLayout布局容器中各子组件的布局分布,GridLayout提供了一个内部类:GridLayout.LayoutParams,来控制Gridlayout布局容器中子组件的布局分布。

GridLayout.LayoutParams常用的XML属性和方法说明
Android基础笔记(一)-LMLPHP

6. AbsoluteLayout绝对布局
即Android不提供任何布局控制,而是由开发人员自己通过X坐标、Y坐标来控制组件的位置。
每个组件都可指定如下两个XML属性:
layour_x;
layout_y;
绝对布局已经过时,不应使用或少使用。
界面布局类型的选择和性能优化
首先得明确,界面布局类型的嵌套越多越深越复杂,会使布局实例化变慢,使Activity的展开时间延长。建议尽量减少布局嵌套,尽量减少创建View对象的数量。
1 . 减少布局层次,可考虑用RelativeLayout来代替LinearLayout。通过Relative的相对其他元素的位置来布局,可减少块状嵌套;
2 . 另一种减少布局层次的技巧是使用 标签来合并布局;3 . 重用布局。Android支持在XML中使用 标签, 通过指定android:layout属性来指定要包含的另一个XML布局。

三.五大存储

在Android中,可供选择的存储方式有SharedPreferences、文件存储、SQLite数据库方式、内容提供器(Content provider)和网络。
一.SharedPreferences方式
Android提供用来存储一些简单的配置信息的一种机制,例如,一些默认欢迎语、登录的用户名和密码等。其以键值对的方式存储,
使得我们可以很方便的读取和存入.
1)程序要实现的功能:
我们在Name文本框中输入wangwu,在Password文本框中输入123456,然后退出这个应用。我们在应用程序列表中找到这个应用,重新启动,可以看到其使用了前面输入的Name和Password
2)实现的代码
package com.demo;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.EditText;

public class SharedPreferencesDemo extends Activity {

public static final String SETTING_INFOS = "SETTING_Infos";
public static final String NAME = "NAME";
public static final String PASSWORD = "PASSWORD";
private EditText field_name;  //接收用户名的组件
private EditText filed_pass;  //接收密码的组件

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    //Find VIew
    field_name = (EditText) findViewById(R.id.name);  //首先获取用来输入用户名的组件
    filed_pass = (EditText) findViewById(R.id.password); //同时也需要获取输入密码的组件

    // Restore preferences
    SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //获取一个SharedPreferences对象
    String name = settings.getString(NAME, "");  //取出保存的NAME
    String password = settings.getString(PASSWORD, ""); //取出保存的PASSWORD

    //Set value
    field_name.setText(name);  //将取出来的用户名赋予field_name
    filed_pass.setText(password);  //将取出来的密码赋予filed_pass
}

@Override
protected void onStop(){
    super.onStop();

    SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //首先获取一个SharedPreferences对象
    settings.edit()
            .putString(NAME, field_name.getText().toString())
            .putString(PASSWORD, filed_pass.getText().toString())
            .commit();
} //将用户名和密码保存进去

}

SharedPreferences保存到哪里去了?
SharedPreferences是以XML的格式以文件的方式自动保存的,在DDMS中的File Explorer中展开到/data/data/package
name/shared_prefs下,以上面这个为例,可以看到一个叫做SETTING_Infos.xml的文件
注意:Preferences只能在同一个包内使用,不能在不同的包之间使用。
二.文件存储方式
在Android中,其提供了openFileInput 和 openFileOuput 方法读取设备上的文件,下面看个例子代码,具体如下所示:
String FILE_NAME = “tempfile.tmp”; //确定要操作文件的文件名
FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); //初始化
FileInputStream fis = openFileInput(FILE_NAME); //创建写入流
上述代码中两个方法只支持读取该应用目录下的文件,读取非其自身目录下的文件将会抛出异常。需要提醒的是,如果调用
FileOutputStream 时指定的文件不存在,Android 会自动创建它。另外,在默认情况下,写入的时候会覆盖原文件内容,如果想把
新写入的内容附加到原文件内容后,则可以指定其模式为Context.MODE_APPEND。

三.SQLite数据库方式
SQLite是Android所带的一个标准的数据库,它支持SQL语句,它是一个轻量级的嵌入式数据库。
1)实现的功能
在这个例子里边,我们在程序的主界面有一些按钮,通过这些按钮可以对数据库进行标准的增、删、改、查。
2)实现代码
package com.sqlite;

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

/*

  • 一个SQLiteDatabase的实例代表了一个SQLite的数据库,通过SQLiteDatabase实例的一些方法,我们可以执行SQL语句,
  • 对数据库进行增、删、查、改的操作。需要注意的是,数据库对于一个应用来说是私有的,并且在一个应用当中,数据库的名字也是惟一的。
    */

/*

  • 什么是SQLiteOpenHelper ?
  • 这个类主要生成一个数据库,并对数据库的版本进行管理。
  • 当在程序当中调用这个类的方法getWritableDatabase()或者getReadableDatabase()方法的时候,如果当时没有数据,那么Android系统就会自动生成一个数据库。
  • SQLiteOpenHelper 是一个抽象类,我们通常需要继承它,并且实现里边的3个函数,
  • onCreate(SQLiteDatabase):在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表。
    
  • onUpgrade(SQLiteDatabase, int, int):当数据库需要升级的时候,Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据表,并建立新的数据表,当然是否还需要做其他的操作,完全取决于应用的需求。
    
  • onOpen(SQLiteDatabase):这是当打开数据库时的回调函数,一般也不会用到。
    

*/

public class SQLiteDemo extends Activity {

OnClickListener listener1 = null;
OnClickListener listener2 = null;
OnClickListener listener3 = null;
OnClickListener listener4 = null;
OnClickListener listener5 = null;

Button button1;
Button button2;
Button button3;
Button button4;
Button button5;

DatabaseHelper mOpenHelper;

private static final String DATABASE_NAME = "dbForTest.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "diary";
private static final String TITLE = "title";
private static final String BODY = "body";

//建立一个内部类,主要生成一个数据库
private static class DatabaseHelper extends SQLiteOpenHelper {

    DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    //在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表。
    @Override
    public void onCreate(SQLiteDatabase db) {

        String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE
                + " text not null, " + BODY + " text not null " + ");";
        Log.i("haiyang:createDB=", sql);
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    prepareListener();
    initLayout();
    mOpenHelper = new DatabaseHelper(this);

}

private void initLayout() {
    button1 = (Button) findViewById(R.id.button1);
    button1.setOnClickListener(listener1);

    button2 = (Button) findViewById(R.id.button2);
    button2.setOnClickListener(listener2);

    button3 = (Button) findViewById(R.id.button3);
    button3.setOnClickListener(listener3);

    button4 = (Button) findViewById(R.id.button4);
    button4.setOnClickListener(listener4);

    button5 = (Button) findViewById(R.id.button5);
    button5.setOnClickListener(listener5);

}

private void prepareListener() {
    listener1 = new OnClickListener() {
        public void onClick(View v) {
            CreateTable();
        }
    };
    listener2 = new OnClickListener() {
        public void onClick(View v) {
            dropTable();
        }
    };
    listener3 = new OnClickListener() {
        public void onClick(View v) {
            insertItem();
        }
    };
    listener4 = new OnClickListener() {
        public void onClick(View v) {
            deleteItem();
        }
    };
    listener5 = new OnClickListener() {
        public void onClick(View v) {
            showItems();
        }
    };
}

/*
 * 重新建立数据表
 */
private void CreateTable() {
    //mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库,如果这个数据库还没有建立,
    //那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE
            + " text not null, " + BODY + " text not null " + ");";
    Log.i("haiyang:createDB=", sql);

    try {
        db.execSQL("DROP TABLE IF EXISTS diary");
        db.execSQL(sql);
        setTitle("数据表成功重建");
    } catch (SQLException e) {
        setTitle("数据表重建错误");
    }
}

/*
 * 删除数据表
 */
private void dropTable() {
    //mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库,如果这个数据库还没有建立,
    //那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    String sql = "drop table " + TABLE_NAME;
    try {
        db.execSQL(sql);
        setTitle("数据表成功删除:" + sql);
    } catch (SQLException e) {
        setTitle("数据表删除错误");
    }
}

/*
 * 插入两条数据
 */
private void insertItem() {
    //mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库,如果这个数据库还没有建立,
    //那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。
    SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    String sql1 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY
            + ") values('haiyang', 'android的发展真是迅速啊');";
    String sql2 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY
            + ") values('icesky', 'android的发展真是迅速啊');";
    try {
        // Log.i()会将参数内容打印到日志当中,并且打印级别是Info级别
        // Android支持5种打印级别,分别是Verbose、Debug、Info、Warning、Error,当然我们在程序当中一般用到的是Info级别
        Log.i("haiyang:sql1=", sql1);
        Log.i("haiyang:sql2=", sql2);
        db.execSQL(sql1);
        db.execSQL(sql2);
        setTitle("插入两条数据成功");
    } catch (SQLException e) {
        setTitle("插入两条数据失败");
    }
}

/*
 * 删除其中的一条数据
 */
private void deleteItem() {
    try {
        //mOpenHelper.getWritableDatabase()语句负责得到一个可写的SQLite数据库,如果这个数据库还没有建立,
        //那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        //第一个参数是数据库表名,在这里是TABLE_NAME,也就是diary。
        //第二个参数,相当于SQL语句当中的where部分,也就是描述了删除的条件。
        //如果在第二个参数当中有“?”符号,那么第三个参数中的字符串会依次替换在第二个参数当中出现的“?”符号。
        db.delete(TABLE_NAME, " title = 'haiyang'", null);
        setTitle("删除title为haiyang的一条记录");
    } catch (SQLException e) {

    }

}

/*
 * 在屏幕的title区域显示当前数据表当中的数据的条数。
 */
/*
 * Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null)语句将查询到的数据放到一个Cursor 当中。
    这个Cursor里边封装了这个数据表TABLE_NAME当中的所有条列。
    query()方法相当的有用,在这里我们简单地讲一下。
        第一个参数是数据库里边表的名字,比如在我们这个例子,表的名字就是TABLE_NAME,也就是"diary"。
        第二个字段是我们想要返回数据包含的列的信息。在这个例子当中我们想要得到的列有title、body。我们把这两个列的名字放到字符串数组里边来。
        第三个参数为selection,相当于SQL语句的where部分,如果想返回所有的数据,那么就直接置为null。
        第四个参数为selectionArgs。在selection部分,你有可能用到“?”,那么在selectionArgs定义的字符串会代替selection中的“?”。
        第五个参数为groupBy。定义查询出来的数据是否分组,如果为null则说明不用分组。
        第六个参数为having ,相当于SQL语句当中的having部分。
        第七个参数为orderBy,来描述我们期望的返回值是否需要排序,如果设置为null则说明不需要排序。
 */

private void showItems() {

    SQLiteDatabase db = mOpenHelper.getReadableDatabase();
    String col[] = { TITLE, BODY };
    //查询数据
    Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null);
    Integer num = cur.getCount();
    setTitle(Integer.toString(num) + " 条记录");
}

四.内容提供器(Content provider)方式
在Android的设计“哲学”里是鼓励开发者使用内部类的,这样不但使用方便,而且执行效率也高。
1.什么是ContentProvider
数据在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。难道两个程序之间就没有办法对于数据进行交换?解决这个问题主要靠ContentProvider。
一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,当然,中间也会涉及一些权限的问题。
下边列举一些较常见的接口,这些接口如下所示。
query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
2.什么是ContentResolver
外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用的ContentResolver实例。
ContentResolver提供的接口和ContentProvider中需要实现的接口对应,主要有以下几个。
query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
3.ContentProvider和ContentResolver中用到的Uri
在ContentProvider和ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据。
我们看下面的例子。
content://contacts/people/ 这个Uri指定的就是全部的联系人数据。
content://contacts/people/1 这个Uri指定的是ID为1的联系人的数据。
在上边两个类中用到的Uri一般由3部分组成。
第一部分是:“content://” 。
第二部分是要获得数据的一个字符串片段。
最后就是ID(如果没有指定ID,那么表示返回全部)。
由于URI通常比较长,而且有时候容易出错,且难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例如下边的代码:
Contacts.People.CONTENT_URI (联系人的URI)。
1)实现的功能
在这个例子里边,首先在系统的联系人应用当中插入一些联系人信息,然后把这些联系人的名字和电话再显示出来
2)实现方法
package com.contentProvider;

import android.app.ListActivity;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.Contacts.Phones;
import android.widget.ListAdapter;
import android.widget.SimpleCursorAdapter;

public class ContentProviderDemo extends ListActivity {

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //getContentResolver()方法得到应用的ContentResolver实例。
    // query(Phones.CONTENT_URI, null, null, null, null)。它是ContentResolver里的方法,负责查询所有联系人,并返回一个Cursor。这个方法参数比较多,每个参数的具体含义如下。
    //·  第一个参数为Uri,在这个例子里边这个Uri是联系人的Uri。
    //·  第二个参数是一个字符串的数组,数组里边的每一个字符串都是数据表中某一列的名字,它指定返回数据表中那些列的值。
    //·  第三个参数相当于SQL语句的where部分,描述哪些值是我们需要的。
    //·  第四个参数是一个字符串数组,它里边的值依次代替在第三个参数中出现的“?”符号。
    //·  第五个参数指定了排序的方式。
    Cursor c = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);
    startManagingCursor(c); //让系统来管理生成的Cursor。
    ListAdapter adapter = new SimpleCursorAdapter(
            this,
            android.R.layout.simple_list_item_2,
            c,
            new String[] { Phones.NAME, Phones.NUMBER },
            new int[] { android.R.id.text1, android.R.id.text2 });
    setListAdapter(adapter); //将ListView和SimpleCursorAdapter进行绑定。
}

}

五. 网络存储方式

1.例子介绍
通过邮政编码查询该地区的天气预报,以POST发送的方式发送请求到webservicex.net站点,访问WebService.webservicex.net站点上提供查询天气预报的服务,具体信息请参考其WSDL文档,网址为:
http://www.webservicex.net/WeatherForecast.asmx?WSDL。
输入:美国某个城市的邮政编码。
输出:该邮政编码对应城市的天气预报。
2.实现步骤如下
(1)如果需要访问外部网络,则需要在AndroidManifest.xml文件中加入如下代码申请权限许可:
<! – Permissions -->
<uses-permission Android:name=“Android.permission.INTERNET” /
(2)以HTTP POST的方式发送(注意:SERVER_URL并不是指WSDL的URL,而是服务本身的URL)。实现的代码如下所示:

private static final String SERVER_URL=“http://www.webservicex.net/WeatherForecast.asmx/GetWeatherByZipCode”; //定义需要获取的内容来源地址
HttpPost request = new HttpPost(SERVER_URL); //根据内容来源地址创建一个Http请求
// 添加一个变量
List params = new ArrayList ();
// 设置一个华盛顿区号
params.add(new BasicNameValuePair(“ZipCode”, “200120”)); //添加必须的参数
request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); //设置参数的编码
try {
HttpResponse httpResponse = new DefaultHttpClient().execute(request); //发送请求并获取反馈
// 解析返回的内容
if(httpResponse.getStatusLine().getStatusCode() != 404)
{
String result = EntityUtils.toString(httpResponse.getEntity());
Log.d(LOG_TAG, result);
}
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage());
}
代码使用Http从webservicex获取ZipCode为“200120”(美国WASHINGTON D.C)的内容

Android小知识

  1. UI中, Padding和Margin有什么区别?
    答: Padding是控件的内容相对控件的边缘的边距,而Margin是控件边缘相对于其他控件的边距。如下图所示:
  2. android本身的一些限制,比如apk包大小限制,读取大文件时的时间限。
    答:apk包大小限制不好说,有的apk为100M,还是能装到手机上。一般的apk大小为5~10M左右。读取大文件的时间应该是在main线程里面,时间限制为5秒左右。
  3. ListView如何提高其效率?
    答:1、使用分页加载,不要一次性加载所有数据。
    2、复用convertView。在getItemView中,判断converView是否为空,如果不为空,可复用。
    3、异步加载图片。Item中如果包含有webimage,那么最好异步加载。
    4、快速滑动时,不显示图片。当快速滑动列表(SCROLL_STATE_FLING),item中的图片或获取需要消耗资源的view,可以不显示出来;而处于其他两种状态(SCROLL_STATE_IDLE和SCROLL_STATE_TOUCH_SCROLL),则将那些view显示出来
10-07 14:30