《第一行代码》扩展总结

Android更新UI的两种方法——handler与runOnUiThread()

1、Intent传递对象方式:

Serializable

Parcelable

2、全局Context获取:

Android提供了一个Application类,每当应用程序启动时,系统会自动将这个类初始化。我们可以定义个一个自己的Application,用户管理程序内的一些全局状态信息。

3、WebView控件展示网页

4、异步消息处理:

Message、Handler、MessageQueue(消息队列)、Looper

每个线程只有一个MessageQueue对象、Looper对象

通过Handler发送了消息后,该Message会被添加到MessageQueue队列中等待被处理,Looper一直尝试从MessageQueue队列取出待处理消息,分发回Handler的handleMessge()方法中。

 

在主线程创建之后会创建一个Looper对象,创建Looper对象的时候会去创建一个messageQueue,而Looper是一个轮询器,会不停的轮询messageQueue中的消息,在获取到消息之后就会把这个消息交给handler来进行处理,在主线程中创建一个handler对象,这个handler对象不仅可以获取到消息进行处理,也可以把一个消息放到消息队列中。

Message、Handler、MessageQueue、Looper之间的关系

 Android消息处理机制(Handler、Looper、MessageQueue与Message)

Android异步消息处理机制~深入理解 Looper、Handler、Message三者关系

Android Handler 异步消息处理机制的妙用~创建强大的图片加载类

 5、在Android中实现异步任务机制有两种方式,Handler和AsyncTask。

Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制。为了简化操作,Android1.5提供了工具类AsyncTask抽象类,它使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。

详解Android中AsyncTask的使用(包含源码解析)

6、Service:

ANDROID中BINDSERVICE的使用方法

Android中BindService方式使用的理解

<1>、新建一个DownloadBind内部类继承Bind类,通过DownloadBind类对象实现与活动绑定交互。

在Activity里,实例化ServiceConnection接口的实现类,重写onServiceConnected()和onServiceDisconnected()方法

绑定:

bindService(intent, conn,BIND_AUTO_CREATE);

 

BindService和Started Service都是Service,有什么地方不一样呢:

1. Started Service中使用StartService()方法来进行方法的调用,调用者和服务之间没有联系,即使调用者退出了,服务依然在进行【onCreate()-  >onStartCommand()->startService()->onDestroy()】,注意其中没有onStart(),主要是被onStartCommand()方法给取代了,onStart方法不推荐使用了。

 

2. BindService中使用bindService()方法来绑定服务,调用者和绑定者绑在一起,调用者一旦退出服务也就终止了【onCreate()->onBind()->onUnbind()->onDestroy()】。

 

bindService()启动service的生命周期和调用bindService()方法的Activity的生命周期是一致的,也就是如果Activity如果结束了,那么Service也就结束了。Service和调用bindService()方法的进程是同生共死的。好的编程习惯,都是在Activity的onStop()方法中加上unBindService(ServiceConnection conn)代码

 

<2>、前台服务

服务的优先级比较低,当内存不足时,系统会回收掉正在后台运行的服务。如果希望服务一直保持运行状态,就需要用前台服务。

譬如墨迹天气一直在通知栏展示天气情况

 

<3>、异步的自动停止的服务

服务默认运行在主线程中,如果处理的任务比较耗时,就会出现ANR(Application Not Responding)所以需要在服务的处理方法中开启一个线程。

public int onStartCommand(Intent intent,int flags, int startId){
	new Thread(new Runnable(){		
		public void run(){
			
		     //执行完毕后自动停止
		     stopSelf();
		}
	}).start();
	return super.onStartCommand(intent,flags,startId);
}

这种方法容易忘记开启线程,或者容易忘记执行stopSelf(),所以Android提供了IntentService类,继承其即可。

 

<4>、Android定时任务的两中方式:JavaAPI里的Timer类,Android的Alarm机制。

Timer方法不适合长期在后台运行的定时任务,因为手机CPU会休眠,就无法执行。

