Android 学习札记

来源:互联网 发布:大数据分析展现工具 编辑:程序博客网 时间:2024/04/29 04:28
Activity

启动模式:使用标签android:launchMode进行设置 即android:launchMode=["multiple" | "singleTop" | "singleTask" | "singleInstance"]

standardsingleTop可以被实例化多次,属于不同的task,可位于activity栈的任何位置。一般使用方法startActivity()来启动,如果intent对象包含FLAG_ACTIVITY_NEW_TASK,在这情况下可以选择不同task。standard是默认模式,不需要配置launchMode。它们之间的区别还在于standard启动时始终都会创建一个新的实例,singleTop也会将intent发送新的实例。不过,如果activity栈顶已经有了要创建的Activity的实例,则将intent发送给该实例(通过onNewIntent() 调用),而不会创建新的实例。如果已经存在实例但不是在栈顶或者在别的task的栈顶,则会创建新的实例。singleTop模式,可用来解决栈顶多个重复相同的Activity的问题。换言之,standard可以有多个相同的实例,且可以叠加,singleTop可以有多个实例,但是不允许多个相同Activity叠加。singleTop适合用于接收通知栏启动显示页面的情况

singleTask模式和singleInstance模式在新task中创建一个实例,如果实例已经存在则将intent发送给该实例(通过onNewIntent() 调用),而不会创建新的实例。只能属于一个Task,总是位于activity栈顶,同一时间内只能保持一个实例。区别在于singleTask允许在task中存在别的activity("standard" and "singleTop"activities) 而singleInstance则独占一个task,如果启动别的activity,则会分配在另外的task里(相当于使用intent里的FLAG_ACTIVITY_NEW_TASK来启动activity,不过仍然要检查是否存在和这个activity相同taskAffinity值的Task) (另外singleInstance模式下onActivityResult将会失去作用,它的resultCode会直接返回Activity.RESULT_CANCELED)

Task

Task可以认为是一个栈,可放入多个Activity。比如启动一个应用,那么Android就创建了一个Task,然后启动这个应用的入口Activity,就是intent-filter中配置为main和launch的那个(利用该属性可实现一个APK文件部署产生多个应用安装的效果)。这个Activity是根(Root)Activity,可能会在它的界面调用其他Activity,这些Activity如果按照启动模式,intent的参数以及taskAffinity属性的不同,可能在这个Task中,也可能在另外的Task栈中。比如地图app,用户看过地图按回退键的时候,会退栈回到调用地图的Activity。对用户来说,并不觉得在操作多个应用。这就是Task的作用。所以特别地,singleTask用于一个task中共享一个Activity的场合singleInstance用于多个Task共享一个Activity的场合(最典型的场合就是不同的应用程序调用地图app的导航功能,这个地图导航界面实际上同一个activity,另外闹钟程序基本上也是基于这个模式,保证activity的独立唯一性)。standardsingleTop则适用于大多数activity的场合

taskAffinity属性

用于指定activity的Task归属,即出了它希望进入的Task,属性值相同的activity属于同一个Task,Task的taskAffinity值由这个Task的root activity的taskAffinity值决定。默认情况下app的所有activity都有相同的taskAffinity值。利用这个值可以让不同的应用程序使用相同的Task。如果设置为空字符串则activity不属于任何Task。如果不设置,则activity继承application的taskAffinity的值,默认值就是<manifest>标签的包名。

allowTaskReparenting属性

通常情况下,一个activity启动后,和它相关联的Task也就启动,并且它的生命周期将会在这个Task中完成。使用这个属性值可以强制一个activity在不可见时重新宿主到另外一个具有相同affinity值的Task中。即当一个activity的这个属性设置为true时,它进入后台后,如果有一个和它有相同taskAffinity值的Task进入前台时,它会重新宿主到该task中并显示出来。由于启动模式"singleTask" 和"singleInstance" 只能存在于一个Task中,因此重新宿主就只局限于"standard" 和"singleTop" 模式。

