一、基础知识

《一》、Activity详解

1、生命周期:详解

activity类处于android.app包中,继承体系如下:
1.java.lang.Object
2.android.content.Context
3.android.app.ApplicationContext
4.android.app.Activity
生命周期图

Activity生命周期图

2、启动模式:详解

当应用运行起来后就会开启一条线程,线程中会运行一个任务栈,当Activity实例创建后就会放入任务栈中。Activity启动模式的设置在AndroidManifest.xml文件中,通过配置Activity的属性android:launchMode=""设置。

启动模式

(1). Standared模式(默认)
我们平时直接创建的Activity都是这种模式的Activity,这种模式的Activity的特点是:只要你创建了Activity实例,一旦激活该Activity,则会向任务栈中加入新创建的实例,退出Activity则会在任务栈中销毁该实例。
(2). SingleTop模式
这种模式会考虑当前要激活的Activity实例在任务栈中是否正处于栈顶,如果处于栈顶则无需重新创建新的实例,会重用已存在的实例,否则会在任务栈中创建新的实例。
(3). SingleTask模式
如果任务栈中存在该模式的Activity实例,则把栈中该实例以上的Activity实例全部移除,调用该实例的newInstance()方法重用该Activity,使该实例处於栈顶位置,否则就重新创建一个新的Activity实例。
(4). SingleInstance模式
当该模式Activity实例在任务栈中创建后,只要该实例还在任务栈中,即只要激活的是该类型的Activity,都会通过调用实例的newInstance()方法重用该Activity,此时使用的都是同一个Activity实例,它都会处于任务栈的栈顶。此模式一般用于加载较慢的,比较耗性能且不需要每次都重新创建的Activity。

3、Activity之间通信

(1)、使用Internt通信
发送:
    Intent intent = new Intent(CurrentActivity.this,OtherActivity.class);
     // 创建一个带“收件人地址”的 email
     Bundle bundle = new Bundle();//创建 email 内容
     bundle.putBoolean("boolean_key", true);// 编写内容
     bundle.putString("string_key", "string_value");
     intent.putExtra("key", bundle);// 封装 email
     startActivity(intent);// 启动新的 Activity
//或者
    Intent intent =new Intent(EX06.this,OtherActivity.class);
    intent.putExtra("boolean_key", true);
    intent.putExtra("string_key", "string_value");
    startActivity(intent);
接收:
    Intent intent = getIntent();// 收取 email
    Bundle bundle = intent.getBundleExtra("key");// 打开 email
    bundle.getBoolean("boolean_key");// 读取内容
    bundle.getString("string_key");
//或者
    Intent intent=getIntent();
    intent.getBooleanExtra("boolean_key",false);
    intent.getStringExtra("string_key");
Intent传递大数据:
1、Android开发经验之intent传递大数据
2、Stackoverflow:Passing large data to second Activity
(2)、使用SharedPreferences
SharedPreferences使用xml格式为 Android 应用提供一种永久的数据存贮方式。对于一个 Android 应用,它存贮在文件系统的 /data/ data/your_app_package_name/shared_prefs/目录下,可以被处在同一个应用中的所有 Activity 访问。Android 提供了相关的 API 来处理这些数据而不需要程序员直接操作这些文件或者考虑数据同步问题。
// 写入 SharedPreferences
    SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);
    Editor editor = preferences.edit();
    editor.putBoolean("boolean_key", true);
    editor.putString("string_key", "string_value");
    editor.commit();
// 读取 SharedPreferences
    SharedPreferences preferences = getSharedPreferences("name", MODE_PRIVATE);
    preferences.getBoolean("boolean_key", false);
    preferences.getString("string_key", "default_value");
(3)、使用SQLITE
在activityA中将数据先写入数据库,跳转到activityB后,activityB中去读取数据库相对应的内容,完成数据传递。
(4)、使用文件
和使用SQLITE方法类似,A中写文件,B中读文件。
(5)、使用IPC,进程间通信
进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。但是,系统空间却是“公共场所”,所以内核显然可以提供这样的条件。除此以外,那就是双方都可以访问的外设了。在这个意义上,两个进程当然也可以通过磁盘上的普通文件交换信息,或者通过“注册表”或其它数据库中的某些表项和记录交换信息。广义上这也是进程间通信的手段,但是一般都不把这算作“进程间通信”。
进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。进程间通信主要包括管道, 系统IPC(Inter-Process Communication,进程间通信)(包括消息队列,信号,共享存储), 套接字(SOCKET)。

管道包括三种:
1)普通管道PIPE,通常有两种限制,一是单工,只能单向传输;二是只能在父子或者兄弟进程间使用。
2)流管道s_pipe:去除了第一种限制,为半双工,可以双向传输。
3)命名管道:name_pipe,去除了第二种限制,可以在许多并不相关的进程之间进行通讯。