Alarm机制可以唤醒CPU。

启动服务后,定时跳转到一个广播接收器中,接收到时再跳转到服务中。

7、Handler、HandlerThread

Handler:

一般来说在子线程中执行耗时任务,当任务完成时,会返回UI线程,一般是更新UI。这时有两种方法可以达到目的。

一种是子线程handler.sendMessage发一个消息主线程接收,然后更新UI

另一种是handler.post(r)。r是要执行的任务代码。意思就是说r的代码实际是在UI线程执行的。可以写更新UI的代码。(子线程是不能更新UI的)   

  

handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程), 也就是说Handler对象初始化后,就默认与对它初始化的进程的消息队列绑定。

handler.post(r)同一个线程的疑惑

Handler handler = new Handler(); 

/**该方法的内部类将在handler.sendMessage(msg)后执行
Handler handler = new Handler(){ 	
	@Override
	public void handleMessage(Message msg){ 	
		System.out.println("msg:"+msg.arg1); 
	} 
};*/

  

HandlerThread

Android HandlerThread 完全解析

HandlerThread三种不同的传值方式1

HandlerThread三种不同的传值方式2

HandlerThread三种不同的传值方式3

Android中Handler的使用,一般都在UI主线程中执行,因此在Handler接收消息后,处理消息时,不能做一些很耗时的操作,否则将出现ANR错误。Android中专门提供了HandlerThread类,来解决该类问题。

HandlerThread类继承自Thread,专门处理Hanlder的消息,依次从Handler的队列中获取信息,逐个进行处理,保证安全,不会出现混乱引发的异常。HandlerThread继承于Thread,所以它本质就是个Thread。与普通Thread的差别就在于,它有个Looper成员变量。

首先Handler和HandlerThread的主要区别是:Handler与Activity在同一个线程中,HandlerThread与Activity不在同一个线程,而是在新的线程中(Handler中不能做耗时的操作)。

<1>、创建一个HandlerThread,即创建了一个包含Looper的线程。

HandlerThread handlerThread = new HandlerThread("leochin.com");

handlerThread.start();

 //创建HandlerThread后一定要记得start()

<2>、获取HandlerThread的Looper

Looper looper = handlerThread.getLooper();

<3>、创建Handler,通过Looper初始化

Handler handler = new Handler(looper);

通过以上三步我们就成功创建HandlerThread。通过handler发送消息,就会在子线程中执行。

如果想让HandlerThread退出,则需要调用handlerThread.quit()。

8、通知Notification

PendingIntent延迟执行的Intent

在MainActivity里发出通知,点击时进入NoticationActivity中

NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 
Notification notification = new Notification(R.drawable.ic_launcher,"This is ticket text",System.currentTimeMillis());  
Intent intent = new Intent(this,NoticationActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);  
notification.setLatestEventInfo(this, "This is content title", "This is content text",pi);  
manager.notify(1, notification);  

 接收到通知后调用manager.cancel(1)可取消通知。

  

接收、发送、拦截短信

发送短信:

<1>、MainActivity中注册接收通知广播

为防止自定义MessageReceiver和系统默认短信程序都接收到短信,可设置自定义MessageReceiver的优先级,然后拦截短信广播即可

IntentFilter receiveFilter = new IntentFilter();
receiveFilter.addAction("android.provicer.Telephony.SMS_RECEIVED");
//receiveFilter.setPriority(100);
MessageReceiver messageReceiver = new MessageReceiver();
registerReceiver(messageReceiver,receiveFilter);

<2>、点击按钮时发送

SmsManager manager = SmsManager.getDefault();
manager.sendTextMessage(to.getText().toString(), null , msgInput,getText().toString() ,null,null);