应用场合举例:电子邮件信息包含一个网页的链接,点击链接后显示该页面,这个页面是由browser应用定义的,但是却成为电子邮件task的一部分。所以当浏览器task处于激活状态,这个activity应该重新宿主到browser应用并显示出来,而且重新启动电子邮件时,它不应该再出现

Handler和android线程

Message

用于描述一个消息体,包含两个整型域和一个对象域,可以通过handler来发送。创建时最好使用Mesage.obtain()或Handler.obtainMessage()。

MessageQueue

底层保存Message的队列(以链表的方式串联起来的),从属于特定Looper(在Looper的构造过程中会去创建一个MessageQueue),通过Looper来调度。Message不是以直接的方式添加到MessageQueue中,而是通过和Looper相关联的Handler来实现消息进入队列。

Looper
线程的消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。默认情况下创建的线程不会自动创建Looper和MessageQueue,如果想让该线程具有消息队列和消息循环,需要调用prepare()来创建,然后运行loop(),来实现消息循环直到loop被停止(loop方法内部是个死循环)。

通过Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的 Looper对象

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. class LooperThread extends Thread {  
  2.          public Handler mHandler;  
  3.          public void run() {   
  4.             Looper.prepare();  
  5.             mHandler = new Handler() {   
  6.                           public void handleMessage(Message msg) {   
  7.                                  // process incoming messages here   
  8.                           }   
  9.              };    
  10.             Looper.loop(); //让Looper开始工作,从消息队列里取消息,处理消息  
  11.          }   
  12. }  

总结:Looper是android线程消息循环机制的封装 ,消息循环是一个很有用的线程方式,往消息队列里添加数据(通过Handler添加),然后异步的等待消息的返回。当消息队列为空的时候就会挂起线程,等待新的消息的加入。当需要子线程不定期的执行一些不需要和应用界面交互的的任务时,就需要靠Loop来让线程挂起等待消息,这种情况最好用HandlerThread来创建这个工作线程。

Handler
handler 可以发送和处理消息以及Runnable对象,每个handler实例与一个单独线程以及线程的MessageQueue关联。实际上是和线程的Looper对象关联。只要线程有Looper就可以通过handler往消息队列里加入消息。
主要用途1)定时发送message和Runnable对象2)在不同的线程中处理并执行一个action。

Handler的作用就是把消息加入Looper的MessageQueue中,并分发和处理该消息队列中的消息。构造Handler的时候可以指定一个Looper对象,如果不指定则利用当前线程的Looper(实际上是通过Looper.myLooper()来获取当前线程中的消息循环)。比如Handler对象以主线程的Looper创建,那么调用 Handler的sendMessage等接口,将会把消息放入主线程的消息队列。并且将会在Handler主线程中调用该handler 的handleMessage接口来处理消息。这样在主线程中就可以更新UI了。

常见异常1)ViewRootImpl实现类中的方法checkThread抛出的非UI线程更新UI异常;2)子线程创建handler时没有指定Looper对象,一种是子线程没有新建Looper,另一种是由于线程并发导致还没有构造出来就使用Looper对象。

总结:handler是android提供用来更新UI的一套机制,也是消息处理机制,可以发送消息,处理消息。(即android封装的消息创建传递,处理机制),这种机制是为了解决多线程并发更新UI时需要同步的问题,如使用加锁则会引起系性能下降。

HandlerThread

创建时包含loop的线程。应用程序可能包含多个线程,并使用handler实现线程间通信,默认情况线程没有消息循环,需要通过Looper.prepare()和,Looper.loop()来实现。但线程并发时,有可能Looper对象还未创建好就被使用,从而引发异常。这是可以通过使用HandlerThread来解决。通过源码可知,HandlerThread的ru()方法中创建了Looper并进行加锁处理。 换言之,这个类对Java的Thread做了很多便利Android系统的封装。

使用步骤1)实例化一个HanderThread(即创建一个含loop的线程);2)获取该线程的looper,使用方法getLooper();;3)使用该Looper对象创建一个Handler对象;最后即可使用该Handler对象和该线程通信。还要记住不需要该线程时使用quit()方法来退出该线程。