Android中实现IPC通信有两种方式:AIDL方式Messager方式

4、Android Binder机制,详解:Android深入浅出之Binder机制

一些文章
Android Bander设计与实现 - 设计篇
Android Binder设计与实现 - 实现篇

《二》、Service详解

来自博客园的 Android 中的 Service 全面总结

1、生命周期:详解

service生命周期图

2、Service分为本地服务(LocalService)和远程服务(RemoteService)

1、本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。主进程被Kill后,服务便会终止。

2、远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。

按使用方式可以分为以下三种:

 1、startService 启动的服务:主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService;
 2、bindService 启动的服务:该方法启动的服务可以进行通信。停止服务使用unbindService;
 3、startService 同时也 bindService 启动的服务:停止服务应同时使用stepService与unbindService

3、Service 与 Thread 的区别

很多时候,你可能会问,为什么要用 Service,而不用 Thread 呢,因为用 Thread 是很方便的,比起 Service 也方便多了,下面我详细的来解释一下。
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!

既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。

因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

《三》、Broadcast Receiver:详解总结篇

1、两种注册方式:

(1)、静态注册:在AndroidManifest.xml中用标签生命注册,并在标签内用标签设置过滤器。
<receiver android:name="myRecevice">
    //继承BroadcastReceiver,重写onReceiver方法
    <intent-filter>
        <action android:name="com.dragon.net"></action>
    //使用过滤器,接收指定action广播
    </intent-filter>
</receiver>
(2)、动态注册
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(String);   //为BroadcastReceiver指定action,使之用于接收同action的广播
registerReceiver(BroadcastReceiver,intentFilter);
//一般:在onStart中注册,onStop中取消unregisterReceiver
//指定广播目标Action:Intent intent = new Intent(actionString);
//并且可通过Intent携带消息 :intent.putExtra("msg", "hi,我通过广播发送消息了");
//发送广播消息:Context.sendBroadcast(intent);

2、广播类型:

1).Normal Broadcast:普通广播
2).System Broadcast: 系统广播
3).Ordered broadcast:有序广播
4).Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)
5).Local Broadcast:App应用内广播

《四》、Content Provider:详解

Android中的Content provider机制可支持在多个应用中存储和读取数据。这也是跨应用共享数据的方式之一,还有文件,sharePreference,SQLite数据库等方式存储共享数据库,但是ContentProvider更好的提供了数据共享接口的统一性。在android系统中,没有一个公共的内存区域,供多个应用共享存储数据。

《五》、sqlite数据库:详解

软件版本升级,数据库升级问题
Andoird的SQLiteOpenHelper类中有一个onUpgrade方法。帮助文档中只是说当数据库升级时该方法被触发。经过实践,解决了我一连串的疑问:
  1. 帮助文档里说的“数据库升级”是指什么?
    你开发了一个程序,当前是1.0版本。该程序用到了数据库。到1.1版本时,你在数据库的某个表中增加了一个字段。那么软件1.0版本用的数据库在软件1.1版本就要被升级了。
  2. 数据库升级应该注意什么?
    软件的1.0版本升级到1.1版本时,老的数据不能丢。那么在1.1版本的程序中就要有地方能够检测出来新的软件版本与老的数据库不兼容,并且能够有办法把1.0软件的数据库升级到1.1软件能够使用的数据库。换句话说,要在1.0软件的数据库的那个表中增加那个字段,并赋予这个字段默认值。
  3. 程序如何知道数据库需要升级?
    SQLiteOpenHelper类的构造函数有一个参数是int version,它的意思就是指数据库版本号。比如在软件1.0版本中,我们使用SQLiteOpenHelper访问数据库时,该参数为1,那么数据库版本号1就会写在我们的数据库中。
    到了1.1版本,我们的数据库需要发生变化,那么我们1.1版本的程序中就要使用一个大于1的整数来构造SQLiteOpenHelper类,用于访问新的数据库,比如2。
    当我们的1.1新程序读取1.0版本的老数据库时,就发现老数据库里存储的数据库版本是1,而我们新程序访问它时填的版本号为2,系统就知道数据库需要升级。
  4. 何时触发数据库升级?如何升级?
    当系统在构造SQLiteOpenHelper类的对象时,如果发现版本号不一样,就会自动调用onUpgrade函数,让你在这里对数据库进行升级。根据上述场景,在这个函数中把老版本数据库的相应表中增加字段,并给每条记录增加默认值即可。
    新版本号和老版本号都会作为onUpgrade函数的参数传进来,便于开发者知道数据库应该从哪个版本升级到哪个版本。
    升级完成后,数据库会自动存储最新的版本号为当前数据库版本号。