/**
* 监控发送状态(发送时第四个参数为PendingIntent)
* SmsManager manager = SmsManager.getDefault();
* Intent sentIntent = new Intent("SEND_SMS_ACTION");
* PendingIntent pi = PendingIntent.getBroadcast(MainActivity.this,0,sentIntent,0);
* manager.sendTextMessage(to.getText().toString(), null , msgInput,getText().toString() , pi, null);
* 
* 注册时:
* IntentFilter receiveFilter = new IntentFilter();
* receiveFilter.addAction("SEND_SMS_ACTION");
* SendStatusReceiver sendStatusReceiver = new SendStatusReceiver();
* registerReceiver(sendStatusReceiver,receiveFilter);
*/

接收短信:

使用广播接收器BroadcastReceiver(自定义一个MessageReceiver)来接收短信

class MessageReceiver extends BroadcastReceiver{...}
//class SendStatusReceiver extends BroadcastReceiver{...}

调用摄像头拍照

从相册中选择图片

播放音频、视频

9、内容提供其Content Provider

为存储和获取数据提供统一的接口。可以在不同的应用程序之间共享数据。

ContentProvider和Uri详解

Content Provider应用实例

URI:

权限(authority/包名) + 路径(path/表名)

标准URI:

content://包名/表名

字符串转URI

Uri uri = Uri.parse(标准URI)

匹配内容URI:

UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

uriMatcher.addURI("权限","路径","自定义代码");

uriMatcher.match(uri):返回自定义代码

 

读取系统联系人

getContentResolver().query()..

 

创建自己的内容提供器

新建类继承ContentProvider即可,注意需要在AndroidMainTest.xml中注册

 

getType():

获取URI对象对应的MIME类型

 

URI对应的MIME字符串有三部分组成:

<1>、必须以vnd开头

<2>、如果URI以路径结尾,后接android.cursor.dir/;如果URI以ID结果,后接android.cursor.item/

<3>、最后接上 vnd.权限.路径

例:

URI: content://com.example.app.provider/table1

MIME为:vnd.android.cursor.dir/vnd.com.example.app.provider.table1

 

URI:content://com.example.app.provider/table1/1

MIME为:vnd.android.cursor.item/vnd.com.example.app.provider.table1

 

10、数据存储

<1>、文件存储

存储到或读取SdCard上文件

Android数据的四种存储方式

Android数据存储实现的5大方式

存储:openFileOutput("文件名","操作模式")

默认存储在/data/data/<package name>/files/目录下

读取:openFileInput("文件名")

new Scanner方式和new BufferReader方式

自动到/data/data/<package name>/files/目录下读取文件

<2>、SharedPreference存储

键值对存储

a、调用SharedPreference对象的edit()方法获取一个SharedPreference.Editor对象

b、向SharedPreference.Editor对象中添加数据 putString..putInt..

c、commit()提交

读取:SharedPreference对象提供的getString..getInt..

范例:记住密码功能

适用范围:保存少量的数据,且这些数据的格式非常简单:字符串型、基本类型的值。比如应用程序的各种配置信息(如是否打开音效、是否使用震动效果、小游戏的玩家积分等)等

<3>、数据库存储

自定义类继承SQLiteOpenHelper抽象类

数据库文件默认存储在/data/data/<package name>/databases/目录下

调用getReadableDatabase()或getWritableDatabase()可以创建或打开一个数据库

查询时别忘了调用cursor的close()方法释放数据库连接

范例:升级数据库的最佳写法

11、广播Broadcast

Android总结篇系列:Android广播机制

Android中的广播Broadcast详解

Android入门:广播发送者与广播接收者

标准广播

没有先后顺序,所有广播接收器几乎都在同一时刻接收到该广播消息

有序广播

有先后顺序,同一时刻只有一个广播接收器可收到该消息

 

发送标准广播

Intent intent = new Intent("com.example.MY_BROADCAST");

sendBroadcast(intent);

 

发送有序广播

先后顺序判定标准遵循为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。

Intent intent = new Intent("com.example.MY_BROADCAST");

sendOrderedBroadcast(intent,null);

创建广播接收器

自定义类继承BroadcastReceiver抽象类