android 线程

Activity的UI线程,在ActivityThread类中启动,会创建Looper(在public static void main()中创建),所以在Activity中创建的Handler默认是关联了UI线程的Looper。

新建的工作线程,默认是不创建Looper的,需要自己的run()方法中创建,如果需要用Handler来接收别的线程发送的消息的话(这种情况最好用HandlerThread来新建工作线程)。

非UI线程更新UI:

1)UI更新时,会通过接口ViewParent的实现类ViewRootImpl来判断是否是UI线程在进行更新UI的操作。实现接口的方法invalidateChild().该方法最终会有checkThread的行为。如果不是UI线程,则抛出“Only the original thread that created a view hierarchy can touch its views.”

2)ViewRootImpl的初始化实在Activity的回调onResume中完成的。在onResume中需要将各类显示控件附着在window上,用的是接口ViewManager的addView方法
该接口的实现类是WindowManagerGlobal,值得注意的是该类的实例是通过单例模式来获取,这也很容易理解,在整个系统中,对window的管理和控制只需要一个对象。这个单例对象的addView方法中对ViewRootImpl进行了初始化。
结论:在ViewRootImpl初始化之前,不会对更新UI进行线程检查,此时非UI线程可以更新UI而不会引发异常!
Service
是一个不需要和用户交互而运行在后头的应用程序组件。有两种启动方法Context.startService() 和Context.bindService().    
1)不是一个单独的进程,不会启动自己的进程除非另外指定(在AndroidManifest.xml中可以使用标签android:process来指定),否它运行在和应用相同的进程中;2)不是一个线程,不能做耗时的工作。
因此会阻塞主线程的操作应该启动新线程来完成。IntentService是Service的标准实现类,并且有自己工作线程,使用它是个不错的选择。
通过Context.startService()启动的Service,一旦启动就在后台运行,就算启动它的组件已被销毁也一样。这种方式通常用于单任务,不需要返回结果的情况。
通过Context.bindService().启动的Service,提供了与组件交互的接口,可以发送请求,返回结果,甚至可以跨进程通信。多个组件可以绑定一个Service(第一次绑定的时候获取IBinder对象,之后系统将同一个IBinder对象传递给绑定者),绑定后才能运行,解除绑定是就会被销毁(所有绑定都解除时,不需要显示的去停止一个绑定的Service)
绑定时必须提供一个IBinder作为交互的接口。
1)继承Binder类,适用于绑定者位于同一个进程中,不需要IPC的时候;
2)使用messager,需要IPC时使用,队列模式不支持多线程(Service和Client各种分别用各自的Handler构造一个Messenger,客户端用IBinder对象构造另一个Messenger作为通信的信使);
3)使用AIDL,多线程,实现较为复杂。
标签android:process可以指定Service所运行的进程(The name of the process where the service is to run),通常情况下一个应用程序的所有组件都运行在同一个进程下,进程名为应用程序的包名。<application>的process属性可以给组件指定一个不同的默认进程,但是每个组件自己的这个属性会覆盖<application>的这样就可以把应用程序放在多个进程中(spread your application across multiple processes.)
这个值如果以冒号“:”开头,则在需要的时候系统会创建一个应用程序私有的新进程(Service就运行在整个进程中),如果以小写字母开头,则Service运行在以这个名字命名的全局进程中(如果有权限的话),这样就允许不同应用程序的组件共享进程,提升资源使用率。

IntentService

继承自Service,可以处理异步请求,每个Intent会启动一个工作线程来处理,任务结束后即停止。不会阻塞主线程,但是一次只能处理一个任务,使用队列方式管理待处理的Intent。处理完所有请求后自动停止Service(stops itself when it runs out of work.)。
使用方法:继承IntentService并实现onHandleIntent(Intent),然后客户端通过startService(Intent)来调用。