做Android应用,不可避免的会与SQLite打交道。随着应用的不断升级,原有的数据库结构可能已经不再适应新的功能,这时候,就需要对SQLite数据库的结构进行升级了。 SQLite提供了ALTER TABLE命令,允许用户重命名或添加新的字段到已有表中,但是不能从表中删除字段。并且只能在表的末尾添加字段,比如,为 Subscription添加两个字段:

1 ALTER TABLE Subscription ADD COLUMN Activation BLOB;
2 ALTER TABLE Subscription ADD COLUMN Key BLOB;

另外,如果遇到复杂的修改操作,比如在修改的同时,需要进行数据的转移,那么可以采取在一个事务中执行如下语句来实现修改表的需求。
  1. 将表名改为临时表

ALTER TABLE Subscription RENAME TO __temp__Subscription;

  2. 创建新表

CREATE TABLE Subscription (OrderId VARCHAR(32) PRIMARY KEY ,UserName VARCHAR(32) NOT NULL ,ProductId VARCHAR(16) NOT NULL);

  3. 导入数据

INSERT INTO Subscription SELECT OrderId, “”, ProductId FROM __temp__Subscription;
//或者
INSERT INTO Subscription() SELECT OrderId, “”, ProductId FROM __temp__Subscription;
* 注意 双引号""是用来补充原来不存在的数据的

  4. 删除临时表

DROP TABLE __temp__Subscription;

通过以上四个步骤,就可以完成旧数据库结构向新数据库结构的迁移,并且其中还可以保证数据不会应为升级而流失。
当然,如果遇到减少字段的情况,也可以通过创建临时表的方式来实现。
Android应用程序更新的时候如果数据库修改了字段需要更新数据库,并且保留原来的数据库数据:
这是原有的数据库表

CREATE_BOOK = "create table book(bookId integer primarykey,bookName text);";

然后我们增加一个字段:

CREATE_BOOK = "create table book(bookId integer primarykey,bookName text,bookContent text);";

首先我们需要把原来的数据库表重命名一下

CREATE_TEMP_BOOK = "alter table book rename to _temp_book";

然后把备份表中的数据copy到新创建的数据库表中

INSERT_DATA = "insert into book select *,' ' from _temp_book";
*注意:' '是为新加的字段插入默认值的必须加上,否则就会出错)。

然后我们把备份表干掉就行啦。

DROP_BOOK = "drop table _temp_book";

然后把数据库的版本后修改一下,再次创建数据库操作对象的时候就会自动更新(注:更新的时候第一个创建的操作数据的对象必须是可写的,也就是通过这个方法getWritableDatabase()获取的数据库操作对象)
然后在onUpgrade()方法中执行上述sql语句就OK了

public class DBservice extends SQLiteOpenHelper{

    private String CREATE_BOOK = "create table book(bookId integer primarykey,bookName text);";

    private String CREATE_TEMP_BOOK = "alter table book rename to _temp_book";

    private String INSERT_DATA = "insert into book select *,'' from _temp_book";

    private String DROP_BOOK = "drop table _temp_book";

    public DBservice(Context context, String name, CursorFactory factory,int version) {
         super(context, name, factory, version);
     }

    @Override

    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
    }

    @Override

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (newVersion) {
            case 2:
                db.execSQL(CREATE_TEMP_BOOK);
                db.execSQL(CREATE_BOOK);
                db.execSQL(INSERT_DATA);
                db.execSQL(DROP_BOOK);
                break;
        }
    }
}

《六》、其他基础知识

1、AIDL

Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。

为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。

2、Android应用Loaders:详解

二、Android多线程与多进程

1、多线程:

1)、Handler.post(r):在主线程(UI线程)中运行;
2)、Thread + Handler:Thread起子线程,处理逻辑事务,耗时操作,处理完成后由handler发送和处理消息,handler可以处理UI线程事务;
3)、Thread + Activity.runOnUiThread(Runnable);
4)、AsyncTask:doInBackground->新线程,onPreExecute/onPostExecute->主线程(UI/main线程);

2、线程同步:

1)、使用synchronized 详解

(1)、synchronized(同一个数据){} 同一个数据:就是N条线程同时访问一个数据。
(2)、public synchronized 数据返回类型方法名(){}
※同步时尽量使得同步粒度小,只在
※不要对线程安全类的所有方法都进行同步,只对那些会改变共享资源方法的进行同步。

三、Activity和Service之间通信方式

1、通过Binder方式:

2、通过接口:

3、通过BrocastReceiver方式:

4、通过AIDL方式:跨进程调用

四、Android TCP链接

1、openfire的android开发库asmack使用TCP连接,默认5222端口;

2、android消息推送:使用androidpn,基于xmpp的扩展的消息推送机制;

3、使用IBM 的MQTT协议实现push消息

五、Android之OOM

1、Android内存优化之OOM