zoukankan      html  css  js  c++  java
  • Android 动态加载 .SO

    需求:

    有时候应用修复了native层一个小BUG,应用需要更新了,但是用户必须下载整个APK包进行安装,而我们需要的只是替换SO

    于是想,能不能加载自定义路径下的 SO 文件呢

    答案是完全没问题:

    使用系统方法:

    void java.lang.System.load(String pathName)

    但是有一点,pathName 路径必须有执行权限,意思就是说我们不能加载SD卡上的SO,因为没有执行权限

    那也没关系,我们复制到应用私有目录下就OK嘛。

    看码

    private void load() {
            File dir = getDir("libs", Context.MODE_PRIVATE);
            File soFile = new File(dir, "libTestJNI.so");
            FileUtils.assetToFile(this, "libTestJNI.so", soFile);
            
            try {
                System.load(soFile.getAbsolutePath());
            } catch (Exception e) {
            }
        }

    这样就完全OK,

    我们只需要架个服务器,每次启动时动态监测 SO 文件有没有更新,有则下载SO,然后加载,这样就可以避免用户安装新的应用,

    要知道重新安装应用的用户体验是很差的,要让用户无感知的更新他。

      1 package com.edroid.common.utils;
      2 
      3 import java.io.File;
      4 import java.io.FileInputStream;
      5 import java.io.FileOutputStream;
      6 import java.io.IOException;
      7 import java.io.InputStream;
      8 import java.lang.ref.WeakReference;
      9 import java.lang.reflect.Method;
     10 import java.nio.channels.FileLock;
     11 import java.util.ArrayList;
     12 import java.util.List;
     13 import java.util.Locale;
     14 
     15 import org.apache.http.util.ByteArrayBuffer;
     16 
     17 import android.content.Context;
     18 import android.content.res.AssetManager;
     19 import android.os.Environment;
     20 import android.os.StatFs;
     21 import android.util.Log;
     22 
     23 /**
     24  * 文件操作工具类
     25  * 
     26  * @author Yichou 
     27  * 
     28  * @date 2013-08-31
     29  */
     30 public final class FileUtils {
     31     static final String LOG_TAG = "FileUtils";
     32 
     33     /**
     34      * 操作成功返回值
     35      */
     36     public static final int SUCCESS = 0;
     37 
     38     /**
     39      * 操作失败返回值
     40      */
     41     public static final int FAILED = -1;
     42 
     43     private static final int BUF_SIZE = 32 * 1024; // 32KB
     44 
     45     public static final int S_IRWXU = 00700;
     46     public static final int S_IRUSR = 00400;
     47     public static final int S_IWUSR = 00200;
     48     public static final int S_IXUSR = 00100;
     49 
     50     public static final int S_IRWXG = 00070;
     51     public static final int S_IRGRP = 00040;
     52     public static final int S_IWGRP = 00020;
     53     public static final int S_IXGRP = 00010;
     54 
     55     public static final int S_IRWXO = 00007;
     56     public static final int S_IROTH = 00004;
     57     public static final int S_IWOTH = 00002;
     58     public static final int S_IXOTH = 00001;
     59     
     60     private static WeakReference<Exception> exReference;
     61         
     62     /**
     63      * 文件类型枚举
     64      */
     65     public static enum FileState {
     66         FState_Dir("I am director!"), // 目录
     67         FState_File("I am file!"), // 文件
     68         FState_None("I am a ghost!"), // 不存在
     69         FState_Other("I am not human!"); // 其他类型
     70 
     71         private String tag;
     72 
     73         private FileState(String tag) {
     74             this.tag = tag;
     75         }
     76 
     77         public String getTag() {
     78             return tag;
     79         }
     80 
     81         @Override
     82         public String toString() {
     83             return tag;
     84         }
     85     }
     86 
     87     private FileUtils() {
     88     }
     89 
     90     /**
     91      * 获取文件状态
     92      * 
     93      * @param path
     94      * @return
     95      */
     96     public static FileState fileState(String path) {
     97         return fileState(new File(path));
     98     }
     99 
    100     public static FileState fileState(File file) {
    101         if (!file.exists())
    102             return FileState.FState_None;
    103 
    104         if (file.isFile())
    105             return FileState.FState_File;
    106 
    107         if (file.isDirectory())
    108             return FileState.FState_Dir;
    109 
    110         return FileState.FState_Other;
    111     }
    112 
    113     /**
    114      * 创建文件夹
    115      * 
    116      * @param path
    117      * @return
    118      */
    119     public static int createDir(String path) {
    120         // int l = path.length();
    121         // if(path.charAt(l-1) == File.separatorChar){ //如果末尾是 /
    122         // 会导致创建目录失败,测试发现不会
    123         // path = path.substring(0, l-1);
    124         // }
    125 
    126         return createDir(new File(path));
    127     }
    128 
    129     public static int createDir(File file) {
    130         if (file.exists()) {
    131             if (file.isDirectory())
    132                 return SUCCESS;
    133             file.delete(); // 避免他是一个文件存在
    134         }
    135 
    136         if (file.mkdirs())
    137             return SUCCESS;
    138 
    139         return FAILED;
    140     }
    141 
    142     public static int removeDir(String path) {
    143         return removeDir(new File(path));
    144     }
    145 
    146     /**
    147      * 删除一个文件夹
    148      * 
    149      * <p>
    150      * by:yichou 2013-5-7 15:24:41
    151      * <p>
    152      * 
    153      * @param dir
    154      * @return
    155      */
    156     public static int removeDir(File dir) {
    157         if (!dir.exists())
    158             return SUCCESS;
    159 
    160         if (dir.isDirectory()) {
    161             File[] files = dir.listFiles();
    162             if (files != null) {
    163                 for (File f : files) {
    164                     if (f.isDirectory())
    165                         removeDir(f);
    166                     else
    167                         f.delete();
    168                 }
    169             }
    170         }
    171 
    172         return dir.delete() ? SUCCESS : FAILED;
    173     }
    174 
    175     /**
    176      * @see {@link #checkParentPath(File)}
    177      */
    178     public static void checkParentPath(String path) {
    179         checkParentPath(new File(path));
    180     }
    181 
    182     /**
    183      * 在打开一个文件写数据之前,先检测该文件路径的父目录是否已创建,保证能创建文件
    184      * 
    185      * @param file
    186      */
    187     public static void checkParentPath(File file) {
    188         File parent = file.getParentFile();
    189         if (parent != null && !parent.isDirectory())
    190             createDir(parent);
    191     }
    192 
    193     /**
    194      * 将一缓冲流写入文件
    195      * 
    196      * @param path
    197      *            目标文件路径
    198      * @param is
    199      *            输入流
    200      * @param isAppend
    201      *            是否追加
    202      * 
    203      * @return 成功 {@link #SUCCESS}; 失败 {@link #FAILED}
    204      */
    205     public static int streamToFile(String path, InputStream is, boolean isAppend) {
    206         return streamToFile(new File(path), is, isAppend);
    207     }
    208 
    209     public static int streamToFile(File file, InputStream is, boolean isAppend) {
    210         checkParentPath(file);
    211 
    212         FileOutputStream fos = null;
    213         try {
    214             fos = new FileOutputStream(file, isAppend);
    215             byte[] buf = new byte[BUF_SIZE];
    216             int readSize = 0;
    217 
    218             while ((readSize = is.read(buf)) != -1)
    219                 fos.write(buf, 0, readSize);
    220             fos.flush();
    221 
    222             return SUCCESS;
    223         } catch (Exception e) {
    224         } finally {
    225             try {
    226                 fos.close();
    227             } catch (Exception e) {
    228             }
    229         }
    230 
    231         return FAILED;
    232     }
    233 
    234     /**
    235      * 写字节数组到文件
    236      * 
    237      * @param file
    238      *            目标文件
    239      * @param data
    240      *            字节数组
    241      * @param offset
    242      *            偏移 {@code >=0&&<=data.length}
    243      * @param length
    244      *            长度 ==0 表示 data.length
    245      * @param isAppend
    246      *            是否追加
    247      * 
    248      * @return 成功 {@link #SUCCESS}; 失败 {@link #FAILED}
    249      */
    250     public static int bytesToFile(File file, byte[] data, int offset,
    251             int length, boolean isAppend) {
    252         checkParentPath(file);
    253 
    254         if (data == null)
    255             return FAILED;
    256 
    257         if (length <= 0)
    258             length = data.length;
    259 
    260         FileOutputStream fos = null;
    261         try {
    262             fos = new FileOutputStream(file, isAppend);
    263             fos.write(data, offset, length);
    264             fos.flush();
    265 
    266             return SUCCESS;
    267         } catch (Exception e) {
    268         } finally {
    269             try {
    270                 fos.close();
    271             } catch (Exception e) {
    272             }
    273         }
    274 
    275         return FAILED;
    276     }
    277 
    278     public static int bytesToFile(File file, byte[] data, boolean isAppend) {
    279         return bytesToFile(file, data, 0, data.length, isAppend);
    280     }
    281     
    282     public static int bytesToFile(File file, byte[] data) {
    283         return bytesToFile(file, data, 0, data.length, false);
    284     }
    285     
    286     public static int stringToFile(File file, String string) {
    287         return bytesToFile(file, string.getBytes());
    288     }
    289 
    290     /**
    291      * @see {@link #bytesToFile(File file, byte[] data, int offset, int length, boolean isAppend)}
    292      */
    293     public static int bytesToFile(String path, byte[] data, int offset,
    294             int length, boolean isAppend) {
    295         return bytesToFile(new File(path), data, offset, length, isAppend);
    296     }
    297 
    298     /**
    299      * 读取文件内容到二进制缓冲区
    300      * 
    301      * @param path
    302      *            文件路径
    303      * @param offset
    304      *            起始位置
    305      * @param length
    306      *            读取长度 ,0为全部
    307      * 
    308      * @return 失败 或 length <=0 返回null,成功返回 字节数组
    309      */
    310     public static byte[] fileToBytes(String path, int offset, int length) {
    311         return fileToBytes(new File(path), offset, length);
    312     }
    313 
    314     public static byte[] fileToBytes(File file) {
    315         return fileToBytes(file, 0, 0);
    316     }
    317 
    318     public static String fileToString(File file) {
    319         byte[] data = fileToBytes(file);
    320         return data!=null? new String(data) : null;
    321     }
    322 
    323     /**
    324      * 读取文件内容到二进制缓冲区
    325      * 
    326      * @param path
    327      *            文件路径
    328      * @param offset
    329      *            起始位置
    330      * @param length
    331      *            读取长度,==0 为全部
    332      * 
    333      * @return 失败 或 length < 0 返回null,成功返回 字节数组
    334      */
    335     public static byte[] fileToBytes(File file, int offset, int length) {
    336         if (length < 0 || !file.exists())
    337             return null;
    338 
    339         InputStream is = null;
    340         try {
    341             is = new FileInputStream(file);
    342             if (length == 0)
    343                 length = is.available();
    344             byte[] outBuf = new byte[length];
    345             is.read(outBuf, offset, length);
    346 
    347             return outBuf;
    348         } catch (Exception e) {
    349         } finally {
    350             try {
    351                 is.close();
    352             } catch (Exception e) {
    353             }
    354         }
    355 
    356         return null;
    357     }
    358 
    359     /**
    360      * 复制文件, 对于大的文件, 推荐开启一个线程来复制. 防止长时间阻塞
    361      * 
    362      * @param newPath
    363      * @param oldPath
    364      * 
    365      * @return 成功 {@link #SUCCESS}; 失败 {@link #FAILED}
    366      */
    367     public static int copyTo(String dstPath, String srcPath) {
    368         return copyTo(new File(dstPath), new File(srcPath));
    369     }
    370 
    371     public static int copyTo(File dstFile, File srcFile) {
    372         if (fileState(srcFile) != FileState.FState_File) // 源非文件
    373             return FAILED;
    374 
    375         FileInputStream fis = null;
    376         try {
    377             fis = new FileInputStream(srcFile);
    378 
    379             return streamToFile(dstFile, fis, false);
    380         } catch (Exception e) {
    381         } finally {
    382             try {
    383                 fis.close();
    384             } catch (Exception e) {
    385             }
    386         }
    387 
    388         return FAILED;
    389     }
    390 
    391     /**
    392      * @see {@link #assetToFile(Context context, String assetName, File file)}
    393      */
    394     public static int assetToFile(Context context, String assetName, String path) {
    395         return assetToFile(context, assetName, new File(path));
    396     }
    397 
    398     /**
    399      * assets 目录下的文件保存到本地文件
    400      * 
    401      * @param context
    402      * @param assetName
    403      *            assets下名字,非根目录需包含路径 a/b.xxx
    404      * @param file
    405      *            目标文件
    406      * 
    407      * @return 成功 {@link #SUCCESS}; 失败 {@link #FAILED}
    408      */
    409     public static int assetToFile(Context context, String assetName, File file) {
    410         InputStream is = null;
    411 
    412         try {
    413             is = context.getAssets().open(assetName);
    414             return streamToFile(file, is, false);
    415         } catch (Exception e) {
    416         } finally {
    417             try {
    418                 is.close();
    419             } catch (Exception e) {
    420             }
    421         }
    422 
    423         return FAILED;
    424     }
    425     
    426     public static int assetToFileIfNotExist(Context context, String assetName, File file) {
    427         InputStream is = null;
    428         try {
    429             is = context.getAssets().open(assetName);
    430             if(!checkExistBySize(file, is.available())) {
    431                 return streamToFile(file, is, false);
    432             } else {
    433                 return SUCCESS;
    434             }
    435         } catch (Exception e) {
    436         } finally {
    437             try {
    438                 is.close();
    439             } catch (Exception e) {
    440             }
    441         }
    442 
    443         return FAILED;
    444     }
    445 
    446     /**
    447      * 读取 assets 下 name 文件返回字节数组
    448      * 
    449      * @param context
    450      * @param name
    451      * @return 失败返回 Null
    452      */
    453     public static byte[] assetToBytes(Context context, String name) {
    454         InputStream is = null;
    455 
    456         try {
    457             is = context.getAssets().open(name);
    458             byte[] buf = new byte[is.available()];
    459             is.read(buf);
    460 
    461             return buf;
    462         } catch (Exception e) {
    463             setLastException(e);
    464         } finally {
    465             try {
    466                 is.close();
    467             } catch (Exception e) {
    468             }
    469         }
    470 
    471         return null;
    472     }
    473     
    474     /**
    475      * 从 Assets 文件读取文件全部,并转为字符串
    476      * 
    477      * @param manager
    478      * @param name
    479      *            文件名
    480      * @return 读取到的字符串
    481      * 
    482      * @author Yichou
    483      *         <p>
    484      *         date 2013-4-2 11:30:05
    485      */
    486     public static String assetToString(Context context, String name) {
    487         byte[] data = assetToBytes(context, name);
    488 
    489         return data!=null? new String(data) : null;
    490     }
    491 
    492     /**
    493      * 检查 assets 下是否存在某文件
    494      * 
    495      * @param am
    496      * @param name
    497      * @return
    498      */
    499     public static boolean assetExist(AssetManager am, String name) {
    500         InputStream is = null;
    501         try {
    502             is = am.open(name);
    503             return true;
    504         } catch (IOException e) {
    505         } finally {
    506             try {
    507                 is.close();
    508             } catch (Exception e) {
    509             }
    510         }
    511 
    512         return false;
    513     }
    514 
    515     /**
    516      * @return SD卡是否已挂载
    517      */
    518     public static boolean isSDMounted() {
    519         String sdState = Environment.getExternalStorageState(); // 判断sd卡是否存在
    520         return sdState.equals(android.os.Environment.MEDIA_MOUNTED);
    521     }
    522     
    523     /**
    524      * 2013-9-27 check if the sdcard mounted and can read and wirte, and remain size > {@value minRemainMB}
    525      * 
    526      * @param minRemainMB unit mb
    527      */
    528     public static boolean isSDAvailable(int minRemainMB) {
    529         if(!isSDAvailable())
    530             return false;
    531         
    532         return (getSDLeftSpace() >= minRemainMB*1024L*1024L);
    533     }
    534     
    535     /**
    536      * 2013-9-27 check if the sdcard mounted and can read and wirte
    537      */
    538     public static boolean isSDAvailable() {
    539         if(!isSDMounted())
    540             return false;
    541             
    542         File file = Environment.getExternalStorageDirectory();
    543         if(!file.canRead() || !file.canWrite())
    544             return false;
    545         
    546         return true;
    547     }
    548 
    549     /**
    550      * @return SD卡剩余容量 
    551      */
    552     public static long getSDLeftSpace() {
    553         if (isSDMounted() == false) {
    554             return 0;
    555         } else {
    556             StatFs statfs = new StatFs(
    557                     Environment.getExternalStorageDirectory() + File.separator);
    558             return (long) statfs.getAvailableBlocks()
    559                     * (long) statfs.getBlockSize();
    560         }
    561     }
    562 
    563     public static String coverSize(long size) {
    564         String s = "";
    565         if (size < 1024)
    566             s += size + "b";
    567         else if (size < 1024 * 1024) {
    568             s = String.format(Locale.getDefault(), "%.1fK", size / 1024f);
    569         } else if (size < 1024 * 1024 * 1024) {
    570             s = String.format(Locale.getDefault(), "%.1fM", size / 1024 / 1024f);
    571         } else {
    572             s = String.format(Locale.getDefault(), "%.1fG", size / 1024 / 1024 / 1024f);
    573         }
    574         
    575         return s;
    576     }
    577 
    578     public static long getROMLeft() {
    579         File data = Environment.getDataDirectory();
    580 
    581         StatFs sf = new StatFs(data.getAbsolutePath());
    582         long blockSize = sf.getBlockSize();
    583         long blockCount = sf.getBlockCount();
    584         long availCount = sf.getAvailableBlocks();
    585 
    586         Log.i("", "ROM Total:" + coverSize(blockSize * blockCount) + ", Left:"
    587                 + coverSize(availCount * blockSize));
    588 
    589         return availCount * blockSize;
    590     }
    591 
    592     /**
    593      * 获取私有目录下的文件夹绝对路径,末尾带 "/",不创建
    594      * 
    595      * @param context
    596      * @param name
    597      *            文件夹名
    598      * @return
    599      */
    600     public static String getDirPathInPrivate(Context context, String name) {
    601         return context.getDir(name, Context.MODE_PRIVATE).getAbsolutePath()
    602                 + File.separator;
    603     }
    604 
    605     /**
    606      * 或者本应用 so 存放路径
    607      * 
    608      * @param context
    609      * @return
    610      */
    611     public static String getSoPath(Context context) {
    612         return context.getApplicationInfo().dataDir + "/lib/";
    613     }
    614 
    615     public static FileLock tryFileLock(String path) {
    616         return tryFileLock(new File(path));
    617     }
    618 
    619     /**
    620      * 占用某个文件锁
    621      * 
    622      * @param file
    623      * @return
    624      */
    625     public static FileLock tryFileLock(File file) {
    626         try {
    627             checkParentPath(file); // 父目录不存在会导致创建文件锁失败
    628 
    629             FileOutputStream fos = new FileOutputStream(file);
    630             FileLock fl = fos.getChannel().tryLock();
    631             if (fl.isValid()) {
    632                 Log.i(LOG_TAG, "tryFileLock " + file + " SUC!");
    633                 return fl;
    634             }
    635         } catch (Exception e) {
    636             Log.e(LOG_TAG, "tryFileLock " + file + " FAIL! " + e.getMessage());
    637         }
    638 
    639         return null;
    640     }
    641 
    642     public static void freeFileLock(FileLock fl, File file) {
    643         if (file != null)
    644             file.delete();
    645 
    646         if (fl == null || !fl.isValid())
    647             return;
    648 
    649         try {
    650             fl.release();
    651             Log.i(LOG_TAG, "freeFileLock " + file + " SUC!");
    652         } catch (IOException e) {
    653         }
    654     }
    655 
    656     /**
    657      * 截取路径名
    658      * 
    659      * @return
    660      */
    661     public static String getPathName(String absolutePath) {
    662         int start = absolutePath.lastIndexOf(File.separator) + 1;
    663         int end = absolutePath.length();
    664         return absolutePath.substring(start, end);
    665     }
    666 
    667     /**
    668      * 重命名
    669      * 
    670      * @param oldName
    671      * @param newName
    672      * @return
    673      */
    674     public static boolean reNamePath(String oldName, String newName) {
    675         File f = new File(oldName);
    676         return f.renameTo(new File(newName));
    677     }
    678 
    679     /**
    680      * 列出root目录下所有子目录
    681      * 
    682      * @param path
    683      * @return 绝对路径
    684      */
    685     public static List<String> listPath(String root) {
    686         List<String> allDir = new ArrayList<String>();
    687         SecurityManager checker = new SecurityManager();
    688         File path = new File(root);
    689         checker.checkRead(root);
    690         if (path.isDirectory()) {
    691             for (File f : path.listFiles()) {
    692                 if (f.isDirectory()) {
    693                     allDir.add(f.getAbsolutePath());
    694                 }
    695             }
    696         }
    697         return allDir;
    698     }
    699 
    700     /**
    701      * 删除空目录
    702      * 
    703      * 返回 0代表成功 ,1 代表没有删除权限, 2代表不是空目录,3 代表未知错误
    704      * 
    705      * @return
    706      */
    707     public static int deleteBlankPath(String path) {
    708         File f = new File(path);
    709         if (!f.canWrite()) {
    710             return 1;
    711         }
    712         if (f.list() != null && f.list().length > 0) {
    713             return 2;
    714         }
    715         if (f.delete()) {
    716             return 0;
    717         }
    718         return 3;
    719     }
    720 
    721     /**
    722      * 获取SD卡的根目录,末尾带
    723      * 
    724      * @return
    725      */
    726     public static String getSDRoot() {
    727         return Environment.getExternalStorageDirectory().getAbsolutePath()
    728                 + File.separator;
    729     }
    730     
    731     /**
    732      * 2013-10-8 by yichou
    733      * 
    734      * 检查一个文件本地是否存在,通过名称,长度,如果2者都符合,返回 true,否则返回 false
    735      * 
    736      * @param file
    737      * @param size
    738      */
    739     public static boolean checkExistBySize(File file, long size) {
    740         if(!file.exists() || !file.isFile() || (file.length() != size))
    741             return false;
    742         
    743         return true;
    744     }
    745     
    746     //public static native int setPermissions(String file, int mode, int uid, int gid);
    747     public static int setPermissions(String file, int mode) {
    748         return setPermissions(file, mode, -1, -1);
    749     }
    750     
    751     private static final Class<?>[] SIG_SET_PERMISSION = 
    752         new Class<?>[]{String.class, int.class, int.class, int.class};
    753     public static int setPermissions(String file, int mode, int uid, int gid) {
    754         try {
    755             Class<?> clazz = Class.forName("android.os.FileUtils");
    756             Method method = clazz.getDeclaredMethod("setPermissions", SIG_SET_PERMISSION);
    757             method.setAccessible(true);
    758             return (Integer) method.invoke(null, file, mode, uid, gid);
    759         } catch (Exception e) {
    760         }
    761         
    762         return -1;
    763     }
    764     
    765     /**
    766      * 把 sd卡上 src 目录 链接到 私有目录 dst
    767      * 
    768      * <p>例:createLink("/mnt/sdcard/freesky", "/data/data/com.test/app_links/free")
    769      * 之后 /data/data/com.test/app_links/free -> /mnt/sdcard/freesky
    770      * 
    771      * @param src 源目录,在SD卡上
    772      * @param dst 目标路径完整
    773      * @return
    774      */
    775     public static boolean createLink(String src, String dst) {
    776         try {
    777             String command = String.format("ln -s %s %s", src, dst);
    778             Runtime runtime = Runtime.getRuntime();
    779             Process ps = runtime.exec(command);
    780             InputStream in = ps.getInputStream();
    781             
    782             int c;
    783             while ((c = in.read()) != -1) {
    784                 System.out.print(c);// 如果你不需要看输出,这行可以注销掉
    785             }
    786             
    787             in.close();
    788             ps.waitFor();
    789             
    790             return true;
    791         } catch (Exception e) {
    792         }
    793         
    794         return false;
    795     }
    796     
    797     /**
    798      * 读取输入流全部内容到字节数组
    799      * 
    800      * @param is 输入流,传入者关闭
    801      * 
    802      * @return 成功 数组,失败 null
    803      * 
    804      * 2014-9-26
    805      */
    806     public static ByteArrayBuffer streamToByteArray(InputStream is) {
    807         try {
    808             byte[] buf = new byte[256];
    809             int read = 0;
    810             ByteArrayBuffer array = new ByteArrayBuffer(1024);
    811             
    812             while ((read = is.read(buf)) != -1) {
    813                 array.append(buf, 0, read);
    814             }
    815             
    816             return array;
    817         } catch (Exception e) {
    818             setLastException(e);
    819         }
    820         
    821         return null;
    822     }
    823     
    824     /**
    825      * 读取输入流全部,转为字符串
    826      * 
    827      * @param is 输入流,传入者关闭
    828      * 
    829      * @return 成功 字串,失败 null
    830      * 
    831      * 2014-9-26
    832      */
    833     public static String streamToString(InputStream is) {
    834         ByteArrayBuffer buffer = streamToByteArray(is);
    835         if(buffer != null)
    836             return new String(buffer.buffer(), 0 , buffer.length());
    837         
    838         return null;
    839     }
    840     
    841     public static void printLastException() {
    842         Exception e = getLastException();
    843         if(e != null)
    844             e.printStackTrace();
    845     }
    846     
    847     private static void setLastException(Exception e) {
    848         exReference = new WeakReference<Exception>(e);
    849     }
    850     
    851     public static Exception getLastException() {
    852         return exReference!=null? exReference.get() : null;
    853     }
    854 }
  • 相关阅读:
    python的with语句
    flask如何实现https以及自定义证书的制作
    flask及扩展源码解读
    加密的那些事
    SQLALchemy如何查询mysql某个区间内的数据
    集群设备之间的资源共享
    pycryptodom的源码安装
    github创建项目,并提交本地文件
    响应头里的"Last-Modified"值是怎么来的?
    SQL2005 数据库——查看索引
  • 原文地址:https://www.cnblogs.com/yichouangle/p/3150603.html
Copyright © 2011-2022 走看看