Broadcast
是一种可用于组件间和应用程序间传输信息的机制(但一般用于传输数据较小,传输频率较低的情况),其特点是发送方和接收方不需要事先知道对方的存在,也就是实现了低耦合。
非跨进程的广播推荐使用LocalBroadcastManager,它更加高效且安全(可以避免考虑其他应用程序使用你的广播的情况)
新建一个类继承BroadcastReceiver,并实现onReceive()方法。
1)静态注册,,在AndroidManifest中添加标签<receiver>(注册广播接收器),可通过<intent-filter>来过滤监听的广播种类。特点是,应用程序退出后,Receiver依然能收到广播。换言之,应用程序一档安装后,这个广播接收器就处于活动状态,通常用于监听系统状态的改变(低点,网络,sim卡弹出等)
2)动态注册,使用 registerReceiver()注册,方法的第二个参数intentFilter可过滤广播种类,unregisterReceiver()取消注册,如果广播用于更新UI那么通常使用这种方法注册。activity启动时注册,不可见时取消注册(onResume中注册,在onPause中注销)。
异步方式:广播内容放入intent对象, 然后Context.sendBroadcast()广播出去,接收器以不确定的顺序收到广播
顺序方式:Context.sendOrderedBroadcast(),一次向一个接收器发送,每个接收器顺序执行,然后发个下一个,中途可以退出不再传递下去,使用android:priority来定义属性优先级别。
BroadcastReceiver只在onReceive()调用期间有效(Once your code returns from this function, the system considers the object to be finished and no longer active.),所以最好不要在BroadcastReceiver中显示对话框(可以用通知栏)或者绑定一个service(可以startService)。

dip,dp,px

dpi(dots per inch):每英寸所显示的点数(像素),也可称为像素密度。
dip: device independent pixels(设备独立像素,).不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGAHVGAQVGA推荐使用这个,不依赖像素。
dp等同于dip,它是一个长度单位,1dp=1/160英寸
px: pixels(像素).不同设备显示效果相同
sp: scaledpixels(放大像素).主要用于字体显示best for textsize
pt: point,是一个标准的长度单位,1pt1/72英寸,用于印刷业,非常简单易用;

当densityDip=160时,1dp=1px。 px = dp*(dpi/160)

(这里需要注意,在android系统中,这里的dpi是归一化后的dpi,能值只有120(low)、160(medium)、240(high)、320(xhigh)四种)。在某些情况下,这会导致显示上的一些细微差别:

G7(HTC Desire)的屏幕尺寸是3.7英寸,分辨率是480*800,像素密度是252dpi
G10(HTC Desire HD)的屏幕尺寸是4.3英寸,分辨率为480*800,像素密度是217dpi
假设现在有一个按钮,它的宽度设为100dp,由于G7和G10同被 划分为hdpi,那么在这两个设备上,这个按钮的实际宽度是:100 * (240 / 160) = 150像素。可由于这两个设备的实际像素密度(dpi)是不一样的,所以真实的显示效果是:这个按钮在两个设备上的实际物理尺寸(英寸数)是不一样大的。

http://zhangkun716717-126-com.iteye.com/blog/1772696
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
基本概念

MVC

(控制器Controller)- 负责转发请求,对请求进行处理。
(视图View) - 图形界面设计。
(模型Model) - 程序应有的功能(实现算法等等)、数据管理和数据库设计(可以实现具体的功能)。

模型(Model) “数据模型”(Model)用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。“模型”有对数据直接访问的权力,例如对数据库的访问。“模型”不依赖“视图”和“控制器”,也就是说,模型不关心它会被如何显示或是如何被操作。但是模型中数据的变化一般会通过一种刷新机制被公布。为了实现这种机制,那些用于监视此模型的视图必须事先在此模型上注册,从而,视图可以了解在数据模型上发生的改变。(比较:观察者模式(软件设计模式))
视图(View) 视图层能够实现数据有目的的显示(理论上,这不是必需的)。在视图中一般没有程序上的逻辑。为了实现视图上的刷新功能,视图需要访问它监视的数据模型(Model),因此应该事先在被它监视的数据那里注册。
控制器(Controller) 控制器起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并作出响应。“事件”包括用户的行为和数据模型上的改变。


