2008年4月研一在读的时候,在百度空间开了一个技术类博客,写了七八十篇文章,主要记录了自己自学以及在企业实习时候的一些经验总结。但从2010年开始,由于各种原因,就很少再写博客了。而且发现百度空间越来越不适合作为一个技术类的博客来使用,花哨有余而简洁不足,另外对代码的支持也比较糟糕。
其实还是应该平时写点东西的,因为写东西的过程,也是一个知识反刍和理顺的过程,而且写下来的内容还可以供以后查询。
希望接下来会经常有时间吧。
以前在百度空间的博客链接:http://hi.baidu.com/mcu99,本来想把里面的文章都搬过来的,但是今天又看了一下,觉得一是麻烦,二是大多数文章都是08、09年发的,也比较基础或过时了,就不搬家了,把其中涉及到Android部分的几篇统一整理在下面。另外,与单片机相关的部分文章,只把原文标题(不能发链接,被封2次了,郁闷)附在本文最后。
【09.03.24】在Ubuntu中和Android中添加开机自启动的守护进程
昨天和今天实验了向Android中添加一个守护进程,鼓捣了2天,小有点收获,自己编写的进程添加进这两个操作系统的开机启动中了。但离完全成功似乎还有些距离。另外今天还看了下解压、修改Android的ramdisk.img的方法。
先把我的守护进程(daemon09.c)发到这里
/************************程序开始**************************/
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
int i=0,fd;
pid_t pid;
struct rlimit rl;
char str[]="Writing Test is going...\n";
umask(0);
if(getrlimit(RLIMIT_NOFILE,&rl)<0)
printf("getrlimit函数调用出现错误!\n");
if((pid=fork())<0)
{
printf("fork出现错误!\n");
exit(1);
}
else if(pid>0)//父进程退出
{
printf("父进程退出,它的ID为%d,它的子进程ID为%d\n",getpid(),pid);
exit(0);
}
//子进程
sleep(2);
printf("子进程ID为%d\n",getpid());
setsid();
if((pid=fork())<0)
{
printf("fork出现错误!\n");
return -1;
}
else if(pid>0)//第一个子进程退出
{
printf("第一个子进程退出,它的ID为%d,它的子进程ID为%d\n",getpid(),pid); /*这个printf的内容可以被输出,貌似是因为它所在的进程虽然失去了终端,但它是一个会话组的首进程,因此看到有printf后,自己又申请了一个终端?*/
exit(0);
}
//第二个子进程
printf("不会输出这一行。\n");/*这个printf的内容将不会在屏幕上输出,原因可能是因为它所在的进程此时已经不是一个会话组的首进程,无法重新申请获得终端?*/
chdir("/");
if(rl.rlim_max==RLIM_INFINITY)
rl.rlim_max=1024;
printf("%d",(int)rl.rlim_max);
for(i=0;i
因为andorid也是基于LINUX内核的,因此我想一开始先试验一下是否可以把这个进程在Ubuntu中开机自启动。
通过在/etc/rc.local文件中加入了一行“/usr/myfiles/test/daemon09”,成功让daemon09在Ubuntu开机时启动了。(注意,这里在LINUX下启动的话,上面的C程序中红字部分可能需要改变一下,因为Ubuntu下没有/data这样一个路径)
在Ubuntu Linux下成功之后,就转而看看在android下是否能行。android启动后的根目录下的init.rc似乎是管这个的(源码目录/system/core/rootdir/init.rc,make后就是它),于是我试着对源文件做修改再make运行,但一开始总是不得要领,不知道init.rc中的语法是什么。后来在google论坛上找了下,原来init.rc的内容在“android源码路径/system/core/init/readme.txt“内有说明。于是根据那个搞了几个小时(没办法,本人是菜鸟),基本上弄出来了,android模拟器启动后,自己写的进程也成功加入了。方法如下:
在init.rc的那一堆以service开头的语句中插入一段新的service语句。格式如下:
#my_deamon_test是服务名称,后面跟的是你编写的要开机启动的程序及路径,这里的路径是android系统中的路径
service my_deamon_test /data/deamon_test
oneshot(不加这一句似乎不行,少了它可能你的程序启动不起来)
oneshot选项的含义(在android源码路径/system/core/init/readme.txt内有说明):
Do not restart the service when it exits.
【09.03.25】Android下动态链接库.so调用的简单例子
在这篇文章中(http://hi.baidu.com/mcu99/blog/item/216f1fce17e1c00b92457edd.html),我在Android下使用dlopen函数调用.so文件没有成功,于是只得改用在编译过程中指定.so文件的方式加以调用,这次在Android下倒是通过了。
先在(Android源码目录)/development/目录下建立一个文件夹,比如起名叫 test123
在test123目录下建立以下三个文件:test.c、max.c、Android.mk
/**************** test.c 主程序 ********************/
#include
extern max(int,int);
int main()
{
int a=5,b=3,c;
c=max(a,b);
printf("%d与%d相比,%d大。\n",a,b,c);
return 0;
}
/**************** max.c 将要被编译成动态链接库 ********************/
int max(int x,int y)
{
return x>y?x:y;
}
/**************** Android.mk 是Android系统中的Makefile文件 ********************/
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
max.c
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE:= libmax
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
test.c
LOCAL_CFLAGS=-lmax
LOCAL_LDFLAGS:= \
-Lout/target/product/generic/obj/lib
LOCAL_SHARED_LIBRARIES := \
libmax
LOCAL_MODULE:= test
include $(BUILD_EXECUTABLE)
建立好这三个文件后,用cd命令回到Android源码目录下,执行make test。注意不是文件夹名 test123
再用命令
adb push (Android源码目录)/out/target/product/generic/system/bin/test /data
adb push out/target/product/generic/system/lib/libmax.so /system/lib
将test程序和编译好的libmax.so送进模拟器下相应的位置,其中libmax.so应该放到 /system/lib下,不然test找不到它,test程序就无所谓了,放在什么地方都行。
用adb shell进入模拟器的控制台,当然事先要先把emulator启动起来,否则adb shell会失败。
进入test所在目录,./test运行
# ./test
5与3相比,5大。
【09.03.25】将自己的程序永久写入Android的/system/bin的方法
Android模拟器运行之后,/system目录即为只读属性。如果想把自己的程序传到该目录下运行,就会发现无法成功。当然,使用adb remount命令可以暂时去除它的只读限制,可以向里面传文件,但是一旦重启Android模拟器,再次使用adb shell进入模拟器终端时就会发现,自己传进去的文件在重启之后被清除掉了。
当然,你可以把文件传到/data文件夹下,这个文件夹不用remount就可写,而且重启模拟器之后自己的文件也不会被清空。但是如果我想做一个较为底层的程序,或者开机启动的程序,每次开机在/data下启动总是显得怪怪的。最好能让自己编写的程序也能享受到Android自带的那些程序的地位,每次运行在system/bin目录下。这可以通过把自己的程序代码加在Android源码中,然后重新make的方法实现。
首先根据你工程的性质,在Android源码的相应位置建立一个文件夹。比如我的这个程序如果和硬件有关的话,可以在hardware下建立一个叫做my_hardware的文件夹,然后把自己的程序源码放在里面,比如叫hard.c。
再给这个程序写一个makefile文件,好让make的时候可以自动找到你的程序并对它进行编译。起名字叫做Android.mk,这个名字不能随便起,否则make不认识。把这个Android.mk和hard.c都放在my_hardware下面。
# Android.mk文件内容举例
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
hard.c
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE:= myhard
include $(BUILD_EXECUTABLE)
做好这些之后,回到总的Android源码目录下,然后执行make,如果你之前已经make过了,那么这个过程会很快,几分钟吧。因为和上一次make相比,源码改动很少,只是加了1个文件夹和2个文件而已。如果你这是第一次make,那么会比较慢,或许大约要1-2个小时,速度应该也和机器配置有关。
make成功之后,运行emulator模拟器,用adb shell进入模拟器终端,cd /system/bin,就可以找到你的程序了(注意名字是myhard,不是hard,也不是my_hardware,这里产生的程序名字由上面程序中红色字体的那一行控制)。而且再次启动之后也不会丢失。
【09.03.30】Android中使用C++程序读写Parcel的简单例子
Android中的Parcel类困惑了我好一段时间(当然现在也没把Parcel完全弄明白),查了一些资料也不知道它里面是以一种什么格式存储数据的。因为它对外提供的读写函数都没有提到具体一个读写函数到底是读写Parcel中的哪一块地址里的数据。比如网上有这么两段代码:
public void writeToParcel(Parcel out) { //当前数据写入到Parcel中
out.writeInt(left);
out.writeInt(top);
out.writeInt(right);
out.writeInt(bottom);
}
public void readFromParcel(Parcel in) { //从Parcel中读取数据
left = in.readInt();
top = in.readInt();
right = in.readInt();
bottom = in.readInt();
}
其中无论是读函数writeInt(),还是写函数readInt(),参数中都没有提及是往Parcel中的具体哪块地方写入left变量,又是从Parcel的哪块地方读出数值赋给right变量。后来看了一些源码,包括Parcel.h和Parcel.cpp。感觉Parcel可能类似一个一维的数据串,各种变量都可以往里面写(通过Parcel提供的针对各种变量的具体函数),有一个位置指针指向这个一维的数据串中的某个位置,这个位置就是默认的读写的位置。也就是说,如果现在调用读写函数,就是读写当前位置指针处的数据,读写结束后,把位置指针向后移动一块空间(跨越的长度正好是你上次调用读写函数读过或者写过数据的长度),继续准备对下一部分空间进行读写操作。
为了验证这个想法,编写了一段对Parcel进行读写的C++程序。由于对C++较为生疏,编写这个小程序花了一下午的时间,不过最后总算是通过了。现在把代码贴在下面:
/********************** 以下是 Parcel_test.cpp 程序 ****************************/
/*这里的文件扩展名应该是.cpp,也就是c++文件,如果此处用c写程序,扩展名为.c的话,加入#include 这句后可能将出现错误*/
#include
#include
#include
int main()
{
using namespace android;/*这一行一开始没有加上,结果总是出错,提示Parcel、status_t、NO_ERROR都没有定义,郁闷了好久,心想在include中加入了utils/Parcel.h呀,而且在Android.mk中也加入了Parcel.h所在的库libutils。后来看了Android源代码才推测出了可能要加入这句*/
status_t status;
size_t parcel_position;
int intp=987654;
char charp='g';
float floatp=3.14159;
double doublep=12345.6789012;
long longp=1234567890;
char *stringp="this is my parcel test!";
Parcel p;
parcel_position = p.dataPosition();/*备份位置*/
printf("当前Parcel的读写位置是:%d\n",parcel_position);
/*************写int类型***************/
status=p.writeInt32(intp);
if (status==NO_ERROR)
printf("write int type success!\n");
else
printf("write int type fail,the errno=%d\n",errno);
/*************写char类型***************/
status=p.writeInt32(charp);
if (status==NO_ERROR)
printf("write char type success!\n");
else
printf("write char type fail,the errno=%d\n",errno);
/*************写Float类型***************/
status=p.writeFloat(floatp);
if (status==NO_ERROR)
printf("write Float type success!\n");
else
printf("write Float type fail,the errno=%d\n",errno);
/*************写Double类型***************/
status=p.writeDouble(doublep);
if (status==NO_ERROR)
printf("write Double type success!\n");
else
printf("write Double type fail,the errno=%d\n",errno);
/*************写long类型***************/
status=p.writeInt64(longp);
if (status==NO_ERROR)
printf("write long type success!\n");
else
printf("write long type fail,the errno=%d\n",errno);
/*************写String类型***************/
status=p.writeCString(stringp);
if (status==NO_ERROR)
printf("write String type success!\n");
else
printf("write String type fail,the errno=%d\n",errno);
/*************将parcel读写位置置回原位***************/
p.setDataPosition(parcel_position);
/*************读出变量***************/
printf("读出的int类型变量为:%d\n",p.readInt32());
printf("读出的char类型变量为:%c\n",(char)p.readInt32());
printf("读出的Float类型变量为:%f\n",(float)p.readFloat());
printf("读出的Double类型变量为:%f\n",(double)p.readDouble());
printf("读出的long类型变量为:%ld\n",(long)p.readInt64());
printf("读出的字符串为:%s\n",p.readCString());
}
/********************** 以上是 Parcel_test.cpp 程序 ****************************/
/***************下面是对应的makefile文件******************/
/********************** 以下是Android.mk文件 ****************************/
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
parcel_test.cpp
LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils
LOCAL_CFLAGS :=
LOCAL_MODULE:= parcel_test
include $(BUILD_EXECUTABLE)
/********************** 以上是Android.mk文件 ****************************/
把这两个文件放在Android源码目录下的development目录下的parcel_test文件夹中(dl文件夹是新建的),然后在终端中使用root权限进入到Android源码目录下,执行 make parcel_test。成功后将会在android源码目录/out/target/product/generic/system/bin/中生成parcel_test可执行文件。
使用以下命令将它们放入Android模拟器,注意要先启动emulator
adb push Android源码目录/out/target/product/generic/system/bin/parcel_test /data
进入data文件夹执行
adb shell
# cd data
# ./parcel_test
////////////////////////// 以下是程序运行结果 //////////////////////////////
当前Parcel的读写位置是:0
write int type success!
write char type success!
write Float type success!
write Double type success!
write long type success!
write String type success!
读出的int类型变量为:987654
读出的char类型变量为:g
读出的Float类型变量为:3.141590
读出的Double类型变量为:12345.678901
读出的long类型变量为:1234567890
读出的字符串为:this is my parcel test!
==============================
由此可知,Parcel中数据的存储结构的确正如之前猜测的那样,它是一个数据串,有一个位置指针标志着它当前的读写位置。写入数据的时候可以遵从某种约定,按照某种顺序把数据依此写进去,读的时候再按照同样的顺序依此把数据读出来。估计应该也可以通过设置指针位置的函数跳过某些数据进行读取或写入,但我这里没有做实验。
另外,如果写完之后再读,那么读之前记得要把位置指针重新置为要读的数据开始的地方,因为之前写的时候数据指针已经移动过了。
【09.06.02】Android环境下通过SOCKET传递Parcel包并解出数据的例子
之前做过了在android下通过socket发送数据的实验,也做过了parcel包的制作和解包的实验(这两个实验的源程序之前都在本博客的其他文章中贴过)。昨天和今天把这两个过程合并了起来:即在Android环境下,甲程序(C++程序)将数据封装在Parcel中,并把Parcel发送到SOCKET;乙程序(C++程序)通过SOCKET接收到Parcel包,并解出封装在其中数据。
原先认为分开的过程都做过了,合并起来应该不是什么问题。但昨天一下午却未能成功,一直到今天上午才基本上弄了出来。现将程序的源代码放在下面:
程序里面的一些无用的调试信息没有去掉,都已经注释掉了,不影响编译运行。如果觉得影响阅读,可先复制下来,再将其中的调试信息删除即可。
/****************** server program (server.cpp)*****************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace android;
Parcel p;
void write_Parcel();
int main()
{
int sockfd,newfd,ret,send_num,send_num_total=0;
char buf[200];
struct sockaddr_in server_addr;
// remove("/data/server.socket");/*不管有没有,先删除一下,否则如果该文件已经存在的的话,bind会失败。*/
// memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;
// strcpy(server_addr.sin_path,"/data/server.socket");
server_addr.sin_addr.s_addr=INADDR_ANY;
server_addr.sin_port=htons(5678);
sockfd=socket(AF_INET,SOCK_STREAM,0);
printf("this is the socket_server *TCP* mode.\n");
if (sockfd<0)
{
printf("调用socket函数建立socket描述符出错!\n");
exit(1);
}
printf("@调用socket函数建立socket描述符成功!\n");
ret=bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(server_addr));
if (ret<0)
{
printf("调用bind函数绑定套接字与地址出错!\n");
exit(2);
}
printf("调用bind函数绑定套接字与地址成功!\n");
ret=listen(sockfd,4);
if (ret<0)
{
printf("调用listen函数出错,无法宣告服务器已经可以接受连接!\n");
exit(3);
}
printf("调用listen函数成功,宣告服务器已经可以接受连接请求!\n");
newfd=accept(sockfd,NULL,NULL);/*newfd连接到调用connect的客户端*/
if (newfd<0)
{
printf("调用accept函数出错,无法接受连接请求,建立连接失败!\n");
exit(4);
}
printf("调用accept函数成功,服务器与客户端建立连接成功!\n");
write_Parcel();
while (1)
{
send_num=send(newfd,p.data(), p.dataSize(),MSG_DONTWAIT);
if (send_num<0)
printf("调用send函数失败!");
else
{
send_num_total+=send_num;
printf("调用send函数成功,本次发送%d个字节。目前共发送了%d个字节的数据。\n",send_num,send_num_total);
}
sleep(2);
}
}
/****************** 向Parcel内写入数据 ********************/
void write_Parcel()
{
status_t status;
size_t parcel_position;
int intp=987654;
char charp='g';
float floatp=3.14159;
double doublep=12345.6789012;
long longp=1234567890;
char *stringp="this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!";
// printf("1:Parcel包的大小是%d字节,Parcel结构体的大小是%d字节\n",sizeof(p),sizeof(Parcel));
// readParcel(&p);
parcel_position = p.dataPosition();/*备份位置*/
// printf("当前Parcel的读写位置是:%d\n",parcel_position);
/*************写int类型***************/
status=p.writeInt32(intp);
if (status==NO_ERROR)
printf("write int type success!\n");
else
printf("write int type fail,the errno=%d\n",errno);
/*************写char类型***************/
status=p.writeInt32(charp);
if (status==NO_ERROR)
printf("write char type success!\n");
else
printf("write char type fail,the errno=%d\n",errno);
/*************写Float类型***************/
status=p.writeFloat(floatp);
if (status==NO_ERROR)
printf("write Float type success!\n");
else
printf("write Float type fail,the errno=%d\n",errno);
/*************写Double类型***************/
status=p.writeDouble(doublep);
if (status==NO_ERROR)
printf("write Double type success!\n");
else
printf("write Double type fail,the errno=%d\n",errno);
/*************写long类型***************/
status=p.writeInt64(longp);
if (status==NO_ERROR)
printf("write long type success!\n");
else
printf("write long type fail,the errno=%d\n",errno);
/*************写String类型***************/
status=p.writeCString(stringp);
if (status==NO_ERROR)
printf("write String type success!\n");
else
printf("write String type fail,the errno=%d\n",errno);
// printf("2:Parcel包的大小是%d字节,Parcel结构体的大小是%d字节\n",sizeof(p),sizeof(Parcel));
// readParcel(&p);
/*************将parcel读写位置置回原位***************/
p.setDataPosition(parcel_position);
printf("3:Parcel包的大小是%d字节,Parcel结构体的大小是%d字节\n",sizeof(p),sizeof(Parcel));
// readParcel(&p);
/*************读出变量***************/
printf("读出的int类型变量为:%d\n",p.readInt32());
printf("读出的char类型变量为:%c\n",(char)p.readInt32());
printf("读出的Float类型变量为:%f\n",(float)p.readFloat());
printf("读出的Double类型变量为:%f\n",(double)p.readDouble());
printf("读出的long类型变量为:%ld\n",(long)p.readInt64());
printf("读出的字符串为:%s\n",p.readCString());
printf("4:Parcel包的大小是%d字节,Parcel结构体的大小是%d字节\n",sizeof(p),sizeof(Parcel));
// readParcel(&p);
}
/****************** client program (client.cpp)*****************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace android;
Parcel p;
int main()
{
int sockfd,ret,recv_num,recv_num_total=0;
char buf[124];
struct sockaddr_in server_addr;
// memset(&server_addr,0,sizeof(server_addr));
server_addr.sin_family=AF_INET;
// strcpy(server_addr.sin_path,"/data/server.socket");
server_addr.sin_addr.s_addr=INADDR_ANY;
server_addr.sin_port=htons(5678);
sockfd=socket(AF_INET,SOCK_STREAM,0);
printf("this is the socket_client *TCP* mode.\n");
if (sockfd<0)
{
printf("调用socket函数建立socket描述符出错!\n");
exit(1);
}
printf("调用socket函数建立socket描述符成功!\n");
ret=connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(server_addr));
if (ret<0)
{
printf("调用connect函数失败,客户端连接服务器失败!\n ");
exit(2);
}
printf("调用connect函数成功,客户端连接服务器成功!\n");
while (1)
{
recv_num=recv(sockfd,buf,sizeof(buf),0);
p.setData((unsigned char *) buf, sizeof(buf));
if (recv_num<0)
printf("调用recv接收失败!\n");
else if(recv_num>0)
{
recv_num_total+=recv_num;
printf("调用recv函数成功,本次接收到%d个字节。共收到%d个字节的数据。\n",recv_num,recv_num_total);
printf("服务器端:调用recv接收成功!本次接收到%d个字节,共收到%d个字节的数据。\n",recv_num,recv_num_total);
printf("读出的int类型变量为:%d\n",p.readInt32());
printf("读出的char类型变量为:%c\n",(char)p.readInt32());
printf("读出的Float类型变量为:%f\n",(float)p.readFloat());
printf("读出的Double类型变量为:%f\n",(double)p.readDouble());
printf("读出的long类型变量为:%ld\n",(long)p.readInt64());
printf("读出的字符串为:%s\n",p.readCString());
}
// else/*收到数据为0,表明服务器与客户端的连接已经中断*/
// {
// printf("与客户端的连接已中断,当前共收到%d个字节的数据。服务器将再次等待客户端的连接。\n",recv_num_total);
// newfd=accept(sockfd,NULL,NULL);/*当客户端退出后,再次开始接收客户端的连接*/
// }
sleep(2);
}
}
/****************** Android Makefile文件 (Android.mk)*****************/
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
server.cpp
LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE:= socket_server_s
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
client.cpp
LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE:= socket_client_s
include $(BUILD_EXECUTABLE)
将这三个文件放在一个文件夹下,我给这个文件夹起了个名字叫parcel_send,其实叫什么都无所谓。然后把parcel_send放到android源代码根目录下的/development目录下面。
用emulator命令启动模拟器后,再另打开一个linux终端,用cd命令进入android源代码根目录下,su取得root权限后,先后执行“make socket_server_s”和“make socket_client_s”对程序进行编译。
编译通过后,将生成的可执行文件送入android环境的/data目录下:
adb push (android源代码根目录)/out/target/product/generic/system/bin/socket_server_s /data
adb push (android源代码根目录)/out/target/product/generic/system/bin/socket_client_s /data
分别再开两个linux终端,用adb shell命令进入模拟器终端,cd /data,先后执行"./socket_server_s"和“./socket_client_s”,即可看到程序运行。
/////////////////////////////////////////////服务器端程序运行结果/////////////////////////////////////////////
# ./socket_server_s
this is the socket_server *TCP* mode.
@调用socket函数建立socket描述符成功!
调用bind函数绑定套接字与地址成功!
调用listen函数成功,宣告服务器已经可以接受连接请求!
调用accept函数成功,服务器与客户端建立连接成功!
write int type success!
write char type success!
write Float type success!
write Double type success!
write long type success!
write String type success!
3:Parcel包的大小是48字节,Parcel结构体的大小是48字节
读出的int类型变量为:987654
读出的char类型变量为:g
读出的Float类型变量为:3.141590
读出的Double类型变量为:12345.678901
读出的long类型变量为:1234567890
读出的字符串为:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
4:Parcel包的大小是48字节,Parcel结构体的大小是48字节
调用send函数成功,本次发送124个字节。目前共发送了124个字节的数据。
调用send函数成功,本次发送124个字节。目前共发送了248个字节的数据。
调用send函数成功,本次发送124个字节。目前共发送了372个字节的数据。
调用send函数成功,本次发送124个字节。目前共发送了496个字节的数据。
调用send函数成功,本次发送124个字节。目前共发送了620个字节的数据。
调用send函数成功,本次发送124个字节。目前共发送了744个字节的数据。
调用send函数成功,本次发送124个字节。目前共发送了868个字节的数据。
…………………………
/////////////////////////////////////////////客户端程序运行结果/////////////////////////////////////////////
# ./socket_client_s
this is the socket_client *TCP* mode.
调用socket函数建立socket描述符成功!
调用connect函数成功,客户端连接服务器成功!
调用recv函数成功,本次接收到124个字节。共收到124个字节的数据。
服务器端:调用recv接收成功!本次接收到124个字节,共收到124个字节的数据。
读出的int类型变量为:987654
读出的char类型变量为:g
读出的Float类型变量为:3.141590
读出的Double类型变量为:12345.678901
读出的long类型变量为:1234567890
读出的字符串为:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
调用recv函数成功,本次接收到124个字节。共收到248个字节的数据。
服务器端:调用recv接收成功!本次接收到124个字节,共收到248个字节的数据。
读出的int类型变量为:987654
读出的char类型变量为:g
读出的Float类型变量为:3.141590
读出的Double类型变量为:12345.678901
读出的long类型变量为:1234567890
读出的字符串为:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
调用recv函数成功,本次接收到124个字节。共收到372个字节的数据。
服务器端:调用recv接收成功!本次接收到124个字节,共收到372个字节的数据。
读出的int类型变量为:987654
读出的char类型变量为:g
读出的Float类型变量为:3.141590
读出的Double类型变量为:12345.678901
读出的long类型变量为:1234567890
读出的字符串为:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
调用recv函数成功,本次接收到124个字节。共收到496个字节的数据。
服务器端:调用recv接收成功!本次接收到124个字节,共收到496个字节的数据。
读出的int类型变量为:987654
读出的char类型变量为:g
读出的Float类型变量为:3.141590
读出的Double类型变量为:12345.678901
读出的long类型变量为:1234567890
读出的字符串为:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
调用recv函数成功,本次接收到124个字节。共收到620个字节的数据。
服务器端:调用recv接收成功!本次接收到124个字节,共收到620个字节的数据。
读出的int类型变量为:987654
读出的char类型变量为:g
读出的Float类型变量为:3.141590
读出的Double类型变量为:12345.678901
读出的long类型变量为:1234567890
读出的字符串为:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
调用recv函数成功,本次接收到124个字节。共收到744个字节的数据。
服务器端:调用recv接收成功!本次接收到124个字节,共收到744个字节的数据。
读出的int类型变量为:987654
读出的char类型变量为:g
读出的Float类型变量为:3.141590
读出的Double类型变量为:12345.678901
读出的long类型变量为:1234567890
读出的字符串为:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
调用recv函数成功,本次接收到124个字节。共收到868个字节的数据。
服务器端:调用recv接收成功!本次接收到124个字节,共收到868个字节的数据。
读出的int类型变量为:987654
读出的char类型变量为:g
读出的Float类型变量为:3.141590
读出的Double类型变量为:12345.678901
读出的long类型变量为:1234567890
读出的字符串为:this is my parcel test! this is my parcel test! this is my parcel test! this is my parcel test!
…………………………………………
=============================
可以看到,例子程序成功实现了将数据封装入parcel、发送到socket、从socket接收、从接收到的parcel中解出数据的过程。
存在的问题:在客户端程序中,有一句char buf[
124];,这个buf是用来接收socket传过来的parcel包的,但问题是,客户端无法确定parcel包的大小。在实验中,最开始并不是用的
124这个数据,而是一个小的多的数,结果解出的数据就出现了混乱和丢失。而使用sizeof(p)和sizeof(Parcel)均无法得出正确的parcel包大小(这一点可以从服务器端运行结果中看出,在parcel包大小为124的情况下,仍然显示“Parcel包的大小是48字节,Parcel结构体的大小是48字节“)。因此无奈之下,只能通过查看服务端程序的send函数的返回值,才得知过程中发送了124个字节,因此再修改客户端程序,也才有了这句的设定:char buf[
124];。
当然,如果在实际应用中,接收和发送端程序都是自己编写,自己心里有数的话,那么也无所谓。只要发送多少字节,然后接收端就相应接多少字节就可以了。但是如果不能满足这个条件,也就是说发送端发送的字节数不确定或者不知道的话,就有一定麻烦了。对于这个问题我的解决思路有三种(但未经严格实验验证):
1、把char buf[
124];中的124换为发送端可能发送的最大数值,这样就都可以接收到了,而不会出现丢失数据。
2、发送端在传送parcel数据之前,先把数据包的大小发过来。接收端根据这个信息再做适当调整。
3、参考android源代码的解决方法,android源代码的“/system/core/libcutils/record_stream.c”中有这方面的较好的解决方法,可以直接调用它里面提供的函数,或根据自己的需要在它的基础上加以修改。
【11.04.02】在Android C/C++层添加LOG调试(LOGI\LOGD\LOGE...)输出支持
最近在研究Android 2.3.3源代码的C/C++层,需要对代码进行一些调试,但是奇怪的是,直接添加LOGD("XXXXXXXX");,使用logcat却看不到任何输出,换成LOGI、LOGV、LOGW、LOGE也没有效果。于是在网上查找解决方法,经过几次试验,终于找到了,现在贴到下面备忘:
第一步:在对应的mk文件中加入:
LOCAL_LDLIBS := -llog
第二步:在要使用LOG的cpp文件中加入:
#include
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "keymatch", __VA_ARGS__)
第三步:这样就可以使用了:LOGD("我要看到的调试信息^_^"); 这样,在logcat端看到的输出是:D/keymatch( 32):我要看到的调试信息^_^ 如果想改变输出中的各项内容,可以参考相应颜色的标示,比如,如果想定义LOGE,就可以把上面的ANDROID_LOG_DEBUG改成ANDROID_LOG_ERROR,同理,LOGI神马的也都以此类推:
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "ProjectName", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , "ProjectName", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , "ProjectName", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN , "ProjectName", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , "ProjectName", __VA_ARGS__)
当然,如果不嫌麻烦,也可以直接使用__android_log_print函数,而不define定义LOGxxx 如果还想了解更详细的内容,也可以搜索这篇文章: 《在android 输出log 信息 用于调试》 另外,有文章称此方法在编译动态库的时候可能会出问题,会提示cannot find -llog的错误。意思是找不到liblog.so这个库文件。 因此需要改成
LOCAL_LDLIBS:= -L$(SYSROOT)/usr/lib -llog 才可以正常编译。但是我这边编译动态库的时候,好像不用这样改也行,没发现编译时提示“cannot find -llog”的错误。如果您在实践过程中发现有这样的问题,可以参考该文章:《如何在android native编程中使用logCat》
【11.06.01】反编译与回编译android的framework.jar(其他jar文件也可参考)
最近对android 2.3.3的framework.jar进行了一些反编译和回编译的操作,写下来备忘。
一、framework.jar反编译为smali文件
1、下载smali-1.2.6.jar和baksmali-1.2.6.jar这两个工具(下载地址:http://code.google.com/p/smali/downloads/list)
2、将framework.jar中的classes.dex解压出来(好像不解压,直接用framework.jar也行)
3、使用baksmali.jar对classes.dex进行反编译(前提是安装了jdk,并且设置好了环境变量),执行命令:
java -jar baksmali-1.2.6.jar classes.dex -o out/
其中classes.dex是要反编译的文件,out/是要把反编译后的文件存放到的文件夹,如果不是在当前目录下,那么baksmali-1.2.6.jar还要加上路径
这样就OK了,在out文件夹中可以看到一堆扩展名为.smali的文件,用记事本就可以打开它们,从中可以窥到一些信息。但是与.java文件还是有一些不同,我也不太清楚这是什么结构。
二、smali文件回编译为classes.dex
1、一条命令就OK了:java -jar smali-1.2.6.jar out/ -o classes.dex
2、再把编译好的classes.dex放回到framework.jar中就行了(可以使用winrar、winzip之类的工具作为辅助)。
三、framework.jar反编译为.class和.java文件
1、下载以下工具:
(1)dex2jar(http://code.google.com/p/dex2jar/)
(2)xjad(http://www.skycn.com/soft/41898.html)或jd-gui(http://java.decompiler.free.fr/?q=jdgui)
2、使用dex2jar对framework.jar进行转换,执行命令:dex2jar.bat framework.jar 将会生成一个framework.jar.dex2jar.jar
3、直接对该jar文件解压,可以看到里面都是.class文件了
4、如果还需要转换成.java文件,可以使用xjad或jd-gui,均可将class文件变为java文件。
注:dex2jar工具也可以处理.dex文件,因此也可以不直接处理framework.jar。而是先将framework.jar解压,生成classes.dex后再处理也行。
与单片机C语言相关的部分文章
【09.07.29】哈希(Hash)表操作实验(基于C语言)
【09.08.12】单片机内存开辟过多带来的程序异常
【09.08.13】单片机IO口模拟串口程序(发送+接收)
【09.08.15】Keil中C语言与汇编语言混合编程需要注意的几个地方
【09.08.31】单片机汇编延时计算小程序(附源码)
【09.09.16】单片机IO管脚采集连续大量数据的两种思路
【09.10.13】STC单片机使用PCA扩展外部中断测试程序
【09.11.18】printf("%d",a.num+1)与printf("%d",++a.num)何时输出结果不同?
【10.01.22】STC单片机掉电及外部中断唤醒测试程序