Android 消息处理机制源码详细分析教程
Android中被使用的消息队列的代码在目录\sources\android-22\android\os下,主要涉及到以下几个类文件
Handler.java 在这里面代表一个消息实体对象
Looper.java 主要用来监听MessageQueue的消息,他存放于ThreadLocal中
Message.java 主要用来处理消息的发送,以及响应消息的业务处理
MessageQueue.java 是一个单独的线程,可与Looper整合,实现一个完全独立的消息处理机制
Message.java public final class Message implements Parcelable { public int what; //消息种类 public int arg1; //低开销的整型参数 public int arg2; public Object obj; //Object型数据 public Messenger replyTo; //消息处理完后通知给发送者 /*package*/ int flags; //消息标记:正在使用和异步等 /*package*/ long when; //消息创建时的时间 /*package*/ Bundle data; //消息附带的额外数据 /*package*/ Handler target; //消息接受者,处理者 /*package*/ Runnable callback; //优先使用回调处理来处理消息 /*package*/ Message next; //下一个消息,形成链表 private static Message sPool; //消息池中的头消息
上面中的target,通常由重新实现的Handler子类的handleMessage函数来处理消息
public static Message obtain() { //获取消息的函数,如果有消息的话则获取出来m,链表指针移动一位,否则则返回一条空消息 synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); } public void sendToTarget() { //发送消息给处理者 target.sendMessage(this); //调用Handler.java中的函数 } }
MessageQueue.java public final class MessageQueue { Message mMessages; //当前要处理的消息 //当需要从链表中获取一个消息时,就会调用next函数,若消息队列中没有消息,则会阻塞等待,通过调用nativePollOnce函数来完成 Message next() {...} boolean enqueueMessage(Message msg, long when) { //按时间顺序添加消息 if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); //调用底层唤醒函数,管道唤醒 } } return true; }
Looper.java public final class Looper { final MessageQueue mQueue; //消息队列 final Thread mThread; //Looper联系的线程 public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //先会检查是否有Looper,若有则抛出异常,没有的话则创建一个Looper实例保存起来 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } //在这个线程中运行消息队列,调用quit()停止 public static void loop() { ... final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // 从消息队列中取出一条消息 if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); //交给msg的handler分发消息处理 ... } //取出当前线程的Looper,返回空则表示当前线程没有Looper public static Looper myLooper() { return sThreadLocal.get(); } }
Handler.java public class Handler { //定义Callback接口 public interface Callback { public boolean handleMessage(Message msg); } //子类要实现的消息处理方法 public void handleMessage(Message msg) { } * Handle system messages here. */ public void dispatchMessage(Message msg) { //分发信息 if (msg.callback != null) { //若指定了msg.callback,则由它处理 handleCallback(msg); } else { if (mCallback != null) { //若指定了Handler.mCallback,则由它处理 if (mCallback.handleMessage(msg)) { //调用mCallback接口的实现方法 return; } } handleMessage(msg); 最后才调用Handler自身重载的handleMessage方法 } } 分发消息函数中,消息先会检查自身有没有处理自身的回调Runnable,若有则由它处理,若没有则会检查该handler有无自身的回调处理,若有则调用,若没有则调用自身重载的handleMessage方法 //Handler的生成总是和它当前所处线程有关的,如果当前线程中没有一个Looper,则会报错,UI线程中默认有产生Looper的函数 public Handler() { this(null, false); } //使用指定的Looper(可以处理那个Looper线程中的消息),不用默认的从当前线程中取出Looper public Handler(Looper looper) { this(looper, null, false); } ... }
Android的系统是linux系统,我们可以使用linux命令mouunt来获取linux的挂载目录。
使用命令获取到的目录我并没有遍历,如果你还是获取不到,可以把mount获去到的所有目录都遍历一次。
File sdcard ; @SuppressLint("SdCardPath") public File getSdCardFile() { if (sdcard != null) { return sdcard; } List<String> list = getExtSDCardPath(); boolean isRun = true; if (list.size() > 0) { sdcard = new File(list.get(list.size() - 1)); if (sdcard.isDirectory()) { if (sdcard.getFreeSpace() == 0) { isRun = true; } else { isRun = false; } } } if (isRun) { sdcard = Environment.getExternalStorageDirectory(); if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { sdcard = new File("/sdcard/"); log.e("sdcard spance:" + sdcard.getFreeSpace()); if (sdcard.getFreeSpace() == 0) { sdcard = new File("/sdcard1/"); log.e("sdcard1 space:" + sdcard.getFreeSpace()); } if (sdcard.getFreeSpace() == 0) { sdcard = new File("/sdcard2/"); log.e("sdcard2 space:" + sdcard.getFreeSpace()); } } } log.e("data:" + sdcard.getAbsolutePath()); return sdcard; } public List<String> getExtSDCardPath() { List<String> lResult = new ArrayList<String>(); try { Runtime rt = Runtime.getRuntime(); Process proc = rt.exec("mount"); InputStream is = proc.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null) { if (line.contains("extSdCard")) { String[] arr = line.split(" "); String path = arr[1]; if (path.lastIndexOf("extSdCard") == path.length() - 9) { File file = new File(path); if (file.isDirectory()) { lResult.add(path); } } } else if (line.contains("/sdcard")) { String[] arr = line.split(" "); String path = arr[1]; if (path.lastIndexOf("/sdcard") == path.length() - 6) { File file = new File(path); if (file.isDirectory()) { lResult.add(path); } } else { String number = path.substring(path.lastIndexOf("/sdcard") + 7); try { Integer.parseInt(number); File file = new File(path); if (file.isDirectory()) { lResult.add(path); } } catch (Exception e) { } } } } isr.close(); } catch (Exception e) { } return lResult; }
不过,首选获取存储还是先使用Android提供的方法,判断并获取Sdcard目录。
//判断sdcard是否存在 Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); //如果存在,获取存储File目录 Environment.getExternalStorageDirectory();
Android SD卡路径问题以及如何获取SDCard 内存
在研究拍照后突破的存储路径的问题,开始存储路径写死为: private String folder = "/sdcard/DCIM/Camera/"(SD卡上拍照程序的图片存储路径); 后来发现这样写虽然一般不会出错,但不是很好,因为不同相机,可能路径会出问题。较好的方法是通过Environment 来获取路径,最后给出一个例子,教你怎样获取SDCard 的内存,显示出来告诉用户。讲述的内容如下:
0、获取sd卡路径。
1、讲述 Environment 类。
2、讲述 StatFs 类。
3、完整例子读取 SDCard 内存
0、获取sd卡路径
方法一: private String folder = "/sdcard/DCIM/Camera/"(SD卡上拍照程序的图片存储路径); //写死绝对路径,不赞成使用
方法二:
public String getSDPath(){ File sdDir = null; boolean sdCardExist = Environment.getExternalStorageState() .equals(Android.os.Environment.MEDIA_MOUNTED); //判断sd卡是否存在 if (sdCardExist) { sdDir = Environment.getExternalStorageDirectory();//获取跟目录 } return sdDir.toString(); }
然后:在后面加上斜杠,在加上文件名
String fileName = getSDPath() +"/" + name;//以name存在目录中
1、讲述 Environment 类
Environment 是一个提供访问环境变量的类。
Environment 包含常量:
MEDIA_BAD_REMOVAL
解释:返回getExternalStorageState() ,表明SDCard 被卸载前己被移除
MEDIA_CHECKING
解释:返回getExternalStorageState() ,表明对象正在磁盘检查。
MEDIA_MOUNTED
解释:返回getExternalStorageState() ,表明对象是否存在并具有读/写权限
MEDIA_MOUNTED_READ_ONLY
解释:返回getExternalStorageState() ,表明对象权限为只读
MEDIA_NOFS
解释:返回getExternalStorageState() ,表明对象为空白或正在使用不受支持的文件系统。
MEDIA_REMOVED
解释:返回getExternalStorageState() ,如果不存在 SDCard 返回
MEDIA_SHARED
解释:返回getExternalStorageState() ,如果 SDCard 未安装 ,并通过 USB 大容量存储共享 返回
MEDIA_UNMOUNTABLE
解释:返回getExternalStorageState() ,返回 SDCard 不可被安装 如果 SDCard 是存在但不可以被安装
MEDIA_UNMOUNTED
解释:返回getExternalStorageState() ,返回 SDCard 已卸掉如果 SDCard 是存在但是没有被安装
Environment 常用方法:
方法:getDataDirectory()
解释:返回 File ,获取 Android 数据目录。
方法:getDownloadCacheDirectory()
解释:返回 File ,获取 Android 下载/缓存内容目录。
方法:getExternalStorageDirectory()
解释:返回 File ,获取外部存储目录即 SDCard
方法:getExternalStoragePublicDirectory(String type)
解释:返回 File ,取一个高端的公用的外部存储器目录来摆放某些类型的文件
方法:getExternalStorageState()
解释:返回 File ,获取外部存储设备的当前状态
方法:getRootDirectory()
解释:返回 File ,获取 Android 的根目录
2、讲述 StatFs 类
StatFs 一个模拟linux的df命令的一个类,获得SD卡和手机内存的使用情况
StatFs 常用方法:
getAvailableBlocks()
解释:返回 Int ,获取当前可用的存储空间
getBlockCount()
解释:返回 Int ,获取该区域可用的文件系统数
getBlockSize()
解释:返回 Int ,大小,以字节为单位,一个文件系统
getFreeBlocks()
解释:返回 Int ,该块区域剩余的空间
restat(String path)
解释:执行一个由该对象所引用的文件系统
3、完整例子读取 SDCard 内存
存储卡在 Android 手机上是可以随时插拔的,每次的动作都对引起操作系统进行 ACTION_BROADCAST,本例子将使用上面学到的方法,计算出 SDCard 的剩余容量和总容量。代码如下:
package com.terry; import java.io.File; import java.text.DecimalFormat; import android.R.integer; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.StatFs; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; public class getStorageActivity extends Activity { private Button myButton; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); findView(); viewHolder.myButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // TODO Auto-generated method stub getSize(); } }); } void findView(){ viewHolder.myButton=(Button)findViewById(R.id.Button01); viewHolder.myBar=(ProgressBar)findViewById(R.id.myProgressBar); viewHolder.myTextView=(TextView)findViewById(R.id.myTextView); } void getSize(){ viewHolder.myTextView.setText(""); viewHolder.myBar.setProgress(0); //判断是否有插入存储卡 if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File path =Environment.getExternalStorageDirectory(); //取得sdcard文件路径 StatFs statfs=new StatFs(path.getPath()); //获取block的SIZE long blocSize=statfs.getBlockSize(); //获取BLOCK数量 long totalBlocks=statfs.getBlockCount(); //己使用的Block的数量 long availaBlock=statfs.getAvailableBlocks(); String[] total=filesize(totalBlocks*blocSize); String[] availale=filesize(availaBlock*blocSize); //设置进度条的最大值 int maxValue=Integer.parseInt(availale[0]) *viewHolder.myBar.getMax()/Integer.parseInt(total[0]); viewHolder.myBar.setProgress(maxValue); String Text="总共:"+total[0]+total[1]+"\n" +"可用:"+availale[0]+availale[1]; viewHolder.myTextView.setText(Text); }else if(Environment.getExternalStorageState().equals(Environment.MEDIA_REMOVED)){ Toast.makeText(getStorageActivity.this, "没有sdCard", 1000).show(); } } //返回数组,下标1代表大小,下标2代表单位 KB/MB String[] filesize(long size){ String str=""; if(size>=1024){ str="KB"; size/=1024; if(size>=1024){ str="MB"; size/=1024; } } DecimalFormat formatter=new DecimalFormat(); formatter.setGroupingSize(3); String result[] =new String[2]; result[0]=formatter.format(size); result[1]=str; return result; } }
下面分为生成JSON数据和解析JSON数据,所用的包是org.json
(1)生成JSON数据方法:
比如要生成一个这样的json文本
{
"phone" : ["12345678", "87654321"], //数组
"name" : "dream9", // 字符串
"age" : 100, // 数值
"address" : { "country" : "china", "province" : "guangdong" }, // 对象
}
try { JSONObject obj = new JSONObject(); // 首先创建一个对象 JSONArray phone = new JSONArray(); // 添加数据到数组中序号是从0递增的 phone.put("12345678"); phone.put("87654321"); obj.put("phone", phone); obj.put("name", "dream9"); obj.put("age", 100); JSONObject address = new JSONObject(); address.put("country", "china"); address.put("province", "jiangsu"); obj.put("address", address); Log.e("huang", obj.toString()); }
结果:
(2)解析JSON数据方法(以上面那个为例):
private void anaylse(String data){
try {
JSONObject obj = new JSONObject((String)data);
JSONArray phone = obj.getJSONArray("phone");
for(int t=0; t<phone.length(); ++t){
Log.e("huang", phone.getString(t)); //解析phone数组
}
Log.e("huang", obj.getString("name"));
Log.e("huang", obj.getInt("age")+"");
JSONObject o = obj.getJSONObject("address");
Log.e("huang", o.getString("country"));
Log.e("huang", o.getString("province"));
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
结果:
android Json解析详解
android2.3提供的json解析类
android的json解析部分都在包org.json下,主要有以下几个类:
JSONObject:可以看作是一个json对象,这是系统中有关JSON定义的基本单元,其包含一对儿(Key/Value)数值。它对外部(External: 应用toString()方法输出的数值)调用的响应体现为一个标准的字符串(例如:{"JSON": "Hello, World"},最外被大括号包裹,其中的Key和Value被冒号":"分隔)。其对于内部(Internal)行为的操作格式略微,例如:初始化一个JSONObject实例,引用内部的put()方法添加数值:new JSONObject().put("JSON", "Hello, World!"),在Key和Value之间是以逗号","分隔。Value的类型包括:Boolean、JSONArray、JSONObject、Number、String或者默认值JSONObject.NULL object 。
JSONStringer:json文本构建类 ,根据官方的解释,这个类可以帮助快速和便捷的创建JSON text。其最大的优点在于可以减少由于 格式的错误导致程序异常,引用这个类可以自动严格按照JSON语法规则(syntax rules)创建JSON text。每个JSONStringer实体只能对应创建一个JSON text。。其最大的优点在于可以减少由于格式的错误导致程序异常,引用这个类可以自动严格按照JSON语法规则(syntax rules)创建JSON text。每个JSONStringer实体只能对应创建一个JSON text。
JSONArray:它代表一组有序的数值。将其转换为String输出(toString)所表现的形式是用方括号包裹,数值以逗号”,”分隔(例如: [value1,value2,value3],大家可以亲自利用简短的代码更加直观的了解其格式)。这个类的内部同样具有查询行为, get()和opt()两种方法都可以通过index索引返回指定的数值,put()方法用来添加或者替换数值。同样这个类的value类型可以包括:Boolean、JSONArray、JSONObject、Number、String或者默认值JSONObject.NULL object。
JSONTokener:json解析类
JSONException:json中用到的异常
JSONObject, JSONArray来构建json文本
// 假设现在要创建这样一个json文本 // { // "phone" : ["12345678", "87654321"], // 数组 // "name" : "yuanzhifei89", // 字符串 // "age" : 100, // 数值 // "address" : { "country" : "china", "province" : "jiangsu" }, // 对象 // "married" : false // 布尔值 // } try { // 首先最外层是{},是创建一个对象 JSONObject person = new JSONObject(); // 第一个键phone的值是数组,所以需要创建数组对象 JSONArray phone = new JSONArray(); phone.put("12345678").put("87654321"); person.put("phone", phone); person.put("name", "yuanzhifei89"); person.put("age", 100); // 键address的值是对象,所以又要创建一个对象 JSONObject address = new JSONObject(); address.put("country", "china"); address.put("province", "jiangsu"); person.put("address", address); person.put("married", false); } catch (JSONException ex) { // 键为null或使用json不支持的数字格式(NaN, infinities) throw new RuntimeException(ex); }
getType和optType api的使用
getType可以将要获取的键的值转换为指定的类型,如果无法转换或没有值则抛出JSONException
optType也是将要获取的键的值转换为指定的类型,无法转换或没有值时返回用户提供或这默认提供的值
try { // 所有使用的对象都是用上面创建的对象 // 将第一个电话号码转换为数值和将名字转换为数值 phone.getLong(0); person.getLong("name"); // 会抛异常,因为名字无法转换为long phone.optLong(0); // 代码内置的默认值 phone.optLong(0, 1000); // 用户提供的默认值 person.optLong("name"); person.optLong("name", 1000); // 不像上面那样抛异常,而是返回1000 } catch (JSONException ex) { // 异常处理代码 }
除了上面的两个类,还可以使用JSONStringer来构建json文本
Java代码
try { JSONStringer jsonText = new JSONStringer(); // 首先是{,对象开始。object和endObject必须配对使用 jsonText.object(); jsonText.key("phone"); // 键phone的值是数组。array和endArray必须配对使用 jsonText.array(); jsonText.value("12345678").value("87654321"); jsonText.endArray(); jsonText.key("name"); jsonText.value("yuanzhifei89"); jsonText.key("age"); jsonText.value(100); jsonText.key("address"); // 键address的值是对象 jsonText.object(); jsonText.key("country"); jsonText.value("china"); jsonText.key("province"); jsonText.value("jiangsu"); jsonText.endObject(); jsonText.key("married"); jsonText.value(false); // },对象结束 jsonText.endObject(); } catch (JSONException ex) { throw new RuntimeException(ex); }
json文本解析类JSONTokener
按照RFC4627规范将json文本解析为相应的对象。
对于将json文本解析为对象,只需要用到该类的两个api:
构造函数
public Object nextValue(); // { // "phone" : ["12345678", "87654321"], // 数组 // "name" : "yuanzhifei89", // 字符串 // "age" : 100, // 数值 // "address" : { "country" : "china", "province" : "jiangsu" }, // 对象 // "married" : false // 布尔值 // } private static final String JSON = "{" + " \"phone\" : [\"12345678\", \"87654321\"]," + " \"name\" : \"yuanzhifei89\"," + " \"age\" : 100," + " \"address\" : { \"country\" : \"china\", \"province\" : \"jiangsu\" }," + " \"married\" : false," + "}"; try { JSONTokener jsonParser = new JSONTokener(JSON); // 此时还未读取任何json文本,直接读取就是一个JSONObject对象。 // 如果此时的读取位置在"name" : 了,那么nextValue就是"yuanzhifei89"(String) JSONObject person = (JSONObject) jsonParser.nextValue(); // 接下来的就是JSON对象的操作了 person.getJSONArray("phone"); person.getString("name"); person.getInt("age"); person.getJSONObject("address"); person.getBoolean("married"); } catch (JSONException ex) { // 异常处理代码 }
其它的api基本就是用来查看json文本中的文本的
try { JSONTokener jsonParser = new JSONTokener(JSON); // 继续向下读8个json文本中的字符。此时刚开始,即在{处 jsonParser.next(8); //{ "phone。tab算一个字符 // 继续向下读1个json文本中的字符 jsonParser.next(); //" // 继续向下读取一个json文本中的字符。该字符不是空白、同时也不是注视中的字符 jsonParser.nextClean(); //: // 返回当前的读取位置到第一次遇到'a'之间的字符串(不包括a)。 jsonParser.nextString('a'); // ["12345678", "87654321"], "n(前面有两个空格) // 返回当前读取位置到第一次遇到字符串中(如"0089")任意字符之间的字符串,同时该字符是trimmed的。(此处就是第一次遇到了89) jsonParser.nextTo("0089"); //me" : "yuanzhifei // 读取位置撤销一个 jsonParser.back(); jsonParser.next(); //i // 读取位置前进到指定字符串处(包括字符串) jsonParser.skipPast("address"); jsonParser.next(8); //" : { "c // 读取位置前进到执行字符处(不包括字符) jsonParser.skipTo('m'); jsonParser.next(8); //married" } catch (JSONException ex) { // 异常处理代码 }
以下是一个标准的JSON请求实现过程:
HttpPost request = new HttpPost(url); // 先封装一个 JSON 对象 JSONObject param = new JSONObject(); param.put("name", "rarnu"); param.put("password", "123456"); // 绑定到请求 Entry StringEntity se = new StringEntity(param.toString()); request.setEntity(se); // 发送请求 HttpResponse httpResponse = new DefaultHttpClient().execute(request); // 得到应答的字符串,这也是一个 JSON 格式保存的数据 String retSrc = EntityUtils.toString(httpResponse.getEntity()); // 生成 JSON 对象 JSONObject result = new JSONObject( retSrc); String token = result.get("token");
下面这个是自己修改别人的小例子,主要是加一些注释和讲解,这个例子主要是使用android进行json解析。
单数据{'singer':{'id':1,'name':'tom','gender':'男'}}
多个数据{'singers':[{'id':'2','name':'tom','gender':'男'},{'id':'3','name':'jerry','gender':'男'},{'id':'4','name':'jim','gender':'男'},{'id':'5','name':'lily','gender':'女'}]}
下面的类主要是解析单个数据parseJson()和多个数据的方法parseJsonMulti():
public class JsonActivity extends Activity { /** Called when the activity is first created. */ private TextView tvJson; private Button btnJson; private Button btnJsonMulti; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); tvJson = (TextView) this.findViewById(R.id.tvJson); btnJson = (Button) this.findViewById(R.id.btnJson); btnJsonMulti = (Button) this.findViewById(R.id.btnJsonMulti); btnJson.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // url // String strUrl = "http://10.158.166.110:8080/AndroidServer/JsonServlet"; String strUrl = ServerPageUtil.getStrUrl(UrlsOfServer.JSON_SINGER); //获得返回的Json字符串 String strResult = connServerForResult(strUrl); //解析Json字符串 parseJson(strResult); } }); btnJsonMulti.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String strUrl = ServerPageUtil.getStrUrl(UrlsOfServer.JSON_SINGERS); String strResult = connServerForResult(strUrl); //获得多个Singer parseJsonMulti(strResult); } }); } private String connServerForResult(String strUrl) { // HttpGet对象 HttpGet httpRequest = new HttpGet(strUrl); String strResult = ""; try { // HttpClient对象 HttpClient httpClient = new DefaultHttpClient(); // 获得HttpResponse对象 HttpResponse httpResponse = httpClient.execute(httpRequest); if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { // 取得返回的数据 strResult = EntityUtils.toString(httpResponse.getEntity()); } } catch (ClientProtocolException e) { tvJson.setText("protocol error"); e.printStackTrace(); } catch (IOException e) { tvJson.setText("IO error"); e.printStackTrace(); } return strResult; } // 普通Json数据解析 private void parseJson(String strResult) { try { JSONObject jsonObj = new JSONObject(strResult).getJSONObject("singer"); int id = jsonObj.getInt("id"); String name = jsonObj.getString("name"); String gender = jsonObj.getString("gender"); tvJson.setText("ID号"+id + ", 姓名:" + name + ",性别:" + gender); } catch (JSONException e) { System.out.println("Json parse error"); e.printStackTrace(); } } //解析多个数据的Json private void parseJsonMulti(String strResult) { try { JSONArray jsonObjs = new JSONObject(strResult).getJSONArray("singers"); String s = ""; for(int i = 0; i < jsonObjs.length() ; i++){ JSONObject jsonObj = (JSONObject)jsonObjs.get(i); int id = jsonObj.getInt("id"); String name = jsonObj.getString("name"); String gender = jsonObj.getString("gender"); s += "ID号"+id + ", 姓名:" + name + ",性别:" + gender+ "\n" ; } tvJson.setText(s); } catch (JSONException e) { System.out.println("Jsons parse error !"); e.printStackTrace(); } } }下面一起来看一篇关于ubuntu 12.04 编译Android下的ffmpeg 1.2的例子,希望此教程能够对各位同学带来有效的帮助。
编译环境:ubuntu 12.04
NDK版本:android-ndk-r8d
ffmpeg版本:1.2
新建Android工程,在工程目录中创建jni文件夹
1、在jni目录下添加如下文件Android.mk ,内容如下:
include $(all-subdir-makefiles)
2.在jni/ffmpeg下添加Android.mk内容如下:
3.在jni/ffmpeg下添加av.mk
4.在jni/ffmpeg/libavformat下添加Android,mk内容如下:
5. 在jni/ffmpeg/libavcodec下添加Android,mk内容如下:
6.在jni/ffmpeg/libavutil libpostproc libswscale libswresample 下添加Android,mk内容如下:
7.在jni/ffmpeg 下添加可执行文件config.sh, 内容如下
上面PREBUILT PLATFORM的路径自己根据NDK的路径自己更改,并增加可执行权限,命令如下:
chmod +x config.sh
4、命令行到jni/ffmpeg目录下执行
./config.sh
5、修改
a、删除 libavformat libavcodec libavutil libpostproc libswscale libswresample 目录下Makefile下的
include $(SUBDIR)../config.mak
b、删除libavcodec libavutil libswresample目录下Makefile下的
log2_tab.o \
c、修改jni/ffmpeg/config.h下的
#define avrestrict restrict为#define restrict
e、把 ffmpeg/libavutil/time.h更名为avtime.h,
同时修改下面文件中的引用libavutil/time.h为libavutil/avtime.h
ffmpeg/libavformat/avformat.h:211
ffmpeg/libavformat/avio.c:25
ffmpeg/libavformat/hls.c:33
ffmpeg/libavformat/mux.c:39:30
ffmpeg/libavformat/utils.c:40
ffmpeg/libavutil/time.c:36
6、接下来就是编译了
cd ..
命令行到项目project目录下执行
$NDK/ndk-build
问题一:
Install : libffplay.so => libs/armeabi-v7a/libffplay.so
Install : libiptv_media_player_jni.so => libs/armeabi-v7a/libiptv_media_player_jni.so
/home/rds/share/xiongms/android-ndk-r8d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: warning: hidden symbol '__aeabi_atexit' in ./obj/local/armeabi-v7a/libgnustl_static.a(atexit_arm.o)
is referenced by DSO ./obj/local/armeabi-v7a/libffplay.so
/home/rds/share/xiongms/android-ndk-r8d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi-v7a/libswscale.a(swscale.o): in function swScale:jni/libffmpeg/libswscale/swscale.c:426:
error: undefined reference to 'av_get_cpu_flags'
/home/rds/share/xiongms/android-ndk-r8d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi-v7a/libswscale.a(utils.o): in function sws_init_context:jni/libffmpeg/libswscale/utils.c:917:
error: undefined reference to 'av_get_cpu_flags'
/home/rds/share/xiongms/android-ndk-r8d/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi-v7a/libswresample.a(resample.o): in function multiple_resample:jni/libffmpeg/libswresample/resample.c:315:
error: undefined reference to 'av_get_cpu_flags'
将Android.mk中的
LOCAL_STATIC_LIBRARIES := libavformat libavcodec libavutil libpostproc libswscale libswresample libmediaplayer
改为
LOCAL_WHOLE_STATIC_LIBRARIES := libavformat libavcodec libavutil libpostproc libswscale libswresample libmediaplayer
Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序。什么是服务:长期后台运行的没有界面的组件,
android是应用场景:
天气预报:后台的连接服务器的逻辑,每隔一段时间获取最新的天气信息
股票显示:后台的连接服务器的逻辑,每隔一段时间获取最新的股票信息
mp3播放器: 后台长期的播放音乐。
new Thread(){}.start(); 子线程没有界面,也是长期后台运行的。
android系统进程管理是按照一定的规则的:
1.应用程序一旦被打开 通常情况下关闭(清空任务栈)后进程不会停止。方面下一次快速启动。
带来内存不足的问题。
2.Android系统有一套 内存清理机制。 按照优先级去回收系统的内存。
进程分为5个等级的优先级:(从高到低)
1.Foreground process 前台进程 用户正在玩的应用程序对应的进程
2.Visible process 可视进程 用户仍然可以看到这个进程的界面。
3.Service process服务进程 应用程序有一个服务组件在后台运行。
4.Background process 后台进程 应用程序没有服务在运行 并且最小化 (activity onstop)
5.Empty process 空进程 没有任何运行的activity, 任务栈空了
长期后台运行的组件,不要在activity开启子线程。
应该是创建服务,在服务里面开启子线程。
服务的目的:
1.长期后台运行。
2.提高进程的优先级,系统不容易回收掉进程,即便回收了,内存充足的时候,把进程重新创建。
案例场景:使用一个按钮开启服务,在控制台打印服务启动状况。程序界面如下:
2 Android清单文件如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.itheima.testservice"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.itheima.testservice.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.itheima.testservice.MyService"></service>
</application>
</manifest>
3 布局文件如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<Button
android:onClick="click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="开启服务"/>
</RelativeLayout>
4 MainActivity的代码如下:
package com.itheima.testservice;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View view) {
Intent intent = new Intent(this,MyService.class);
startService(intent);
}
}
5 MyService的代码如下:
package com.itheima.testservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
//oncreate ondestory onstart onstop onresume onpause
@Override
public void onCreate() {
System.out.println("服务创建了");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("服务器");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
System.out.println("服务器销毁了");
super.onDestroy();
}
}
6.关于接受者的说明
四大组件:
Activity
Content provider 内容提供者
Broadcast receiver 广播接受者
Service 服务
电台: 发送广播
收音机: 接受广播
android系统下的广播:
电池电量低。
电池充电完毕
短信到来了
程序安装卸载
sd卡卸载 安装
1.写一个类继承广播接受者
2.在清单文件配置关心的动作
3.一旦广播事件发生了,就会执行广播接受者的onreceive方法
Android通过广播接收者调用服务内方法
Android通过广播接收者调用服务内方法 以及利用代码注册广播接收器(4大组件中唯一可以使用代码声明的组件(activity receiver provider service))
服务;
package com.pas.callmethod;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.widget.Toast;
public class MyService extends Service
{
private MyReciver receiver;
@Override
public void onCreate()
{
//采用代码方式注册广播接收者
receiver=new MyReciver();
IntentFilter filter=new IntentFilter();
filter.addAction("com.pas.call");
registerReceiver(receiver, filter);
super.onCreate();
}
@Override
public void onDestroy()
{
unregisterReceiver(receiver);
receiver=null;
super.onDestroy();
}
@Override
public IBinder onBind(Intent arg0)
{
return null;
}
private void method_inservice()
{
Toast.makeText(getApplicationContext(), "我的服务的方法……", Toast.LENGTH_SHORT).show();
}
private class MyReciver extends BroadcastReceiver
{
@Override
public void onReceive(Context arg0, Intent arg1)
{
System.out.println("内部接收者");
method_inservice();
}
}
}
MAINAC:
package com.pas.callmethod;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent=new Intent(this,MyService.class);
startService(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void call(View v)
{
//发送自定义广播
Intent intent=new Intent();
intent.setAction("com.pas.call");
sendBroadcast(intent);
}
}
相关文章
- 下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
- 这篇文章主要介绍了源码分析系列之json_encode()如何转化一个对象,对json_encode()感兴趣的同学,可以参考下...2021-04-22
- 拜读一个开源框架,最想学到的就是设计的思想和实现的技巧。废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过,不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍我也不会照本宣科的翻译...2014-05-31
Android开发中findViewById()函数用法与简化
findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20- 如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
- 夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
- 为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
- 如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
- 深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
- 下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
- java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
- TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
android.os.BinderProxy cannot be cast to com解决办法
本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20- 这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
- 下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
- 首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20
- 下面来给各位简单的介绍一下关于Android开发之PhoneGap打包及错误解决办法,希望碰到此类问题的同学可进入参考一下哦。 在我安装、配置好PhoneGap项目的所有依赖...2016-09-20
用Intel HAXM给Android模拟器Emulator加速
Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。 周末试玩了一下在Eclipse中使...2016-09-20- 在安卓开发时我碰到一个问题就是需要实现全屏,但又需要我们来判断出用户是使用了全屏或非全屏了,下面我分别找了两段代码,大家可参考。 先来看一个android屏幕全屏实...2016-09-20
Android开发中布局中的onClick简单完成多控件时的监听的利与弊
本文章来为各位介绍一篇关于Android开发中布局中的onClick简单完成多控件时的监听的利与弊的例子,希望这个例子能够帮助到各位朋友. 首先在一个控件加上这么一句:and...2016-09-20