package cn.com;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
//文件的拆分和组合
//步骤:
//1 依据源文件大小和每块的大小计算出块数
//2 将每一块写到一个对应的文件
public class FileSeparatorAndUnite {
long rawFileSize;
long blocksNumber;
String rawFileName=null;
String [] allPaths=null;
public static void main(String[] args) {
FileSeparatorAndUnite test=new FileSeparatorAndUnite();
//拆分文件
String [] allPaths=test.separatorFile("F:\\2.jpg", 1024*10);
//组合文件
test.uniteFile(allPaths, "F:\\99.jpg");
}
/**
* @param rawFilePath 待拆分文件的路径
* @param perBlockSize 拆分后每份的大小
* @return 各部分路径的数组集合
*/
private String[] separatorFile(String rawFilePath,long perBlockSize){
File rawFile=new File(rawFilePath);
rawFileName=rawFile.getName();
rawFileSize=rawFile.length();
blocksNumber=getBlocksNumber(rawFileSize,perBlockSize);
allPaths=new String[(int)blocksNumber];
if (blocksNumber==1) {
perBlockSize=rawFileSize;
}
long perPartSize=0;
long perPartBeginPosition=0;
String perPartPath=null;
for (int i = 1; i <=blocksNumber; i++) {
if (i<blocksNumber) {
//每一块的大小就为perBlockSize
perPartSize=perBlockSize;
} else {
//最后一块的大小为总大小-该块的起始位置
perPartSize=rawFileSize-perPartBeginPosition;
}
//得到每一块的path
if (blocksNumber==1) {
perPartPath=rawFilePath+".bat";
} else {
perPartPath=getPerPartFilePath(rawFilePath, i);
}
//操作每一块
//第一次调用时perPartBeginPosition当然为0
System.out.println("该Block开始位置:perPartBeginPosition="+perPartBeginPosition);
writePerPartToFile(rawFilePath, perPartPath, perPartSize, perPartBeginPosition);
//计算每一块的在原文件中的起始位置.
perPartBeginPosition=perPartBeginPosition+perPartSize;
//保存每一块的路径
allPaths[i-1]=perPartPath;
System.out.println("该Block大小:perPartSize="+perPartSize);
}
return allPaths;
}
/**
* @param rawFilePath 原文件路径
* @param perPartFilePath 每部分对应的路径
* @param blockSize 每部分的块大小
* @param beginPosition 每部分在原文件中的开始位置
* @return
*
* rafForReader.read(buffer, 0, everyTimeReadLen)
* 表示:向buffer中读入everyTimeReadLen个字节
*/
public boolean writePerPartToFile(String rawFilePath,String perPartFilePath,long blockSize,long beginPosition){
RandomAccessFile rafForReader=null;
RandomAccessFile rafForWriter=null;
boolean isContinueReading=true;
//每次应该读取的字节数
int everyTimeReadLen=0;
byte [] buffer=new byte[1024*8];
try {
rafForReader=new RandomAccessFile(rawFilePath, "r");
rafForReader.seek(beginPosition);
File perPartFile=new File(perPartFilePath);
rafForWriter=new RandomAccessFile(perPartFile, "rw");
//设置文件大小
rafForWriter.setLength(blockSize);
int writerOff=0;
//设置第一次read()应该读取的字节
if (blockSize>buffer.length) {
everyTimeReadLen=buffer.length;
}else {
everyTimeReadLen=(int)blockSize;
}
while (rafForReader.read(buffer, 0, everyTimeReadLen)!=-1&&isContinueReading){
rafForWriter.seek(writerOff);
writerOff+=everyTimeReadLen;
rafForWriter.write(buffer, 0, everyTimeReadLen);
//动态改变下次该读取的字节数
if (blockSize-writerOff>buffer.length) {
everyTimeReadLen=buffer.length;
}else {
everyTimeReadLen=(int)blockSize-writerOff;
}
//读取完毕
if (everyTimeReadLen==0) {
isContinueReading=false;
}
}
rafForReader.close();
rafForWriter.close();
} catch (Exception e) {
return false;
}
return true;
}
/**
* @param rawFileSize 原文件大小
* @param perBlockSize 每块的大小
* @return 拆分后块数
*/
public long getBlocksNumber(long rawFileSize,long perBlockSize){
if (rawFileSize<=perBlockSize) {
return 1;
} else {
if (rawFileSize%perBlockSize==0) {
return (rawFileSize/perBlockSize);
} else {
return (rawFileSize/perBlockSize)+1;
}
}
}
/**
* @param rawFilePath 原文件路径
* @param blockNumer 当前块号码
* @return 当前块对应的路径
*/
public String getPerPartFilePath(String rawFilePath,int blockNumer){
String perPartFilePath=rawFilePath+".part"+blockNumer;
return perPartFilePath;
}
/**
* @param partsPaths 各部分路径
* @param unitedFilePath 合并后文件路径
*/
public void uniteFile(String [] partsPaths,String unitedFilePath){
try {
File perPartFile=null;
File unitedFile=new File(unitedFilePath);
FileOutputStream fos=new FileOutputStream(unitedFile);
FileInputStream fis=null;
for (int i = 0; i < partsPaths.length; i++) {
perPartFile=new File(partsPaths[i]);
fis=new FileInputStream(perPartFile);
byte [] buffer=new byte[1024*8];
int len=0;
while ((len=fis.read(buffer))!=-1) {
fos.write(buffer, 0, len);
}
}
fis.close();
fos.close();
} catch (Exception e) {
}
}
}
当我们使用ExpandableListView时,实现点击一个GroupView则展开ChidView,那么这个时候,Adapter的大小前后是有变化的。
例如:假设有20个GroupView,每个GroupView都有一个ChildView。当全部GroupView都没有被展开的时候,Adapter的size是20;而当我们展开一个GroupView,显示出一个ChildView的时候,Adapter的size就增加了1。这个必须了解的。
当我们需要添加长按每一个GroupView的时候,获取当前被长按的GroupView的对象时,使用以下方法会出现如下问题。
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.booklist_context, menu);
ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
// 获取当前长按项的下标
final int index = ExpandableListView
.getPackedPositionGroup(info.packedPosition.);
//System.out.println("index----------->" + index);
//System.out.println("Adapter的size------------->" + expListView.getAdapter().getCount());
mBookInfo = (BookInfoDTO) expListView.getAdapter().getitem(index);
}1、当所有的GroupView都没有展开的时候,能够正确获取每个GroupView的下标,通过Adapter就能得到绑定到这个GroupView中的对象。
2,、但,当有的GroupView被展开的时候,就不能使用上面的方法获取绑定在GroupView中的对象了。原因上面已经给出,因为此时的Adapter的size已经被改变。而此时获取的下标是20个GroupView的相对下标。跟Adapter中的下标所对应的对象并不一致,因为Adapter中还包含了被展开的ChildView。
使用以下的方法可以解决上面遇到的问题。
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.booklist_context, menu);
ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
View vin = (View)info.targetView;
mBookInfo = (BookInfoDTO)vin.getTag();
}在设配器中,为每一个GroupView set 一个Tag,也就是我们需要长按时候提出的对象。
这样我们就可以在以上的方法中取出每个GroupView中 的Tag,也就是我们绑定到GroupView中的对象。
这样就不用依赖于变化的Index。
我们知道在 Objective-C 中给 nil 发送消息程序不会崩溃,
Objective-C 是以 C 语言为基础的,
PC 上,在 C 语言中对空指针进行操作,
程序会由于越界访问而出现保护错进而崩溃,
但是 Objective-C 中为什么不会崩溃呢?
原因需要从源代码中寻找,
下面是 objc_msgSend 的 arm 版汇编代码片段:
在 arm 的函数调用过程中,
一般用 r0-r4 传递参数,
用 r0 传递返回值。
对应 objc_msgSend,第一个参数为 self,返回值也是 self,都放在 r0(a1)中。
/********************************************************************
* idobjc_msgSend(idself, SELop, ...)
* On entry: a1 is the message receiver,
* a2 is the selector
********************************************************************/
ENTRY objc_msgSend
# check whether receiver is nil
teq a1, #0
moveq a2, #0
bxeq lr
teq 指令说明:
TEQ Rn, Operand2 The TEQ instruction performs a bitwise Exclusive OR operation on the value in Rn and the value of Operand2.
测试 self 是否为空。
moveq 指令说明:
如果self为空,则将 selector 也设置为空。
bx 指令说明:
在 arm 中 bx lr 用来返回到调用子程序的地方(即:返回到调用者),此处是:如果 self 为空,就返回到调用 objc_msgSend 的地方继续执行。
总之:
如果传递给 objc_msgSend 的 self 参数是 nil,该函数不会执行有意义的操作,直接返回。