默认情况下,广播接收器也是运行在UI线程,因此,onReceive方法中不能执行太耗时的操作。否则将因此ANR。一般情况下,根据实际业务需求,onReceive方法中都会涉及到与其他组件之间的交互,如发送Notification、启动service等。

当广播接收者onReceive方法需要执行很长时间时,最好将此耗时工作通过Intent发送给Service,由Service完成,并且不能使用子线程解决,因为BroadcastReceiver是接收到广播后才创建的,并且生命周期很短,因此子线程可能在没有执行完就已经被杀死了。

 

截断广播

在广播接收器的onReceive()中使用abortBroadcast()

 

注册广播方式

动态注册:在代码里注册

必须程序启动才能接收广播

IntentFilter intentFilter = new IntentFilter();

intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); //网路发生变化时,系统发出的值为该Action的系统广播

NetworkChangeReceiver receiver = new NetworkChangeReceiver();//自定义的广播接收器类

registerReceiver(receiver,intentFilter);

 

注意在onDestory()方法中取消注册:unregisterReceiver(receiver);

 

静态注册:在AndroidManifest.xml里注册

程序不启动也可接收广播

<receiver android:name=".NetworkChangeReceiver">
	<intent-filter android:priority="100">  <!-- 有序广播需要加该参数-->
		<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
	</intent-filter>
</receiver>
  

使用本地广播

使用LocalBroadcastManager来对广播进行管理

对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。

 

LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);

manager.sendBroadcast(intent);

manager.unregisterReceiver(receiver);

..

  

范例:广播最佳实践-实现强制下线功能

思路:在界面上弹出一个对话框,让用户无法进行其他操作,必须点击对话框中的确定按钮,然后回到登录界面即可

在LoginActivity登录后,跳转到登录成功主界面MainActivity,在MainActivity中点击按钮发出强制下线广播,跳转到登录界面

LoginActivity   -> MainActivity    ->   ForceOfflineReceiver  ->   LoginActivity

12、

=============================================================================

天气APP总结

文件夹:

activity

db

model

service

receiver

util

 

建库:

CoolWeatherOpenHelper.java -->  SQLiteOpenHelper

 

Model:

Province.java

City.java

Country.java

  

数据库操作类:

CoolWeatherDB.java

        单例模式

        saveProvince(Province province)..

        loadProvince()..

  

服务器交互:

HttpUtil.java

        通过URL取数据

        sendHttpRequest(final String address,final HttpCallbackListener listener);

        新建一个线程发送请求

 

        HttpCallbackListener

        回调服务接口,定义两个方法onFinish、onError

 

 

工具类解析数据:

Utility.java

        返回的数据格式为"代号|城市,代号|城市"

        解析数据后保存到数据库

        handleProvincesResponse(CoolWeatherDB coolWeatherDB ,String response)

        handleCitiesResponse(CoolWeatherDB coolWeatherDB ,String response ,int provinceId)

        ...

         取到天气数据后,将数据保存到本地SharedPreferences

         handleWeatherResponse(Context context , String respone)

         saveWeatherInfo(..)

 

 

选择省、市、县活动:

CoolWeatherActivity.java

        queryFromServer(final String code , final String type )

                queryFromServer(null , "province" )

                queryFromServer(selectProvince.getProvinceCode() , "city" )

        调用HttpUtil中方法取数据,取到数据后通过runOnUiThread回到主线程处理逻辑

         runOnUiThread是Activity内部的方法

 

 展示天气活动:

        WeatherActivity.java

        有县code时直接从本地取,没有时通过URL获取

        直接显示:showWeather()

 

后台自动更新天气:

AutoUpdateService.java -> Service

        在onStartCommand里启动一个线程,通过HttpUtil取到最新的天气,然后更新updateWeather(更新SharePreferences)

        启动一个定时任务,跳转到通知AutoUpdateReceiver.java

        在通知的onReceive()方法里再跳转到服务AutoUpdateService里并启动

 

猜你喜欢

转载自uule.iteye.com/blog/2308017