Android启动过程:

bootloader-> kernel->init.rc:(内核启动的用户级进程)

启动 ServiceManager守护进程 和zygote(app_main.cpp)

app_main.cpp–> Andriodruntime.start(“com.android.internal.os.ZygoteInit”);

ZygoteInit.java-> startSystemServer->SystemServer.java

SystemServer.java:main->init1(com.android.server_SystemServer.java->system.init)->int2->ServerThread(Activity manager,Package manager Window manager)


Android应用程序启动:

虚拟机

通过软件模拟的具有完整硬件系统功能的,运行在一个完全隔离环境中的完整计算机系统。一种抽象,一种仿真

一种系统虚拟机(VM Ware),一种进程虚拟机(JVM,.NET)

Java虚拟机(jvm):基于栈。

android虚拟机(dalvik): 基于寄存器(一次读两个寄存器),省指令,省cpu;没有使用JIT,

(1)虚拟机很小,使用的空间也小;
(2)早期并没有使用
JIT(Just-In-Time)技术. 从 Android 2.2 开始, Dalvik 虚拟机也支持 JIT
(3)常量池已被修改为只使用32位的索引,以简化解释器;
(4)它使用自己的字节码dex,而非Java字节码。


SQLite:
使用SQLiteOpenHelper来访问数据库(也可以用其他方法,这个类封装了一些方法使用起来比较方便)
,它是一个抽象类,所以必须继承使用。
SQLiteDatabase(通过SQLiteOpenHelper来获得它的对象),然后可以进行数据库的增删改查。
adb shell 进入linux命令行,进入目录/data/data/应用程序包名 就是存放数据库的地方。 使用命令sqlite3 "数据库名” 可以进入数据库执行一些数据库操作以及sql语句调试,比如.schema(查询当前数据库存在的表)
数据类型:1.NULL:空值。2.INTEGER:带符号的整型,具体取决有存入数字的范围大小。3.REAL:浮点数字,存储为8-byte IEEE浮点数。4.TEXT:字符串文本。5.BLOB:二进制对象。SQLite允许忽略数据类型, 但仍然建议在的Create Table语句中指定数据类型,以增强程序的可读性。(http://www.2cto.com/kf/201109/105639.html)

SD卡:
Environment.getExternalStorageDirectory()得到设备SD卡目录;
android.permission.WRITE_EXTERNAL_STORAGE访问SD卡权限;

Content Provider为存储和获取数据提供统一接口,可以在不同的应用程序之间共享数据。每一个ContentProvier都拥有一个公共的URI

文件下载步骤:

新建一个HttpURLConnection对象:

HttpURLConnection urlConn=(HttpURLConnection)url.openConnection();

获得一个InputStream对象urlConn.getInputStream()

访问网络的权限android.permission.INTERNET


异常:

Caused by: java lang IllegalArgumentExcpetion: The key must be an application-specific resource id

一般是由于传入的参数(int型的id值)不是由系统自动生成的(就是位于gen文件夹中的,gen中的id值可以保证唯一性)所导致。

比如方法setTag(int key, Object tag),其中的参数key必须是由系统自动生成,不能是类的成员变量所声明的int型变量。

解决办法可以在strings.xml中定义一个key,通过R.string.***来引用



eclipse使用相关
1.添加多语言文件:
右键新建Android XML File,输入文件名string.xml,选中Values单选框,
把左列表中的Region添加到右边,并在输入框里输入
cn
把左列表中的Language添加到右边,并在输入框里输入填入zh

 

Adb相关

cls
@echo off

adb remount  
echo rm apk
adb shell rm /system/app/Contacts.apk 
echo uninstall apk
adb uninstall com.android.contacts 
echo push apk
adb push  ./Contacts.apk /system/app/Contacts.apk 
echo install apk
adb install -r ./Contacts.apk

echo Contacts install done!

0 0
原创粉丝点击