单线程模型中Message、Handler、MessageQueue、Looper之间的关系

最后更新于:2022-04-01 20:09:33

Handler简介: 一个Handler允许你发送和处理Message和Runable对象,这些对象和一个线程的MessageQueue相关联。每一个线程实例和一个单独的线程以及该线程的MessageQueue相关联。当你创建一个新的Handler时,它就和创建它的线程绑定在一起了。这里,线程我们也可以理解为线程的MessageQueue。从这一点上来看,Handler把Message和Runable对象传递给MessageQueue,而且在这些对象离开MessageQueue时,Handler负责执行他们。 Handler有两个主要的用途: (1)确定在将来的某个时间点执行一个或者一些Message和Runnable对象。 (2)在其他线程(不是Handler绑定线程)中排入一些要执行的动作。 Scheduling Message,即(1),可以通过以下方法完成: post(Runnable):Runnable在handler绑定的线程上执行,也就是说不创建新线程。 postAtTime(Runnable,long): postDelayed(Runnable,long): sendEmptyMessage(int): sendMessage(Message): sendMessageAtTime(Message,long): sendMessageDelayed(Message,long): post这个动作让你把Runnable对象排入MessageQueue,MessageQueue受到这些消息的时候执行他们,当然以一定的排序。sendMessage这个动作允许你把Message对象排成队列,这些Message对象包含一些信息,Handler的hanlerMessage(Message)会处理这些Message.当然,handlerMessage(Message)必须由Handler的子类来重写。这是编程人员需要作的事。 当posting或者sending到一个Hanler时,你可以有三种行为:当MessageQueue准备好就处理,定义一个延迟时间,定义一个精确的时间去处理。后两者允许你实现timeout,tick,和基于时间的行为。 当你的应用创建一个新的进程时,主线程(也就是UI线程)自带一个MessageQueue,这个MessageQueue管理顶层的应用对象(像activities,broadcast receivers等)和主线程创建的窗体。你可以创建自己的线程,并通过一个Handler和主线程进行通信。这和之前一样,通过post和sendmessage来完成,差别在于在哪一个线程中执行这么方法。在恰当的时候,给定的Runnable和Message将在Handler的MessageQueue中被Scheduled。 Message简介: Message类就是定义了一个信息,这个信息中包含一个描述符和任意的数据对象,这个信息被用来传递给Handler.Message对象提供额外的两个int域和一个Object域,这可以让你在大多数情况下不用作分配的动作。 尽管Message的构造函数是public的,但是获取Message实例的最好方法是调用Message.obtain(),或者Handler.obtainMessage()方法,这些方法会从回收对象池中获取一个。 MessageQueue简介: 这是一个包含message列表的底层类。Looper负责分发这些message。Messages并不是直接加到一个MessageQueue中,而是通过MessageQueue.IdleHandler关联到Looper。 你可以通过Looper.myQueue()从当前线程中获取MessageQueue。 Looper简介: Looper类被用来执行一个线程中的message循环。默认情况,没有一个消息循环关联到线程。在线程中调用prepare()创建一个Looper,然后用loop()来处理messages,直到循环终止。 大多数和message loop的交互是通过Handler。 下面是一个典型的带有Looper的线程实现。   ~~~ class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public voidhandleMessage(Message msg) { // process incomingmessages here } }; Looper.loop(); } } ~~~
';

有关synchronized同步笔记

最后更新于:2022-04-01 20:09:31

synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。 注意: A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。 B.每个对象只有一个锁(lock)与之相关联。 C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。 1.对象实例的锁 ~~~ class Test{ public synchronized void f1(){ //do something here } public void f2(){ synchronized(this){ //do something here } } } ~~~ 上面的f1()和f2()效果一致, synchronized取得的锁都是Test某个实列(this)的锁.比如: Test t = new Test();线程A调用t.f2()时, 线程B无法进入t.f1(),直到t.f2()结束. 作用:多线程中访问Test的同一个实例的同步方法时会进行同步. 2.class的锁 ~~~ class Test{ final static Object o= new Object(); public static synchronized void f1(){ //do something here } public static void f2(){ synchronized(Test.class){ //do something here } } public static void f3(){ try { synchronized (Class.forName("Test")) { //do something here } } catch (ClassNotFoundException ex) { } } public static void g(){ synchronized(o){ //do something here } } } ~~~ 上面f1(),f2(),f3(),g()效果一致   f1(),f2(),f3()中synchronized取得的锁都是Test.class的锁.   g()是自己产生一个对象o,利用o的锁做同步    作用:多线程中访问此类或此类任一个实例的同步方法时都会同步. singleton模式lazily initializing属于此类. 3.static method ~~~ class Test{ private static int v = 0; public static void f1(){ //do something, 但函数中没用用到v } public synchronized static void f2(){ //do something, 函数中对v进行了读/写. } } ~~~ 多线程中使用Test的某个实列时,    (1)f1()是线程安全的,不需要同步    (2)f2()这个静态方法中使用了函数外静态变量,所以需要同步.
';

进程间通信—AIDL的使用实例

最后更新于:2022-04-01 20:09:29

AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。本文简单介绍AIDL的使用。 1.新建IRemoteService.aidl ~~~ package com.tang.remoteservicedemo; interface IRemoteService { String getInfo(); } ~~~ 从内容中也可以看出,这东西类似一个接口。既然定义了这么一个玩意,那么我们就要去实现它。 2.新建IService“实现”IRemoteService“接口” ~~~ package com.tang.remoteservicedemo; import java.util.Date; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class IService extends Service { private IBinder iBinder = new IRemoteService.Stub() { @Override public String getInfo() throws RemoteException { // TODO Auto-generated method stub return new Date(System.currentTimeMillis()).toLocaleString()+" 来自远程服务的信息!"; } }; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return iBinder; } } ~~~ 基于Binder的不同进程间通信,Client与Service在不同的进程中,对用户程序而言当调用Service返回的IBinder接口后,访问Service中的方法就如同调用自己的函数一样。 3.配置IService供其他进程调用 ~~~ ~~~ 配置一个所属进程名和一个action。 通过前面三个步骤,这个含有getInfo()方法的service就可以给别人调用了,下面在客户端调用它 4.新建一个ClientDemo工程将含有IRemoteService.aidl的那个包全部拷贝到src下,只留下aidl文件,其他全删除。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bc12ace.jpg)               ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bc2b9c9.jpg) 5.新建MainActivity,其他就是绑定service的操作了 ~~~ package com.tang.clientdemo; import java.util.Timer; import java.util.TimerTask; import com.tang.remoteservicedemo.IRemoteService; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; public class MainActivity extends Activity { private IRemoteService iService = null; private boolean isBinded =false; ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub isBinded = false; iService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub iService = IRemoteService.Stub.asInterface(service); isBinded = true; } }; public void doBind() { Intent intent = new Intent("com.tang.remoteservicedemo.IService"); bindService(intent, conn, Context.BIND_AUTO_CREATE); } public void doUnbind() { if (isBinded) { unbindService(conn); iService = null; isBinded = false; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub doBind(); } }).start(); Timer timer = new Timer(); timer.schedule(task, 0, 2000); } TimerTask task = new TimerTask() { @Override public void run() { // TODO Auto-generated method stub if(iService!=null) { try { Log.i("AAA", iService.getInfo()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { Log.i("AAA", "iService!=null"); } } }; @Override protected void onDestroy() { // TODO Auto-generated method stub doUnbind(); task.cancel(); super.onDestroy(); } } ~~~ 执行之后的Log如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bc42a16.jpg) 为什么会有一个为空的Log呢,因为绑定也是要时间的嘛.... 简单的AIDL的使用就这么多了 [源码下载](http://download.csdn.net/detail/tangnengwu/8124283)
';

数据存储(三)–JSON数据处理

最后更新于:2022-04-01 20:09:27

    JSON是一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性,从而可以在不同平台间进行数据交换。JSON采用兼容性很高的文本格式,同时也具备类似于C语言体系的行为。JSON可以将Java对象转成json格式的字符串,可以将json字符串转换成Java。比XML更轻量级,Json使用起来比较轻便和简单。JSON数据格式,在Android中被广泛运用于客户端和服务器通信,在网络数据传输与解析时非常方便。 JSONObject--这是系统中有关JSON定义的基本单元,其包含一对儿(Key/Value)数值。 JSONArray--它代表一组有序的数值。将其转换为String输出(toString)所表现的形式是用方括号包裹,数值以逗号”,”分隔(例如:[value1,value2,value3] JSONStringer--这个类可以帮助快速和便捷的创建JSONtext。其最大的优点在于可以减少由于格式的错误导致程序异常,引用这个类可以自动严格按照JSON语法规则(syntaxrules)创建JSON text。每个JSONStringer实体只能对应创建一个JSON text。 JSONTokener--json解析类 JSONException--json中用到的异常  下面以[数据存储(二)](http://blog.csdn.net/tangnengwu/article/details/38043793)中的Book对象用Json格式转为String ~~~ public static String ObjectToJsonString(List books) throws JSONException { JSONStringer stringer = new JSONStringer(); stringer.object(); stringer.key("books"); stringer.array(); for(int i=0;i jsonStringToObject(String str) throws JSONException { List books = new ArrayList(); JSONTokener jsonTokener = new JSONTokener(str); JSONObject jsonObject = (JSONObject) jsonTokener.nextValue(); JSONArray array =jsonObject.getJSONArray("books"); for(int i =0;i ';

数据存储(二)–SAX引擎XML存储(附Demo)

最后更新于:2022-04-01 20:09:24

Android SDK支持采用SAX技术读取XML,SAX采用顺序读取的方式来处理XML文档。这就要求在每读取XML文档的某个节点时会触发相应的事件来处理这个节点。下面基于一个实例讲述SAX的使用: ~~~ public class Book { private String name; private String id; private String price; private String publisher; private int count; .... get,set方法省略 } ~~~ XML文件如下: ~~~ 12 10 21 ~~~ XMLTool.java 1.构建一个工厂SAXParserFactory 2.构建并实例化SAXPraser对象 ~~~ public class XMLTool { private static SAXParser getSAXParser() throws ParserConfigurationException, SAXException { SAXParserFactory parserFactory = SAXParserFactory.newInstance(); return parserFactory.newSAXParser(); } public static DefaultHandler parse(InputStream inStream,DefaultHandler handler){ if(inStream!=null){ try { SAXParser parser = getSAXParser(); parser.parse(inStream, handler); return handler; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ if(inStream!=null){ try { inStream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } return null; } } ~~~ BookXMLParser.java ~~~ public class BookXMLParser extends DefaultHandler { private ArrayList dataList; private Book book; private StringBuffer stringBuffer = new StringBuffer(); //private StringBuffer buffer=new StringBuffer(); public ArrayList getData() { return dataList; } public void startDocument() throws SAXException { // TODO Auto-generated method stub dataList = new ArrayList(); } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // TODO Auto-generated method stub if(qName.equals("book")) { book = new Book(); book.setName(attributes.getValue("book:name")); book.setId(attributes.getValue("book:id")); book.setPrice(attributes.getValue("book:price")); book.setPublisher(attributes.getValue("book:publisher")); } super.startElement(uri, localName, qName, attributes); } @Override public void characters(char[] ch, int start, int length) throws SAXException { // TODO Auto-generated method stub stringBuffer.append(ch,start,length); super.characters(ch, start, length); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { // TODO Auto-generated method stub if(qName.equals("book")) { if(stringBuffer.toString()!=null && !stringBuffer.toString().equals("")) { book.setCount(Integer.parseInt(stringBuffer.toString().trim())); stringBuffer.setLength(0);//必须清空缓冲区 } dataList.add(book); } super.endElement(uri, localName, qName); } } ~~~ SAX引擎需要处理5个分析点,也可以称为分析事件。 1.开始分析XML文件。该分析点表示SAX引擎刚刚开始处理XML文件,但是还没有读取XML文件中的内容,该分析点对应: ~~~ public void startDocument() throws SAXException { // TODO Auto-generated method stub dataList = new ArrayList(); } ~~~ 在此方法里面可以做一些初始化的工作。 2.开始处理每一个XML元素。也就是遇到这样的起始标记的时候都会触发这个分析节点,所对应的事件方法是startElement。在这个节点可以获得元素的名称、属性的相关信息。 ~~~ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { // TODO Auto-generated method stub if(qName.equals("book")) { book = new Book(); book.setName(attributes.getValue("book:name")); book.setId(attributes.getValue("book:id")); book.setPrice(attributes.getValue("book:price")); book.setPublisher(attributes.getValue("book:publisher")); } super.startElement(uri, localName, qName, attributes); } ~~~ 3.处理完每一个XML元素。也就是遇到这样的结束标记的时候会触发endElement方法,在该事件中可以获得当前处理完元素的全部信息。 ~~~ public void endElement(String uri, String localName, String qName) throws SAXException { // TODO Auto-generated method stub if(qName.equals("book")) { if(stringBuffer.toString()!=null && !stringBuffer.toString().equals("")) { book.setCount(Integer.parseInt(stringBuffer.toString().trim())); stringBuffer.setLength(0);//必须清空缓冲区 } dataList.add(book); } super.endElement(uri, localName, qName); } ~~~ 4.处理完XML文件。如果SAX引擎将整个XML文件全部扫描完就会出发endDocument方法。这个方法可能不是必须的,但在这个方法中可以完成一些收尾工作,比如说释放资源等。在该例中我没有使用。 5.读取字符分析点。这是一个很重要的分析点。如果没有这个分析点, 前面的工作相当于白做,虽然扫描了XML文件,但是没有保存.....而这个分析点所对应的characters事件方法的主要作用就是保存SAX读取的XML文件内容。具体的说就是12中的“12” ~~~ public void characters(char[] ch, int start, int length) throws SAXException { // TODO Auto-generated method stub stringBuffer.append(ch,start,length); super.characters(ch, start, length); } ~~~ 使用SAX解析XML: ~~~ public class MainActivity extends Activity { private List books; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); InputStream inStream = getResources().openRawResource(R.raw.books); BookXMLParser parser = new BookXMLParser(); books = ((BookXMLParser)XMLTool.parse(inStream, parser)).getData(); if(books!=null && books.size()>0) { for(int i = 0;i books, OutputStream out) throws Exception { XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(out, "UTF-8"); serializer.startDocument("UTF-8", true); serializer.startTag(null, "books"); for (Book book : books) { serializer.startTag(null, "book"); serializer.attribute(null, "book:name", book.getName()); serializer.attribute(null, "book:id",book.getId()); serializer.attribute(null, "book:price", book.getPrice()); serializer.attribute(null, "book:publisher",book.getPublisher()); serializer.text(String.valueOf(book.getCount())); serializer.endTag(null, "book"); } serializer.endTag(null, "books"); serializer.endDocument(); out.flush(); out.close(); } ~~~ Demo:[http://download.csdn.net/detail/tangnengwu/7664719](http://download.csdn.net/detail/tangnengwu/7664719)
';

数据存储(一)–SharedPreferences之你不知道的事

最后更新于:2022-04-01 20:09:22

一、SharedPreferences将数据文件保存在指定路径上 SharedPreferences原则上是只能保存在当前应用程序私有的shared_prefs目录中,不过也不是绝对的,我们可以用一些非常规的方法改变存储目录,反射技术是很好的选择。 先上实现代码: ~~~ private SharedPreferences share; private SharedPreferences.Editor editor; ~~~ 修改路径关键代码: ~~~ private void initSharedPreferences(String path,String name,int mode) { try { Field field =ContextWrapper.class.getDeclaredField("mBase"); field.setAccessible(true); Object obj = field.get(this); field = obj.getClass().getDeclaredField("mPreferencesDir"); field.setAccessible(true); File file = new File(path); field.set(obj, file); share = getSharedPreferences(name, mode); editor = share.edit(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } ~~~ ~~~ Field field =ContextWrapper.class.getDeclaredField("mBase"); ~~~ 获取ContextWrapper对象中的mBase变量,该变量保存了ContextImpl对象,ContextImpl对象中的mPreferencesDir保存了数据文件的保存路径。 ~~~ share = getSharedPreferences(name, mode); ~~~ 执行这句后会在指定目录下创建文件用来保存数据。 PS:使用反射技术,要求仔细研究源码,这样才会知道要去修改哪个地方,哪个变量。 使用: ~~~ initSharedPreferences("/data/fly","config",Activity.MODE_PRIVATE); editor.putString("AA", "AAaa"); editor.commit(); Toast.makeText(this, share.getString("AA", ""), 1000).show(); ~~~ 二、SharedPreferences保存图片 SharedPreferences原则上只能将字符串以key-value的形式保存,但是我们可以采用编码的方式将任何二进制数据转化为字符串,从而将可以将二进制数据保存在SharedPreferences文件中,最常用的编码格式是Base64. ~~~ private void saveDrawable(int id) { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), id); ByteArrayOutputStream baos = new ByteArrayOutputStream(); bitmap.compress(CompressFormat.JPEG, 50, baos); String imageBase64 = new String(Base64.encodeToString(baos.toByteArray(),Base64.DEFAULT)); editor.putString("P",imageBase64 ); editor.commit(); } private Drawable loadDrawable() { String temp = share.getString("P", ""); ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(temp.getBytes(), Base64.DEFAULT)); return Drawable.createFromStream(bais, ""); } ~~~ 三、SharedPreferences保存对象 由于二进制数据经过编码后可以用SharedPreferences以字符串的形式存储,所以保存对象也称为可能。 ~~~ private void saveProduct(Product product) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(product); String temp = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT)); Log.i("AAA", temp); editor.putString("product", temp); editor.commit(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private Product getProduct() { String temp = share.getString("product", ""); ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(temp.getBytes(), Base64.DEFAULT)); Product product = null; try { ObjectInputStream ois = new ObjectInputStream(bais); product = (Product) ois.readObject(); } catch (IOException e) { // TODO Auto-generated catch block Log.i("AAA", e.toString()); }catch(ClassNotFoundException e1) { Log.i("AAA", e1.toString()); } return product; } ~~~ 对象可以被SharedPreferences存储的前提是该对象被序列化了,也就是说要实现Serializable接口,实际上Serializable接口是个空接口,只是为了标记该对象是被序列化的。 ~~~ public static class Product implements Serializable { String name; String id; int count; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public String toString() { return "name:"+name+" id:"+id+" count:"+count; } } ~~~ 如果该类是内部类的话要写成static 不然会出现java.io.NotSerializableException异常:[解决办法点这里 ](http://blog.csdn.net/tangnengwu/article/details/37901059)
';

android中反射技术使用实例

最后更新于:2022-04-01 20:09:20

在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义.反射 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性。Java 的反射机制的实现要借助于4个类:class,Constructor,Field,Method;其中class代表的时类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。 **1.通过反射技术可以访问到其他包名下数据方法等,这些为一些APK换皮肤提供了方便** 首先初始化skinContext ~~~ try { skinContext = this.createPackageContext("com.skin", CONTEXT_IGNORE_SECURITY|CONTEXT_INCLUDE_CODE); } catch (NameNotFoundException e) { // TODO Auto-generated catch block skinContext=null; e.printStackTrace(); } ~~~ 可以通过下面的方法访问到指定包名下的资源ID ~~~ /** * 取得对应包的所有资源的ID * 存在MAP中 * @param packageName * @return */ private Map> getSkinResourcesId(String packageName) { Map temp = null; Map> resMap =new HashMap>(); try { //取得皮肤包中的R文件 Class rClass = skinContext.getClassLoader().loadClass(packageName+".R"); //取得记录各种资源的ID的类 Class[] resClass =rClass.getClasses(); String className,resourceName; int resourceId=0; for(int i=0;i(); temp.put(resourceName, resourceId); Log.i("DDDDD", "className:"+className+" resourceName:"+resourceName+" " + "resourceId:"+Integer.toHexString(resourceId)); } } //由于内部类的关系className应该是com.skin.R$layout的形式 //截掉前面的包名和.R$以方便使用 className = className.substring(packageName.length()+3); resMap.put(className, temp); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return resMap; } ~~~ 最后通过资源ID和skinContext可以访问到指定包下的所有资源,例如要访问layout ~~~ /** * 获取皮肤包中的layout * 并转化为VIEW * @param layoutName * @return */ private View getLayoutFromSkin(String layoutName) { View view; if(resMap == null) return null; Map temp = resMap.get("layout"); int viewId = (Integer) temp.get(layoutName); if(viewId != 0) { //引用皮肤包资源转化View LayoutInflater inflater =LayoutInflater.from(skinContext); view = inflater.inflate(skinContext.getResources().getLayout(viewId), null); } else { view = null; } return view; } ~~~ 注:换皮肤思路详见:[http://blog.csdn.net/tangnengwu/article/details/22801107](http://blog.csdn.net/tangnengwu/article/details/22801107) **2. 访问android 隐藏的API** Toast信息框的关闭是由系统管理的,因为hide方法是隐藏的开发者没有办法直接调用,这种情况下可以用发射机制获取这个方法,创建一个显示和隐藏都由开发者控制的Toast信息框。 ~~~ package com.example.reflection; import java.lang.reflect.Field; import java.lang.reflect.Method; import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; import android.widget.Toast; public class MyToast { Context context=null; Object obj =null; public MyToast(Context context,String text) { this.context =context; Toast toast =Toast.makeText(context, text, 1); try { Field field = toast.getClass().getDeclaredField("mTN"); field.setAccessible(true); obj =field.get(toast); } catch (Exception e) { // TODO: handle exception Log.d("AAA", "MyToast Exception--->"+e.toString()); } } public void show() { try { //android4.0以上就要以下处理 // Field mNextViewField = obj.getClass().getDeclaredField("mNextView"); // mNextViewField.setAccessible(true); // LayoutInflater inflate = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); // View v = inflate.inflate(R.layout.ui_toast, null); // mNextViewField.set(obj, v); Method method =obj.getClass().getDeclaredMethod("show", null); method.invoke(obj, null); } catch (Exception e) { // TODO Auto-generated catch block Log.d("AAA", "show Exception--->"+e.toString()); e.printStackTrace(); } } public void hide() { try { Method method =obj.getClass().getDeclaredMethod("hide", null); method.invoke(obj, null); } catch (Exception e) { // TODO Auto-generated catch block Log.d("AAA", "hide Exception--->"+e.toString()); e.printStackTrace(); } } } ~~~ 显示toast: ~~~ MyToast toast = new MyToast(this, "反射机制!"); toast.show(); ~~~ 隐藏toast: toast.hide(); **注意在4.0以上的版本中,还需要对Toast 中的View进行处理,如代码中所示** **3. 修改某些“不可改” 的系统资源** ListView组件没有提供修改快速滑块图像的API,因此不能直接修改,但可通过反射实现 ~~~ package com.example.reflection; import java.lang.reflect.Field; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.AbsListView; import android.widget.ListView; public class MListView extends ListView { public MListView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub setNewDrawable(context); } private void setNewDrawable(Context context) { try { Field field = AbsListView.class.getDeclaredField("mFastScroller"); field.setAccessible(true); Object obj = field.get(this); field =field.getType().getDeclaredField("mThumbDrawable"); field.setAccessible(true); Drawable drawable = (Drawable)field.get(obj); drawable = context.getResources().getDrawable(R.drawable.ic_launcher); field.set(obj, drawable); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } ~~~ ~~~ Field field = AbsListView.class.getDeclaredField("mFastScroller"); ~~~ FastScroller.mThunbDrawable变量保存了快速滑块图像,但首先要获取AbsListView.mFastScroller变量 ~~~ ~~~ ~~~ android:fastScrollEnabled="true" ~~~ 使用快速滑块 效果图如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bbdd97e.jpg) 总结:      Java中的反射机制,被称为Reflection,它允许运行中的Java程序对自身进行检查,并能直接操作程序的内部属性或方法。Reflection机制允许程序在正在执行的过程中,利用Reflection APIs取得任何已知名称的类的内部信息,包括:package、 type parameters、 superclass、 implemented interfaces、 inner classes、 outer classes、 fields、 constructors、 methods、 modifiers等,并可以在执行的过程中,动态生成Instances、变更fields内容或唤起methods。再次基础上我们可以利用反射机制在Java程序中,动态的去调用一些protected甚至是private的方法或类,这样可以很大程度上满足我们的一些比较特殊需求。 有关反射技术的API: Class类: Class类代表着某个类的字节码,要使用反射,就需要取得对应的Class对象,然后就通过这个对象,就可解剖出类的成员变量,成员方法等等。 获取Class类对象 //通过Class的forName()方法,此方法最为常用   Class class1 = Class.forName(className);   //通过 .class   Class class2 = XXX.class;   //通过对象获得   Class class3 = new XXX().getClass();  Class类的常用方法: getConstructor() 获取构造函数 getMethod()  获取成员方法 getField() 获取成员变量 getDeclaredConstructor() 获取私有的构造函数 getDeclaredMethod()  获取私有的成员方法 getDeclaredField() 获取私有的成员变量 取得method对象之后 调用 ~~~ method.invoke(obj, null) ~~~ 使用该方法
';

android SQLite 使用实例

最后更新于:2022-04-01 20:09:17

    Android作为目前主流的移动操作系统,完全符合SQLite占用资源少的优势,故在Android平台上,集成了一个嵌入式关系型数据库—SQLite。如果想要开发 Android 应用程序,需要在 Android 上存储数据,使用SQLite 数据库是一种非常好的选择。在一般程序中使用数据库的过程都可以框架化,套路化,实例如下: 表说明: 1.班级 classes: class_id  主键 class_name  2.学生 students: student_id 主键 student_name  score  class_id 外键 创建表: CREATE TABLE classes(class_id varchar(10) primary key , class_name varchar(20)) CREATE TABLE students(student_id varchar(10) primary key ,                                               student_name varchar(20) ,                                               score varchar(4) ,                                               class_id varchar(10),                                              foreign key (class_id) references classes(class_id) **on delete cascade on update cascade**) **1. 继承扩展 SQLiteOpenHelper 创建数据库和对应表** ~~~ package com.tang.databasedemo; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class DBHelper extends SQLiteOpenHelper { public DBHelper(Context context) { super(context, "info.db", null, 1); // TODO Auto-generated constructor stub } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub String classesSQL = "CREATE TABLE classes(class_id varchar(10) primary key , " + "class_name varchar(20))"; String studentsSQL = "CREATE TABLE students(student_id varchar(10) primary key , " + "student_name varchar(20) ,score varchar(4) ,class_id varchar(10), " + "foreign key (class_id) references classes(class_id) " + "on delete cascade on update cascade )"; db.execSQL(classesSQL); Log.d("my", "create table classes:"+classesSQL); db.execSQL(studentsSQL); Log.d("my", "create table students:"+studentsSQL); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } } ~~~ **2. 创建学生(Class)学生(Student)实体** ~~~ package com.tang.databasedemo; import android.util.Log; public class Class { private String classId; private String className; public String getClassId() { return classId; } public void setClassId(String classId) { this.classId = classId; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public String toString() { return "Class--->"+"classId:"+classId+" className:"+className; } } ~~~ ~~~ package com.tang.databasedemo; public class Student { private String studentId; private String studentName; private String score; private String classId; public String getStudentId() { return studentId; } public void setStudentId(String studentId) { this.studentId = studentId; } public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName = studentName; } public String getScore() { return score; } public void setScore(String score) { this.score = score; } public String getClassId() { return classId; } public void setClassId(String classId) { this.classId = classId; } public String toString() { return "Student--->"+"studentId:"+studentId+" studentName:"+studentName+" score:"+score+" classId:"+classId; } } ~~~ **3. 创建DBServer类,在该类中定义增删改查等方法来操作数据库** ~~~ package com.tang.databasedemo; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; public class DBServer { private DBHelper dbhelper; public DBServer(Context context) { this.dbhelper = new DBHelper(context); } /** * 添加班级 * @param entity */ public void addClass(Class entity) { SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Object[] arrayOfObject = new Object[2]; arrayOfObject[0] = entity.getClassId(); arrayOfObject[1] = entity.getClassName(); localSQLiteDatabase.execSQL("insert into classes(class_id,class_name) values(?,?)", arrayOfObject); localSQLiteDatabase.close(); } /** * 添加学生 * @param entity */ public void addStudent(Student entity) { SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Object[] arrayOfObject = new Object[4]; arrayOfObject[0] = entity.getStudentId(); arrayOfObject[1] = entity.getStudentName(); arrayOfObject[2] = entity.getScore(); arrayOfObject[3] = entity.getClassId(); localSQLiteDatabase.execSQL("insert into students(student_id,student_name,score,class_id) values(?,?,?,?)", arrayOfObject); localSQLiteDatabase.close(); } /** * 删除一个班级 * 同时会删除students中该班级的学生 * @param class_id */ public void deleteClass(String class_id) { SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); //设置了级联删除和级联更新 //在执行有级联关系的语句的时候必须先设置“PRAGMA foreign_keys=ON” //否则级联关系默认失效 localSQLiteDatabase.execSQL("PRAGMA foreign_keys=ON"); Object[] arrayOfObject = new Object[1]; arrayOfObject[0] =class_id; localSQLiteDatabase.execSQL("delete from classes where class_id=?", arrayOfObject); localSQLiteDatabase.close(); } /** * 删除一个学生 * @param student_id */ public void deleteStudent(String student_id) { SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Object[] arrayOfObject = new Object[1]; arrayOfObject[0] =student_id; localSQLiteDatabase.execSQL("delete from students where student_id=?", arrayOfObject); localSQLiteDatabase.close(); } /** * 修改学生信息 * @param entity */ public void updateStudentInfo(Student entity) { SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Object[] arrayOfObject = new Object[4]; arrayOfObject[0] = entity.getStudentName(); arrayOfObject[1] = entity.getScore(); arrayOfObject[2] = entity.getClassId(); arrayOfObject[3] = entity.getStudentId(); localSQLiteDatabase.execSQL("update students set student_name=?,score=?,class_id=? where student_id=?", arrayOfObject); localSQLiteDatabase.close(); } /** * 使用班级编号查找该班级所有学生 * @param classId * @return */ public List findStudentsByClassId(String classId) { List localArrayList=new ArrayList(); SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Cursor localCursor = localSQLiteDatabase.rawQuery("select student_id, student_name ,score from students " + "where class_id=? order by score desc", new String[]{classId}); while (localCursor.moveToNext()) { Student temp=new Student(); temp.setStudentId(localCursor.getString(localCursor.getColumnIndex("student_id"))); temp.setStudentName(localCursor.getString(localCursor.getColumnIndex("student_name"))); temp.setScore(localCursor.getString(localCursor.getColumnIndex("score"))); temp.setClassId(classId); localArrayList.add(temp); } localSQLiteDatabase.close(); return localArrayList; } /** * 使用班级名查找该班级所有学生 * @param className * @return */ public List findStudentsByClassName(String className) { List localArrayList=new ArrayList(); SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Cursor localCursor = localSQLiteDatabase.rawQuery("select student_id, student_name,score,classes.class_id from students,classes" + " where students.class_id=classes.class_id and classes.class_name =? order by score asc" , new String[]{className}); while (localCursor.moveToNext()) { Student temp=new Student(); temp.setStudentId(localCursor.getString(localCursor.getColumnIndex("student_id"))); temp.setStudentName(localCursor.getString(localCursor.getColumnIndex("student_name"))); temp.setScore(localCursor.getString(localCursor.getColumnIndex("score"))); temp.setClassId(localCursor.getString(3)); localArrayList.add(temp); } localSQLiteDatabase.close(); return localArrayList; } /** * 查找所有学生 * @param className * @return */ public List findAllStudents() { List localArrayList=new ArrayList(); SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Cursor localCursor = localSQLiteDatabase.rawQuery("select * from students " + "where 1=1 order by score desc ", null); while (localCursor.moveToNext()) { Student temp=new Student(); temp.setStudentId(localCursor.getString(localCursor.getColumnIndex("student_id"))); temp.setStudentName(localCursor.getString(localCursor.getColumnIndex("student_name"))); temp.setScore(localCursor.getString(localCursor.getColumnIndex("score"))); temp.setClassId(localCursor.getString(localCursor.getColumnIndex("class_id"))); localArrayList.add(temp); } localSQLiteDatabase.close(); return localArrayList; } /** * 取得所有班级 * @return */ public List findAllClasses() { List localArrayList=new ArrayList(); SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Cursor localCursor = localSQLiteDatabase.rawQuery("select * from classes " + "where 1=1", null); while (localCursor.moveToNext()) { Class temp=new Class(); temp.setClassId(localCursor.getString(localCursor.getColumnIndex("class_id"))); temp.setClassName(localCursor.getString(localCursor.getColumnIndex("class_name"))); localArrayList.add(temp); } localSQLiteDatabase.close(); return localArrayList; } /** * 成绩最好 * @return */ public Student findMaxScoreStudent() { Student temp =new Student(); SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Cursor localCursor = localSQLiteDatabase.rawQuery("select student_id,student_name,class_id,max(score) from students " + "where 1=1",null ); localCursor.moveToFirst(); temp.setStudentId(localCursor.getString(0)); temp.setStudentName(localCursor.getString(1)); temp.setClassId(localCursor.getString(2)); temp.setScore(localCursor.getString(3)); return temp; } /** * 查找是否有该学生 * @param studentId * @return */ public boolean isStudentsExists(String studentId) { SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Cursor localCursor = localSQLiteDatabase.rawQuery("select count(*) from students " + "where student_id=?", new String[]{studentId}); localCursor.moveToFirst(); if(localCursor.getLong(0)>0) return true; else return false; } /** * 确认该班级是否存在 * @param classId * @return */ public boolean isClassExists(String s) { SQLiteDatabase localSQLiteDatabase = this.dbhelper.getWritableDatabase(); Cursor localCursor = localSQLiteDatabase.rawQuery("select count(*) from classes " + "where class_id=? or class_name=?", new String[]{s,s}); localCursor.moveToFirst(); if(localCursor.getLong(0)>0) return true; else return false; } } ~~~ **4.调用DBServer里的方法,操作数据** ~~~ package com.tang.databasedemo; import java.util.ArrayList; import java.util.List; import java.util.Random; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.SharedPreferences; import android.text.AlteredCharSequence; import android.util.Log; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener { private List classData =new ArrayList(); private List studentsData =new ArrayList(); private static final String className ="A/B/C/D/E"; private static final String studentName ="彭大/黄二/张三/李四/王五/郑六/田七/周八/叶九/孔十/萧十一"; private DBServer db; private SharedPreferences share; private SharedPreferences.Editor editor; private String info =""; private EditText editText; private Button b,b1,b2,b3,b4,b5,b6; private EditText sId,sName,score,cId,cName; private Handler hander =new Handler() { @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub if(msg.what==0) { sId.setText(""); sName.setText(""); score.setText(""); cName.setText(""); cId.setText(""); } else if(msg.what==1) { db.deleteClass((String)msg.obj); info += "删除一个班级及班级里面的学生:班级Id:"+(String)msg.obj; editText.setText(info); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); share = getSharedPreferences("DatabaseDamo", 0); editor =share.edit(); db=new DBServer(this); if(share.getInt("times", 0)==0) { initDatabase(); editor.putInt("times", 1); editor.commit(); } } private void initView() { editText = (EditText) findViewById(R.id.info); sId = (EditText) findViewById(R.id.studentId); sName = (EditText) findViewById(R.id.studentName); score = (EditText) findViewById(R.id.score); cId = (EditText) findViewById(R.id.classId); cName = (EditText) findViewById(R.id.className); b =(Button) findViewById(R.id.button); b1 =(Button) findViewById(R.id.button1); b2 =(Button) findViewById(R.id.button2); b3 =(Button) findViewById(R.id.button3); b4 =(Button) findViewById(R.id.button4); b5 =(Button) findViewById(R.id.button5); b6 =(Button) findViewById(R.id.button6); b.setOnClickListener(this); b1.setOnClickListener(this); b2.setOnClickListener(this); b3.setOnClickListener(this); b4.setOnClickListener(this); b5.setOnClickListener(this); b6.setOnClickListener(this); } private void initDatabase() { info=""; editText.setText(""); String []classTemp = className.split("/"); Class c; for(int i=0;iOnce opened successfully, the database is cached, so you can * call this method every time you need to write to the database. * (Make sure to call {@link #close} when you no longer need the database.) * Errors such as bad permissions or a full disk may cause this method * to fail, but future attempts may succeed if the problem is fixed.

* *

Database upgrade may take a long time, you * should not call this method from the application main thread, including * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}. * * @throws SQLiteException if the database cannot be opened for writing * @return a read/write database object valid until {@link #close} is called */ public SQLiteDatabase getWritableDatabase() { synchronized (this) { return getDatabaseLocked(true); } } /** * Create and/or open a database. This will be the same object returned by * {@link #getWritableDatabase} unless some problem, such as a full disk, * requires the database to be opened read-only. In that case, a read-only * database object will be returned. If the problem is fixed, a future call * to {@link #getWritableDatabase} may succeed, in which case the read-only * database object will be closed and the read/write object will be returned * in the future. * *

Like {@link #getWritableDatabase}, this method may * take a long time to return, so you should not call it from the * application main thread, including from * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}. * * @throws SQLiteException if the database cannot be opened * @return a database object valid until {@link #getWritableDatabase} * or {@link #close} is called. */ public SQLiteDatabase getReadableDatabase() { synchronized (this) { return getDatabaseLocked(false); } } private SQLiteDatabase getDatabaseLocked(boolean writable) { if (mDatabase != null) { if (!mDatabase.isOpen()) { // Darn! The user closed the database by calling mDatabase.close(). mDatabase = null; } else if (!writable || !mDatabase.isReadOnly()) { // The database is already open for business. return mDatabase; } } if (mIsInitializing) { throw new IllegalStateException("getDatabase called recursively"); } SQLiteDatabase db = mDatabase; try { mIsInitializing = true; if (db != null) { if (writable && db.isReadOnly()) { db.reopenReadWrite(); } } else if (mName == null) { db = SQLiteDatabase.create(null); } else { try { if (DEBUG_STRICT_READONLY && !writable) { final String path = mContext.getDatabasePath(mName).getPath(); db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler); } else { db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ? Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0, mFactory, mErrorHandler); } } catch (SQLiteException ex) { if (writable) { throw ex; } Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", ex); final String path = mContext.getDatabasePath(mName).getPath(); db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler); } } onConfigure(db); final int version = db.getVersion(); if (version != mNewVersion) { if (db.isReadOnly()) { throw new SQLiteException("Can't upgrade read-only database from version " + db.getVersion() + " to " + mNewVersion + ": " + mName); } db.beginTransaction(); try { if (version == 0) { onCreate(db); } else { if (version > mNewVersion) { onDowngrade(db, version, mNewVersion); } else { onUpgrade(db, version, mNewVersion); } } db.setVersion(mNewVersion); db.setTransactionSuccessful(); } finally { db.endTransaction(); } } onOpen(db); if (db.isReadOnly()) { Log.w(TAG, "Opened " + mName + " in read-only mode"); } mDatabase = db; return db; } finally { mIsInitializing = false; if (db != null && db != mDatabase) { db.close(); } } } ~~~ 对于getReadableDatabase()的注释,大致意思也就是:    getWritableDatabase()和getReadableDatabase()会返回相同的对象,除非出现了一些如空间已满的问题,这时就会返回一个只读的对象。当问题解决了之后,只读对象将会被关闭,这时就会返回一个可读写的对象。 SQL实例可执行代码: [http://download.csdn.net/detail/tangnengwu/7369503](http://download.csdn.net/detail/tangnengwu/7369503)

';

launchMode 总结

最后更新于:2022-04-01 20:09:15

转载:[http://blog.csdn.net/shift_wwx/article/details/9225951](http://blog.csdn.net/shift_wwx/article/details/9225951) launchMode在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task里。这里简单介绍一下task的概念,task是一个具有栈结构的对象,一个task可以管理多个Activity,启动一个应用,也就创建一个与之对应的task。 Activity一共有以下四种launchMode: 1.standard 2.singleTop 3.singleTask 4.singleInstance 我们可以在AndroidManifest.xml配置的android:launchMode属性为以上四种之一即可。 下面我们结合实例一一介绍这四种lanchMode: **1.standard** standard模式是默认的启动模式,不用为配置android:launchMode属性即可,当然也可以指定值为standard。 我们将会一个Activity,命名为FirstActivity,来演示一下标准的启动模式。FirstActivity代码如下: ~~~ 1. package com.shift.launchermode;   1.    1. import android.os.Bundle;   1. import android.view.View;   1. import android.view.View.OnClickListener;   1. import android.widget.Button;   1. import android.widget.TextView;   1. import android.app.Activity;   1. import android.content.Intent;   1.    1. public class FirstActivity extends Activity{   1.    1.     @Override   1.     protected void onCreate(Bundle savedInstanceState) {   1.         super.onCreate(savedInstanceState);   1.         setContentView(R.layout.first);   1.         TextView show = (TextView)findViewById(R.id.first_show);   1.         show.setText(this.toString());   1.            1.         Button go = (Button)findViewById(R.id.first_go);   1.         go.setOnClickListener(new OnClickListener() {   1.                1.             @Override   1.             public void onClick(View v) {   1.                 gotoSelf();   1.             }   1.         });   1.     }   1.        1.     private void gotoSelf(){   1.         Intent intent = new Intent(FirstActivity.this, FirstActivity.class);   1.         startActivity(intent);   1.     }   ~~~ 我们FirstActivity界面中的TextView用于显示当前Activity实例的序列号,Button用于跳转到下一个FirstActivity界面。 然后我们连续点击几次按钮,将会出现下面的现象: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d746d497.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d748ec5c.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d74ae922.jpg) 我们注意到都是FirstActivity的实例,但序列号不同,并且我们需要连续按后退键两次,才能回到第一个FristActivity。standard模式的原理如下图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d74caa7c.jpg) 如图所示,每次跳转系统都会在task中生成一个新的FirstActivity实例,并且放于栈结构的顶部,当我们按下后退键时,才能看到原来的FirstActivity实例。 当然,你也可以在两个activity之间切换,你会发现序列号也是不同的,如果first中start second,在second中start first,例如按照1 - 2 - 1 - 2,然后你会发现序列号是不可能重复的。 这就是standard启动模式,不管有没有已存在的实例,都生成新的实例。 **2.singleTop** 我们在上面的基础上为指定属性android:launchMode="singleTop",系统就会按照singleTop启动模式处理跳转行为。我们重复上面几个动作,将会出现下面的现象: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d7548a0d.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d7548a0d.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d7548a0d.jpg) 我们看到这个结果跟standard有所不同,三个序列号是相同的,也就是说使用的都是同一个FirstActivity实例;如果按一下后退键,程序立即退出,说明当前栈结构中只有一个Activity实例。singleTop模式的原理如下图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b42d7576b82.jpg) 正如上图所示,跳转时系统会先在栈结构中寻找是否有一个FirstActivity实例正位于栈顶,如果有则不再生成新的,而是直接使用。也许朋友们会有疑问,我只看到栈内只有一个Activity,如果是多个Activity怎么办,如果不是在栈顶会如何?我们接下来再通过一个示例来证实一下大家的疑问。 我们再新建一个Activity命名为SecondActivity,如下: ~~~ 1. package com.shift.launchermode;   1.    1. import android.os.Bundle;   1. import android.view.View;   1. import android.view.View.OnClickListener;   1. import android.widget.Button;   1. import android.widget.TextView;   1. import android.app.Activity;   1. import android.content.Intent;   1.    1. public class SecondActivity extends Activity {   1.    1.     @Override   1.     protected void onCreate(Bundle savedInstanceState) {   1.         super.onCreate(savedInstanceState);   1.         setContentView(R.layout.second);   1.         TextView show = (TextView)findViewById(R.id.second_show);   1.         show.setText(this.toString());   1.            1.         Button go = (Button)findViewById(R.id.second_go);   1.         go.setOnClickListener(new OnClickListener() {   1.                1.             @Override   1.             public void onClick(View v) {   1.                 Intent intent = new Intent(SecondActivity.this, FirstActivity.class);   1.                 startActivity(intent);   1.             }   1.         });   1.     }   1. }   ~~~ 然后将之前的FirstActivity跳转代码改为: ~~~ 1. Intent intent = new Intent(FirstActivity.this, SecondActivity.class);     1. startActivity(intent);     ~~~ 是的,FirstActivity会跳转到SecondActivity,SecondActivity又会跳转到FirstActivity。演示结果如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550b911222.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550b92e5d9.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550b94a739.jpg) 我们看到,两个FirstActivity的序列号是不同的,证明从SecondActivity跳转到FirstActivity时生成了新的FirstActivity实例。原理图如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550ba671d4.jpg) 我们看到,当从SecondActivity跳转到FirstActivity时,系统发现存在有FirstActivity实例,但不是位于栈顶,于是重新生成一个实例。 这就是singleTop启动模式,如果发现有对应的Activity实例正位于栈顶,则重复利用,不再生成新的实例。 **3.singleTask** 在上面的基础上我们修改FirstActivity的属性android:launchMode="singleTask"。演示的结果如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550ba83a9e.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550baa351a.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550ba83a9e.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550badc7b4.jpg) 我们注意到,在上面的过程中,FirstActivity的序列号是不变的,SecondActivity的序列号却不是唯一的,说明从SecondActivity跳转到FirstActivity时,没有生成新的实例,但是从FirstActivity跳转到SecondActivity时生成了新的实例。singleTask模式的原理图如下图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb07abf.jpg) 在图中的下半部分是SecondActivity跳转到FirstActivity后的栈结构变化的结果,我们注意到,SecondActivity消失了,没错,在这个跳转过程中系统发现有存在的FirstActivity实例,于是不再生成新的实例,而是将FirstActivity之上的Activity实例统统出栈,将FirstActivity变为栈顶对象,显示到幕前。也许朋友们有疑问,如果将SecondActivity也设置为singleTask模式,那么SecondActivity实例是不是可以唯一呢?在我们这个示例中是不可能的,因为每次从SecondActivity跳转到FirstActivity时,SecondActivity实例都被迫出栈,下次等FirstActivity跳转到SecondActivity时,找不到存在的SecondActivity实例,于是必须生成新的实例。但是如果我们有ThirdActivity,让SecondActivity和ThirdActivity互相跳转,那么SecondActivity实例就可以保证唯一。 这就是singleTask模式,如果发现有对应的Activity实例,则使此Activity实例之上的其他Activity实例统统出栈,使此Activity实例成为栈顶对象,显示到幕前。 **4.singleInstance** 这种启动模式比较特殊,因为它会启用一个新的栈结构,将Acitvity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。 我们修改FirstActivity的launchMode="standard",SecondActivity的launchMode="singleInstance",由于涉及到了多个栈结构,我们需要在每个Activity中显示当前栈结构的id,所以我们为每个Activity添加如下代码: **[java]**[view plain](http://blog.csdn.net/shift_wwx/article/details/9225951# "view plain")[copy](http://blog.csdn.net/shift_wwx/article/details/9225951# "copy") 1. TextView taskIdView = (TextView) findViewById(R.id.taskIdView);     1. taskIdView.setText("current task id: " + this.getTaskId());    然后我们再演示一下这个流程: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb24b3f.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb3d64a.jpg) 我们发现这两个Activity实例分别被放置在不同的栈结构中,关于singleInstance的原理图如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb626ed.jpg) 我们看到从FirstActivity跳转到SecondActivity时,重新启用了一个新的栈结构,来放置SecondActivity实例,然后按下后退键,再次回到原始栈结构;图中下半部分显示的在SecondActivity中再次跳转到FirstActivity,这个时候系统会在原始栈结构中生成一个FirstActivity实例,然后回退两次,注意,并没有退出,而是回到了SecondActivity,为什么呢?是因为从SecondActivity跳转到FirstActivity的时候,我们的起点变成了SecondActivity实例所在的栈结构,这样一来,我们需要“回归”到这个栈结构。 如果我们修改FirstActivity的launchMode值为singleTop、singleTask、singleInstance中的任意一个,流程将会如图所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb7b114.jpg) singleInstance启动模式可能是最复杂的一种模式,为了帮助大家理解,我举一个例子,假如我们有一个share应用,其中的ShareActivity是入口Activity,也是可供其他应用调用的Activity,我们把这个Activity的启动模式设置为singleInstance,然后在其他应用中调用。我们编辑ShareActivity的配置: ~~~ 1.      1.          1.              1.              1.          1.          1.              1.              1.          1.     ~~~ 然后我们在其他应用中这样启动该Activity: ~~~ 1. Intent intent = new Intent("android.intent.action.SINGLE_INSTANCE_SHARE");     1. startActivity(intent);    ~~~ 当我们打开ShareActivity后再按后退键回到原来界面时,ShareActivity做为一个独立的个体存在,如果这时我们打开share应用,无需创建新的ShareActivity实例即可看到结果,因为系统会自动查找,存在则直接利用。大家可以在ShareActivity中打印一下taskId,看看效果。关于这个过程,原理图如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-18_57b550bb97805.jpg)
';

android 延时执行任务

最后更新于:2022-04-01 20:09:13

~~~ package com.example.delaydemo; import java.util.Timer; import java.util.TimerTask; import android.os.Bundle; import android.os.Handler; import android.app.Activity; import android.util.Log; import android.view.Menu; public class MainActivity extends Activity { //非UI线程不能更新UI需要借助Handler Handler handler =new Handler() { public void handleMessage(android.os.Message msg) { setContentView(R.layout.main); }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.welcome); //第一种方法就是启动一个子线程做加载数据之类的耗时操作 //做完之后使用handler通知主线程更新UI // new Thread(new Runnable() { // @Override // public void run() { // // TODO Auto-generated method stub // try { // Thread.sleep(4000); // } catch (InterruptedException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // handler.sendEmptyMessage(0); // } // }).start(); //第二种方法 //直接使用handler,这里可以更新UI,原因是new Handler()相当于new Handler(getMainLooper()) //下面的Log打印的是main 说明还是运行在主线程中 // new Handler().postDelayed(new Runnable() // { // @Override // public void run() { // // TODO Auto-generated method stub // setContentView(R.layout.main); // Log.i("MainActivity", Thread.currentThread().getName()); // } // }, 4000); Timer timer = new Timer(); timer.schedule(task, 4000); //schedule(TimerTask task, long delay) //在延时delay毫秒后执行task。并没有重复执行 // schedule(TimerTask task, long delay, long period) //在延时delay毫秒后重复的执行task,周期是period毫秒。 //也就是说在delay毫秒后每隔period毫秒执行一次task //schedule(TimerTask task, Date time) //在指定的时间执行一次 } //第三种方法 //使用TimerTask和handler TimerTask task = new TimerTask() { @Override public void run() { // TODO Auto-generated method stub handler.sendEmptyMessage(0); //这里打印的是 Timer-0 说明不是运行在主线程中 //若在这里直接使用setContentView(R.layout.main); //会报CalledFromWrongThreadException: Only the original thread that created a //view hierarchy can touch its views.错误 Log.i("MainActivity", Thread.currentThread().getName()); } }; } ~~~ 除了上面3种方法其实还可以使用AlarmManager来实现延时执行或重复执行,只是这种方法有点繁琐,是用广播机制实现的,对于循环执行的任务,如果需要停止执行的话,必须执行closeAlarm()方法,不然即使是退出了程序,这个任务还是会一直执行。 ~~~ package com.example.delaydemo; import java.util.Calendar; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class Alarm { /** * delay秒后执行一次 * @param context * @param delay */ public Alarm(Context context,int delay) { Intent intent =new Intent(context, AlarmReceiver.class); intent.setAction("com.alarm.justonce"); PendingIntent sender= PendingIntent.getBroadcast(context, 0, intent, 0); Calendar calendar=Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.add(Calendar.SECOND, delay); AlarmManager alarm=(AlarmManager)context.getSystemService(context.ALARM_SERVICE); alarm.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender); Toast.makeText(context, delay+"后alarm开启", 2000).show(); } /** * 到firstTime 后 以period周期执行 * @param context * @param firstTime * @param period */ public Alarm(Context context,long firstTime,long period) { Intent intent =new Intent(context, AlarmReceiver.class); intent.setAction("com.alarm.repeating"); PendingIntent sender=PendingIntent .getBroadcast(context, 0, intent, 0); //开始时间 long firstime=firstTime; AlarmManager am=(AlarmManager)context.getSystemService(context.ALARM_SERVICE); //period秒一个周期,不停的发送广播 am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP , firstime, period*1000, sender); Toast.makeText(context, firstTime+"后alarm开启", 2000).show(); } /** * 取消周期执行任务 * @param context */ public void closeAlarm(Context context) { Intent intent =new Intent(context, AlarmReceiver.class); intent.setAction("repeating"); PendingIntent sender=PendingIntent .getBroadcast(context, 0, intent, 0); AlarmManager alarm=(AlarmManager)context.getSystemService(context.ALARM_SERVICE); alarm.cancel(sender); } } ~~~ 广播接受器,别忘了在Manifest中注册 ~~~ package com.example.delaydemo; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub if(intent.getAction().equals("com.alarm.justonce")) { Toast.makeText(context, "justonce alarm", 2000).show(); } else { Toast.makeText(context, "repeating alarm", 2000).show(); } } } ~~~ 最后附上程序:[http://download.csdn.net/detail/tangnengwu/7217595](http://download.csdn.net/detail/tangnengwu/7217595)
';

android数据读写

最后更新于:2022-04-01 20:09:11

~~~ package com.example.filerw; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.RandomAccessFile; import java.net.URL; import org.apache.http.util.EncodingUtils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Environment; public class FileUtil { private Context context; public FileUtil(Context context) { this.context=context; } /** * 读取data/data//files 目录下的文件 * @param fileName * @return */ public String readFromData(String fileName) { try { FileInputStream fis =context.openFileInput(fileName); //图片如此操作 //InputStream in = new BufferedInputStream(fis); //Bitmap bitmap= BitmapFactory.decodeStream(in); byte[]buff =new byte[1024]; int hasRead=0; StringBuilder sb =new StringBuilder(""); while((hasRead=fis.read(buff))>0) { sb.append(new String(buff,0,hasRead)); } fis.close(); return sb.toString(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** * 将数据写入data/data//files 目录下的文件 * @param content * @param fileName * @param mode */ public void writeToData(String content,String fileName,int mode) { try { FileOutputStream fos =context.openFileOutput(fileName, mode); PrintStream ps =new PrintStream(fos); ps.println(content); ps.close(); } catch (Exception e) { // TODO: handle exception } } /** * 读取SD卡 目录下的文件 * @param fileName * @return */ public String readFromSDCard(String fileName) { try { //检测SD卡是否存在 if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { File sdCardDir = Environment.getExternalStorageDirectory(); FileInputStream fis =new FileInputStream(sdCardDir.getCanonicalPath()+"/"+fileName); BufferedReader br =new BufferedReader(new InputStreamReader(fis)); StringBuilder sb =new StringBuilder(""); String line=null; while((line =br.readLine())!=null) { sb.append(line); } br.close(); return sb.toString(); } } catch (Exception e) { // TODO: handle exception } return null; } /** * 接着写入SD卡目录下的文件 追加内容 * @param content * @param FileName * @param mode */ public void writeToSDCard(String content,String FileName,String mode) { try { //检测SD卡是否存在 if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { File sdCardDir = Environment.getExternalStorageDirectory(); File targetFile =new File(sdCardDir.getCanonicalPath()+"/"+FileName); RandomAccessFile raf =new RandomAccessFile(targetFile,mode); raf.seek(targetFile.length()); raf.write(content.getBytes()); raf.close(); } } catch (Exception e) { // TODO: handle exception } } /** * fileName可以是路径加文件名 * 必须先检测SD卡是否存在 * @param fileName * @return * @throws IOException */ public String readSDFile(String fileName) throws IOException { if(isSDCardExists()) { File file = new File(fileName); FileInputStream fis = new FileInputStream(file); //如果是图片可以这样得到BitMap // InputStream is = new BufferedInputStream(fis); // Bitmap bitmap = BitmapFactory.decodeStream(is); int length = fis.available(); byte [] buffer = new byte[length]; fis.read(buffer); String res = EncodingUtils.getString(buffer, "UTF-8"); fis.close(); return res; } else { return null; } } /** * 必须先检测SD卡是否存在 * fileName可以是路径加文件名 * @param fileName * @param write_str * @throws IOException */ public void writeSDFile(String fileName, String write_str) throws IOException { if(isSDCardExists()) { File file = new File(fileName); FileOutputStream fos = new FileOutputStream(file); byte [] bytes = ((String) write_str).getBytes(); fos.write(bytes); fos.close(); } } /** * 获取网络资源 * 图片 文件 。。。 * @throws IOException */ public void writeToSDFromURL(String s,String fileName) throws IOException { if(isSDCardExists()) { URL url =new URL(s); InputStream is =url.openStream(); //转化为Bitmap //Bitmap bitmap =BitmapFactory.decodeStream(is); String path = Environment.getExternalStorageDirectory().getAbsolutePath(); File file =new File(path+"/"+fileName); FileOutputStream fop= new FileOutputStream(file); OutputStream os = new BufferedOutputStream(fop); //若写入到DATA中就如下做 //OutputStream os=context.openFileOutput(fileName, context.MODE_WORLD_READABLE); byte [] buff=new byte[1024]; int hasRead=0; while((hasRead=is.read(buff))>0) { os.write(buff,0,hasRead); } is.close(); os.close(); } } public boolean isSDCardExists() { try { String s= Environment.getExternalStorageDirectory().getCanonicalPath(); if(new File(s).exists()) return true; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); return false; } return false; } //有关SD卡读写都要加上读写权限 // // // String Name = File.getName(); //获得文件或文件夹的名称: // String parentPath = File.getParent(); //获得文件或文件夹的父目录 // String path = File.getAbsoultePath();//绝对路经 // String path = File.getPath();//相对路经 // File.createNewFile();//建立文件 // File.mkDir(); //建立文件夹 // File.isDirectory(); //判断是文件或文件夹 // File[] files = File.listFiles(); //列出文件夹下的所有文件和文件夹名 // File.renameTo(dest); //修改文件夹和文件名 // File.delete(); //删除文件夹或文件 } ~~~ 文件读写总结: SD卡中的文件使用FileInputStream和FileOutputStream进行文件的操作。 存放在数据区(/data/data/..)的文件只能使用openFileOutput和openFileInput进行操作,不能使用FileInputStream和FileOutputStream进行文件的操作。 文件操作的一些方法: String Name = File.getName();  //获得文件或文件夹的名称:   String parentPath = File.getParent();  //获得文件或文件夹的父目录   String path = File.getAbsoultePath();//绝对路经   String path = File.getPath();//相对路经    File.createNewFile();//建立文件     File.mkDir(); //建立文件夹     File.isDirectory(); //判断是文件或文件夹   File[] files = File.listFiles();  //列出文件夹下的所有文件和文件夹名   File.renameTo(dest);  //修改文件夹和文件名   File.delete();  //删除文件夹或文件  
';

前言

最后更新于:2022-04-01 20:09:08

> 原文出处:[Android基础技术总结](http://blog.csdn.net/column/details/androidbasetang.html) 作者:[tangnengwu](http://blog.csdn.net/tangnengwu) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # Android基础技术总结 > 基础知识总结,为进阶奠基。
';